1
0
mirror of https://github.com/myhdl/myhdl.git synced 2024-12-14 07:44:38 +08:00
myhdl/doc/manual/intro.tex
2005-12-29 22:01:00 +00:00

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}