1
0
mirror of https://github.com/myhdl/myhdl.git synced 2025-01-24 21:52:56 +08:00
myhdl/doc/whatsnew04/whatsnew04.tex
2004-01-28 22:45:06 +00:00

567 lines
16 KiB
TeX

\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: Conversion to Verilog}
\release{0.4}
\author{Jan Decaluwe}
\authoraddress{\email{jan@jandecaluwe.com}}
\begin{document}
\maketitle
\tableofcontents
\section{Introduction}
\myhdl\ 0.4 introduces a major new capability:
generating a hardware implementation automatically
from a hardware description in Python.
The solution works as follows. The hardware description should be
modeled using the \myhdl\ library, 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 a function 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.
As mentioned earlier, a hardware model intended for implementation
should satisfy certain constraints. Because of the nature of hardware,
these constraints are relatively severe. For example,
it must be possible to infer the bit width of signals in order to
implement them as hardware busses or registers. In the following, I
will assume that the reader is familiar with this kind of constraints.
\section{Feature overview\label{section-feature}}
\begin{description}
\item[The design is converted after elaboration.]
\emph{Elaboration} refers to the initial processing of
a hardware description to achieve a representation 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.
An elaborated design corresponds to a \class{Simulation}
argument. Likewise, the Verilog conversion works on an
elaborated design. The Python interpreter is thus used
as much as possible, resulting in more power to the
\myhdl\ user and less work for the developer.
\item[The design structure can be arbitrarily complex and hierarchical.]
As the conversion works on an elaborated design, 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.
\item[If-then-else structures with enumeration type items are 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.
\item[One hot and one cold encoding of enumeration type items are supported.]
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.
\end{description}
\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_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{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}
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)[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 [8-1:0] index;
if ((reset_n == 0)) begin
SOF <= 0;
index[8-1:0] = 0;
state <= 3'b001;
end
else begin
SOF <= 0;
// synthesis parallel_case full_case
casez (state)
3'b??1: begin
index[8-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[8-1:0] = ((index + 1) % 8);
end
end
endmodule
\end{verbatim}
\section{The convertible subset}
\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 optional \keyword{else}
statement 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.]
\item[The \keyword{return} statement.]
\item[The \keyword{yield} statement.]
\item[The \keyword{while} statement.]
The optional \keyword{else}
statement is not supported.
\end{description}
\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}