From f1a73b4923d49971aa5222ab2a84eb0de250a5c5 Mon Sep 17 00:00:00 2001 From: jand Date: Fri, 9 Dec 2005 14:58:42 +0000 Subject: [PATCH] partial checkin --- doc/manual/background.tex | 70 ++++++--- doc/manual/informal.tex | 311 +++++++++++++++++++++++--------------- doc/manual/reference.tex | 25 +-- 3 files changed, 256 insertions(+), 150 deletions(-) diff --git a/doc/manual/background.tex b/doc/manual/background.tex index 0e04d5cf..dea204fa 100644 --- a/doc/manual/background.tex +++ b/doc/manual/background.tex @@ -17,13 +17,17 @@ see \url{http://www.python.org/doc/Newbies.html}. A working knowledge of a hardware description language such as Verilog or VHDL is helpful. -\section{A small tutorial on generators \label{tutorial}} -\index{generators!tutorial on|(} +Code examples in this manual are sometimes shortened for clarity. + Complete executable examples can be found in the distribution directory at +\file{example/manual/}. -Generators are a recent Python feature, introduced in -Python~2.2. Therefore, there isn't a lot of tutorial material -available yet. Because generators are the key concept in -\myhdl{}, I include a small tutorial here. +\section{A small tutorial on generators \label{tutorial}} +\index{generators!tutorial on} + +Generators are a relatively recent Python feature. They +were introduced in Python~2.2. +Because generators are the key concept in +\myhdl{}, a small tutorial is included a here. Consider the following nonsensical function: @@ -113,19 +117,49 @@ statements work as generalized \index{sensitivity list}% sensitivity lists. -If you want to know more about generators, consult the on-line Python +For more info about generators, consult the on-line Python documentation, e.g. at \url{http://www.python.org/doc/2.2.2/whatsnew}. + +\section{About decorators \label{deco}} +\index{decorators!about} + +Python 2.4 introduced a new feature called decorators. MyHDL 0.5 defines +a number of decorators to facilitate hardware descriptions, but many users may +not be familiar with the concept. Therefore, an introduction +is included here. + +A decorator consists of special syntax in front of a function +declaration. It refers to a decorator function. The decorator +function automatically transforms the declared function into some +other callable object. + +A decorator function \function{deco} is used in a decorator statement as follows: + +\begin{verbatim} +@deco +def func(arg1, arg2, ...): + + +This code is equivalent to the following template: + +def func(arg1, arg2, ...): + +func = deco(func) +\end{verbatim} + +Note that the decorator statement goes directly in front of the +function declaration, and that the function name func is automatically +reused. + +MyHDL 0.5 uses decorators to create ready-to-simulate generators +from local (embedded) function definitions. Their functionality +and usage will be described extensively in this manual. + +For more info about Python decorators, consult the on-line Python +documentation, e.g. at \url{http://www.python.org/doc/2.4/whatsnew/node6.html}. + \begin{notice}[warning] -As mentioned earlier, generators were introduced in Python~2.2. In -that version, they were introduced as a ``future'' feature that has to -be enabled explicitly. In Python~2.3, which is the latest stable -Python version at the time of this writing, generators are enabled by -default. - -Besides generators, Python~2.3 has several other interesting new -features, and it runs 25--35\% faster than Python~2.2. For these -reasons, \myhdl\ requires Python~2.3. +Because MyHDL 0.5 uses decorators, it requires Python 2.4 or a +later version. \end{notice} -\index{generators!tutorial on|)} - diff --git a/doc/manual/informal.tex b/doc/manual/informal.tex index 59395900..2159e0c9 100644 --- a/doc/manual/informal.tex +++ b/doc/manual/informal.tex @@ -3,177 +3,229 @@ \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}: +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 delay, now, Simulation +from myhdl import Signal, delay, always, now, Simulation -def sayHello(): - while 1: - yield delay(10) +def HelloWorld(): + + @always(delay(10)) + def sayHello(): print "%s Hello World!" % now() -gen = sayHello() -sim = Simulation(gen) + return sayHello + +inst = HelloWorld() +sim = Simulation(inst) 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: +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 +_SuspendSimulation: Simulated 30 timesteps \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 +\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.}. -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{}. +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. -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. +Inside the top level function we declared a local function called +\function{sayHello} that defines the desired behavior. This function +is decorated with an \function{@always} decorator that has a delay +object as its parameter. The meaning is that the function will be +executed whenever the specified delay has expired. -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}. +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. -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. +Finally, the top level function returns the local generator. This code +pattern is the simplest incarnation 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{Concurrent generators and signals \label{intro-conc}} +\section{Signals, ports, and concurrency \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 +In the previous section, we simulated a design that consisted +of a single generator. Of course, +real hardware descriptions are not like that: 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\ +support deterministic communication between concurrent code. +For this purpose \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: +We will demonstrate the use of signals and the concept of 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} -clk = Signal(0) +from myhdl import Signal, delay, always, now, Simulation -def clkGen(): - while 1: - yield delay(10) - clk.next = 1 - yield delay(10) - clk.next = 0 + +def ClkDriver(clk): + + @always(delay(10)) + 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 \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 +The clock driver function \function{ClkDriver} has a +clock signal as its parameter. This is how a +\emph{port} is modelled 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 \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 +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}% -wait for a rising edge on the signal. +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 \class{Simulation} is now constructed with 2 generator arguments: +The \code{clk} signal is constructed with an initial value +\code{0}. When creating an instance of each to the two +hardware modules, the same clock signal is passed as +the argument. The result is that the two instances +are now connected through the clock signal. +The \class{Simulation} object is constructed with the +two instances. -\begin{verbatim} -sim = Simulation(clkGen(), sayHello()) -sim.run(50) -\end{verbatim} - -When we run this simulation, we get: +When we run the simulation, we get: \begin{verbatim} % python hello2.py 10 Hello World! 30 Hello World! 50 Hello World! -StopSimulation: Simulated for duration 50 +_SuspendSimulation: Simulated 50 timesteps \end{verbatim} -\section{Parameters, instances and hierarchy \label{intro-hier}} +\section{Parameters 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 +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, as follows: +and reusable, by making the clock period parametrizable, as +follows: \begin{verbatim} -def clkGen(clock, period=20): - lowTime = int(period / 2) +from myhdl import Signal, delay, instance, always, now, Simulation + +def ClkDriver(clk, period=20): + + lowTime = int(period/2) highTime = period - lowTime - while 1: - yield delay(lowTime) - clock.next = 1 - yield delay(highTime) - clock.next = 0 + + @instance + def driveClk(): + while True: + yield delay(lowTime) + clk.next = 1 + yield delay(highTime) + clk.next = 0 + + return driveClk \end{verbatim} -The clock signal is now a parameter of the function. Also, the clock +In addition to the clock signal, 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: +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. You can see that \function{driveClk} is a generator function (as +opposed to a classic function) because it contains \code{yield} +statements. + +When a generator function is called, it returns a generator object. In +fact, this is mainly 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. + +Not that to make sure that the generator runs ``forever'', we wrap its +behavior in a \code{while True} loop. + +Likewise, we can define a general \function{Hello} function as follows: \begin{verbatim} -def sayHello(clock, to="World!"): - while 1: - yield posedge(clock) +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 generators by calling generator functions @@ -190,15 +242,20 @@ 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 + + clkdriver_1 = ClkDriver(clk1) # positional and default association + clkdriver_2 = ClkDriver(clk=clk2, period=19) # named assocation + hello_1 = Hello(clk=clk1) # named and default association + hello_2 = Hello(to="MyHDL", clk=clk2) # named assocation -sim = Simulation(greetings()) + return clkdriver_1, clkdriver_2, hello_1, hello_2 + + +inst = greetings() +sim = Simulation(inst) sim.run(50) \end{verbatim} @@ -220,7 +277,7 @@ The simulation produces the following output: 30 Hello World! 47 Hello MyHDL 50 Hello World! -StopSimulation: Simulated for duration 50 +_SuspendSimulation: Simulated 50 timesteps \end{verbatim} @@ -273,30 +330,38 @@ 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 + G -- output intbv signal, gray encoded width -- bit width - - """xc - while 1: - yield B + """ + + @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. Moreover, we -use a third form of the \keyword{yield} statement: -\samp{yield \var{signal}}. This specifies that the generator should +Python practice for structured documentation of code. + +Furthermore, we introduce a third 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 signal value change. This is typically used to +wait for a value change on any input signal. This is typically used to describe \index{combinatorial logic}% -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}. @@ -310,15 +375,16 @@ def testBench(width): B = Signal(intbv(0)) G = Signal(intbv(0)) - dut = bin2gray(B, G, width) + dut = traceSignals(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()) + return dut, stimulus \end{verbatim} We use the conversion function \code{bin} to get a binary @@ -329,7 +395,8 @@ by the \code{myhdl} package and complements the standard Python To demonstrate, we set up a simulation for a small width: \begin{verbatim} -Simulation(testBench(width=3)).run() +sim = Simulation(testBench(width=3)) +sim.run() \end{verbatim} The simulation produces the following output: @@ -355,8 +422,7 @@ slicing. The following function calculates the HEC byte of an ATM header. \begin{verbatim} -from myhdl import intbv -concat = intbv.concat # shorthand alias +from myhdl import intbv, concat COSET = 0x55 @@ -395,6 +461,9 @@ to avoid one-off count issues in practice. For example, the slice 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 always has a positive value, even when the original object +was negative. \section{Bus-functional procedures \label{intro-bfm}} diff --git a/doc/manual/reference.tex b/doc/manual/reference.tex index e40a361b..ed4804e9 100644 --- a/doc/manual/reference.tex +++ b/doc/manual/reference.tex @@ -192,7 +192,7 @@ are forked, while the original generator resumes immediately. MyHDL defines a number of decorator functions, that make it easier to create generators from local generator function. -\begin{funcdesc}{instance}{} +\begin{funcdesc}{@instance}{} The \code{@instance} decorator is the most general decorator. It automatically creates a generator by calling the generator function, and by reusing its name. @@ -214,10 +214,10 @@ This is equivalent to: \begin{verbatim} def top(...): ... - def gen_func(): + def _gen_func(): ... - inst = gen_func() + inst = _gen_func() ... return inst, ... \end{verbatim} @@ -225,10 +225,10 @@ def top(...): \end{funcdesc} -\begin{funcdesc}{always}{arg \optional{, *args}} +\begin{funcdesc}{@always}{arg \optional{, *args}} -The \code{@always} decorator is a specialized decorator for a widely used -pattern. It is used as follows: +The \code{@always} decorator is a specialized decorator that targets a widely used +coding pattern. It is used as follows: \begin{verbatim} def top(...): @@ -242,16 +242,18 @@ def top(...): This is equivalent to the following: - \begin{verbatim} def top(...): ... - def gen_func() + def _func(): + + + def _gen_func() while True: yield event1, event2, ... - + _func() ... - inst = gen_func() + inst = _gen_func() ... return inst, ... \end{verbatim} @@ -259,12 +261,13 @@ def top(...): The argument list of the decorator corresponds to the sensitivity list. Only signals, edge specifiers, or delay objects are allowed. +The decorated function should be a classic function. \end{funcdesc} -\begin{funcdesc}{always_comb}{} +\begin{funcdesc}{@always_comb}{} The \code{@always_comb} decorator is used to describe combinatorial