mirror of
https://github.com/myhdl/myhdl.git
synced 2024-12-14 07:44:38 +08:00
556 lines
18 KiB
TeX
556 lines
18 KiB
TeX
\chapter{Introduction to \myhdl\ \label{intro}}
|
|
|
|
\section{A basic \myhdl\ simulation \label{intro-basic}}
|
|
|
|
We will introduce \myhdl\ with a classic \code{Hello World} style
|
|
example. All example code can be found in the distribution directory
|
|
under \file{example/manual/}. Here are the contents of a \myhdl\
|
|
simulation script called \file{hello1.py}:
|
|
|
|
\begin{verbatim}
|
|
from myhdl import Signal, delay, always, now, Simulation
|
|
|
|
def HelloWorld():
|
|
|
|
interval = delay(10)
|
|
|
|
@always(interval)
|
|
def sayHello():
|
|
print "%s Hello World!" % now()
|
|
|
|
return sayHello
|
|
|
|
|
|
inst = HelloWorld()
|
|
sim = Simulation(inst)
|
|
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!
|
|
_SuspendSimulation: Simulated 30 timesteps
|
|
\end{verbatim}
|
|
|
|
The first line of the script imports a number of objects from the
|
|
\code{myhdl} package. In Python we can only use identifiers that are
|
|
literally defined in the source file
|
|
\footnote{The exception is the \samp{from module import *} syntax,
|
|
that imports all the symbols from a module. Although this is generally
|
|
considered bad practice, it can be tolerated for large modules that
|
|
export a lot of symbols. One may argue that
|
|
\code{myhdl} falls into that category.}.
|
|
|
|
Then, we define a function called \function{HelloWorld}. In MyHDL,
|
|
classic functions are used to model hardware modules. In particular,
|
|
the parameter list is used to define the interface. In this first
|
|
example, the interface is empty.
|
|
|
|
Inside the top level function we declare a local function called
|
|
\function{sayHello} that defines the desired behavior. This function
|
|
is decorated with an \function{always} decorator that has a delay
|
|
\index{decorator!\function{always}}
|
|
object as its parameter. The meaning is that the function will be
|
|
executed whenever the specified delay interval has expired.
|
|
|
|
Behind the curtains, the \function{always} decorator creates a Python
|
|
\emph{generator} and reuses the name of the decorated function for
|
|
it. Generators are the fundamental objects in MyHDL, and we will say
|
|
much more about them further on.
|
|
|
|
Finally, the top level function returns the local generator. This is
|
|
the simplest case of the basic MyHDL code pattern
|
|
to define the contents of a hardware module. We will describe the
|
|
general case further on.
|
|
|
|
In MyHDL, we create an \emph{instance} of a hardware module by calling
|
|
the corresponding function. In the example, variable \code{inst} refers
|
|
to an instance of \function{HelloWorld}. To simulate the instance, we
|
|
pass it as an argument to a \class{Simulation} object constructor. We
|
|
then run the simulation for the desired amount of timesteps.
|
|
|
|
\section{Signals, ports, and concurrency \label{intro-conc}}
|
|
|
|
In the previous section, we simulated a design with a single
|
|
generator and no concurrency. On the other hand, real hardware
|
|
descriptions are typically massively concurrent.
|
|
\myhdl\ supports this by allowing an
|
|
arbitrary number of concurrently running generators.
|
|
|
|
With concurrency comes the problem of deterministic
|
|
communication. Hardware languages use special objects to
|
|
support deterministic communication between concurrent code.
|
|
In particular, \myhdl\
|
|
has a \class{Signal} object which is roughly modeled after VHDL
|
|
signals.
|
|
|
|
We will demonstrate signals and concurrency
|
|
by extending and modifying our first example. We define two hardware
|
|
modules, one that drives a clock signal, and one that is sensitive
|
|
to a positive edge on a clock signal:
|
|
|
|
|
|
\begin{verbatim}
|
|
from myhdl import Signal, delay, always, now, Simulation
|
|
|
|
|
|
def ClkDriver(clk):
|
|
|
|
halfPeriod = delay(10)
|
|
|
|
@always(halfPeriod)
|
|
def driveClk():
|
|
clk.next = not clk
|
|
|
|
return driveClk
|
|
|
|
|
|
def HelloWorld(clk):
|
|
|
|
@always(clk.posedge)
|
|
def sayHello():
|
|
print "%s Hello World!" % now()
|
|
|
|
return sayHello
|
|
|
|
|
|
clk = Signal(0)
|
|
clkdriver_inst = ClkDriver(clk)
|
|
hello_inst = HelloWorld(clk)
|
|
sim = Simulation(clkdriver_inst, hello_inst)
|
|
sim.run(50)
|
|
\end{verbatim}
|
|
|
|
The clock driver function \function{ClkDriver} has a
|
|
clock signal as its parameter. This is how a
|
|
\emph{port} is modeled in MyHDL. The function
|
|
defines a generator
|
|
that continuously toggles a clock signal after a certain delay.
|
|
A new value of a signal is specified by assigning to its
|
|
\code{next} attribute. This is the \myhdl\ equivalent of
|
|
\index{VHDL!signal assignment}%
|
|
the VHDL signal assignment and the
|
|
\index{Verilog!non-blocking assignment}%
|
|
Verilog non-blocking assignment.
|
|
|
|
The \function{HelloWorld} function is modified from the
|
|
first example. It now also takes a clock signal as parameter.
|
|
Its generator is made sensitive to a rising
|
|
\index{wait!for a rising edge}%
|
|
edge of the clock signal. This is specified by the
|
|
\code{posedge} attribute of a signal. The edge
|
|
specifier is the argument of the \code{always}
|
|
decorator. As a result, the decorated function
|
|
will be executed on every rising clock edge.
|
|
|
|
The \code{clk} signal is constructed with an initial value
|
|
\code{0}. When creating an instance of each
|
|
hardware module, the same clock signal is passed as
|
|
the argument. The result is that the instances
|
|
are now connected through the clock signal.
|
|
The \class{Simulation} object is constructed with the
|
|
two instances.
|
|
|
|
When we run the simulation, we get:
|
|
|
|
\begin{verbatim}
|
|
% python hello2.py
|
|
10 Hello World!
|
|
30 Hello World!
|
|
50 Hello World!
|
|
_SuspendSimulation: Simulated 50 timesteps
|
|
\end{verbatim}
|
|
|
|
|
|
\section{Parameters and hierarchy \label{intro-hier}}
|
|
|
|
We have seen that MyHDL uses functions to model hardware
|
|
modules. We have also seen that ports are modeled by using
|
|
signals as parameters. To make designs reusable we will also
|
|
want to use other objects as parameters. For example, we can
|
|
change the clock generator function to make it more general
|
|
and reusable, by making the clock period parameterizable, as
|
|
follows:
|
|
|
|
\begin{verbatim}
|
|
from myhdl import Signal, delay, instance, always, now, Simulation
|
|
|
|
def ClkDriver(clk, period=20):
|
|
|
|
lowTime = int(period/2)
|
|
highTime = period - lowTime
|
|
|
|
@instance
|
|
def driveClk():
|
|
while True:
|
|
yield delay(lowTime)
|
|
clk.next = 1
|
|
yield delay(highTime)
|
|
clk.next = 0
|
|
|
|
return driveClk
|
|
\end{verbatim}
|
|
|
|
In addition to the clock signal, the clock
|
|
period is a parameter, with a default value of \code{20}.
|
|
|
|
As the low time of the clock may differ from the high time in case of
|
|
an odd period, we cannot use the \function{always} decorator with a
|
|
single delay value anymore. Instead, the \function{driveClk} function
|
|
is now a generator function with an explicit definition of the desired
|
|
behavior. It is decorated with the \function{instance} decorator.
|
|
\index{decorator!\function{instance}}
|
|
You can see that \function{driveClk} is a generator function
|
|
because it contains \code{yield} statements.
|
|
|
|
When a generator function is called, it returns a generator object.
|
|
This is basically what the \function{instance} decorator does. It
|
|
is less sophisticated than the \function{always} decorator,
|
|
but it can be used to create a generator from any local generator
|
|
function.
|
|
|
|
The \code{yield} statement is a general Python construct, but MyHDL
|
|
uses it in a dedicated way. In MyHDL, it has a similar meaning as the
|
|
wait statement in VHDL: the statement suspends execution of a
|
|
generator, and its clauses specify the conditions on which the
|
|
generator should wait before resuming. In this case, the generator
|
|
waits for a certain delay.
|
|
|
|
Note that to make sure that the generator runs ``forever'', we wrap its
|
|
behavior in a \code{while True} loop.
|
|
|
|
Similarly, we can define a general \function{Hello} function as follows:
|
|
|
|
\begin{verbatim}
|
|
def Hello(clk, to="World!"):
|
|
|
|
@always(clk.posedge)
|
|
def sayHello():
|
|
print "%s Hello %s" % (now(), to)
|
|
|
|
return sayHello
|
|
\end{verbatim}
|
|
|
|
|
|
We can create any number of instances by calling the functions with
|
|
the appropriate parameters. Hierarchy can be modeled by defining the
|
|
instances in a higher-level function, and returning them.
|
|
This pattern can be repeated for an arbitrary number of
|
|
hierarchical levels. Consequently, the general definition
|
|
of a \myhdl\ instance is recursive: an instance
|
|
\index{instance!defined}%
|
|
is either a sequence of instances, or a generator.
|
|
|
|
As an example, we will create a higher-level function with
|
|
four instances of the lower-level functions, and simulate it:
|
|
|
|
\begin{verbatim}
|
|
def greetings():
|
|
|
|
clk1 = Signal(0)
|
|
clk2 = Signal(0)
|
|
|
|
clkdriver_1 = ClkDriver(clk1) # positional and default association
|
|
clkdriver_2 = ClkDriver(clk=clk2, period=19) # named association
|
|
hello_1 = Hello(clk=clk1) # named and default association
|
|
hello_2 = Hello(to="MyHDL", clk=clk2) # named association
|
|
|
|
return clkdriver_1, clkdriver_2, hello_1, hello_2
|
|
|
|
|
|
inst = greetings()
|
|
sim = Simulation(inst)
|
|
sim.run(50)
|
|
\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 go before any named parameter.}. All these styles
|
|
are demonstrated in the example above. Named association can be very
|
|
useful if there are a lot of parameters, as the argument order in the
|
|
call does not matter in that case.
|
|
|
|
The simulation 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!
|
|
_SuspendSimulation: Simulated 50 timesteps
|
|
\end{verbatim}
|
|
|
|
|
|
\begin{notice}[warning]
|
|
Some commonly used terminology has different meanings
|
|
in Python versus hardware design. Rather than artificially
|
|
changing terminology, I think it's best to keep it
|
|
and explicitly describing the differences.
|
|
|
|
A \dfn{module} in Python refers to all source code
|
|
in a particular file. A module can be reused by
|
|
other modules by importing. In hardware design,
|
|
\index{module!in Python versus hardware design}%
|
|
a module is a reusable block of hardware with
|
|
a well defined interface. It can be reused in
|
|
another module by \dfn{instantiating} it.
|
|
|
|
An \dfn{instance} in Python (and other object-oriented
|
|
languages) refers to the object created by a
|
|
\index{instance!in Python versus hardware design}%
|
|
class constructor. In hardware design, an instance
|
|
is a particular incarnation of a hardware module.
|
|
|
|
Normally, the meaning should be clear from
|
|
the context. Occasionally, I may qualify terms
|
|
with the words 'hardware' or '\myhdl{}' to
|
|
avoid ambiguity.
|
|
\end{notice}
|
|
|
|
|
|
\section{Bit oriented operations \label{intro-bit}}
|
|
|
|
Hardware design involves dealing with bits and bit-oriented
|
|
operations. The standard Python type \class{int} has most of the
|
|
desired features, but lacks support for indexing and slicing. For this
|
|
reason, \myhdl\ provides the \class{intbv} class. The name was chosen
|
|
to suggest an integer with bit vector flavor.
|
|
|
|
Class \class{intbv} works transparently with other
|
|
integer-like types. Like class \class{int}, it provides access to the
|
|
underlying two'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.
|
|
|
|
\subsection{Bit indexing \label{intro-indexing}}
|
|
\index{bit indexing}
|
|
|
|
As an example, we will consider the design of a Gray encoder. The
|
|
following code is a Gray encoder modeled in \myhdl{}:
|
|
|
|
\begin{verbatim}
|
|
from myhdl import Signal, delay, Simulation, always_comb, instance, intbv, bin
|
|
|
|
def bin2gray(B, G, width):
|
|
""" Gray encoder.
|
|
|
|
B -- input intbv signal, binary encoded
|
|
G -- output intbv signal, gray encoded
|
|
width -- bit width
|
|
"""
|
|
|
|
@always_comb
|
|
def logic():
|
|
for i in range(width):
|
|
G.next[i] = B[i+1] ^ B[i]
|
|
|
|
return logic
|
|
\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.
|
|
|
|
Furthermore, we introduce a third decorator: \function{always_comb}.
|
|
\index{decorator!\function{always_comb}}
|
|
It is used with a classic function and specifies that the
|
|
resulting generator should
|
|
\index{wait!for a signal value change}%
|
|
wait for a value change on any input signal. This is typically used to
|
|
describe
|
|
\index{combinatorial logic}%
|
|
combinatorial logic. The \function{always_comb} decorator
|
|
automatically infers which signals are used as inputs.
|
|
|
|
Finally, the code contains bit indexing operations and an exclusive-or
|
|
operator as required for a Gray encoder. By convention, the lsb of an
|
|
\class{intbv} object has index~\code{0}.
|
|
|
|
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)
|
|
|
|
@instance
|
|
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 supplements the standard Python
|
|
\code{hex} and \code{oct} conversion functions.
|
|
|
|
As a demonstration, we set up a simulation for a small width:
|
|
|
|
\begin{verbatim}
|
|
sim = Simulation(testBench(width=3))
|
|
sim.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}
|
|
|
|
\subsection{Bit slicing \label{intro-slicing}}
|
|
\index{bit slicing}
|
|
|
|
For a change, we will use a traditional function as an example to illustrate
|
|
slicing. The following function calculates the HEC byte of an ATM
|
|
header.
|
|
|
|
\begin{verbatim}
|
|
from myhdl import intbv, concat
|
|
|
|
COSET = 0x55
|
|
|
|
def calculateHec(header):
|
|
""" Return hec for an ATM header, represented as an intbv.
|
|
|
|
The hec polynomial is 1 + x + x**2 + x**8.
|
|
"""
|
|
hec = intbv(0)
|
|
for bit in header[32:]:
|
|
hec[8:] = concat(hec[7:2],
|
|
bit ^ hec[1] ^ hec[7],
|
|
bit ^ hec[0] ^ hec[7],
|
|
bit ^ hec[7]
|
|
)
|
|
return hec ^ COSET
|
|
\end{verbatim}
|
|
|
|
The code shows how slicing access and assignment is supported on the
|
|
\class{intbv} data type. In accordance with the most common hardware
|
|
convention, and unlike standard Python, slicing ranges are
|
|
downward. The code also demonstrates concatenation of \class{intbv}
|
|
objects.
|
|
|
|
As in standard Python, the slicing range is half-open: the highest
|
|
index bit is not included. Unlike standard Python however, this index
|
|
corresponds to the \emph{leftmost} item. Both indices can be omitted
|
|
from the slice. If the leftmost index is omitted, the meaning is to
|
|
access ``all'' higher order bits. If the rightmost index is omitted,
|
|
it is \code{0} by default.
|
|
|
|
The half-openness of a slice may seem awkward at first, but it helps
|
|
to avoid one-off count issues in practice. For example, the slice
|
|
\code{hex[8:]} has exactly \code{8} bits. Likewise, the slice
|
|
\code{hex[7:2]} has \code{7-2=5} bits. You can think about it as
|
|
follows: for a slice \code{[i:j]}, only bits below index \code{i} are
|
|
included, and the bit with index \code{j} is the last bit included.
|
|
|
|
When an intbv object is sliced, a new intbv object is returned. This
|
|
new intbv object is always positive, even when the original object
|
|
was negative.
|
|
|
|
|
|
\section{Some remarks on \myhdl\ and Python \label{intro-python}}
|
|
|
|
To conclude this introductory chapter, it is useful to stress that
|
|
\myhdl\ is not a language in itself. The underlying language is Python,
|
|
and \myhdl\ is implemented as a Python package called \code{myhdl}.
|
|
Moreover, it is a design goal to keep the \code{myhdl} package as
|
|
minimalistic as possible, so that \myhdl\ descriptions are very much
|
|
``pure Python''.
|
|
|
|
To have Python as the underlying language is significant in several
|
|
ways:
|
|
|
|
\begin{itemize}
|
|
|
|
\item Python is a very powerful high level language. This translates
|
|
into high productivity and elegant solutions to complex problems.
|
|
|
|
\item Python is continuously improved by some very clever
|
|
minds, supported by a large and fast growing user base. Python profits
|
|
fully from the open source development model.
|
|
|
|
\item Python comes with an extensive standard library. Some
|
|
functionality is likely to be of direct interest to \myhdl\ users:
|
|
examples include string handling, regular expressions, random number
|
|
generation, unit test support, operating system interfacing and GUI
|
|
development. In addition, there are modules for mathematics, database
|
|
connections, networking programming, internet data handling, and so
|
|
on.
|
|
|
|
\item Python has a powerful C extension model. All built-in types are
|
|
written with the same C API that is available for custom
|
|
extensions. To a module user, there is no difference between a
|
|
standard Python module and a C extension module --- except
|
|
performance. The typical Python development model is to prototype
|
|
everything in Python until the application is stable, and (only) then
|
|
rewrite performance critical modules in C if necessary.
|
|
|
|
\end{itemize}
|
|
|
|
|
|
\section{Summary and perspective \label{intro-summary}}
|
|
Here is an overview of what we have learned in this chapter:
|
|
|
|
\begin{itemize}
|
|
\item Generators are the basic building blocks of MyHDL models. They
|
|
provide the way to model massive concurrency and sensitiviy lists.
|
|
|
|
\item MyHDL provides decorators that create useful generators from local functions.
|
|
|
|
\item Hardware structure and hierarchy is described with classic Python functions.
|
|
|
|
\item \code{Signal} objects are used to communicate between concurrent generators.
|
|
|
|
\item \code{intbv} objects are used to describe bit-oriented operations.
|
|
|
|
\item A \code{Simulation} object is used to simulate MyHDL models.
|
|
\end{itemize}
|
|
|
|
These concepts are sufficient to start describing and simulating MyHDL models.
|
|
|
|
However, there is much more to MyHDL. Here is an overview of what can
|
|
be learned from the following chapters:
|
|
|
|
\begin{itemize}
|
|
\item MyHDL supports sophisticated and high level modeling techniques.
|
|
This is described in Chapter~\ref{model}
|
|
|
|
\item MyHDL enables the use of modern software verfication techniques,
|
|
such as unit testing, on hardware designs. This is the topic of
|
|
Chapter~\ref{unittest}.
|
|
|
|
\item It is possible to co-simulate MyHDL models with other HDL
|
|
languages such as Verilog and VHDL. This is described in
|
|
Chapter~\ref{cosim}.
|
|
|
|
\item Last but not least, MyHDL models can be converted to
|
|
Verilog, providing a path to a silicon implementation. This
|
|
is the topic of Chapter~\ref{conv}.
|
|
\end{itemize}
|