3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-04-26 18:45:34 +00:00

Tidy/reflow some things

This commit is contained in:
Krystine Sherwin 2023-08-03 10:23:39 +12:00
parent 2c75b103d6
commit 4b40372446
No known key found for this signature in database
10 changed files with 433 additions and 443 deletions

View file

@ -19,54 +19,53 @@ Transforming Verilog to AST
---------------------------
The Verilog frontend converts the Verilog sources to an internal AST
representation that closely resembles the structure of the original
Verilog code. The Verilog frontend consists of three components, the
Preprocessor, the Lexer and the Parser.
representation that closely resembles the structure of the original Verilog
code. The Verilog frontend consists of three components, the Preprocessor, the
Lexer and the Parser.
The source code to the Verilog frontend can be found in
frontends/verilog/ in the Yosys source tree.
The source code to the Verilog frontend can be found in ``frontends/verilog/``
in the Yosys source tree.
The Verilog preprocessor
~~~~~~~~~~~~~~~~~~~~~~~~
The Verilog preprocessor scans over the Verilog source code and
interprets some of the Verilog compiler directives such as
:literal:`\`include`, :literal:`\`define` and :literal:`\`ifdef`.
The Verilog preprocessor scans over the Verilog source code and interprets some
of the Verilog compiler directives such as :literal:`\`include`,
:literal:`\`define` and :literal:`\`ifdef`.
It is implemented as a C++ function that is passed a file descriptor as
input and returns the pre-processed Verilog code as a ``std::string``.
It is implemented as a C++ function that is passed a file descriptor as input
and returns the pre-processed Verilog code as a ``std::string``.
The source code to the Verilog Preprocessor can be found in
frontends/verilog/preproc.cc in the Yosys source tree.
``frontends/verilog/preproc.cc`` in the Yosys source tree.
The Verilog lexer
~~~~~~~~~~~~~~~~~
The Verilog Lexer is written using the lexer generator flex . Its source
code can be found in frontends/verilog/verilog_lexer.l in the Yosys
source tree. The lexer does little more than identifying all keywords
and literals recognised by the Yosys Verilog frontend.
The Verilog Lexer is written using the lexer generator flex. Its source code
can be found in ``frontends/verilog/verilog_lexer.l`` in the Yosys source tree.
The lexer does little more than identifying all keywords and literals recognised
by the Yosys Verilog frontend.
The lexer keeps track of the current location in the Verilog source code
using some global variables. These variables are used by the constructor
of AST nodes to annotate each node with the source code location it
originated from.
Finally the lexer identifies and handles special comments such as
"``// synopsys translate_off``" and "``// synopsys full_case``". (It is
recommended to use :literal:`\`ifdef` constructs instead of the
Synsopsys translate_on/off comments and attributes such as
``(* full_case *)`` over "``// synopsys full_case``" whenever possible.)
Finally the lexer identifies and handles special comments such as "``// synopsys
translate_off``" and "``// synopsys full_case``". (It is recommended to use
:literal:`\`ifdef` constructs instead of the Synsopsys translate_on/off comments
and attributes such as ``(* full_case *)`` over "``// synopsys full_case``"
whenever possible.)
The Verilog parser
~~~~~~~~~~~~~~~~~~
The Verilog Parser is written using the parser generator bison . Its
source code can be found in frontends/verilog/verilog_parser.y in the
Yosys source tree.
The Verilog Parser is written using the parser generator bison. Its source code
can be found in ``frontends/verilog/verilog_parser.y`` in the Yosys source tree.
It generates an AST using the ``AST::AstNode`` data structure defined in
frontends/ast/ast.h. An ``AST::AstNode`` object has the following
``frontends/ast/ast.h``. An ``AST::AstNode`` object has the following
properties:
.. list-table:: AST node types with their corresponding Verilog constructs.
@ -152,35 +151,32 @@ properties:
- | The node type
| This enum (``AST::AstNodeType``) specifies the role of the node.
:numref:`Table %s <tab:Verilog_AstNodeType>`
contains a list of all node types.
:numref:`Table %s <tab:Verilog_AstNodeType>` contains a list of all node
types.
- | The child nodes
| This is a list of pointers to all children in the abstract syntax
tree.
| This is a list of pointers to all children in the abstract syntax tree.
- | Attributes
| As almost every AST node might have Verilog attributes assigned to
it, the ``AST::AstNode`` has direct support for attributes. Note
that the attribute values are again AST nodes.
| As almost every AST node might have Verilog attributes assigned to it, the
``AST::AstNode`` has direct support for attributes. Note that the attribute
values are again AST nodes.
- | Node content
| Each node might have additional content data. A series of member
variables exist to hold such data. For example the member
``std::string str`` can hold a string value and is used e.g. in the
AST_IDENTIFIER node type to store the identifier name.
| Each node might have additional content data. A series of member variables
exist to hold such data. For example the member ``std::string str`` can
hold a string value and is used e.g. in the ``AST_IDENTIFIER`` node type to
store the identifier name.
- | Source code location
| Each ``AST::AstNode`` is automatically annotated with the current
source code location by the ``AST::AstNode`` constructor. It is
stored in the ``std::string filename`` and ``int linenum`` member
variables.
| Each ``AST::AstNode`` is automatically annotated with the current source
code location by the ``AST::AstNode`` constructor. It is stored in the
``std::string filename`` and ``int linenum`` member variables.
The ``AST::AstNode`` constructor can be called with up to two child
nodes that are automatically added to the list of child nodes for the
new object. This simplifies the creation of AST nodes for simple
expressions a bit. For example the bison code for parsing
multiplications:
The ``AST::AstNode`` constructor can be called with up to two child nodes that
are automatically added to the list of child nodes for the new object. This
simplifies the creation of AST nodes for simple expressions a bit. For example
the bison code for parsing multiplications:
.. code:: none
:number-lines:
@ -190,49 +186,50 @@ multiplications:
append_attr($$, $3);
} |
The generated AST data structure is then passed directly to the AST
frontend that performs the actual conversion to RTLIL.
The generated AST data structure is then passed directly to the AST frontend
that performs the actual conversion to RTLIL.
Note that the Yosys command ``read_verilog`` provides the options ``-yydebug``
and ``-dump_ast`` that can be used to print the parse tree or abstract
syntax tree respectively.
and ``-dump_ast`` that can be used to print the parse tree or abstract syntax
tree respectively.
Transforming AST to RTLIL
-------------------------
The AST Frontend converts a set of modules in AST representation to
modules in RTLIL representation and adds them to the current design.
This is done in two steps: simplification and RTLIL generation.
The AST Frontend converts a set of modules in AST representation to modules in
RTLIL representation and adds them to the current design. This is done in two
steps: simplification and RTLIL generation.
The source code to the AST frontend can be found in ``frontends/ast/`` in
the Yosys source tree.
The source code to the AST frontend can be found in ``frontends/ast/`` in the
Yosys source tree.
AST simplification
~~~~~~~~~~~~~~~~~~
A full-featured AST is too complex to be transformed into RTLIL
directly. Therefore it must first be brought into a simpler form. This
is done by calling the ``AST::AstNode::simplify()`` method of all
AST_MODULE nodes in the AST. This initiates a recursive process that
performs the following transformations on the AST data structure:
A full-featured AST is too complex to be transformed into RTLIL directly.
Therefore it must first be brought into a simpler form. This is done by calling
the ``AST::AstNode::simplify()`` method of all ``AST_MODULE`` nodes in the AST.
This initiates a recursive process that performs the following transformations
on the AST data structure:
- Inline all task and function calls.
- Evaluate all ``generate``-statements and unroll all ``for``-loops.
- Perform const folding where it is necessary (e.g. in the value part
of AST_PARAMETER, AST_LOCALPARAM, AST_PARASET and AST_RANGE nodes).
- Perform const folding where it is necessary (e.g. in the value part of
``AST_PARAMETER``, ``AST_LOCALPARAM``, ``AST_PARASET`` and ``AST_RANGE``
nodes).
- Replace AST_PRIMITIVE nodes with appropriate AST_ASSIGN nodes.
- Replace ``AST_PRIMITIVE`` nodes with appropriate ``AST_ASSIGN`` nodes.
- Replace dynamic bit ranges in the left-hand-side of assignments with
AST_CASE nodes with AST_COND children for each possible case.
``AST_CASE`` nodes with ``AST_COND`` children for each possible case.
- Detect array access patterns that are too complicated for the
RTLIL::Memory abstraction and replace them with a set of signals and
``RTLIL::Memory`` abstraction and replace them with a set of signals and
cases for all reads and/or writes.
- Otherwise replace array accesses with AST_MEMRD and AST_MEMWR nodes.
- Otherwise replace array accesses with ``AST_MEMRD`` and ``AST_MEMWR`` nodes.
In addition to these transformations, the simplifier also annotates the
AST with additional information that is needed for the RTLIL generator,
@ -242,70 +239,66 @@ namely:
folded but (when a constant value is found) are also written to
member variables in the AST_RANGE node.
- All identifiers are resolved and all AST_IDENTIFIER nodes are
annotated with a pointer to the AST node that contains the
declaration of the identifier. If no declaration has been found, an
AST_AUTOWIRE node is created and used for the annotation.
- All identifiers are resolved and all ``AST_IDENTIFIER`` nodes are annotated
with a pointer to the AST node that contains the declaration of the
identifier. If no declaration has been found, an ``AST_AUTOWIRE`` node is
created and used for the annotation.
This produces an AST that is fairly easy to convert to the RTLIL format.
Generating RTLIL
~~~~~~~~~~~~~~~~
After AST simplification, the ``AST::AstNode::genRTLIL()`` method of
each AST_MODULE node in the AST is called. This initiates a recursive
process that generates equivalent RTLIL data for the AST data.
After AST simplification, the ``AST::AstNode::genRTLIL()`` method of each
``AST_MODULE`` node in the AST is called. This initiates a recursive process
that generates equivalent RTLIL data for the AST data.
The ``AST::AstNode::genRTLIL()`` method returns an ``RTLIL::SigSpec``
structure. For nodes that represent expressions (operators, constants,
signals, etc.), the cells needed to implement the calculation described
by the expression are created and the resulting signal is returned. That
way it is easy to generate the circuits for large expressions using
depth-first recursion. For nodes that do not represent an expression
(such as AST_CELL), the corresponding circuit is generated and an empty
``RTLIL::SigSpec`` is returned.
The ``AST::AstNode::genRTLIL()`` method returns an ``RTLIL::SigSpec`` structure.
For nodes that represent expressions (operators, constants, signals, etc.), the
cells needed to implement the calculation described by the expression are
created and the resulting signal is returned. That way it is easy to generate
the circuits for large expressions using depth-first recursion. For nodes that
do not represent an expression (such as ``AST_CELL``), the corresponding circuit
is generated and an empty ``RTLIL::SigSpec`` is returned.
Synthesizing Verilog always blocks
--------------------------------------
For behavioural Verilog code (code utilizing ``always``- and
``initial``-blocks) it is necessary to also generate ``RTLIL::Process``
objects. This is done in the following way:
For behavioural Verilog code (code utilizing ``always``- and ``initial``-blocks)
it is necessary to also generate ``RTLIL::Process`` objects. This is done in the
following way:
Whenever ``AST::AstNode::genRTLIL()`` encounters an ``always``- or
``initial``-block, it creates an instance of
``AST_INTERNAL::ProcessGenerator``. This object then generates the
``RTLIL::Process`` object for the block. It also calls
``AST::AstNode::genRTLIL()`` for all right-hand-side expressions
contained within the block.
``initial``-block, it creates an instance of ``AST_INTERNAL::ProcessGenerator``.
This object then generates the ``RTLIL::Process`` object for the block. It also
calls ``AST::AstNode::genRTLIL()`` for all right-hand-side expressions contained
within the block.
First the ``AST_INTERNAL::ProcessGenerator`` creates a list of all
signals assigned within the block. It then creates a set of temporary
signals using the naming scheme $\ <number> \\\ <original_name> for each
of the assigned signals.
First the ``AST_INTERNAL::ProcessGenerator`` creates a list of all signals
assigned within the block. It then creates a set of temporary signals using the
naming scheme ``$ <number> \ <original_name>`` for each of the assigned signals.
Then an ``RTLIL::Process`` is created that assigns all intermediate
values for each left-hand-side signal to the temporary signal in its
Then an ``RTLIL::Process`` is created that assigns all intermediate values for
each left-hand-side signal to the temporary signal in its
``RTLIL::CaseRule``/``RTLIL::SwitchRule`` tree.
Finally a ``RTLIL::SyncRule`` is created for the ``RTLIL::Process`` that
assigns the temporary signals for the final values to the actual
signals.
Finally a ``RTLIL::SyncRule`` is created for the ``RTLIL::Process`` that assigns
the temporary signals for the final values to the actual signals.
A process may also contain memory writes. A ``RTLIL::MemWriteAction`` is
created for each of them.
A process may also contain memory writes. A ``RTLIL::MemWriteAction`` is created
for each of them.
Calls to ``AST::AstNode::genRTLIL()`` are generated for right hand sides
as needed. When blocking assignments are used,
``AST::AstNode::genRTLIL()`` is configured using global variables to use
the temporary signals that hold the correct intermediate values whenever
one of the previously assigned signals is used in an expression.
Calls to ``AST::AstNode::genRTLIL()`` are generated for right hand sides as
needed. When blocking assignments are used, ``AST::AstNode::genRTLIL()`` is
configured using global variables to use the temporary signals that hold the
correct intermediate values whenever one of the previously assigned signals is
used in an expression.
Unfortunately the generation of a correct
``RTLIL::CaseRule``/``RTLIL::SwitchRule`` tree for behavioural code is a
non-trivial task. The AST frontend solves the problem using the approach
described on the following pages. The following example illustrates what
the algorithm is supposed to do. Consider the following Verilog code:
described on the following pages. The following example illustrates what the
algorithm is supposed to do. Consider the following Verilog code:
.. code:: verilog
:number-lines:
@ -325,9 +318,8 @@ the algorithm is supposed to do. Consider the following Verilog code:
out1 = out1 ^ out2;
end
This is translated by the Verilog and AST frontends into the following
RTLIL code (attributes, cell parameters and wire declarations not
included):
This is translated by the Verilog and AST frontends into the following RTLIL
code (attributes, cell parameters and wire declarations not included):
.. code:: RTLIL
:number-lines:
@ -372,47 +364,47 @@ included):
update \out3 $0\out3[0:0]
end
Note that the two operators are translated into separate cells outside
the generated process. The signal ``out1`` is assigned using blocking
assignments and therefore ``out1`` has been replaced with a different
signal in all expressions after the initial assignment. The signal
``out2`` is assigned using nonblocking assignments and therefore is not
substituted on the right-hand-side expressions.
Note that the two operators are translated into separate cells outside the
generated process. The signal ``out1`` is assigned using blocking assignments
and therefore ``out1`` has been replaced with a different signal in all
expressions after the initial assignment. The signal ``out2`` is assigned using
nonblocking assignments and therefore is not substituted on the right-hand-side
expressions.
The ``RTLIL::CaseRule``/``RTLIL::SwitchRule`` tree must be interpreted
the following way:
The ``RTLIL::CaseRule``/``RTLIL::SwitchRule`` tree must be interpreted the
following way:
- On each case level (the body of the process is the root case), first
the actions on this level are evaluated and then the switches within
the case are evaluated. (Note that the last assignment on line 13 of
the Verilog code has been moved to the beginning of the RTLIL process
to line 13 of the RTLIL listing.)
- On each case level (the body of the process is the root case), first the
actions on this level are evaluated and then the switches within the case are
evaluated. (Note that the last assignment on line 13 of the Verilog code has
been moved to the beginning of the RTLIL process to line 13 of the RTLIL
listing.)
I.e. the special cases deeper in the switch hierarchy override the
defaults on the upper levels. The assignments in lines 12 and 22 of
the RTLIL code serve as an example for this.
I.e. the special cases deeper in the switch hierarchy override the defaults
on the upper levels. The assignments in lines 12 and 22 of the RTLIL code
serve as an example for this.
Note that in contrast to this, the order within the
``RTLIL::SwitchRule`` objects within a ``RTLIL::CaseRule`` is
preserved with respect to the original AST and Verilog code.
Note that in contrast to this, the order within the ``RTLIL::SwitchRule``
objects within a ``RTLIL::CaseRule`` is preserved with respect to the
original AST and Verilog code.
- The whole ``RTLIL::CaseRule``/``RTLIL::SwitchRule`` tree describes an
asynchronous circuit. I.e. the decision tree formed by the switches
can be seen independently for each assigned signal. Whenever one
assigned signal changes, all signals that depend on the changed
signals are to be updated. For example the assignments in lines 16
and 18 in the RTLIL code in fact influence the assignment in line 12,
even though they are in the "wrong order".
asynchronous circuit. I.e. the decision tree formed by the switches can be
seen independently for each assigned signal. Whenever one assigned signal
changes, all signals that depend on the changed signals are to be updated.
For example the assignments in lines 16 and 18 in the RTLIL code in fact
influence the assignment in line 12, even though they are in the "wrong
order".
The only synchronous part of the process is in the ``RTLIL::SyncRule``
object generated at line 35 in the RTLIL code. The sync rule is the only
part of the process where the original signals are assigned. The
synchronization event from the original Verilog code has been translated
into the synchronization type (posedge) and signal (\\clock) for the
``RTLIL::SyncRule`` object. In the case of this simple example the
``RTLIL::SyncRule`` object is later simply transformed into a set of
d-type flip-flops and the ``RTLIL::CaseRule``/``RTLIL::SwitchRule`` tree
to a decision tree using multiplexers.
The only synchronous part of the process is in the ``RTLIL::SyncRule`` object
generated at line 35 in the RTLIL code. The sync rule is the only part of the
process where the original signals are assigned. The synchronization event from
the original Verilog code has been translated into the synchronization type
(posedge) and signal (``\clock``) for the ``RTLIL::SyncRule`` object. In the
case of this simple example the ``RTLIL::SyncRule`` object is later simply
transformed into a set of d-type flip-flops and the
``RTLIL::CaseRule``/``RTLIL::SwitchRule`` tree to a decision tree using
multiplexers.
In more complex examples (e.g. asynchronous resets) the part of the
``RTLIL::CaseRule``/``RTLIL::SwitchRule`` tree that describes the
@ -426,64 +418,62 @@ The ``AST_INTERNAL::ProcessGenerator`` uses the following internal state
variables:
- | ``subst_rvalue_from`` and ``subst_rvalue_to``
| These two variables hold the replacement pattern that should be
used by ``AST::AstNode::genRTLIL()`` for signals with blocking
assignments. After initialization of
``AST_INTERNAL::ProcessGenerator`` these two variables are empty.
| These two variables hold the replacement pattern that should be used by
``AST::AstNode::genRTLIL()`` for signals with blocking assignments. After
initialization of ``AST_INTERNAL::ProcessGenerator`` these two variables are
empty.
- | ``subst_lvalue_from`` and ``subst_lvalue_to``
| These two variables contain the mapping from left-hand-side signals
(\\\ <name>) to the current temporary signal for the same thing
(initially $0\\\ <name>).
- | ``subst_lvalue_from`` and ``subst_lvalue_to``
| These two variables contain the mapping from left-hand-side signals (``\
<name>``) to the current temporary signal for the same thing (initially
``$0\ <name>``).
- | ``current_case``
| A pointer to a ``RTLIL::CaseRule`` object. Initially this is the
root case of the generated ``RTLIL::Process``.
- | ``current_case``
| A pointer to a ``RTLIL::CaseRule`` object. Initially this is the root case
of the generated ``RTLIL::Process``.
As the algorithm runs these variables are continuously modified as well
as pushed to the stack and later restored to their earlier values by
popping from the stack.
As the algorithm runs these variables are continuously modified as well as
pushed to the stack and later restored to their earlier values by popping from
the stack.
On startup the ProcessGenerator generates a new ``RTLIL::Process``
object with an empty root case and initializes its state variables as
described above. Then the ``RTLIL::SyncRule`` objects are created using
the synchronization events from the AST_ALWAYS node and the initial
values of ``subst_lvalue_from`` and ``subst_lvalue_to``. Then the AST
for this process is evaluated recursively.
On startup the ProcessGenerator generates a new ``RTLIL::Process`` object with
an empty root case and initializes its state variables as described above. Then
the ``RTLIL::SyncRule`` objects are created using the synchronization events
from the AST_ALWAYS node and the initial values of ``subst_lvalue_from`` and
``subst_lvalue_to``. Then the AST for this process is evaluated recursively.
During this recursive evaluation, three different relevant types of AST
nodes can be discovered: AST_ASSIGN_LE (nonblocking assignments),
AST_ASSIGN_EQ (blocking assignments) and AST_CASE (``if`` or ``case``
During this recursive evaluation, three different relevant types of AST nodes
can be discovered: ``AST_ASSIGN_LE`` (nonblocking assignments),
``AST_ASSIGN_EQ`` (blocking assignments) and ``AST_CASE`` (``if`` or ``case``
statement).
Handling of nonblocking assignments
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When an AST_ASSIGN_LE node is discovered, the following actions are
When an ``AST_ASSIGN_LE`` node is discovered, the following actions are
performed by the ProcessGenerator:
- The left-hand-side is evaluated using ``AST::AstNode::genRTLIL()``
and mapped to a temporary signal name using ``subst_lvalue_from`` and
- The left-hand-side is evaluated using ``AST::AstNode::genRTLIL()`` and mapped
to a temporary signal name using ``subst_lvalue_from`` and
``subst_lvalue_to``.
- The right-hand-side is evaluated using ``AST::AstNode::genRTLIL()``.
For this call, the values of ``subst_rvalue_from`` and
``subst_rvalue_to`` are used to map blocking-assigned signals
correctly.
- The right-hand-side is evaluated using ``AST::AstNode::genRTLIL()``. For this
call, the values of ``subst_rvalue_from`` and ``subst_rvalue_to`` are used to
map blocking-assigned signals correctly.
- Remove all assignments to the same left-hand-side as this assignment
from the ``current_case`` and all cases within it.
- Remove all assignments to the same left-hand-side as this assignment from the
``current_case`` and all cases within it.
- Add the new assignment to the ``current_case``.
Handling of blocking assignments
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When an AST_ASSIGN_EQ node is discovered, the following actions are
When an ``AST_ASSIGN_EQ`` node is discovered, the following actions are
performed by the ProcessGenerator:
- Perform all the steps that would be performed for a nonblocking
assignment (see above).
- Perform all the steps that would be performed for a nonblocking assignment
(see above).
- Remove the found left-hand-side (before lvalue mapping) from
``subst_rvalue_from`` and also remove the respective bits from
@ -496,36 +486,33 @@ performed by the ProcessGenerator:
Handling of cases and if-statements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When an AST_CASE node is discovered, the following actions are performed
by the ProcessGenerator:
When an ``AST_CASE`` node is discovered, the following actions are performed by
the ProcessGenerator:
- The values of ``subst_rvalue_from``, ``subst_rvalue_to``,
``subst_lvalue_from`` and ``subst_lvalue_to`` are pushed to the
stack.
``subst_lvalue_from`` and ``subst_lvalue_to`` are pushed to the stack.
- A new ``RTLIL::SwitchRule`` object is generated, the selection
expression is evaluated using ``AST::AstNode::genRTLIL()`` (with the
use of ``subst_rvalue_from`` and ``subst_rvalue_to``) and added to
the ``RTLIL::SwitchRule`` object and the object is added to the
``current_case``.
- A new ``RTLIL::SwitchRule`` object is generated, the selection expression is
evaluated using ``AST::AstNode::genRTLIL()`` (with the use of
``subst_rvalue_from`` and ``subst_rvalue_to``) and added to the
``RTLIL::SwitchRule`` object and the object is added to the ``current_case``.
- All lvalues assigned to within the AST_CASE node using blocking
- All lvalues assigned to within the ``AST_CASE`` node using blocking
assignments are collected and saved in the local variable
``this_case_eq_lvalue``.
- New temporary signals are generated for all signals in
``this_case_eq_lvalue`` and stored in ``this_case_eq_ltemp``.
- The signals in ``this_case_eq_lvalue`` are mapped using
``subst_rvalue_from`` and ``subst_rvalue_to`` and the resulting set
of signals is stored in ``this_case_eq_rvalue``.
- The signals in ``this_case_eq_lvalue`` are mapped using ``subst_rvalue_from``
and ``subst_rvalue_to`` and the resulting set of signals is stored in
``this_case_eq_rvalue``.
Then the following steps are performed for each AST_COND node within the
AST_CASE node:
Then the following steps are performed for each ``AST_COND`` node within the
``AST_CASE`` node:
- Set ``subst_rvalue_from``, ``subst_rvalue_to``, ``subst_lvalue_from``
and ``subst_lvalue_to`` to the values that have been pushed to the
stack.
- Set ``subst_rvalue_from``, ``subst_rvalue_to``, ``subst_lvalue_from`` and
``subst_lvalue_to`` to the values that have been pushed to the stack.
- Remove ``this_case_eq_lvalue`` from
``subst_lvalue_from``/``subst_lvalue_to``.
@ -535,33 +522,30 @@ AST_CASE node:
- Push the value of ``current_case``.
- Create a new ``RTLIL::CaseRule``. Set ``current_case`` to the new
object and add the new object to the ``RTLIL::SwitchRule`` created
above.
- Create a new ``RTLIL::CaseRule``. Set ``current_case`` to the new object and
add the new object to the ``RTLIL::SwitchRule`` created above.
- Add an assignment from ``this_case_eq_rvalue`` to
``this_case_eq_ltemp`` to the new ``current_case``.
- Add an assignment from ``this_case_eq_rvalue`` to ``this_case_eq_ltemp`` to
the new ``current_case``.
- Evaluate the compare value for this case using
``AST::AstNode::genRTLIL()`` (with the use of ``subst_rvalue_from``
and ``subst_rvalue_to``) modify the new ``current_case`` accordingly.
- Recursion into the children of the AST_COND node.
- Recursion into the children of the ``AST_COND`` node.
- Restore ``current_case`` by popping the old value from the stack.
Finally the following steps are performed:
- The values of ``subst_rvalue_from``, ``subst_rvalue_to``,
``subst_lvalue_from`` and ``subst_lvalue_to`` are popped from the
stack.
``subst_lvalue_from`` and ``subst_lvalue_to`` are popped from the stack.
- The signals from ``this_case_eq_lvalue`` are removed from the
``subst_rvalue_from``/``subst_rvalue_to``-pair.
- The value of ``this_case_eq_lvalue`` is appended to
``subst_rvalue_from`` and the value of ``this_case_eq_ltemp`` is
appended to ``subst_rvalue_to``.
- The value of ``this_case_eq_lvalue`` is appended to ``subst_rvalue_from`` and
the value of ``this_case_eq_ltemp`` is appended to ``subst_rvalue_to``.
- Map the signals in ``this_case_eq_lvalue`` using
``subst_lvalue_from``/``subst_lvalue_to``.
@ -569,98 +553,99 @@ Finally the following steps are performed:
- Remove all assignments to signals in ``this_case_eq_lvalue`` in
``current_case`` and all cases within it.
- Add an assignment from ``this_case_eq_ltemp`` to
``this_case_eq_lvalue`` to ``current_case``.
- Add an assignment from ``this_case_eq_ltemp`` to ``this_case_eq_lvalue`` to
``current_case``.
Further analysis of the algorithm for cases and if-statements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
With respect to nonblocking assignments the algorithm is easy: later
assignments invalidate earlier assignments. For each signal assigned
using nonblocking assignments exactly one temporary variable is
generated (with the $0-prefix) and this variable is used for all
assignments of the variable.
With respect to nonblocking assignments the algorithm is easy: later assignments
invalidate earlier assignments. For each signal assigned using nonblocking
assignments exactly one temporary variable is generated (with the ``$0``-prefix)
and this variable is used for all assignments of the variable.
Note how all the ``_eq_``-variables become empty when no blocking
assignments are used and many of the steps in the algorithm can then be
ignored as a result of this.
Note how all the ``_eq_``-variables become empty when no blocking assignments
are used and many of the steps in the algorithm can then be ignored as a result
of this.
For a variable with blocking assignments the algorithm shows the
following behaviour: First a new temporary variable is created. This new
temporary variable is then registered as the assignment target for all
assignments for this variable within the cases for this AST_CASE node.
Then for each case the new temporary variable is first assigned the old
temporary variable. This assignment is overwritten if the variable is
actually assigned in this case and is kept as a default value otherwise.
For a variable with blocking assignments the algorithm shows the following
behaviour: First a new temporary variable is created. This new temporary
variable is then registered as the assignment target for all assignments for
this variable within the cases for this ``AST_CASE`` node. Then for each case
the new temporary variable is first assigned the old temporary variable. This
assignment is overwritten if the variable is actually assigned in this case and
is kept as a default value otherwise.
This yields an ``RTLIL::CaseRule`` that assigns the new temporary
variable in all branches. So when all cases have been processed a final
assignment is added to the containing block that assigns the new
temporary variable to the old one. Note how this step always overrides a
previous assignment to the old temporary variable. Other than
nonblocking assignments, the old assignment could still have an effect
somewhere in the design, as there have been calls to
This yields an ``RTLIL::CaseRule`` that assigns the new temporary variable in
all branches. So when all cases have been processed a final assignment is added
to the containing block that assigns the new temporary variable to the old one.
Note how this step always overrides a previous assignment to the old temporary
variable. Other than nonblocking assignments, the old assignment could still
have an effect somewhere in the design, as there have been calls to
``AST::AstNode::genRTLIL()`` with a
``subst_rvalue_from``/``subst_rvalue_to``-tuple that contained the
``subst_rvalue_from``/ ``subst_rvalue_to``-tuple that contained the
right-hand-side of the old assignment.
The proc pass
~~~~~~~~~~~~~
The ProcessGenerator converts a behavioural model in AST representation
to a behavioural model in ``RTLIL::Process`` representation. The actual
conversion from a behavioural model to an RTL representation is
performed by the proc pass and the passes it launches:
The ProcessGenerator converts a behavioural model in AST representation to a
behavioural model in ``RTLIL::Process`` representation. The actual conversion
from a behavioural model to an RTL representation is performed by the ``proc``
pass and the passes it launches:
- | proc_clean and proc_rmdead
| These two passes just clean up the ``RTLIL::Process`` structure.
The proc_clean pass removes empty parts (eg. empty assignments)
from the process and proc_rmdead detects and removes unreachable
branches from the process's decision trees.
- | proc_clean and proc_rmdead
| These two passes just clean up the ``RTLIL::Process`` structure. The
``proc_clean`` pass removes empty parts (eg. empty assignments) from the
process and ``proc_rmdead`` detects and removes unreachable branches from
the process's decision trees.
- | proc_arst
- | proc_arst
| This pass detects processes that describe d-type flip-flops with
asynchronous resets and rewrites the process to better reflect what
they are modelling: Before this pass, an asynchronous reset has two
edge-sensitive sync rules and one top-level for the reset path.
After this pass the sync rule for the reset is level-sensitive and
the top-level has been removed.
asynchronous resets and rewrites the process to better reflect what they
are modelling: Before this pass, an asynchronous reset has two
edge-sensitive sync rules and one top-level ``RTLIL::SwitchRule`` for the
reset path. After this pass the sync rule for the reset is level-sensitive
and the top-level ``RTLIL::SwitchRule`` has been removed.
- | proc_mux
| This pass converts the /-tree to a tree of multiplexers per written
signal. After this, the structure only contains the s that describe
the output registers.
- | proc_mux
| This pass converts the ``RTLIL::CaseRule``/ ``RTLIL::SwitchRule``-tree to a
tree of multiplexers per written signal. After this, the ``RTLIL::Process``
structure only contains the ``RTLIL::SyncRule`` s that describe the output
registers.
- | proc_dff
| This pass replaces the s to d-type flip-flops (with asynchronous
resets if necessary).
| This pass replaces the ``RTLIL::SyncRule`` s to d-type flip-flops (with
asynchronous resets if necessary).
- | proc_dff
| This pass replaces the s with $memwr cells.
| This pass replaces the ``RTLIL::MemWriteAction`` s with ``$memwr`` cells.
- | proc_clean
| A final call to proc_clean removes the now empty objects.
| A final call to ``proc_clean`` removes the now empty ``RTLIL::Process``
objects.
Performing these last processing steps in passes instead of in the
Verilog frontend has two important benefits:
Performing these last processing steps in passes instead of in the Verilog
frontend has two important benefits:
First it improves the transparency of the process. Everything that
happens in a separate pass is easier to debug, as the RTLIL data
structures can be easily investigated before and after each of the
steps.
First it improves the transparency of the process. Everything that happens in a
separate pass is easier to debug, as the RTLIL data structures can be easily
investigated before and after each of the steps.
Second it improves flexibility. This scheme can easily be extended to
support other types of storage-elements, such as sr-latches or
d-latches, without having to extend the actual Verilog frontend.
Second it improves flexibility. This scheme can easily be extended to support
other types of storage-elements, such as sr-latches or d-latches, without having
to extend the actual Verilog frontend.
Synthesizing Verilog arrays
---------------------------
Add some information on the generation of $memrd and $memwr cells and
.. TODO: these
Add some information on the generation of ``$memrd`` and ``$memwr`` cells and
how they are processed in the memory pass.
Synthesizing parametric designs
-------------------------------
Add some information on the ``RTLIL::Module::derive()`` method and how
it is used to synthesize parametric modules via the hierarchy pass.
Add some information on the ``RTLIL::Module::derive()`` method and how it is
used to synthesize parametric modules via the hierarchy pass.