mirror of
https://github.com/myhdl/myhdl.git
synced 2024-12-14 07:44:38 +08:00
318 lines
9.5 KiB
TeX
318 lines
9.5 KiB
TeX
\chapter{Introduction to \myhdl\ }
|
|
|
|
\section{A basic \myhdl\ simulation}
|
|
|
|
We will introduce \myhdl\ with a classical \code{Hello World} style
|
|
example. Here are the contents of a \myhdl\ simulation script called
|
|
\file{hello1.py}:
|
|
|
|
\begin{verbatim}
|
|
from myhdl import delay, now, Simulation
|
|
|
|
def sayHello():
|
|
while 1:
|
|
yield delay(10)
|
|
print "%s Hello World!" % now()
|
|
|
|
gen = sayHello()
|
|
sim = Simulation(gen)
|
|
sim.run(30)
|
|
|
|
\end{verbatim}
|
|
|
|
When we run this simulation, we get the following output:
|
|
|
|
\begin{verbatim}
|
|
% python hello1.py
|
|
10 Hello World!
|
|
20 Hello World!
|
|
30 Hello World!
|
|
StopSimulation: Simulated for duration 30
|
|
|
|
\end{verbatim}
|
|
|
|
The first line of the script imports a
|
|
number of objects from the \code{myhdl} package. In good Python style, and
|
|
unlike most other languages, we can only use identifiers that are
|
|
\emph{literally} defined in the source file \footnote{I don't want to
|
|
explain the \samp{import *} syntax}.
|
|
|
|
Next, we define a generator function called
|
|
\code{sayHello}. This is a generator function (as opposed to
|
|
a classic Python function) because it contains a \keyword{yield}
|
|
statement (instead of \keyword{return} statement). In \myhdl\, a
|
|
\keyword{yield} statement has a similar purpose as a \keyword{wait}
|
|
statement in VHDL: the statement suspends execution of the function,
|
|
and its clauses specify when the function should resume. In this case,
|
|
there is a \code{delay} clause, that specifies the required delay.
|
|
|
|
To make sure that the generator runs ``forever'', we wrap its behavior
|
|
in a \code{while 1} loop. This is a standard Python idiom, and it is
|
|
the \myhdl\ equivalent of the implicit looping behavior of a Verilog
|
|
\keyword{always} block and a VHDL \keyword{process}.
|
|
|
|
In \myhdl\, the basic simulation objects are generators. Generators
|
|
are created by calling generator functions. For example, variable
|
|
\code{gen} refers to a generator. To simulate this generator, we pass
|
|
it as an argument to a \class{Simulation} object constructor. We then
|
|
run the simulation for the desired amount of time. In \myhdl\, time
|
|
is modeled by a natural integer.
|
|
|
|
|
|
\section{Concurrent generators and signals}
|
|
|
|
In the previous section, we simulated a single generator. Of course,
|
|
real hardware descriptions are not like that: in fact, they are
|
|
typically massively concurrent. \myhdl\ supports this by allowing an
|
|
arbitrary number of concurrent generators. More specifically, a
|
|
\class{Simulation} constructor can take an arbitrary number of
|
|
arguments, each of which can be a generator or a nested sequence of
|
|
generators.
|
|
|
|
With concurrency comes the problem of deterministic
|
|
communication. Therefore, hardware languages use special objects to
|
|
support deterministic communication between concurrent code. \myhdl\
|
|
has as \class{Signal} object which is roughly modeled after VHDL
|
|
signals.
|
|
|
|
We will demonstrate these concepts by extending and modifying our
|
|
first example. We introduce a clock signal, driven by a second
|
|
generator:
|
|
|
|
\begin{verbatim}
|
|
clk = Signal(0)
|
|
|
|
def clkGen():
|
|
while 1:
|
|
yield delay(10)
|
|
clk.next = 1
|
|
yield delay(10)
|
|
clk.next = 0
|
|
|
|
\end{verbatim}
|
|
|
|
The \code{clk} signal is constructed with an initial value
|
|
\code{0}. In the clock generator function \code{clkGen}, it is
|
|
continuously assigned a new value after a certain delay. In \myhdl{},
|
|
the new value of a signal is specified by assigning to its
|
|
\code{next} attribute. This is the \myhdl\ equivalent of VHDL signal
|
|
assignments and Verilog's non-blocking assignments.
|
|
|
|
The \code{sayHello} generator function is modified to wait for a
|
|
rising edge of the clock instead of a delay:
|
|
|
|
\begin{verbatim}
|
|
def sayHello():
|
|
while 1:
|
|
yield posedge(clk)
|
|
print "%s Hello World!" % now()
|
|
|
|
\end{verbatim}
|
|
|
|
Waiting for a clock edge is achieved with a second form of the
|
|
\keyword{yield} statement: \samp{yield posedge(\var{signal})}.
|
|
A \class{Simulation} object will suspend the generator as that point,
|
|
and resume it when there is a rising edge on the signal.
|
|
|
|
The \class{Simulation} is now constructed with 2 generator arguments:
|
|
|
|
\begin{verbatim}
|
|
sim = Simulation(clkGen(), sayHello())
|
|
sim.run(50)
|
|
|
|
\end{verbatim}
|
|
|
|
When we run this simulation, we get:
|
|
|
|
\begin{verbatim}
|
|
% python hello2.py
|
|
10 Hello World!
|
|
30 Hello World!
|
|
50 Hello World!
|
|
StopSimulation: Simulated for duration 50
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
\section{Parameters, instantiations and hierarchy}
|
|
|
|
So far, the generator function examples had no parameters. For
|
|
example, the \code{clk} signal was defined in the enclosing scope of
|
|
the generator functions. However, to make the code reusable we will
|
|
want to pass arguments through a parameter list. For example, we can
|
|
change the clock generator function to make it more general
|
|
and reusable, as follows:
|
|
|
|
\begin{verbatim}
|
|
def clkGen(clock, period=20):
|
|
lowTime = int(period / 2)
|
|
highTime = period - lowTime
|
|
while 1:
|
|
yield delay(lowTime)
|
|
clock.next = 1
|
|
yield delay(highTime)
|
|
clock.next = 0
|
|
|
|
\end{verbatim}
|
|
|
|
The clock signal is now a parameter of the function. Also, the clock
|
|
\var{period} is a parameter with a default value of \code{20}.
|
|
This makes \var{period} an \dfn{optional} parameter; if it is not
|
|
specified in a call, the default value will be used.
|
|
|
|
Similarly, the \code{sayHello} function can be made more general:
|
|
|
|
\begin{verbatim}
|
|
def sayHello(clock, to="World!"):
|
|
while 1:
|
|
yield posedge(clock)
|
|
print "%s Hello %s" % (now(), to)
|
|
|
|
\end{verbatim}
|
|
|
|
We can create any number of generators by calling generator functions
|
|
with the appropriate parameters. This is very similar to the concept of
|
|
\dfn{instantiation} in hardware description languages and we will use
|
|
the same terminology in \myhdl{}. Hierarchy can be modeled by defining
|
|
the instances in a higher-level function, and returning them. For
|
|
example:
|
|
|
|
\begin{verbatim}
|
|
def greetings():
|
|
|
|
clk1 = Signal(0)
|
|
clk2 = Signal(0)
|
|
|
|
clkGen1 = clkGen(clk1)
|
|
clkGen2 = clkGen(clock=clk2, period=19)
|
|
sayHello1 = sayHello(clock=clk1)
|
|
sayHello2 = sayHello(to="MyHDL", clock=clk2)
|
|
|
|
return clkGen1, clkGen2, sayHello1, sayHello2
|
|
|
|
\end{verbatim}
|
|
As in standard Python, positional or named parameter association can
|
|
be used in instantiations, or a mix of both\footnote{All positional
|
|
parameters have to come before any named parameter.}. All these styles
|
|
are demonstrated in the example above. As in hardware description
|
|
languages, named association can be very useful if there are a lot of
|
|
parameters, as the parameter order in the call does not matter in that
|
|
case.
|
|
|
|
\class{Simulation} constructor arguments can also be sequences of
|
|
generators. In this way, they support hierarchy: the return value of a
|
|
higher-level instantiating function can directly be used an
|
|
argument. For example:
|
|
|
|
\begin{verbatim}
|
|
sim = Simulation(greetings())
|
|
sim.run(50)
|
|
|
|
\end{verbatim}
|
|
|
|
This produces the following output:
|
|
|
|
\begin{verbatim}
|
|
% python greetings.py
|
|
9 Hello MyHDL
|
|
10 Hello World!
|
|
28 Hello MyHDL
|
|
30 Hello World!
|
|
47 Hello MyHDL
|
|
50 Hello World!
|
|
StopSimulation: Simulated for duration 50
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
\section{Bit-oriented operations}
|
|
\label{gray}
|
|
|
|
Hardware design involves dealing with bits and bit-oriented
|
|
operations. The standard Python \class{int} has most of the desired
|
|
features, but lacks support for indexing and slicing. Therefore,
|
|
\myhdl\ provides the \class{intbv} class. It works
|
|
transparently as an integer and with integers, and like \class{int},
|
|
offers access to the underlying a 2's complement representation for
|
|
bitwise operations. In addition, it is a mutable type that provides
|
|
indexing and slicing operations, and some additional bit-oriented
|
|
support such as concatenation.
|
|
|
|
As an example, we will consider the design of a Gray encoder. The
|
|
following code is a Gray encoder modeled in \myhdl{}:
|
|
|
|
\begin{verbatim}
|
|
def bin2gray(B, G, width):
|
|
""" Gray encoder.
|
|
|
|
B -- input intbv signal, binary encoded
|
|
G -- output intbv signal, Gray encoded
|
|
width -- bit width
|
|
"""
|
|
while 1:
|
|
yield B
|
|
for i in range(width):
|
|
G.next[i] = B[i+1] ^ B[i]
|
|
|
|
\end{verbatim}
|
|
|
|
This code introduces a few new concepts. The string in triple quotes
|
|
at the start of the function is a \dfn{doc string}. This is standard
|
|
Python practice for structured documentation of code. Moreover, we
|
|
use a third form of the \keyword{yield} statement:
|
|
\samp{yield \var{signal}}. This specifies that the generator should
|
|
resume whenever \var{signal} changes value. This is typically used to
|
|
describe combinatorial logic.
|
|
Finally, the code contains bit indexing operations and an exclusive-or
|
|
operator as required for a Gray encoder.
|
|
|
|
To verify the Gray encoder, we write a test bench that prints input
|
|
and output for all possible input values:
|
|
|
|
\begin{verbatim}
|
|
def testBench(width):
|
|
|
|
B = Signal(intbv(0))
|
|
G = Signal(intbv(0))
|
|
|
|
dut = bin2gray(B, G, width)
|
|
|
|
def stimulus():
|
|
for i in range(2**width):
|
|
B.next = intbv(i)
|
|
yield delay(10)
|
|
print "B: " + bin(B, width) + "| G: " + bin(G, width)
|
|
|
|
return (dut, stimulus())
|
|
|
|
\end{verbatim}
|
|
|
|
We use the conversion function \code{bin} to get a binary
|
|
string representation of the signal values. This function is exported
|
|
by the \code{myhdl} package and complements the standard Python
|
|
\code{hex} and \code{oct} conversion functions.
|
|
|
|
To demonstrate, we set up a simulation for a small width:
|
|
|
|
\begin{verbatim}
|
|
Simulation(testBench(width=3)).run()
|
|
|
|
\end{verbatim}
|
|
|
|
The simulation produces the following output:
|
|
|
|
\begin{verbatim}
|
|
% python bin2gray.py
|
|
B: 000 | G: 000
|
|
B: 001 | G: 001
|
|
B: 010 | G: 011
|
|
B: 011 | G: 010
|
|
B: 100 | G: 110
|
|
B: 101 | G: 111
|
|
B: 110 | G: 101
|
|
B: 111 | G: 100
|
|
StopSimulation: No more events
|
|
|
|
\end{verbatim}
|
|
|