mirror of
https://github.com/myhdl/myhdl.git
synced 2025-01-24 21:52:56 +08:00
748 lines
22 KiB
TeX
748 lines
22 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. 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}
|
|
|
|
All example code can be found in the distribution directory under
|
|
\file{example/manual}. 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{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.}.
|
|
|
|
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).
|
|
When called, a generator function returns a \dfn{generator},
|
|
which is the basic simulation object in \myhdl{}.
|
|
|
|
The \keyword{yield} statement in \myhdl{} has a similar meaning as
|
|
the \keyword{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, it should
|
|
\index{wait!for a delay}%
|
|
wait for a delay.
|
|
|
|
To make sure that a 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
|
|
\index{Verilog!always block}%
|
|
Verilog \keyword{always} block
|
|
\index{VHDL!process}%
|
|
and a VHDL \keyword{process}.
|
|
|
|
In the example, variable
|
|
\code{gen} refers to a generator. To simulate it, 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 as a natural integer.
|
|
|
|
|
|
\section{Concurrent generators and signals \label{intro-conc}}
|
|
|
|
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.
|
|
|
|
With concurrency comes the problem of deterministic
|
|
communication. Hardware languages use special objects to
|
|
support deterministic communication between concurrent code. \myhdl\
|
|
has a \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
|
|
\index{VHDL!signal assignment}%
|
|
the VHDL signal assignment and the
|
|
\index{Verilog!non-blocking assignment}%
|
|
Verilog non-blocking assignment.
|
|
|
|
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 the clock edge is achieved with a second form of the
|
|
\keyword{yield} statement: \samp{yield posedge(\var{signal})}.
|
|
At that point, the generator will
|
|
\index{wait!for a rising edge}%
|
|
wait for 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, instances and hierarchy \label{intro-hier}}
|
|
|
|
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. To make 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 as follows:
|
|
|
|
\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 instantiation in hardware description languages and we will use
|
|
the same terminology in \myhdl{}. A \myhdl\ \dfn{instance} is
|
|
\index{instance!defined}%
|
|
recursively defined as being either a sequence of instances, or a
|
|
generator. Hierarchy can be modeled by defining the instances in a
|
|
higher-level function, and returning them.
|
|
A \class{Simulation} constructor takes an arbitrary number of
|
|
instances as its arguments.
|
|
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
|
|
|
|
sim = Simulation(greetings())
|
|
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. 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.
|
|
|
|
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!
|
|
StopSimulation: Simulated for duration 50
|
|
\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 as an integer and 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}
|
|
def bin2gray(B, G, width):
|
|
""" Gray encoder.
|
|
|
|
B -- input intbv signal, binary encoded
|
|
G -- output intbv signal, Gray encoded
|
|
width -- bit width
|
|
|
|
"""xc
|
|
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
|
|
\index{wait!for a signal value change}%
|
|
wait for a signal value change. This is typically used to
|
|
describe
|
|
\index{combinatorial logic}%
|
|
combinatorial logic.
|
|
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)
|
|
|
|
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}
|
|
|
|
\subsection{Bit slicing \label{intro-slicing}}
|
|
\index{bit slicing}
|
|
|
|
For a change, we will use a plain 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 = intbv.concat # shorthand alias
|
|
|
|
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.
|
|
|
|
|
|
\section{Bus-functional procedures \label{intro-bfm}}
|
|
|
|
\index{bus-functional procedure}%
|
|
A \dfn{bus-functional procedure} is a reusable encapsulation of the
|
|
low-level operations needed to implement some abstract transaction on
|
|
a physical interface. Bus-functional procedures are typically used in
|
|
flexible verification environments.
|
|
|
|
Once again, \myhdl\ uses generator functions to support
|
|
bus-functional procedures. In \myhdl\, the difference between
|
|
instances and bus-functional procedure calls comes from the way in
|
|
which a generator function is used.
|
|
|
|
As an example, we will design a bus-functional procedure of a
|
|
simplified UART transmitter. We assume 8 data bits, no parity bit, and
|
|
a single stop bit, and we add print statements to follow the
|
|
simulation behavior:
|
|
|
|
\begin{verbatim}
|
|
T_9600 = int(1e9 / 9600)
|
|
|
|
def rs232_tx(tx, data, duration=T_9600):
|
|
|
|
""" Simple rs232 transmitter procedure.
|
|
|
|
tx -- serial output data
|
|
data -- input data byte to be transmitted
|
|
duration -- transmit bit duration
|
|
|
|
"""
|
|
|
|
print "-- Transmitting %s --" % hex(data)
|
|
print "TX: start bit"
|
|
tx.next = 0
|
|
yield delay(duration)
|
|
|
|
for i in range(8):
|
|
print "TX: %s" % data[i]
|
|
tx.next = data[i]
|
|
yield delay(duration)
|
|
|
|
print "TX: stop bit"
|
|
tx.next = 1
|
|
yield delay(duration)
|
|
\end{verbatim}
|
|
|
|
This looks exactly like the generator functions in previous sections. It
|
|
becomes a bus-functional procedure when we use it differently. Suppose
|
|
that in a test bench, we want to generate a number of data bytes to be
|
|
transmitted. This can be modeled as follows:
|
|
|
|
|
|
\begin{verbatim}
|
|
testvals = (0xc5, 0x3a, 0x4b)
|
|
|
|
def stimulus():
|
|
tx = Signal(1)
|
|
for val in testvals:
|
|
txData = intbv(val)
|
|
yield rs232_tx(tx, txData)
|
|
\end{verbatim}
|
|
|
|
We use the bus-functional procedure call as a clause in a
|
|
\code{yield} statement. This introduces a fourth form of the
|
|
\code{yield} statement: using a generator as a clause. Although this is
|
|
a more dynamic usage than in the previous cases, the meaning is
|
|
actually very similar: at that point,
|
|
the original generator should
|
|
\index{wait!for the completion of a generator}%
|
|
wait for the completion of a generator.
|
|
In this case, the original generator resumes when the
|
|
\code{rs232_tx(tx, txData)} generator returns.
|
|
|
|
When simulating this, we get:
|
|
|
|
\begin{verbatim}
|
|
-- Transmitting 0xc5 --
|
|
TX: start bit
|
|
TX: 1
|
|
TX: 0
|
|
TX: 1
|
|
TX: 0
|
|
TX: 0
|
|
TX: 0
|
|
TX: 1
|
|
TX: 1
|
|
TX: stop bit
|
|
-- Transmitting 0x3a --
|
|
TX: start bit
|
|
TX: 0
|
|
TX: 1
|
|
TX: 0
|
|
TX: 1
|
|
...
|
|
\end{verbatim}
|
|
|
|
We will continue with this example by designing the corresponding UART
|
|
receiver bus-functional procedure. This will allow us to introduce
|
|
further capabilities of \myhdl\ and its use of the \code{yield}
|
|
statement.
|
|
|
|
Until now, the \code{yield} statements had a single clause. However,
|
|
they can have multiple clauses as well. In that case, the generator
|
|
resumes as soon as the wait condition specified by one
|
|
of the clauses is satisfied. This corresponds to the functionality of
|
|
\index{sensitivity list}%
|
|
sensitivity lists in Verilog and VHDL.
|
|
|
|
For example, suppose we want to design an UART receive procedure with
|
|
a timeout. We can specify the timeout condition while waiting for the
|
|
start bit, as in the following generator function:
|
|
|
|
\begin{verbatim}
|
|
def rs232_rx(rx, data, duration=T_9600, timeout=MAX_TIMEOUT):
|
|
|
|
""" Simple rs232 receiver procedure.
|
|
|
|
rx -- serial input data
|
|
data -- data received
|
|
duration -- receive bit duration
|
|
|
|
"""
|
|
|
|
# wait on start bit until timeout
|
|
yield negedge(rx), delay(timeout)
|
|
if rx == 1:
|
|
raise StopSimulation, "RX time out error"
|
|
|
|
# sample in the middle of the bit duration
|
|
yield delay(duration // 2)
|
|
print "RX: start bit"
|
|
|
|
for i in range(8):
|
|
yield delay(duration)
|
|
print "RX: %s" % rx
|
|
data[i] = rx
|
|
|
|
yield delay(duration)
|
|
print "RX: stop bit"
|
|
print "-- Received %s --" % hex(data)
|
|
\end{verbatim}
|
|
|
|
If the timeout condition is triggered, the receive bit \code{rx}
|
|
will still be \code{1}. In that case, we raise an exception to stop
|
|
the simulation. The \code{StopSimulation} exception is predefined in
|
|
\myhdl\ for such purposes. In the other case, we proceed by
|
|
positioning the sample point in the middle of the bit duration, and
|
|
sampling the received data bits.
|
|
|
|
When a \code{yield} statement has multiple clauses, they can be of any
|
|
type that is supported as a single clause, including generators. For
|
|
example, we can verify the transmitter and receiver generator against
|
|
each other by yielding them together, as follows:
|
|
|
|
\begin{verbatim}
|
|
def test():
|
|
tx = Signal(1)
|
|
rx = tx
|
|
rxData = intbv(0)
|
|
for val in testvals:
|
|
txData = intbv(val)
|
|
yield rs232_rx(rx, rxData), rs232_tx(tx, txData)
|
|
\end{verbatim}
|
|
|
|
Both forked generators will run concurrently, and the original
|
|
generator will resume as soon as one of them finishes (which will be
|
|
the transmitter in this case). The simulation output shows how
|
|
the UART procedures run in lockstep:
|
|
|
|
\begin{verbatim}
|
|
-- Transmitting 0xc5 --
|
|
TX: start bit
|
|
RX: start bit
|
|
TX: 1
|
|
RX: 1
|
|
TX: 0
|
|
RX: 0
|
|
TX: 1
|
|
RX: 1
|
|
TX: 0
|
|
RX: 0
|
|
TX: 0
|
|
RX: 0
|
|
TX: 0
|
|
RX: 0
|
|
TX: 1
|
|
RX: 1
|
|
TX: 1
|
|
RX: 1
|
|
TX: stop bit
|
|
RX: stop bit
|
|
-- Received 0xc5 --
|
|
-- Transmitting 0x3a --
|
|
TX: start bit
|
|
RX: start bit
|
|
TX: 0
|
|
RX: 0
|
|
...
|
|
\end{verbatim}
|
|
|
|
For completeness, we will verify the timeout behavior with a test
|
|
bench that disconnects the \code{rx} from the \code{tx} signal, and we
|
|
specify a small timeout for the receive procedure:
|
|
|
|
\begin{verbatim}
|
|
def testTimeout():
|
|
tx = Signal(1)
|
|
rx = Signal(1)
|
|
rxData = intbv(0)
|
|
for val in testvals:
|
|
txData = intbv(val)
|
|
yield rs232_rx(rx, rxData, timeout=4*T_9600-1), rs232_tx(tx, txData)
|
|
\end{verbatim}
|
|
|
|
The simulation now stops with a timeout exception after a few
|
|
transmit cycles:
|
|
|
|
\begin{verbatim}
|
|
-- Transmitting 0xc5 --
|
|
TX: start bit
|
|
TX: 1
|
|
TX: 0
|
|
TX: 1
|
|
StopSimulation: RX time out error
|
|
\end{verbatim}
|
|
|
|
Recall that the original generator resumes as soon as one of the
|
|
forked generators returns. In the previous cases, this is just fine,
|
|
as the transmitter and receiver generators run in lockstep. However,
|
|
it may be desirable to resume the caller only when \emph{all} of the
|
|
forked generators have finished. For example, suppose that we want to
|
|
characterize the robustness of the transmitter and receiver design to
|
|
bit duration differences. We can adapt our test bench as follows, to
|
|
run the transmitter at a faster rate:
|
|
|
|
\begin{verbatim}
|
|
T_10200 = int(1e9 / 10200)
|
|
|
|
def testNoJoin():
|
|
tx = Signal(1)
|
|
rx = tx
|
|
rxData = intbv(0)
|
|
for val in testvals:
|
|
txData = intbv(val)
|
|
yield rs232_rx(rx, rxData), rs232_tx(tx, txData, duration=T_10200)
|
|
\end{verbatim}
|
|
|
|
Simulating this shows how the transmission of the new byte starts
|
|
before the previous one is received, potentially creating additional
|
|
transmission errors:
|
|
|
|
\begin{verbatim}
|
|
-- Transmitting 0xc5 --
|
|
TX: start bit
|
|
RX: start bit
|
|
...
|
|
TX: 1
|
|
RX: 1
|
|
TX: 1
|
|
TX: stop bit
|
|
RX: 1
|
|
-- Transmitting 0x3a --
|
|
TX: start bit
|
|
RX: stop bit
|
|
-- Received 0xc5 --
|
|
RX: start bit
|
|
TX: 0
|
|
\end{verbatim}
|
|
|
|
It is more likely that we want to characterize the design on a byte
|
|
by byte basis, and align the two generators before transmitting each
|
|
byte. In \myhdl{}, this is done with the \function{join} function. By
|
|
joining clauses together in a \code{yield} statement, we create a new
|
|
clause that triggers only when all of its clause arguments have
|
|
triggered. For example, we can adapt the test bench as follows:
|
|
|
|
\begin{verbatim}
|
|
def testJoin():
|
|
tx = Signal(1)
|
|
rx = tx
|
|
rxData = intbv(0)
|
|
for val in testvals:
|
|
txData = intbv(val)
|
|
yield join(rs232_rx(rx, rxData), rs232_tx(tx, txData, duration=T_10200))
|
|
\end{verbatim}
|
|
|
|
Now, transmission of a new byte only starts when the previous one is received:
|
|
|
|
\begin{verbatim}
|
|
-- Transmitting 0xc5 --
|
|
TX: start bit
|
|
RX: start bit
|
|
...
|
|
TX: 1
|
|
RX: 1
|
|
TX: 1
|
|
TX: stop bit
|
|
RX: 1
|
|
RX: stop bit
|
|
-- Received 0xc5 --
|
|
-- Transmitting 0x3a --
|
|
TX: start bit
|
|
RX: start bit
|
|
TX: 0
|
|
RX: 0
|
|
\end{verbatim}
|
|
|
|
\section{\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}
|
|
|
|
|
|
|
|
|