custom
behavior
Custom fields allow you to describe the behavior of the field using
VHDL snippits. This is very powerful, but not for the faint-hearted. Also,
with great power comes great responsibility: you can easily break
vhdmmio
's generated entities by writing incorrect code. Furthermore,
vhdmmio
's internal template engine can execute arbitrary Python code, so
building a register file from an untrusted source that contains custom
fields is a big security risk. Therefore, vhdmmio
requires you to specify
--trusted
on the command line to be able to use these fields.
vhdmmio
implements custom fields in a tightly integrated way; it doesn't
just copypaste your code directly into the generated output file. Instead,
it lets you write code snippits for various actions, that get executed
within the generated VHDL process at the appropriate times. This is exactly
how the predefined field behaviors are programmed within vhdmmio
- custom
fields basically just allow direct access to this API. The big advantage of
this is that you still don't need to worry about things like the AXI4L
interface or interoperability with any other fields in the logical register
or register file, but it does mean that you need to specify quite some
metadata to vhdmmio
to make things work, and that you need a good
understanding of how vhdmmio
's generated VHDL code works.
Specifying field behavior
The behavior of a field needs to be specified in two different ways. The
first is obvious: (templates for) the VHDL code that gets inserted into
the generated entity. The second consists of a more abstract description of
the field's capabilities and characteristics, which vhdmmio
uses to
generate the glue logic between the fields and the AXI4L bus, the header
files for accessing the registers, and in some cases the documentation.
vhdmmio
allows you to specify the following VHDL code blocks. Note that
vhdmmio
's generated entity is entirely synchronous and uses variables to
maintain state, so the order of the blocks is important; the blocks are
listed in the order in which they are executed.
pre-access
: this code block is executed every clock cycle before the AXI4L bus access is handled.read
: this code block is executed when the bus is trying to read the field, and the bus interface logic is ready for the field's response.read-lookahead
: likeread
, but the bus interface logic is not yet ready for the response.read-request
: this code block is executed when a read request for the field is available, regardless of whether the bus interface logic is ready for the response.read-response
: this code block is executed when one of the above blocks deferred a read, and the bus interface logic is ready for the response.write
,write-lookahead
,write-request
, andwrite-response
: the write analogues for the above.post-access
: this code block is executed every clock cycle after the AXI4L bus access is handled.
For more information about each block, refer to the documentation below.
vhdmmio
needs the following metadata in addition to the code blocks:
- whether the
read
/read-request
/write
/write-request
code blocks can block a bus access (that is, delay the bus response); - whether reads/writes are volatile (that is, the result of accessing the field once differs from accessing it twice);
- how the field can be accessed without affecting its state, if this is possible at all (used when a different field within a single logical register is to be accessed).
This metadata is also used to determine whether two fields can coexist within the same register. The rules are:
- blocking fields cannot be combined with other blocking fields;
- blocking fields cannot be combined with volatile fields;
- fields that support multiple outstanding requests cannot be combined with any other field.
Template syntax
vhdmmio
uses a custom template engine to preprocess VHDL code. The code
blocks specified in for custom fields pass through this template engine as
well.
The template engine is controlled using three characters that are normally
not used in VHDL: |
, $
, and @
. Each corresponds to a different phase
of the template logic.
Indentation stripping phase (|
)
In this phase, all whitespace at the start of a line followed by a |
is
stripped. This allows you to indent your template however you want, without
this indentation appearing in the generated code block. If you need to
start a line with an |
for some reason, simply prefix a second |
; the
substitution is only done once per line.
Substitution phase ($
)
This is the most important phase, dealing with substitutions and conditionals, similar to the functionality of the C preprocessor.
The template engine supports both inline and single-line directives.
Single-line directives start with a $
, followed by any amount of
whitespace, followed by a command, followed by some arguments depending on
the command. There should not be any whitespace before the $
sign; to
indent such commands you need to use the |
syntax described above.
Conversely, inline directives both start and end with a $
and can be
anywhere on a line. To insert a $
in the output, use $$
.
The following single-line directives are available:
$if <python-expression>
: opens a conditional block based on an expression evaluated by Python.$else
: opens theelse
block for a previous$if
.$endif
: terminates an$if
or$else
block.$block <name>
: defines/adds code to a named block. This is like#define
in the C preprocessor, but multiline.$end_block
: terminates a block definition.$<name>
: inserts a previously defined block. If there are no spaces between the$
and the name, no indentation is added; otherwise one space plus the spacing between the$
and the name is added for each line.
Inline directives are always evaluated directly by Python. The result of the Python expression is cast to a string and inserted in place of the directive.
The custom field logic makes some variables available within these Python expressions. These are:
i
: the field index within the field descriptor, orNone
if the field descriptor describes only one field (that is,repeat
is not specified).s
: structure containing the VHDL identifiers for each signal/variable specified through theinterfaces
configuration key.
Additional variables may be available depending on the block.
Comment and wrapping phase (@
)
This phase deals with generating aestetically pleasing, properly line-wrapped code regardless of the current indentation depth, which is impossible (or at least hard) to know when the template is written. The following constructs are available:
- Lines that start with
@
are treated as comments. Subsequent comment lines are treated as markdown, and are rewrapped as such to get to column 80. - Lines that start with
@@
are also treated as comments, but are not rewrapped. @@@
at the start of a line maps to a single@
in the output.- Inline
@
are replaced with either a space or a newline followed by appropriate indentation, depending on line wrapping. - Inline
@@
maps to a single@
in the output.
Configuration keys
This structure supports the following configuration keys.
interfaces
This key specifies the interfaces and state variables that this field uses.
This key must be set to a list of dictionaries, of which the structure is defined here.
This key is optional. Not specifying it is equivalent to specifying an empty list.
pre-access
This key specifies the VHDL template for the code block that is executed each cycle before the bus is accessed. This is usually used for initializing non-state variables and for connecting input signals to the internal state.
The following values are supported:
-
null
(default): no pre-access code block is needed. -
a string: insert the given pre-access code block.
This key is optional unless required by context. If not specified, the default value (null
) is used.
read
This key specifies the VHDL template for the code block that is executed when the bus is attempting to read the field while also ready for the response.
The template must perform one of the following actions to respond to the bus.
- Set the
$ack$
boolean variable totrue
, and the$data$
variable to the read result.$data$
is appropriately sliced to represent the shape of the field; that is, it is sliced to anstd_logic
for scalar fields and to an appropriately sizedstd_logic_vector
for vector fields. - Set the
$nack$
boolean variable totrue
to respond with a slave error. - Set the
$block$
boolean variable totrue
to stall. In this case, the block will be executed again the next cycle. Theread-can-block
key must be set in order to use this variable. - Set the
$defer$
boolean variable totrue
to defer. In this case, the request logic will accept the bus request and send subsequent requests toread-lookahead
blocks, while the response logic will start calling theread-response
block to get the response. Such aread-response
block must be specified in order to use this variable. - Nothing: the bus behaves as if the field does not exist. If there are no other fields in the addressed register, a decode error is returned.
In addition to the field's interfaces, the template can use the following variables:
$prot$
: theprot
field associated with the request as a 3-bitstd_logic_vector
.$addr$
: the incoming bus address for the request as a 32-bitstd_logic_vector
.$sub$
: the subaddress for the for the request as anstd_logic_vector
slice, of which the width depends on the field's subaddress configuration.
The following values are supported:
-
null
(default): no read code block is needed. -
a string: insert the given read code block.
This key is optional unless required by context. If not specified, the default value (null
) is used.
read-lookahead
This key specifies the VHDL template for the code block that is executed when the bus is attempting to read the field, but the response logic is not yet ready for the response. This can be used to initiate long transactions a few cycles earlier, or can be used in conjunction with multiple outstanding request support.
The template must perform one of the following actions to respond to the bus.
- Set the
$defer$
boolean variable totrue
to defer. In this case, the request logic will accept the bus request and send subsequent requests toread-lookahead
blocks, while the response logic will start calling theread-response
block to get the response. Such aread-response
block must be specified in order to use this variable. - Nothing: the bus interface logic will continue to call this block
in subsequent cycles until the response logic is ready, at which
point the regular
read
block is executed instead.
In addition to the field's interfaces, the template can use the following variables:
$prot$
: theprot
field associated with the request as a 3-bitstd_logic_vector
.$addr$
: the incoming bus address for the request as a 32-bitstd_logic_vector
.$sub$
: the subaddress for the for the request as anstd_logic_vector
slice, of which the width depends on the field's subaddress configuration.
The following values are supported:
-
null
(default): no read lookahead code block is needed. -
a string: insert the given read lookahead code block.
This key is optional unless required by context. If not specified, the default value (null
) is used.
read-request
This key specifies the VHDL template for the code block that is
executed when the bus is attempting to read the field, regardless of
whether the bus is ready for the response. It is up to the code block
itself to check this where needed, using the $resp_ready$
boolean
variable. Depending on this boolean, the block must respond to the
access as described in the documentation for the read
and
read-lookahead
blocks.
While uncommon, both read
/read-lookahead
and read-request
may be
specified at the same time. In this case, read
/read-lookahead
is
executed before read-request
.
The following values are supported:
-
null
(default): no read request code block is needed. -
a string: insert the given read request code block.
This key is optional unless required by context. If not specified, the default value (null
) is used.
read-response
This key specifies the VHDL template for the code block that is executed when the bus is ready for the response of a previously deferred read.
The template must perform one of the following actions to respond to the bus.
- Set the
$ack$
boolean variable totrue
, and the$data$
variable to the read result.$data$
is appropriately sliced to represent the shape of the field; that is, it is sliced to anstd_logic
for scalar fields and to an appropriately sizedstd_logic_vector
for vector fields. - Set the
$nack$
boolean variable totrue
to respond with a slave error. - Set the
$block$
boolean variable totrue
to stall. In this case, the block will be executed again the next cycle. Theread-can-block
key must be set in order to use this variable. - Nothing: the bus behaves as if the field does not exist. If there are no other fields in the addressed register, a decode error is returned.
The following values are supported:
-
null
(default): no read response code block is needed; multiple outstandingread requests are not supported for this field. -
a string: insert the given read response code block; this field supports multiple outstanding requests in read mode.
This key is optional unless required by context. If not specified, the default value (null
) is used.
write
This key specifies the VHDL template for the code block that is executed when the bus is attempting to write to the field while also ready for the response.
The template must perform one of the following actions to respond to the bus.
- Set the
$ack$
boolean variable totrue
to respond with an acknowledgement. - Set the
$nack$
boolean variable totrue
to respond with a slave error. - Set the
$block$
boolean variable totrue
to stall. In this case, the block will be executed again the next cycle. Thewrite-can-block
key must be set in order to use this variable. - Set the
$defer$
boolean variable totrue
to defer. In this case, the request logic will accept the bus request and send subsequent requests towrite-lookahead
blocks, while the response logic will start calling thewrite-response
block to get the response. Such awrite-response
block must be specified in order to use this variable. - Nothing: the bus behaves as if the field does not exist. If there are no other fields in the addressed register, a decode error is returned.
In addition to the field's interfaces, the template can use the following variables:
$data$
: the write data for the request, appropriately sliced for the shape of the field. That is,$data$
behaves like anstd_logic
for scalar fields, and like an appropriately sizedstd_logic_vector
for vector fields.$strb$
: the write strobe for the request. The variable has the same shape as$data$
and thus behaves as a bit strobe, even though AXI4L's strobe signal is byte-oriented. If a bit in$strb$
is zero, the respective$data$
bit is guaranteed to also be zero, and should not be written.$prot$
: theprot
field associated with the request as a 3-bitstd_logic_vector
.$addr$
: the incoming bus address for the request as a 32-bitstd_logic_vector
.$sub$
: the subaddress for the for the request as anstd_logic_vector
slice, of which the width depends on the field's subaddress configuration.
The following values are supported:
-
null
(default): no write code block is needed. -
a string: insert the given write code block.
This key is optional unless required by context. If not specified, the default value (null
) is used.
write-lookahead
This key specifies the VHDL template for the code block that is executed when the bus is attempting to write to the field, but the response logic is not yet ready for the response. This can be used to initiate long transactions a few cycles earlier, or can be used in conjunction with multiple outstanding request support.
The template must perform one of the following actions to respond to the bus.
- Set the
$defer$
boolean variable totrue
to defer. In this case, the request logic will accept the bus request and send subsequent requests towrite-lookahead
blocks, while the response logic will start calling thewrite-response
block to get the response. Such awrite-response
block must be specified in order to use this variable. - Nothing: the bus interface logic will continue to call this block
in subsequent cycles until the response logic is ready, at which
point the regular
write
block is executed instead.
In addition to the field's interfaces, the template can use the following variables:
$data$
: the write data for the request, appropriately sliced for the shape of the field. That is,$data$
behaves like anstd_logic
for scalar fields, and like an appropriately sizedstd_logic_vector
for vector fields.$strb$
: the write strobe for the request. The variable has the same shape as$data$
and thus behaves as a bit strobe, even though AXI4L's strobe signal is byte-oriented. If a bit in$strb$
is zero, the respective$data$
bit is guaranteed to also be zero, and should not be written.$prot$
: theprot
field associated with the request as a 3-bitstd_logic_vector
.$addr$
: the incoming bus address for the request as a 32-bitstd_logic_vector
.$sub$
: the subaddress for the for the request as anstd_logic_vector
slice, of which the width depends on the field's subaddress configuration.
The following values are supported:
-
null
(default): no write lookahead code block is needed. -
a string: insert the given write lookahead code block.
This key is optional unless required by context. If not specified, the default value (null
) is used.
write-request
This key specifies the VHDL template for the code block that is
executed when the bus is attempting to write to the field, regardless
of whether the bus is ready for the response. It is up to the code
block itself to check this where needed, using the $resp_ready$
boolean variable. Depending on this boolean, the block must respond to
the access as described in the documentation for the write
and
write-lookahead
blocks.
While uncommon, both write
/write-lookahead
and write-request
may
be specified at the same time. In this case, write
/write-lookahead
is executed before write-request
.
The following values are supported:
-
null
(default): no write request code block is needed. -
a string: insert the given write request code block.
This key is optional unless required by context. If not specified, the default value (null
) is used.
write-response
This key specifies the VHDL template for the code block that is executed when the bus is ready for the response of a previously deferred write.
The template must perform one of the following actions to respond to the bus.
- Set the
$ack$
boolean variable totrue
to respond with an acknowledgement. - Set the
$nack$
boolean variable totrue
to respond with a slave error. - Set the
$block$
boolean variable totrue
to stall. In this case, the block will be executed again the next cycle. Thewrite-can-block
key must be set in order to use this variable. - Nothing: the bus behaves as if the field does not exist. If there are no other fields in the addressed register, a decode error is returned.
The following values are supported:
-
null
(default): no write response code block is needed; multiple outstandingwrite requests are not supported for this field. -
a string: insert the given write response code block; this field supports multiple outstanding requests in write mode.
This key is optional unless required by context. If not specified, the default value (null
) is used.
post-access
This key specifies the VHDL template for the code block that is executed each cycle after the bus is accessed. This is usually used for connecting the internal state to output signals.
The following values are supported:
-
null
(default): no pre-access code block is needed. -
a string: insert the given pre-access code block.
This key is optional unless required by context. If not specified, the default value (null
) is used.
read-can-block
This key specifies whether the field can block read accesses.
The following values are supported:
-
no
(default): this field cannot block reads. -
yes
: this field can block reads.
This key is optional unless required by context. If not specified, the default value (no
) is used.
read-volatile
This key specifies whether the field is volatile in read mode.
vhdmmio
defines volatility as the read result or side effects being
different when the field is accessed once versus when it is accessed
more than once.
The following values are supported:
-
no
(default): this field is not read-volatile. -
yes
: this field is read-volatile.
This key is optional unless required by context. If not specified, the default value (no
) is used.
read-has-side-effects
This key specifies whether reads have side effects.
The following values are supported:
-
no
(default): reading this field does not have side effects. -
yes
: reading this field may have side effects.
This key is optional unless required by context. If not specified, the default value (no
) is used.
read-write-related
This key specifies whether the read data carries the same
significance as the write data. If this is not set, vhdmmio
will
never attempt to read-modify-write this field.
The following values are supported:
-
no
(default): the read and write data of this field are not related. -
yes
: the read and write data of this field are related.
This key is optional unless required by context. If not specified, the default value (no
) is used.
write-can-block
This key specifies whether the field can block write accesses.
The following values are supported:
-
no
(default): this field cannot block writes. -
yes
: this field can block writes.
This key is optional unless required by context. If not specified, the default value (no
) is used.
write-volatile
This key specifies whether the field is volatile in write mode.
vhdmmio
defines volatility as the write result or side effects being
different when the field is accessed once versus when it is accessed
more than once.
The following values are supported:
-
no
(default): this field is not write-volatile. -
yes
: this field is write-volatile.
This key is optional unless required by context. If not specified, the default value (no
) is used.
write-no-op
This key specifies what strategies vhdmmio
can use to write the
logical register that this field resides in without affecting this
field, if this is possible at all.
The following values are supported:
-
never
(default): it is impossible to write to this field without side effects. -
zero
: writing zero to this field never has any side effects (flags). -
current
: writing the current value never has any side effects. The current value must either be known from context, or, ifread-write-related
is set, can be read from the field first (read-modify-write). -
mask
: this field can only be masked out using the AXI4L byte strobe signal. -
current-or-mask
: this field can be masked out by writing the current value, or through the AXI4L byte strobe signal. -
always
: anything goes: writing to this field never has any side effects thatvhdmmio
has to concern itself with.
This key is optional unless required by context. If not specified, the default value (never
) is used.