mirror of https://github.com/YosysHQ/yosys.git
850 lines
35 KiB
TeX
850 lines
35 KiB
TeX
|
|
\chapter{The Verilog and AST Frontends}
|
|
\label{chapter:verilog}
|
|
|
|
This chapter provides an overview of the implementation of the Yosys Verilog
|
|
and AST frontends. The Verilog frontend reads Verilog-2005 code and creates
|
|
an abstract syntax tree (AST) representation of the input. This AST representation
|
|
is then passed to the AST frontend that converts it to RTLIL data, as illustrated
|
|
in Fig.~\ref{fig:Verilog_flow}.
|
|
|
|
\begin{figure}[b!]
|
|
\hfil
|
|
\begin{tikzpicture}
|
|
\tikzstyle{process} = [draw, fill=green!10, rectangle, minimum height=3em, minimum width=10em, node distance=5em, font={\ttfamily}]
|
|
\tikzstyle{data} = [draw, fill=blue!10, ellipse, minimum height=3em, minimum width=7em, node distance=5em, font={\ttfamily}]
|
|
|
|
\node[data] (n1) {Verilog Source};
|
|
\node[process] (n2) [below of=n1] {Verilog Frontend};
|
|
\node[data] (n3) [below of=n2] {AST};
|
|
\node[process] (n4) [below of=n3] {AST Frontend};
|
|
\node[data] (n5) [below of=n4] {RTLIL};
|
|
|
|
\draw[-latex] (n1) -- (n2);
|
|
\draw[-latex] (n2) -- (n3);
|
|
\draw[-latex] (n3) -- (n4);
|
|
\draw[-latex] (n4) -- (n5);
|
|
|
|
\tikzstyle{details} = [draw, fill=yellow!5, rectangle, node distance=6cm, font={\ttfamily}]
|
|
|
|
\node[details] (d1) [right of=n2] {\begin{minipage}{5cm}
|
|
\hfil
|
|
\begin{tikzpicture}
|
|
\tikzstyle{subproc} = [draw, fill=green!10, rectangle, minimum height=2em, minimum width=10em, node distance=3em, font={\ttfamily}]
|
|
\node (s0) {};
|
|
\node[subproc] (s1) [below of=s0] {Preprocessor};
|
|
\node[subproc] (s2) [below of=s1] {Lexer};
|
|
\node[subproc] (s3) [below of=s2] {Parser};
|
|
\node[node distance=3em] (s4) [below of=s3] {};
|
|
\draw[-latex] (s0) -- (s1);
|
|
\draw[-latex] (s1) -- (s2);
|
|
\draw[-latex] (s2) -- (s3);
|
|
\draw[-latex] (s3) -- (s4);
|
|
\end{tikzpicture}
|
|
\end{minipage}};
|
|
|
|
\draw[dashed] (n2.north east) -- (d1.north west);
|
|
\draw[dashed] (n2.south east) -- (d1.south west);
|
|
|
|
\node[details] (d2) [right of=n4] {\begin{minipage}{5cm}
|
|
\hfil
|
|
\begin{tikzpicture}
|
|
\tikzstyle{subproc} = [draw, fill=green!10, rectangle, minimum height=2em, minimum width=10em, node distance=3em, font={\ttfamily}]
|
|
\node (s0) {};
|
|
\node[subproc] (s1) [below of=s0] {Simplifier};
|
|
\node[subproc] (s2) [below of=s1] {RTLIL Generator};
|
|
\node[node distance=3em] (s3) [below of=s2] {};
|
|
\draw[-latex] (s0) -- (s1);
|
|
\draw[-latex] (s1) -- (s2);
|
|
\draw[-latex] (s2) -- (s3);
|
|
\end{tikzpicture}
|
|
\end{minipage}};
|
|
|
|
\draw[dashed] (n4.north east) -- (d2.north west);
|
|
\draw[dashed] (n4.south east) -- (d2.south west);
|
|
|
|
\end{tikzpicture}
|
|
\caption{Simplified Verilog to RTLIL data flow}
|
|
\label{fig:Verilog_flow}
|
|
\end{figure}
|
|
|
|
|
|
\section{Transforming Verilog to AST}
|
|
|
|
The {\it 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
|
|
{\it Preprocessor}, the {\it Lexer} and the {\it Parser}.
|
|
|
|
The source code to the Verilog frontend can be found in {\tt frontends/verilog/} in the Yosys source tree.
|
|
|
|
\subsection{The Verilog Preprocessor}
|
|
|
|
The Verilog preprocessor scans over the Verilog source code and interprets some of the Verilog compiler
|
|
directives such as \lstinline[language=Verilog]{`include}, \lstinline[language=Verilog]{`define} and
|
|
\lstinline[language=Verilog]{`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 \lstinline[language=C++]{std::string}.
|
|
|
|
The source code to the Verilog Preprocessor can be found in {\tt
|
|
frontends/verilog/preproc.cc} in the Yosys source tree.
|
|
|
|
\subsection{The Verilog Lexer}
|
|
|
|
\begin{sloppypar}
|
|
The Verilog Lexer is written using the lexer generator {\it flex} \citeweblink{flex}. Its source code
|
|
can be found in {\tt frontends/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.
|
|
\end{sloppypar}
|
|
|
|
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.
|
|
|
|
\begin{sloppypar}
|
|
Finally the lexer identifies and handles special comments such as
|
|
``\lstinline[language=Verilog]{// synopsys translate_off}'' and
|
|
``\lstinline[language=Verilog]{// synopsys full_case}''. (It is recommended to
|
|
use \lstinline[language=Verilog]{`ifdef} constructs instead of the Synsopsys
|
|
translate\_on/off comments and attributes such as
|
|
\lstinline[language=Verilog]{(* full_case *)} over ``\lstinline[language=Verilog]{// synopsys full_case}''
|
|
whenever possible.)
|
|
\end{sloppypar}
|
|
|
|
\subsection{The Verilog Parser}
|
|
|
|
The Verilog Parser is written using the parser generator {\it bison} \citeweblink{bison}. Its source code
|
|
can be found in {\tt frontends/verilog/parser.y} in the Yosys source tree.
|
|
|
|
It generates an AST using the \lstinline[language=C++]{AST::AstNode} data structure
|
|
defined in {\tt frontends/ast/ast.h}. An \lstinline[language=C++]{AST::AstNode} object has
|
|
the following properties:
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
\begin{table}[b!]
|
|
\hfil
|
|
\begin{tabular}{>{\raggedright\arraybackslash}p{7cm}>{\raggedright\arraybackslash}p{8cm}}
|
|
AST Node Type & Corresponding Verilog Construct \\
|
|
\hline
|
|
\hline
|
|
\arrayrulecolor{gray}
|
|
{\tt AST\_NONE} & This Node type should never be used. \\
|
|
\hline
|
|
%
|
|
{\tt AST\_DESIGN} & This node type is used for the top node of the AST tree. It
|
|
has no corresponding Verilog construct. \\
|
|
\hline
|
|
%
|
|
{\tt AST\_MODULE},
|
|
{\tt AST\_TASK},
|
|
{\tt AST\_FUNCTION} &
|
|
\lstinline[language=Verilog];module;,
|
|
\lstinline[language=Verilog];task; and
|
|
\lstinline[language=Verilog];function; \\
|
|
\hline
|
|
%
|
|
{\tt AST\_WIRE} &
|
|
\lstinline[language=Verilog];input;,
|
|
\lstinline[language=Verilog];output;,
|
|
\lstinline[language=Verilog];wire;,
|
|
\lstinline[language=Verilog];reg; and
|
|
\lstinline[language=Verilog];integer; \\
|
|
\hline
|
|
%
|
|
{\tt AST\_MEMORY} &
|
|
Verilog Arrays \\
|
|
\hline
|
|
%
|
|
{\tt AST\_AUTOWIRE} &
|
|
Created by the simplifier when an undeclared signal name is used. \\
|
|
\hline
|
|
%
|
|
{\tt AST\_PARAMETER},
|
|
{\tt AST\_LOCALPARAM} &
|
|
\lstinline[language=Verilog];parameter; and
|
|
\lstinline[language=Verilog];localparam; \\
|
|
\hline
|
|
%
|
|
{\tt AST\_PARASET} &
|
|
Parameter set in cell instantiation \\
|
|
\hline
|
|
%
|
|
{\tt AST\_ARGUMENT} &
|
|
Port connection in cell instantiation \\
|
|
\hline
|
|
%
|
|
{\tt AST\_RANGE} &
|
|
Bit-Index in a signal or element index in array \\
|
|
\hline
|
|
%
|
|
{\tt AST\_CONSTANT} &
|
|
A literal value \\
|
|
\hline
|
|
%
|
|
{\tt AST\_CELLTYPE} &
|
|
The type of cell in cell instantiation \\
|
|
\hline
|
|
%
|
|
{\tt AST\_IDENTIFIER} &
|
|
An Identifier (signal name in expression or cell/task/etc. name in other contexts) \\
|
|
\hline
|
|
%
|
|
{\tt AST\_PREFIX} &
|
|
Construct an identifier in the form {\tt <prefix>[<index>].<suffix>} (used only in
|
|
advanced generate constructs) \\
|
|
\hline
|
|
%
|
|
{\tt AST\_FCALL},
|
|
{\tt AST\_TCALL} &
|
|
Call to function or task \\
|
|
\hline
|
|
%
|
|
{\tt AST\_TO\_SIGNED},
|
|
{\tt AST\_TO\_UNSIGNED} &
|
|
The \lstinline[language=Verilog];$signed(); and
|
|
\lstinline[language=Verilog];$unsigned(); functions \\
|
|
\hline
|
|
\end{tabular}
|
|
\caption{AST node types with their corresponding Verilog constructs. \\ (continued on next page)}
|
|
\label{tab:Verilog_AstNodeType}
|
|
\end{table}
|
|
|
|
\begin{table}[t!]
|
|
\ContinuedFloat
|
|
\hfil
|
|
\begin{tabular}{>{\raggedright\arraybackslash}p{7cm}>{\raggedright\arraybackslash}p{8cm}}
|
|
AST Node Type & Corresponding Verilog Construct \\
|
|
\hline
|
|
\hline
|
|
\arrayrulecolor{gray}
|
|
{\tt AST\_CONCAT}
|
|
{\tt AST\_REPLICATE} &
|
|
The \lstinline[language=Verilog];{...}; and
|
|
\lstinline[language=Verilog];{...{...}}; operators \\
|
|
\hline
|
|
%
|
|
{\tt AST\_BIT\_NOT},
|
|
{\tt AST\_BIT\_AND},
|
|
{\tt AST\_BIT\_OR},
|
|
{\tt AST\_BIT\_XOR},
|
|
{\tt AST\_BIT\_XNOR} &
|
|
The bitwise operators \break
|
|
\lstinline[language=Verilog];~;,
|
|
\lstinline[language=Verilog];&;,
|
|
\lstinline[language=Verilog];|;,
|
|
\lstinline[language=Verilog];^; and
|
|
\lstinline[language=Verilog];~^; \\
|
|
\hline
|
|
%
|
|
{\tt AST\_REDUCE\_AND},
|
|
{\tt AST\_REDUCE\_OR},
|
|
{\tt AST\_REDUCE\_XOR},
|
|
{\tt AST\_REDUCE\_XNOR} &
|
|
The unary reduction operators \break
|
|
\lstinline[language=Verilog];~;,
|
|
\lstinline[language=Verilog];&;,
|
|
\lstinline[language=Verilog];|;,
|
|
\lstinline[language=Verilog];^; and
|
|
\lstinline[language=Verilog];~^; \\
|
|
\hline
|
|
%
|
|
{\tt AST\_REDUCE\_BOOL} &
|
|
Conversion from multi-bit value to boolean value
|
|
(equivalent to {\tt AST\_REDUCE\_OR}) \\
|
|
\hline
|
|
%
|
|
{\tt AST\_SHIFT\_LEFT},
|
|
{\tt AST\_SHIFT\_RIGHT},
|
|
{\tt AST\_SHIFT\_SLEFT},
|
|
{\tt AST\_SHIFT\_SRIGHT} &
|
|
The shift operators \break
|
|
\lstinline[language=Verilog];<<;,
|
|
\lstinline[language=Verilog];>>;,
|
|
\lstinline[language=Verilog];<<<; and
|
|
\lstinline[language=Verilog];>>>; \\
|
|
\hline
|
|
%
|
|
{\tt AST\_LT},
|
|
{\tt AST\_LE},
|
|
{\tt AST\_EQ},
|
|
{\tt AST\_NE},
|
|
{\tt AST\_GE},
|
|
{\tt AST\_GT} &
|
|
The relational operators \break
|
|
\lstinline[language=Verilog];<;,
|
|
\lstinline[language=Verilog];<=;,
|
|
\lstinline[language=Verilog];==;,
|
|
\lstinline[language=Verilog];!=;,
|
|
\lstinline[language=Verilog];>=; and
|
|
\lstinline[language=Verilog];>; \\
|
|
\hline
|
|
%
|
|
{\tt AST\_ADD},
|
|
{\tt AST\_SUB},
|
|
{\tt AST\_MUL},
|
|
{\tt AST\_DIV},
|
|
{\tt AST\_MOD},
|
|
{\tt AST\_POW} &
|
|
The binary operators \break
|
|
\lstinline[language=Verilog];+;,
|
|
\lstinline[language=Verilog];-;,
|
|
\lstinline[language=Verilog];*;,
|
|
\lstinline[language=Verilog];/;,
|
|
\lstinline[language=Verilog];%; and
|
|
\lstinline[language=Verilog];**; \\
|
|
\hline
|
|
%
|
|
{\tt AST\_POS},
|
|
{\tt AST\_NEG} &
|
|
The prefix operators
|
|
\lstinline[language=Verilog];+; and
|
|
\lstinline[language=Verilog];-; \\
|
|
\hline
|
|
%
|
|
{\tt AST\_LOGIC\_AND},
|
|
{\tt AST\_LOGIC\_OR},
|
|
{\tt AST\_LOGIC\_NOT} &
|
|
The logic operators
|
|
\lstinline[language=Verilog];&&;,
|
|
\lstinline[language=Verilog];||; and
|
|
\lstinline[language=Verilog];!; \\
|
|
\hline
|
|
%
|
|
{\tt AST\_TERNARY} &
|
|
The ternary \lstinline[language=Verilog];?:;-operator \\
|
|
\hline
|
|
%
|
|
{\tt AST\_MEMRD}
|
|
{\tt AST\_MEMWR} &
|
|
Read and write memories. These nodes are generated by
|
|
the AST simplifier for writes/reads to/from Verilog arrays. \\
|
|
\hline
|
|
%
|
|
{\tt AST\_ASSIGN} &
|
|
An \lstinline[language=Verilog];assign; statement \\
|
|
\hline
|
|
%
|
|
{\tt AST\_CELL} &
|
|
A cell instantiation \\
|
|
\hline
|
|
%
|
|
{\tt AST\_PRIMITIVE} &
|
|
A primitive cell (\lstinline[language=Verilog];and;,
|
|
\lstinline[language=Verilog];nand;,
|
|
\lstinline[language=Verilog];or;, etc.) \\
|
|
\hline
|
|
%
|
|
{\tt AST\_ALWAYS},
|
|
{\tt AST\_INITIAL} &
|
|
Verilog \lstinline[language=Verilog];always;- and \lstinline[language=Verilog];initial;-blocks \\
|
|
\hline
|
|
%
|
|
{\tt AST\_BLOCK} &
|
|
A \lstinline[language=Verilog];begin;-\lstinline[language=Verilog];end;-block \\
|
|
\hline
|
|
%
|
|
{\tt AST\_ASSIGN\_EQ}.
|
|
{\tt AST\_ASSIGN\_LE} &
|
|
Blocking (\lstinline[language=Verilog];=;) and nonblocking (\lstinline[language=Verilog];<=;)
|
|
assignments within an \lstinline[language=Verilog];always;- or \lstinline[language=Verilog];initial;-block \\
|
|
\hline
|
|
%
|
|
{\tt AST\_CASE}.
|
|
{\tt AST\_COND},
|
|
{\tt AST\_DEFAULT} &
|
|
The \lstinline[language=Verilog];case; (\lstinline[language=Verilog];if;) statements, conditions within a case
|
|
and the default case respectively \\
|
|
\hline
|
|
%
|
|
{\tt AST\_FOR} &
|
|
A \lstinline[language=Verilog];for;-loop with an
|
|
\lstinline[language=Verilog];always;- or
|
|
\lstinline[language=Verilog];initial;-block \\
|
|
\hline
|
|
%
|
|
{\tt AST\_GENVAR},
|
|
{\tt AST\_GENBLOCK},
|
|
{\tt AST\_GENFOR},
|
|
{\tt AST\_GENIF} &
|
|
The \lstinline[language=Verilog];genvar; and
|
|
\lstinline[language=Verilog];generate; keywords and
|
|
\lstinline[language=Verilog];for; and \lstinline[language=Verilog];if; within a
|
|
generate block. \\
|
|
\hline
|
|
%
|
|
{\tt AST\_POSEDGE},
|
|
{\tt AST\_NEGEDGE},
|
|
{\tt AST\_EDGE} &
|
|
Event conditions for \lstinline[language=Verilog];always; blocks. \\
|
|
\hline
|
|
\end{tabular}
|
|
\caption{AST node types with their corresponding Verilog constructs. \\ (continuation from previous page)}
|
|
\label{tab:Verilog_AstNodeTypeCont}
|
|
\end{table}
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
\begin{itemize}
|
|
\item {\bf The node type} \\
|
|
This enum (\lstinline[language=C++]{AST::AstNodeType}) specifies the role of the node.
|
|
Table~\ref{tab:Verilog_AstNodeType} contains a list of all node types.
|
|
\item {\bf The child nodes} \\
|
|
This is a list of pointers to all children in the abstract syntax tree.
|
|
\item {\bf Attributes} \\
|
|
As almost every AST node might have Verilog attributes assigned to it, the
|
|
\lstinline[language=C++]{AST::AstNode} has direct support for attributes. Note that the
|
|
attribute values are again AST nodes.
|
|
\item {\bf Node content} \\
|
|
Each node might have additional content data. A series of member variables exist to hold such data.
|
|
For example the member \lstinline[language=C++]{std::string str} can hold a string value and is
|
|
used e.g.~in the {\tt AST\_IDENTIFIER} node type to store the identifier name.
|
|
\item {\bf Source code location} \\
|
|
Each \lstinline[language=C++]{AST::AstNode} is automatically annotated with the current
|
|
source code location by the \lstinline[language=C++]{AST::AstNode} constructor. It is
|
|
stored in the \lstinline[language=C++]{std::string filename} and \lstinline[language=C++]{int linenum}
|
|
member variables.
|
|
\end{itemize}
|
|
|
|
The \lstinline[language=C++]{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:
|
|
|
|
\begin{lstlisting}[numbers=left,frame=single]
|
|
basic_expr '*' attr basic_expr {
|
|
$$ = new AstNode(AST_MUL, $1, $4);
|
|
append_attr($$, $3);
|
|
} |
|
|
\end{lstlisting}
|
|
|
|
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 {\tt read\_verilog} provides the options {\tt -yydebug}
|
|
and {\tt -dump\_ast} that can be used to print the parse tree or abstract syntax tree
|
|
respectively.
|
|
|
|
\section{Transforming AST to RTLIL}
|
|
|
|
The {\it 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: {\it simplification} and {\it RTLIL generation}.
|
|
|
|
The source code to the AST frontend can be found in {\tt frontends/ast/} in the Yosys source tree.
|
|
|
|
\subsection{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 \lstinline[language=C++]{AST::AstNode::simplify()}
|
|
method of all {\tt AST\_MODULE} nodes in the AST. This initiates a recursive process that performs the following transformations
|
|
on the AST data structure:
|
|
|
|
\begin{itemize}
|
|
\item Inline all task and function calls.
|
|
\item Evaluate all \lstinline[language=Verilog]{generate}-statements and unroll all \lstinline[language=Verilog]{for}-loops.
|
|
\item Perform const folding where it is necessary (e.g.~in the value part of {\tt AST\_PARAMETER}, {\tt AST\_LOCALPARAM},
|
|
{\tt AST\_PARASET} and {\tt AST\_RANGE} nodes).
|
|
\item Replace {\tt AST\_PRIMITIVE} nodes with appropriate {\tt AST\_ASSIGN} nodes.
|
|
\item Replace dynamic bit ranges in the left-hand-side of assignments with {\tt AST\_CASE} nodes with {\tt AST\_COND} children
|
|
for each possible case.
|
|
\item Detect array access patterns that are too complicated for the {\tt RTLIL::Memory} abstraction and replace them
|
|
with a set of signals and cases for all reads and/or writes.
|
|
\item Otherwise replace array accesses with {\tt AST\_MEMRD} and {\tt AST\_MEMWR} nodes.
|
|
\end{itemize}
|
|
|
|
In addition to these transformations, the simplifier also annotates the AST with additional information that is needed
|
|
for the RTLIL generator, namely:
|
|
|
|
\begin{itemize}
|
|
\item All ranges (width of signals and bit selections) are not only const folded but (when a constant value
|
|
is found) are also written to member variables in the {\tt AST\_RANGE} node.
|
|
\item All identifiers are resolved and all {\tt 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 {\tt AST\_AUTOWIRE} node
|
|
is created and used for the annotation.
|
|
\end{itemize}
|
|
|
|
This produces an AST that is fairly easy to convert to the RTLIL format.
|
|
|
|
\subsection{Generating RTLIL}
|
|
|
|
After AST simplification, the \lstinline[language=C++]{AST::AstNode::genRTLIL()} method of each {\tt AST\_MODULE} node
|
|
in the AST is called. This initiates a recursive process that generates equivalent RTLIL data for the AST data.
|
|
|
|
The \lstinline[language=C++]{AST::AstNode::genRTLIL()} method returns an \lstinline[language=C++]{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 {\tt
|
|
AST\_CELL}), the corresponding circuit is generated and an empty \lstinline[language=C++]{RTLIL::SigSpec} is returned.
|
|
|
|
\section{Synthesizing Verilog always Blocks}
|
|
|
|
For behavioural Verilog code (code utilizing \lstinline[language=Verilog]{always}- and
|
|
\lstinline[language=Verilog]{initial}-blocks) it is necessary to also generate \lstinline[language=C++]{RTLIL::Process}
|
|
objects. This is done in the following way:
|
|
|
|
\begin{itemize}
|
|
\item Whenever \lstinline[language=C++]{AST::AstNode::genRTLIL()} encounters an \lstinline[language=Verilog]{always}-
|
|
or \lstinline[language=Verilog]{initial}-block, it creates an instance of
|
|
\lstinline[language=Verilog]{AST_INTERNAL::ProcessGenerator}. This object then generates the
|
|
\lstinline[language=C++]{RTLIL::Process} object for the block. It also calls \lstinline[language=C++]{AST::AstNode::genRTLIL()}
|
|
for all right-hand-side expressions contained within the block.
|
|
%
|
|
\begin{sloppypar}
|
|
\item First the \lstinline[language=Verilog]{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 {\tt \$\it<number>\tt
|
|
\textbackslash\it <original\_name>} for each of the assigned signals.
|
|
\end{sloppypar}
|
|
%
|
|
\item Then an \lstinline[language=C++]{RTLIL::Process} is created that assigns all intermediate values for each left-hand-side
|
|
signal to the temporary signal in its \lstinline[language=C++]{RTLIL::CaseRule}/\lstinline[language=C++]{RTLIL::SwitchRule} tree.
|
|
%
|
|
\item Finally a \lstinline[language=C++]{RTLIL::SyncRule} is created for the \lstinline[language=C++]{RTLIL::Process} that
|
|
assigns the temporary signals for the final values to the actual signals.
|
|
%
|
|
\item Calls to \lstinline[language=C++]{AST::AstNode::genRTLIL()} are generated for right hand sides as needed. When blocking
|
|
assignments are used, \lstinline[language=C++]{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.
|
|
\end{itemize}
|
|
|
|
Unfortunately the generation of a correct \lstinline[language=C++]{RTLIL::CaseRule}/\lstinline[language=C++]{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:
|
|
|
|
\begin{lstlisting}[numbers=left,frame=single,language=Verilog]
|
|
always @(posedge clock) begin
|
|
out1 = in1;
|
|
if (in2)
|
|
out1 = !out1;
|
|
out2 <= out1;
|
|
if (in3)
|
|
out2 <= out2;
|
|
if (in4)
|
|
if (in5)
|
|
out3 <= in6;
|
|
else
|
|
out3 <= in7;
|
|
out1 = out1 ^ out2;
|
|
end
|
|
\end{lstlisting}
|
|
|
|
This is translated by the Verilog and AST frontends into the following RTLIL code (attributes, cell parameters
|
|
and wire declarations not included):
|
|
|
|
\begin{lstlisting}[numbers=left,frame=single,language=rtlil]
|
|
cell $logic_not $logic_not$<input>:4$2
|
|
connect \A \in1
|
|
connect \Y $logic_not$<input>:4$2_Y
|
|
end
|
|
cell $xor $xor$<input>:13$3
|
|
connect \A $1\out1[0:0]
|
|
connect \B \out2
|
|
connect \Y $xor$<input>:13$3_Y
|
|
end
|
|
process $proc$<input>:1$1
|
|
assign $0\out3[0:0] \out3
|
|
assign $0\out2[0:0] $1\out1[0:0]
|
|
assign $0\out1[0:0] $xor$<input>:13$3_Y
|
|
switch \in2
|
|
case 1'1
|
|
assign $1\out1[0:0] $logic_not$<input>:4$2_Y
|
|
case
|
|
assign $1\out1[0:0] \in1
|
|
end
|
|
switch \in3
|
|
case 1'1
|
|
assign $0\out2[0:0] \out2
|
|
case
|
|
end
|
|
switch \in4
|
|
case 1'1
|
|
switch \in5
|
|
case 1'1
|
|
assign $0\out3[0:0] \in6
|
|
case
|
|
assign $0\out3[0:0] \in7
|
|
end
|
|
case
|
|
end
|
|
sync posedge \clock
|
|
update \out1 $0\out1[0:0]
|
|
update \out2 $0\out2[0:0]
|
|
update \out3 $0\out3[0:0]
|
|
end
|
|
\end{lstlisting}
|
|
|
|
Note that the two operators are translated into separate cells outside the generated process. The signal
|
|
\lstinline[language=Verilog]{out1} is assigned using blocking assignments and therefore \lstinline[language=Verilog]{out1}
|
|
has been replaced with a different signal in all expressions after the initial assignment. The signal
|
|
\lstinline[language=Verilog]{out2} is assigned using nonblocking assignments and therefore is not substituted
|
|
on the right-hand-side expressions.
|
|
|
|
The \lstinline[language=C++]{RTLIL::CaseRule}/\lstinline[language=C++]{RTLIL::SwitchRule}
|
|
tree must be interpreted the following way:
|
|
|
|
\begin{itemize}
|
|
\item On each case level (the body of the process is the {\it 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.
|
|
|
|
Note that in contrast to this, the order within the \lstinline[language=C++]{RTLIL::SwitchRule} objects
|
|
within a \lstinline[language=C++]{RTLIL::CaseRule} is preserved with respect to the original AST and
|
|
Verilog code.
|
|
%
|
|
\item \begin{sloppypar}
|
|
The whole \lstinline[language=C++]{RTLIL::CaseRule}/\lstinline[language=C++]{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''.
|
|
\end{sloppypar}
|
|
\end{itemize}
|
|
|
|
The only synchronous part of the process is in the \lstinline[language=C++]{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 ({\tt posedge})
|
|
and signal ({\tt \textbackslash clock}) for the \lstinline[language=C++]{RTLIL::SyncRule} object. In the case of
|
|
this simple example the \lstinline[language=C++]{RTLIL::SyncRule} object is later simply transformed into a set of
|
|
d-type flip-flops and the \lstinline[language=C++]{RTLIL::CaseRule}/\lstinline[language=C++]{RTLIL::SwitchRule} tree
|
|
to a decision tree using multiplexers.
|
|
|
|
\begin{sloppypar}
|
|
In more complex examples (e.g.~asynchronous resets) the part of the
|
|
\lstinline[language=C++]{RTLIL::CaseRule}/\lstinline[language=C++]{RTLIL::SwitchRule}
|
|
tree that describes the asynchronous reset must first be transformed to the
|
|
correct \lstinline[language=C++]{RTLIL::SyncRule} objects. This is done by the {\tt proc\_adff} pass.
|
|
\end{sloppypar}
|
|
|
|
\subsection{The ProcessGenerator Algorithm}
|
|
|
|
The \lstinline[language=C++]{AST_INTERNAL::ProcessGenerator} uses the following internal state variables:
|
|
|
|
\begin{itemize}
|
|
\item \begin{sloppypar}
|
|
\lstinline[language=C++]{subst_rvalue_from} and \lstinline[language=C++]{subst_rvalue_to} \\
|
|
These two variables hold the replacement pattern that should be used by \lstinline[language=C++]{AST::AstNode::genRTLIL()}
|
|
for signals with blocking assignments. After initialization of \lstinline[language=C++]{AST_INTERNAL::ProcessGenerator}
|
|
these two variables are empty.
|
|
\end{sloppypar}
|
|
%
|
|
\item \lstinline[language=C++]{subst_lvalue_from} and \lstinline[language=C++]{subst_lvalue_to} \\
|
|
These two variables contain the mapping from left-hand-side signals ({\tt \textbackslash \it <name>}) to the current
|
|
temporary signal for the same thing (initially {\tt \$0\textbackslash \it <name>}).
|
|
%
|
|
\item \lstinline[language=C++]{current_case} \\
|
|
A pointer to a \lstinline[language=C++]{RTLIL::CaseRule} object. Initially this is the root case of the
|
|
generated \lstinline[language=C++]{RTLIL::Process}.
|
|
\end{itemize}
|
|
|
|
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
|
|
\lstinline[language=C++]{RTLIL::Process} object with an empty root case and
|
|
initializes its state variables as described above. Then the \lstinline[language=C++]{RTLIL::SyncRule} objects
|
|
are created using the synchronization events from the {\tt AST\_ALWAYS} node and the initial values of
|
|
\lstinline[language=C++]{subst_lvalue_from} and \lstinline[language=C++]{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:
|
|
{\tt AST\_ASSIGN\_LE} (nonblocking assignments), {\tt AST\_ASSIGN\_EQ} (blocking assignments) and
|
|
{\tt AST\_CASE} (\lstinline[language=Verilog]{if} or \lstinline[language=Verilog]{case} statement).
|
|
|
|
\subsubsection{Handling of Nonblocking Assignments}
|
|
|
|
When an {\tt AST\_ASSIGN\_LE} node is discovered, the following actions are performed by the
|
|
ProcessGenerator:
|
|
|
|
\begin{itemize}
|
|
\item The left-hand-side is evaluated using \lstinline[language=C++]{AST::AstNode::genRTLIL()} and mapped to
|
|
a temporary signal name using \lstinline[language=C++]{subst_lvalue_from} and \lstinline[language=C++]{subst_lvalue_to}.
|
|
%
|
|
\item The right-hand-side is evaluated using \lstinline[language=C++]{AST::AstNode::genRTLIL()}. For this call,
|
|
the values of \lstinline[language=C++]{subst_rvalue_from} and \lstinline[language=C++]{subst_rvalue_to} are used to
|
|
map blocking-assigned signals correctly.
|
|
%
|
|
\item Remove all assignments to the same left-hand-side as this assignment from the \lstinline[language=C++]{current_case}
|
|
and all cases within it.
|
|
%
|
|
\item Add the new assignment to the \lstinline[language=C++]{current_case}.
|
|
\end{itemize}
|
|
|
|
\subsubsection{Handling of Blocking Assignments}
|
|
|
|
When an {\tt AST\_ASSIGN\_EQ} node is discovered, the following actions are performed by
|
|
the ProcessGenerator:
|
|
|
|
\begin{itemize}
|
|
\item Perform all the steps that would be performed for a nonblocking assignment (see above).
|
|
%
|
|
\item Remove the found left-hand-side (before lvalue mapping) from
|
|
\lstinline[language=C++]{subst_rvalue_from} and also remove the respective
|
|
bits from \lstinline[language=C++]{subst_rvalue_to}.
|
|
%
|
|
\item Append the found left-hand-side (before lvalue mapping) to \lstinline[language=C++]{subst_rvalue_from}
|
|
and append the found right-hand-side to \lstinline[language=C++]{subst_rvalue_to}.
|
|
\end{itemize}
|
|
|
|
\subsubsection{Handling of Cases and if-Statements}
|
|
|
|
\begin{sloppypar}
|
|
When an {\tt AST\_CASE} node is discovered, the following actions are performed by
|
|
the ProcessGenerator:
|
|
|
|
\begin{itemize}
|
|
\item The values of \lstinline[language=C++]{subst_rvalue_from}, \lstinline[language=C++]{subst_rvalue_to},
|
|
\lstinline[language=C++]{subst_lvalue_from} and \lstinline[language=C++]{subst_lvalue_to} are pushed to the stack.
|
|
%
|
|
\item A new \lstinline[language=C++]{RTLIL::SwitchRule} object is generated, the selection expression is evaluated using
|
|
\lstinline[language=C++]{AST::AstNode::genRTLIL()} (with the use of \lstinline[language=C++]{subst_rvalue_from} and
|
|
\lstinline[language=C++]{subst_rvalue_to}) and added to the \lstinline[language=C++]{RTLIL::SwitchRule} object and the
|
|
object is added to the \lstinline[language=C++]{current_case}.
|
|
%
|
|
\item All lvalues assigned to within the {\tt AST\_CASE} node using blocking assignments are collected and
|
|
saved in the local variable \lstinline[language=C++]{this_case_eq_lvalue}.
|
|
%
|
|
\item New temporary signals are generated for all signals in \lstinline[language=C++]{this_case_eq_lvalue} and stored
|
|
in \lstinline[language=C++]{this_case_eq_ltemp}.
|
|
%
|
|
\item The signals in \lstinline[language=C++]{this_case_eq_lvalue} are mapped using \lstinline[language=C++]{subst_rvalue_from}
|
|
and \lstinline[language=C++]{subst_rvalue_to} and the resulting set of signals is stored in
|
|
\lstinline[language=C++]{this_case_eq_rvalue}.
|
|
\end{itemize}
|
|
|
|
Then the following steps are performed for each {\tt AST\_COND} node within the {\tt AST\_CASE} node:
|
|
|
|
\begin{itemize}
|
|
\item Set \lstinline[language=C++]{subst_rvalue_from}, \lstinline[language=C++]{subst_rvalue_to},
|
|
\lstinline[language=C++]{subst_lvalue_from} and \lstinline[language=C++]{subst_lvalue_to} to the values
|
|
that have been pushed to the stack.
|
|
%
|
|
\item Remove \lstinline[language=C++]{this_case_eq_lvalue} from
|
|
\lstinline[language=C++]{subst_lvalue_from}/\lstinline[language=C++]{subst_lvalue_to}.
|
|
%
|
|
\item Append \lstinline[language=C++]{this_case_eq_lvalue} to \lstinline[language=C++]{subst_lvalue_from} and append
|
|
\lstinline[language=C++]{this_case_eq_ltemp} to \lstinline[language=C++]{subst_lvalue_to}.
|
|
%
|
|
\item Push the value of \lstinline[language=C++]{current_case}.
|
|
%
|
|
\item Create a new \lstinline[language=C++]{RTLIL::CaseRule}. Set \lstinline[language=C++]{current_case} to the
|
|
new object and add the new object to the \lstinline[language=C++]{RTLIL::SwitchRule} created above.
|
|
%
|
|
\item Add an assignment from \lstinline[language=C++]{this_case_eq_rvalue} to \lstinline[language=C++]{this_case_eq_ltemp}
|
|
to the new \lstinline[language=C++]{current_case}.
|
|
%
|
|
\item Evaluate the compare value for this case using \lstinline[language=C++]{AST::AstNode::genRTLIL()} (with the use of
|
|
\lstinline[language=C++]{subst_rvalue_from} and \lstinline[language=C++]{subst_rvalue_to}) modify the new
|
|
\lstinline[language=C++]{current_case} accordingly.
|
|
%
|
|
\item Recursion into the children of the {\tt AST\_COND} node.
|
|
%
|
|
\item Restore \lstinline[language=C++]{current_case} by popping the old value from the stack.
|
|
\end{itemize}
|
|
|
|
Finally the following steps are performed:
|
|
|
|
\begin{itemize}
|
|
\item The values of \lstinline[language=C++]{subst_rvalue_from}, \lstinline[language=C++]{subst_rvalue_to},
|
|
\lstinline[language=C++]{subst_lvalue_from} and \lstinline[language=C++]{subst_lvalue_to} are popped from the stack.
|
|
%
|
|
\item The signals from \lstinline[language=C++]{this_case_eq_lvalue} are removed from the
|
|
\lstinline[language=C++]{subst_rvalue_from}/\lstinline[language=C++]{subst_rvalue_to}-pair.
|
|
%
|
|
\item The value of \lstinline[language=C++]{this_case_eq_lvalue} is appended to \lstinline[language=C++]{subst_rvalue_from}
|
|
and the value of \lstinline[language=C++]{this_case_eq_ltemp} is appended to \lstinline[language=C++]{subst_rvalue_to}.
|
|
%
|
|
\item Map the signals in \lstinline[language=C++]{this_case_eq_lvalue} using
|
|
\lstinline[language=C++]{subst_lvalue_from}/\lstinline[language=C++]{subst_lvalue_to}.
|
|
%
|
|
\item Remove all assignments to signals in \lstinline[language=C++]{this_case_eq_lvalue} in \lstinline[language=C++]{current_case}
|
|
and all cases within it.
|
|
%
|
|
\item Add an assignment from \lstinline[language=C++]{this_case_eq_ltemp} to \lstinline[language=C++]{this_case_eq_lvalue}
|
|
to \lstinline[language=C++]{current_case}.
|
|
\end{itemize}
|
|
\end{sloppypar}
|
|
|
|
\subsubsection{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
|
|
{\tt \$0}-prefix) and this variable is used for all assignments of the variable.
|
|
|
|
Note how all the \lstinline[language=C++]{_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 {\tt 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 \lstinline[language=C++]{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 \lstinline[language=C++]{AST::AstNode::genRTLIL()} with a
|
|
\lstinline[language=C++]{subst_rvalue_from}/\lstinline[language=C++]{subst_rvalue_to}-tuple that contained
|
|
the right-hand-side of the old assignment.
|
|
|
|
\subsection{The proc pass}
|
|
|
|
The ProcessGenerator converts a behavioural model in AST representation to a behavioural model in
|
|
\lstinline[language=C++]{RTLIL::Process} representation. The actual conversion from a behavioural
|
|
model to an RTL representation is performed by the {\tt proc} pass and the passes it launches:
|
|
|
|
\begin{itemize}
|
|
\item {\tt proc\_clean} and {\tt proc\_rmdead} \\
|
|
These two passes just clean up the \lstinline[language=C++]{RTLIL::Process} structure. The {\tt proc\_clean}
|
|
pass removes empty parts (eg. empty assignments) from the process and {\tt proc\_rmdead} detects and removes
|
|
unreachable branches from the process's decision trees.
|
|
%
|
|
\item {\tt 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 \C{RTLIL::SwitchRule} for the reset path. After this pass the
|
|
sync rule for the reset is level-sensitive and the top-level
|
|
\C{RTLIL::SwitchRule} has been removed.
|
|
%
|
|
\item {\tt proc\_mux} \\
|
|
This pass converts the \C{RTLIL::CaseRule}/\C{RTLIL::SwitchRule}-tree to a tree
|
|
of multiplexers per written signal. After this, the \C{RTLIL::Process} structure only contains
|
|
the \C{RTLIL::SyncRule}s that describe the output registers.
|
|
%
|
|
\item {\tt proc\_dff} \\
|
|
This pass replaces the \C{RTLIL::SyncRule}s to d-type flip-flops (with
|
|
asynchronous resets if necessary).
|
|
%
|
|
\item {\tt proc\_clean} \\
|
|
A final call to {\tt proc\_clean} removes the now empty \C{RTLIL::Process} objects.
|
|
\end{itemize}
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
\section{Synthesizing Verilog Arrays}
|
|
|
|
\begin{fixme}
|
|
Add some information on the generation of {\tt \$memrd} and {\tt \$memwr} cells
|
|
and how they are processed in the {\tt memory} pass.
|
|
\end{fixme}
|
|
|
|
\section{Synthesizing Parametric Designs}
|
|
|
|
\begin{fixme}
|
|
Add some information on the \lstinline[language=C++]{RTLIL::Module::derive()} method and how it
|
|
is used to synthesize parametric modules via the {\tt hierarchy} pass.
|
|
\end{fixme}
|
|
|