\documentclass{howto} % \usepackage{distutils} \usepackage{palatino} \renewcommand{\ttdefault}{cmtt} \renewcommand{\sfdefault}{cmss} \newcommand{\myhdl}{\protect \mbox{MyHDL}} \usepackage{graphicx} % $Id$ \title{New in \myhdl\ 0.4: Verilog conversion for implementation} \release{0.4} \author{Jan Decaluwe} \authoraddress{\email{jan@jandecaluwe.com}} \begin{document} \maketitle \tableofcontents \section{Introduction and rationale\label{section-intro}} MyHDL 0.4 provides a path to automatic implementation through conversion to Verilog. Automatic implementation is called \emph{synthesis} in the hardware design world and the corresponding source code is called \emph{synthesizable code}. In a typical design project, the amount of synthesizable code is only a small fraction all source code: the larger part will consist of high-level models, project maintenance and automation code, and verification related code such as test benches. Still, synthesizable code is of special importance in several ways. First, the implementation is what counts in the end. Secondly, synthesizable code has important restrictions and a different concept of Quality of Results. Finally, synthesizable code is what many designers like to do most and first (regardless of whether this is a good idea from a methodology viewpoint). MyHDL aims to be a complete solution, but prior to 0.4 a MyHDL user had to translate synthesizable code manually to Verilog or VHDL. Needless to say, this is very inconvenient. With 0.4, this manual step should no longer be necessary. XXX advantages. \section{Solution description\label{section-solution}} The solution works as follows. The hardware description should be modeled in \myhdl\ style, and satisfy certain constraints that are typical for implementation-oriented hardware modeling. Subsequently, such a design is converted to an equivalent model in the Verilog language, using the function \function{toVerilog} from the \myhdl\ library. Finally, an third-party \emph{synthesis tool} is used to convert the Verilog design into a gate implementation for an ASIC or FPGA. There are a number of Verilog synthesis tools available, varying in price, capabilities, and target implementation space. \section{Features\label{section-features}} \subsection{The design is converted after elaboration} \emph{Elaboration} refers to the initial processing of a hardware description to achieve a representation of a design instance that is ready for simulation or synthesis. In particular, structural parameters and constructs are processed in this step. In \myhdl{}, the Python interpreter itself is used for elaboration. A \class{Simulation} is constructed with elaborated design instances as arguments. Likewise, the Verilog conversion works on an elaborated design instance. The Python interpreter is thus used as much as possible. \subsection{The structural description can be arbitrarily complex and hierarchical} As the conversion works on an elaborated design instance, any modeling constraints only apply to the leaf elements of the design structure, that is, the co-operating generators. In other words, there are no restrictions on the description of the design structure: Python's full power can be used for that purpose. Also, the design hierarchy can be arbitrarily deep. \subsection{If-then-else structures may be mapped to case statements} Python does not provide a case statement. However, the converter recognizes if-then-else structures in which a variable is sequentially compared to items of an enumeration type, and maps such a structure to a Verilog case statement with the appropriate synthesis attributes. \subsection{Support for one hot and one cold encoding} The \function{enum} function in \myhdl\ returns an enumeration type. This function takes an additional parameter \var{encoding} that specifies the desired encoding in the implementation: binary, one hot, or one cold. The Verilog converter generates the appropriate code. \section{The convertible subset} \subsection{Introduction} Unsurprisingly, not all Python code can be converted into Verilog. In fact, there are very important restrictions. As the goal of the conversion functionality is implementation, this should not be a big issue: anyone familiar with synthesis is used to similar restrictions in the \emph{synthesizable subset} of Verilog and VHDL. The implementation attempts to issue clear error messages when it encounters a construct that cannot be converted. In practice, the synthesizable subset usually refers to RTL synthesis, which is by far the most popular type of synthesis today. There are industry standards that define the RTL synthesis subset. However, those were not used as a model for the restrictions of the MyHDL converter, but as a minimal starting point. On that basis, whenever it was judged easy or useful to support an additional feature, this was done. For example, it is actually easier to convert while loops than for loops even though they are not RTL-synthesizable. As another example, 'print' is supported because it's so useful for debugging, even though it's not synthesizable. In summary, the convertible subset is a superset of the standard RTL synthesis subset, and supports synthesis tools with more advanced capabilities, such as behavioral synthesis. Recall that any restrictions only apply to the design post elaboration. In practice, this means that they apply only to the code of the generators, that are the "leaf" functional blocks in a MyHDL design. \subsection{Coding style} A natural restriction on convertible code is that it should be written in MyHDL style: cooperating generators, communicating through signals, and with \code{yield} statements specifying wait points and resume conditions. Supported resume conditions are a signal edge, a signal change, or a tuple of such conditions. \subsection{Supported types} The most important restriction regards object types. Verilog is an almost typeless language, while Python is strongly (albeit dynamically) typed. The converter needs to infer the types of variables and map them to Verilog types. Therefore, it does type inferencing of object constructors and expressions. Only a limited amount of types can be converted. Python \class{int} and \class{long} objects are mapped to Verilog integers. All other supported types are mapped to Verilog regs (or wires), and therefore need to have a defined bit width. The supported types are the Python \class{bool} type, the MyHDL \class{intbv} type, and MyHDL enumeration types returned by function \function{enum}. The latter objects can also be used as the base object of a \class{Signal}. \class{intbv} objects need to be constructed so that a bit width can be inferred. This can be done by specifying minimum and maximum values, e.g. as follows: \begin{verbatim} index = intbv(0, min=0, max=2**N) \end{verbatim} Alternatively, a slice can be taken from an \class{intbv} object as follows: \begin{verbatim} index = intbv(0)[N:] \end{verbatim} Such as slice returns a new \class{intbv} object, with minimum value \code{0} inclusive, and maximum value \code{2**N} exclusive. \subsection{Supported statements} The following is a list of the statements that are supported by the Verilog converter, possibly qualified with restrictions. Recall that this list only applies to the design post elaboration: in practice, this means it applies to the code of the generators that are the leaf blocks in a design. \begin{description} \item[The \keyword{break} statement.] \item[The \keyword{continue} statement.] \item[The \keyword{def} statement.] \item[The \keyword{for} statement.] The only supported iteration scheme is through lists of integers returned by builtin function \function{range} or \myhdl\ function \function{downrange} is supported. The optional \keyword{else} clause is not supported. \item[The \keyword{if} statement.] \keyword{if}, \keyword{elif}, and \keyword{else} clauses are fully supported. \item[The \keyword{pass} statement.] \item[The \keyword{print} statement.] The only supported expression for printing is a single literal string. The string can be interpolated, but the format specifiers are copied verbatim to the Verilog output. Print to a file with syntax \code{'>>'} is not supported. \item[The \keyword{raise} statement.] This statement is mapped to Verilog statements that end the simulation with an error message. \item[The \keyword{return} statement.] \item[The \keyword{yield} statement.] The yielded expression can be a signal, a signal edge as specified by \myhdl\ functions \function{posedge} or \function{negedge}, or a tuple of signals and edge specifications. \item[The \keyword{while} statement.] The optional \keyword{else} clause is not supported. \end{description} \section{Methodology notes} XXX \section{Converter usage} We will demonstrate the conversion process by showing some examples. \subsection{A small design with a single generator} Consider the following MyHDL code for an incrementer module: \begin{verbatim} def inc(count, enable, clock, reset, n): """ Incrementer with enable. count -- output enable -- control input, increment when 1 clock -- clock input reset -- asynchronous reset input n -- counter max value """ def incProcess(): while 1: yield posedge(clock), negedge(reset) if reset == ACTIVE_LOW: count.next = 0 else: if enable: count.next = (count + 1) % n return incProcess() \end{verbatim} In Verilog terminology, function \function{inc} corresponds to a module, while generator function \function{incProcess} roughly corresponds to an always block. Normally, to simulate the design, we would "elaborate" an instance as follows: \begin{verbatim} m = 8 n = 2 ** m count = Signal(intbv(0)[m:]) enable = Signal(bool(0)) clock, reset = [Signal(bool()) for i in range(2)] inc_inst = inc(count, enable, clock, reset, n=n) \end{verbatim} \code{inc_inst} is an elaborated design instance that can be simulated. To convert it to Verilog, we change the last line as follows: \begin{verbatim} inc_inst = toVerilog(inc, count, enable, clock, reset, n=n) \end{verbatim} Again, this creates an instance that can be simulated, but as a side effect, it also generates a Verilog module in file \file{inc_inst.v}, that is supposed to have identical behavior. The Verilog code is as follows: \begin{verbatim} module inc_inst ( count, enable, clock, reset ); output [7:0] count; reg [7:0] count; input enable; input clock; input reset; always @(posedge clock or negedge reset) begin: _MYHDL1_BLOCK if ((reset == 0)) begin count <= 0; end else begin if (enable) begin count <= ((count + 1) % 256); end end end endmodule \end{verbatim} You can see the module interface and the always block, as expected from the MyHDL design. \subsection{Converting a generator directly} It is also possible to convert a generator directly. For example, consider the following generator function: \begin{verbatim} def bin2gray(B, G, width): """ Gray encoder. B -- input intbv signal, binary encoded G -- output intbv signal, gray encoded width -- bit width """ Bext = intbv(0)[width+1:] while 1: yield B Bext[:] = B for i in range(width): G.next[i] = Bext[i+1] ^ Bext[i] \end{verbatim} As before, you can create an instance and convert to Verilog as follows: \begin{verbatim} width = 8 B = Signal(intbv(0)[width:]) G = Signal(intbv(0)[width:]) bin2gray_inst = toVerilog(bin2gray, B, G, width) \end{verbatim} The generate Verilog module is as follows: \begin{verbatim} module bin2gray_inst ( B, G ); input [7:0] B; output [7:0] G; reg [7:0] G; always @(B) begin: _MYHDL1_BLOCK integer i; reg [9-1:0] Bext; Bext[9-1:0] = B; for (i=0; i<8; i=i+1) begin G[i] <= (Bext[(i + 1)] ^ Bext[i]); end end endmodule \end{verbatim} \subsection{A hierarchical design} The hierarchy of convertible designs can be arbitrarily deep. For example, suppose we want to design an incrementer with Gray code output. Using the designs from previous sections, we can proceed as follows: \begin{verbatim} def GrayInc(graycnt, enable, clock, reset, width): bincnt = Signal(intbv()[width:]) INC_1 = inc(bincnt, enable, clock, reset, n=2**width) BIN2GRAY_1 = bin2gray(B=bincnt, G=graycnt, width=width) return INC_1, BIN2GRAY_1 \end{verbatim} According to Gray code properties, only a single bit will change in consecutive values. However, as the \code{bin2gray} module is combinatorial, the output bits may have transient glitches, which may not be desirable. To solve this, let's create an additional level of hierarchy an add an output register to the design. (This will create an additional latency of a clock cycle, which may not be acceptable, but we will ignore that here.) \begin{verbatim} def GrayIncReg(graycnt, enable, clock, reset, width): graycnt_comb = Signal(intbv()[width:]) GRAY_INC_1 = GrayInc(graycnt_comb, enable, clock, reset, width) def reg(): while 1: yield posedge(clock) graycnt.next = graycnt_comb REG_1 = reg() return GRAY_INC_1, REG_1 \end{verbatim} We can convert this hierchical design as usual: \begin{verbatim} width = 8 graycnt = Signal(intbv()[width:]) enable, clock, reset = [Signal(bool()) for i in range(3)] GRAY_INC_REG_1 = toVerilog(GrayIncReg, graycnt, enable, clock, reset, width) \end{verbatim} The Verilog output module looks as follows: \begin{verbatim} module GRAY_INC_REG_1 ( graycnt, enable, clock, reset ); output [7:0] graycnt; reg [7:0] graycnt; input enable; input clock; input reset; reg [7:0] graycnt_comb; reg [7:0] _GRAY_INC_1_bincnt; always @(posedge clock or negedge reset) begin: _MYHDL1_BLOCK if ((reset == 0)) begin _GRAY_INC_1_bincnt <= 0; end else begin if (enable) begin _GRAY_INC_1_bincnt <= ((_GRAY_INC_1_bincnt + 1) % 256); end end end always @(_GRAY_INC_1_bincnt) begin: _MYHDL4_BLOCK integer i; reg [9-1:0] Bext; Bext[9-1:0] = _GRAY_INC_1_bincnt; for (i=0; i<8; i=i+1) begin graycnt_comb[i] <= (Bext[(i + 1)] ^ Bext[i]); end end always @(posedge clock) begin: _MYHDL9_BLOCK graycnt <= graycnt_comb; end endmodule \end{verbatim} Note that the output is a flat ``netlist of blocks'', and that hierarchical signal names are generated as necessary. \subsection{Optimizations for finite state machines} As often in hardware design, finite state machines deserve special attention. In Verilog and VHDL, finite state machines are typically described using case statements. Python doesn't have a case statement, but the converter recognizes particular if-then-else structures and maps them to case statements. This optimization occurs when a variable whose type is an enumerated type is sequentially tested against enumeration items in an if-then-else structure. Also, the appropriate synthesis pragmas for efficient synthesis are generated in the Verilog code. As a further optimization, function \function{enum} was enhanced to support alternative encoding schemes elegantly, using an additional parameter 'encoding'. For example: \begin{verbatim} t_State = enum('SEARCH', 'CONFIRM', 'SYNC', encoding="one_hot") \end{verbatim} The default encoding is \code{binary}; the other possibilities are \code{one_hot} and \code{one_cold}. This parameter only affects the conversion output, not the behavior of the type. Verilog case statements are optimized for an efficient implementation according to the encoding. Note that in contrast, a Verilog designer needs to make nontrivial code changes to implement a different encoding scheme. As an example, consider the following finite state machine, whose state variable used the enumeration type defined above: \begin{verbatim} FRAME_SIZE = 8 def FramerCtrl(SOF, state, syncFlag, clk, reset_n): """ Framing control FSM. SOF -- start-of-frame output bit state -- FramerState output syncFlag -- sync pattern found indication input clk -- clock input reset_n -- active low reset """ index = intbv(0, min=0, max=8) # position in frame while 1: yield posedge(clk), negedge(reset_n) if reset_n == ACTIVE_LOW: SOF.next = 0 index[:] = 0 state.next = t_State.SEARCH else: SOF.next = 0 if state == t_State.SEARCH: index[:] = 0 if syncFlag: state.next = t_State.CONFIRM elif state == t_State.CONFIRM: if index == 0: if syncFlag: state.next = t_State.SYNC else: state.next = t_State.SEARCH elif state == t_State.SYNC: if index == 0: if not syncFlag: state.next = t_State.SEARCH SOF.next = (index == FRAME_SIZE-1) else: raise ValueError("Undefined state") index[:]= (index + 1) % FRAME_SIZE \end{verbatim} The conversion is done as usual: \begin{verbatim} SOF = Signal(bool(0)) syncFlag = Signal(bool(0)) clk = Signal(bool(0)) reset_n = Signal(bool(1)) state = Signal(t_State.SEARCH) framerctrl_inst = toVerilog(FramerCtrl, SOF, state, syncFlag, clk, reset_n) \end{verbatim} The verilog output looks as follows: \begin{verbatim} module framerctrl_inst ( SOF, state, syncFlag, clk, reset_n ); output SOF; reg SOF; output [2:0] state; reg [2:0] state; input syncFlag; input clk; input reset_n; always @(posedge clk or negedge reset_n) begin: _MYHDL1_BLOCK reg [3-1:0] index; if ((reset_n == 0)) begin SOF <= 0; index[3-1:0] = 0; state <= 3'b001; end else begin SOF <= 0; // synthesis parallel_case full_case casez (state) 3'b??1: begin index[3-1:0] = 0; if (syncFlag) begin state <= 3'b010; end end 3'b?1?: begin if ((index == 0)) begin if (syncFlag) begin state <= 3'b100; end else begin state <= 3'b001; end end end 3'b1??: begin if ((index == 0)) begin if ((!syncFlag)) begin state <= 3'b001; end end SOF <= (index == (8 - 1)); end default: begin $display("Verilog: ValueError(Undefined state)"); $finish; end endcase index[3-1:0] = ((index + 1) % 8); end end endmodule \end{verbatim} \section{Known issues} \begin{description} \item[Negative values of \class{intbv} instances are not supported.] The \class{intbv} class is quite capable of representing negative values. However, the \code{signed} type support in Verilog is relatively recent and mapping to it may be tricky. In my judgment, this is not the most urgent requirement, so I decided to leave this for later. \item[Verilog integers are 32 bit wide] Usually, Verilog integers are 32 bit wide. In contrast, Python is moving towards integers with undefined width. Python \class{int} and \class{long} variables are mapped to Verilog integers; so for values larger than 32 bit this mapping is incorrect. \item[Synthesis pragmas are specified as Verilog comments.] The recommended way to specify synthesis pragmas in Verilog is through attribute lists. However, my Verilog simulator (Icarus) doesn't support them for \code{case} statements (to specify \code{parallel_case} and \code{full_case} pragmas). Therefore, I still used the old but deprecated method of synthesis pragmas in Verilog comments. \item[Inconsistent place of the sensitivity list inferred from \code{always_comb}.] The semantics of \code{always_comb}, both in Verilog and \myhdl{}, is to have an implicit sensitivity list at the end of the code. However, this may not be synthesizable. Therefore, the inferred sensitivity list is put at the top of the corresponding \code{always} block. This may cause inconsistent behavior at the start of the simulation. The workaround is to create events at time 0. \item[Non-blocking assignments to task arguments don't work.] I didn't get non-blocking (signal) assignments to task arguments to work. I don't know yet whether the issue is my own, a Verilog issue, or an issue with my Verilog simulator Icarus. I'll need to check this further. \end{description} \end{document}