7 Variables
| variable-definition
|
::= |
(private) ? variable ident = vardef |
| vardef
|
::= |
var-registers (, var-attribute) * : type-expr |
| var-registers
|
::= |
var-regs (, var-regs) ? |
| var-regs
|
::= |
(rw) ? register-bits |
| register-bits
|
::= |
ident |
| |
| |
ident[integer-ranges] |
| |
| |
ident(exprs) |
| |
| |
ident(exprs)[integer-ranges] |
| |
| |
register-bits # register-bits |
| var-attribute
|
::= |
volatile |
| |
| |
(rw) ? trigger |
| |
| |
(rw) ? trigger for trigexpr-paren |
| |
| |
(rw) ? trigger except trigexpr-paren |
| |
| |
(rw) ? serial-spec |
trigexpr-paren
|
::= |
trigexpr |
| |
| |
(trigexpr (, trigexpr) *) |
| trigexpr
|
::= |
integer-range |
| |
| |
ident |
| |
| |
boolean-literal |
A variable object provides an abstract interface to concrete register
values and specifies the semantics of the data stored in registers. A
variable definition has two optional parts: the read part and the
write part. If a variable definition does not have a read (write)
part, the register is said to be write-only (read-only), otherwise it
is referred to be read-write. The read part of a variable definition
contains the specification of four components: register-bits,
serialization, volatile and trigger. The write part specifies three
components: register-bits, serialization and trigger. The type
specification is shared for both the read and write parts.
Visibility
A variable object is introduced by the variable keyword,
followed by the identifier name of the variable being defined, and
optionally preceded by the private keyword. When a variable
is declared as private, its definition is not exported in the
generated interface and must be used at least once within the devil
program where its definition occurs. As an example, private variables
are often declared to model indexes and used in pre-actions of
indexed-registers.
private variable index = index_reg[6..5] : int(2);
Register-bits
The register-bits component specifies which bit of which register
must be concatenated in order to obtain the bit-string that forms a
device variable. The register-bits component is recursively defined as
follows: (1) a register identifier name denotes all its bits; (2) a
register identifier name followed by an integer range listed between
square brackets denotes all bits which have a position number that
appear in the list; (3) the expression
rb1 # rb2 denotes the concatenation (from left
to right) of the bits specified by rb1 and rb2. Note
that the bit number 0 of register reg is the less
significant bit. One or two register-bits must be provided in a
variable definition. The read or write part of a variable is defined
if and only if a read or write register-bits definition is provided.
If only one register-bits is provided, without a modifier, its
definition is used for both the read and write parts.
Volatile
The volatile attribute is only attached to the read part of a
variable definition. When a variable is declared as volatile, each
read operation may produce a different value. If the volatile attribute is not
provided, it defaults to non volatile.
Trigger
The trigger attribute is introduced by the trigger keyword,
optionally preceded by a read or write modifier. When a variable is
declared as trigger, each access to this variable triggers an action
or a set of actions inside the device. For example, writing twice the
same value does not produce the same result that one write operation.
As a consequence, two trigger variables that map bits of the same
register have to be grouped in a structure. When the trigger attribute
is used for the write part of a variable definition, some specific
values can sometimes cancel the trigger effect. These values can be
specified by using the attributes trigger for and trigger
except. If no trigger attribute is provided, it defaults to non
trigger. If the trigger attribute is preceded by the read or write
modifier, its definition applies only to the read or write part of the
variable definition.
Trigger for ...
When the trigger keyword is followed by the for keyword and
a list of expressions, the variable is declared as trigger only for
values that match the specified list of expressions.
Trigger except ...
When the trigger keyword is followed by the except keyword
and then by a list of expressions, the variable is declared as trigger
only for values that do not match the specified list of expressions.
Since all Devil types are finite, the set of values that cancel the
trigger effect of a variable is also finite.
7.1 Structures
| structure-definition
|
::= |
structure ident = structdef |
| structdef
|
::= |
{ (variable-definition;) + } (structure-attributes) ? |
| structure-attributes
|
::= |
structure-attribute (, structure-attribute) ? |
| structure-attribute
|
::= |
(rw) ? serial-spec |
A structure is a collection of variable definitions.
structure it_status = {
variable it_status_rst = read isr[7], volatile : status;
variable it_status_rdc = read isr[6], volatile : status;
variable it_status_cnt = read isr[5], volatile : status;
variable it_status_ovw = read isr[4], volatile : status;
variable it_status_txe = read isr[3], volatile : status;
variable it_status_rxe = read isr[2], volatile : status;
variable it_status_ptx = read isr[1], volatile : status;
variable it_status_prx = read isr[0], volatile : status;
};
When more than one register is used for defining variables contained
in the structure, the serialized as expression has to be
provided. When a structure is read or written, the serial
construction specifies in which order read or write operations are
executed. When bits of a register are mapped to variables defined in
the same structure, only one read or write operation is executed for
this register.
structure init = {
variable sngl = icw1[1] : { SINGLE => '1', CASCADED => '0'};
variable ic4 = icw1[0] : bool;
...
variable microprocessor = icw4[0] : { X8086 => '1', MCS80_85 => '0'};
} serialized as {
icw1; icw2;
if (sngl == SINGLE) icw3;
if (ic4 == true) icw4;
};
7.2 Serialization
| serial-spec
|
::= |
serialized as serial-desc |
| serial-desc
|
::= |
ident |
| |
| |
{ serial-def (; serial-def) * } |
| serial-def
|
::= |
ident |
| |
| |
if (expr) serial-desc (else serial-desc) ? |
The serialized as construct is used to specify in which order
registers of a variable or a structure have to be accessed. The
serialized keyword is optionally preceded by a read or write
modifier, in which case its definition is restricted to the read or
write part of the variable or structure being defined. If no modifier
is specified, the same serial definition is used for both the read and
write part. If no serial definition is provided, one is deduced from
the variable or structure definition. Identifiers used in the rules
serial-desc ::= ident and
serial-def ::= ident must refer to register names.
Sequence
The serial description r1; r2 specifies that register r1 must be
accessed first, followed by an access to register r2.
Conditional
The serial description
if (expr) s1 else s2
specifies conditionnal definitions (depending on runtime values of
given variables) of register sequences.
-
Example: The 8259A interrupt controller possesses
various execution modes that depend on the hardware configuration
(processor type, cascaded/single controller). The initialization of
the controller is performed by writing to configuration variables
defined over four initialization registers. In fact, the
initialization sequence varies with the actual values of
configuration variables. Additionally, three of the configuration
registers (e.g., icw2, icw3, icw4) are mapped on a single port
and their addressing is implicitly done by previously written
configuration values. The following example shows how such an
addressing mode can be specified in Devil: configuration variables
are grouped together within the init structure whose register
write operations are ordered using tests on variable values.
register icw1 = write base@0, mask '***1****' : bit[8];
register icw2 = write base@1 : bit[8];
register icw3 = write base@1 : bit[8];
register icw4 = write base@1, mask '000*****' : bit[8];
structure init = {
variable sngl = icw1[1] : { SINGLE => '1', CASCADED => '0'};
variable ic4 = icw1[0] : bool;
...
variable microprocessor = icw4[0] : { X8086 => '1', MCS80_85 => '0'};
} serialized as {
icw1; icw2;
if (sngl == SINGLE) icw3;
if (ic4 == true) icw4;
};
7.3 Variable Extensions
When all bits of a register are mapped to a single variable and when
this register can be expressed with a parameterized register, one can
write:
variable foo = reg(1) : int(8);
instead of
register r1 = reg(1);
variable foo = r1 : int(8);
This two constructions are strictly equivalent. A similar case is when
all bits (of a register) that can be mapped are mapped to a single
variable. As illustrated previously, it is possible to insert a
parameterized register application in a variable definition, but it is
also possible to extract bits of this defined register with the
[...] construct. The Devil code below illustrates this
construction:
variable foo = reg(1)[4..0] : int(4);