File: intermediate_code.txt

package info (click to toggle)
fauhdlc 20180504-2
  • links: PTS
  • area: main
  • in suites: buster
  • size: 2,956 kB
  • sloc: cpp: 23,188; ansic: 6,077; yacc: 3,764; lex: 763; makefile: 605; sh: 494; xml: 403
file content (152 lines) | stat: -rw-r--r-- 6,668 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
===========================================
= FAUHdlc - intermediate code description =
===========================================

Code containers
---------------

Code containers are the basic containers in which all intermediate code
resides. A code container can be seen as a function container, defining
one function.

Inside a code container, an arbitrary number of code containers can be 
nested.

A code container can have four segments, all of which are optional:

* type definitions segment:
  Types, e.g. arrays and records are stored here. These ar valid in
  the entire code container and subcontainers.

* transfer segment:
  The transfer segment defines function call parameters, that can be
  passed into the code container (or out of it as well, e.g. for OUT
  parameters).

* stack segment:
  The stack segment defines local variables of the code container.

* text segment:
  The text segment contains the function definition, i.e. a number of
  opcodes defining the behaviour of the container.
  The text segment must either end with a return opcode, or must be 
  implemented as an infinite loop.


Visibility
----------

In a code container, the definitions of each surrounding code container are
visible. This means that types and data from surrounding code container can
be accessed in the text segment, and call statements may call surrounding
code containers.
Furthermore, the direct child containers are visible for calls as well, hence
allowing to call one layer downwards. Data/transfer segments of nested
containers are however not visible to the surrounding container. As an
exception, the transfer segment may be made visible via BEGINTRANS, ENDTRANS
and SETPARAM opcodes, in order to pass parameters to function calls.
Labels found in text segment are only visible within this text segment, as
jump/branch statements must not cross the boundary of one text segment.


Basic simulation structure
--------------------------

The basic simulation structure should define one top container, which doesn't
have a transfer segment (i.e. no parameters) and the text segment of this
top container should initialize the simulation by creating signals, connecting
drivers and creating processes. Usually, sub-containers holding initialization
functionality for sub-components will be called in this phase in order to 
create a hierarchy. The call to the top container's text segment must always
return, and may not be an infinite loop. This allows that process handling
needn't be present during this initialization phase. As an additional
restriction, no process affecting statements (SUSPEND, WAKEON, WAKEAT) may
be present in this phase.

The entity that should get simulated, must be a direct child of the top
container, and must not need any parameters from outside, i.e. it must not
have a transfer segment.


Opcodes
-------
The definition of opcodes can be found in intermediate/opcodes/*, where the
behaviour of the opcodes is described as well.
An example implementation is interpreter/*. In case of doubt, the example 
implementation denotes the desired effect.

Operands
--------
All operands denote values of at least 64 bit, or at least the size of a 
pointer on the desired platform, whichever is more.
Specifications of all operands can be found in intermediate/operands.
Operands in the form of References to code containers (SETPARAM, CALL, etc.),
labels and type elements must be resolved according to the visibility rules 
above. It is an error, if any ambiguity arises from resolution, or if the 
resolution fails. The types universal_int, and universal_real must be 
predefined to denote basic signed integer/basic floating point types.
Other names may be predefined as well, as long as this will not result in
ambiguity. (TODO: simulation_abort?)


Annotations
-----------

Annotations are basically comments, and can be placed at any place where
whitespace is allowed in the intermediate code.
An annotation starts with a "{" character, and ends at the next closing curly
bracket "}" (unless it is specified in a specification string, see below).

Annotations in the form of 
	name="string" 
or 
	name=123

(cf. scanner of example implementation for lexical definition) denotes a 
annotation specification, that can have an implementation dependant effect.
An implementation may restrict annotation specifications to be present only
at limited places inside the intermediate code.


===================
= Foreign signals =
===================

The treatment of foreign signals is implementation spefic. However, since I 
constantly forget the reason why fauhdli does it that way, here are some
notes how it is done:

For a foreign signal that is being read by VHDL, a driver is created which
will get connected via glue_vhdl.
For a foreign signal that is written to from VHDL, a foreign driver is 
created. The value of this driver is only passed later on 
via glue-vhdl, after all non-foreign signals have been handled.

The reason is the following:
  Propagating the value via a glue-vhdl means to basically execute a 
  foreign process. This must happen at a discrete step in the 
  simulation cycle to guarantuee correctness:
  First, all drivers update signal values. Next, all processes run, where
  the previously changed signals can result in driver updates. In the same
  discrete point in time, a signal update may never result in a signal
  update, and a driver update may never result in a driver update.

Resolution functions:
It's not possible to use VHDL resolution functions for foreign signals,
as the aforementioned semantics imply that actually resolution is performed
by the foreign signal (Consider a foreign signal that is read from and 
written to in VHDL, it has one or more foreign drivers, which forwards 
VHDL changes to glue_vhdl and one hidden driver, which actually represents
the signal value).

If resolution was possible by taking all drivers, resolve them to the signal
value and forward the result, then a lock to a dominant value could occur:
As an example, both the foreign signal and the VHDL counterpart have all
driving values set to 'Z', so all signal values are also 'Z'. If now VHDL
sets one driver to '0', this one will be dominant. It's passed on to
the foreign signal which will also set it's value to '0', like the VHDL value.
As a result the hidden foreign input driver for the VHDL signal will have the
driving value '0'. Setting the driver back to 'Z' which caused the transition
to '0' will still locally resolve to '0', due to the other "segment" of the 
bus having been forced to '0'. And that's why it all comes down to using 
only the foreign side for resolutions.