\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} 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{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, while 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 as 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. 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 VHDL signal assignments and Verilog's nonblocking 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. 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 \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. To support hierarchy, \class{Simulation} constructor arguments can be sequences of generators. More specifically, the return value of a higher-level instantiating function can be used as an argument of the constructor. 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} 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{Indexing operations} \label{gray} 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. 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{Slicing operations} 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. The rightmost index is \code{0} by default, and can be omitted from the slice. 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} 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\ supports bus-functional procedures by generator functions. 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. When the calling generator \code{stimulus()} encounters the \code{yield} statement, it suspends execution, and the clause specifies when it should resume. In this case, the generator \code{rs232_tx(tx, txData)} is \dfn{forked}, and the caller resumes when the forked 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 calling generator is triggered as soon as the condition corresponding to one of the clauses is satisfied. This corresponds to the functionality of 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 calling 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 calling 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 designs 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 argument clauses 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} To conclude this introductory chapter, it is useful to stress that \myhdl\ is not a language. The 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}