mirror of
https://github.com/myhdl/myhdl.git
synced 2025-01-24 21:52:56 +08:00
merged with myhdl.org repo
--HG-- branch : 0.8-dev
This commit is contained in:
commit
61a4bad598
@ -21,6 +21,8 @@ transcript
|
||||
*.0
|
||||
*.bak
|
||||
*.wlf
|
||||
*.rej
|
||||
*.out
|
||||
doc/build
|
||||
build/
|
||||
dist/
|
||||
|
@ -34,15 +34,15 @@ master_doc = 'index'
|
||||
|
||||
# General substitutions.
|
||||
project = 'MyHDL'
|
||||
copyright = '2008-2010, Jan Decaluwe'
|
||||
copyright = '2008-2012, Jan Decaluwe'
|
||||
|
||||
# The default replacements for |version| and |release|, also used in various
|
||||
# other places throughout the built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.7'
|
||||
version = '0.8'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.7'
|
||||
release = '0.8-dev'
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
|
@ -35,9 +35,9 @@ or VHDL, using the function :func:`toVerilog` or :func:`toVHDL` from the MyHDL
|
||||
library.
|
||||
|
||||
When the design is intended for implementation
|
||||
a third-party :dfn:`synthesis tool` is used to convert the Verilog or VHDL
|
||||
design to a gate implementation for an ASIC or FPGA. With this step, there is
|
||||
a path from a hardware description in Python to an FPGA or ASIC implementation.
|
||||
a third-party :dfn:`synthesis tool` is used to compile the Verilog or VHDL
|
||||
model into an implementation for an ASIC or FPGA. With this step, there is
|
||||
an automated path from a hardware description in Python to an FPGA or ASIC implementation.
|
||||
|
||||
The conversion does not start from source files, but from an instantiated design
|
||||
that has been *elaborated* by the Python interpreter. The converter uses the
|
||||
|
@ -1,639 +1,40 @@
|
||||
.. currentmodule:: myhdl
|
||||
|
||||
|
||||
.. _model:
|
||||
|
||||
*******************
|
||||
Modeling techniques
|
||||
*******************
|
||||
|
||||
|
||||
.. _model-structure:
|
||||
|
||||
Structural modeling
|
||||
===================
|
||||
|
||||
.. index:: single: modeling; structural
|
||||
|
||||
Hardware descriptions need to support the concepts of module instantiation and
|
||||
hierarchy. In MyHDL, an instance is recursively defined as being either a
|
||||
sequence of instances, or a generator. Hierarchy is modeled by defining
|
||||
instances in a higher-level function, and returning them. The following is a
|
||||
schematic example of the basic case. ::
|
||||
|
||||
def top(...):
|
||||
...
|
||||
instance_1 = module_1(...)
|
||||
instance_2 = module_2(...)
|
||||
...
|
||||
instance_n = module_n(...)
|
||||
...
|
||||
return instance_1, instance_2, ... , instance_n
|
||||
|
||||
Note that MyHDL uses conventional procedural techniques for modeling structure.
|
||||
This makes it straightforward to model more complex cases.
|
||||
|
||||
|
||||
.. _model-conf:
|
||||
|
||||
Conditional instantiation
|
||||
-------------------------
|
||||
|
||||
.. index:: single: conditional instantiation
|
||||
|
||||
To model conditional instantiation, we can select the returned instance under
|
||||
parameter control. For example::
|
||||
|
||||
SLOW, MEDIUM, FAST = range(3)
|
||||
|
||||
def top(..., speed=SLOW):
|
||||
...
|
||||
def slowAndSmall():
|
||||
...
|
||||
...
|
||||
def fastAndLarge():
|
||||
...
|
||||
if speed == SLOW:
|
||||
return slowAndSmall()
|
||||
elif speed == FAST:
|
||||
return fastAndLarge()
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
.. _model-instarray:
|
||||
|
||||
Lists of instances and signals
|
||||
------------------------------
|
||||
|
||||
.. index:: single: lists of instances and signals
|
||||
|
||||
Python lists are easy to create. We can use them to model lists of instances.
|
||||
|
||||
Suppose we have a top module that instantiates a single ``channel`` submodule,
|
||||
as follows::
|
||||
|
||||
def top(...):
|
||||
|
||||
din = Signal(0)
|
||||
dout = Signal(0)
|
||||
clk = Signal(bool(0))
|
||||
reset = Signal(bool(0))
|
||||
|
||||
channel_inst = channel(dout, din, clk, reset)
|
||||
|
||||
return channel_inst
|
||||
|
||||
If we wanted to support an arbitrary number of channels, we can use lists of
|
||||
signals and a list of instances, as follows::
|
||||
|
||||
def top(..., n=8):
|
||||
|
||||
din = [Signal(0) for i in range(n)]
|
||||
dout = [Signal(0) for in range(n)]
|
||||
clk = Signal(bool(0))
|
||||
reset = Signal(bool(0))
|
||||
channel_inst = [None for i in range(n)]
|
||||
|
||||
for i in range(n):
|
||||
channel_inst[i] = channel(dout[i], din[i], clk, reset)
|
||||
|
||||
return channel_inst
|
||||
|
||||
.. _model-shadow-signals:
|
||||
|
||||
Converting between lists of signals and bit vectors
|
||||
---------------------------------------------------
|
||||
|
||||
Compared to HDLs such as VHDL and Verilog, MyHDL signals are less
|
||||
flexible for structural modeling. For example, slicing a signal
|
||||
returns a slice of the current value. For behavioral code, this is
|
||||
just fine. However, it implies that you cannot use such as slice in
|
||||
structural descriptions. In other words, a signal slice cannot be used
|
||||
as a signal.
|
||||
|
||||
In MyHDL, you can address such cases by a concept called
|
||||
shadow signals. A shadow signal is constructed out of
|
||||
other signals and follows their value changes automatically.
|
||||
For example, a :class:`_SliceSignal` follows the value of
|
||||
an index or a slice from another signal. Likewise,
|
||||
A :class:`ConcatSignal` follows the
|
||||
values of a number of signals as a concatenation.
|
||||
|
||||
As an example, suppose we have a system with N requesters that
|
||||
need arbitration. Each requester has a ``request`` output
|
||||
and a ``grant`` input. To connect them in the system, we can
|
||||
use list of signals. For example, a list of request signals
|
||||
can be constructed as follows::
|
||||
|
||||
request_list = [Signal(bool()) for i in range(M)]
|
||||
|
||||
Suppose that an arbiter module is available that is
|
||||
instantiated as follows::
|
||||
|
||||
arb = arbiter(grant_vector, request_vector, clock, reset)
|
||||
|
||||
The ``request_vector`` input is a bit vector that can have
|
||||
any of its bits asserted. The ``grant_vector`` is an output
|
||||
bit vector with just a single bit asserted, or none.
|
||||
Such a module is typically based on bit vectors because
|
||||
they are easy to process in RTL code. In MyHDL, a bit vector
|
||||
is modeled using the :class:`intbv` type.
|
||||
|
||||
We need a way to "connect" the list of signals to the
|
||||
bit vector and vice versa. Of course, we can do this with explicit
|
||||
code, but shadow signals can do this automatically. For
|
||||
example, we can construct a ``request_vector`` as a
|
||||
:class:`ConcatSignal` object::
|
||||
|
||||
request_vector = ConcatSignal(*reversed(request_list)
|
||||
|
||||
Note that we reverse the list first. This is done because the index range
|
||||
of lists is the inverse of the range of :class:`intbv` bit vectors.
|
||||
By reversing, the indices correspond to the same bit.
|
||||
|
||||
The inverse problem exist for the ``grant_vector``. It would be defined as follows::
|
||||
|
||||
grant_vector = Signal(intbv(0)[M:])
|
||||
|
||||
To construct a list of signals that are connected automatically to the
|
||||
bit vector, we can use the :class:`Signal` call interface to construct
|
||||
:class:`_SliceSignal` objects::
|
||||
|
||||
grant_list = [grant_vector(i) for i in range(M)]
|
||||
|
||||
Note the round brackets used for this type of slicing. Also, it may not be
|
||||
necessary to construct this list explicitly. You can simply use
|
||||
``grant_vector(i)`` in an instantiation.
|
||||
|
||||
To decide when to use normal or shadow signals, consider the data
|
||||
flow. Use normal signals to connect to *outputs*. Use shadow signals to
|
||||
transform these signals so that they can be used as *inputs*.
|
||||
|
||||
|
||||
.. _model-infer-instlist:
|
||||
|
||||
Inferring the list of instances
|
||||
-------------------------------
|
||||
|
||||
In MyHDL, instances have to be returned explicitly by a top level function. It
|
||||
may be convenient to assemble the list of instances automatically. For this
|
||||
purpose, MyHDL provides the function :func:`instances`. Using the first example
|
||||
in this section, it is used as follows::
|
||||
|
||||
from myhdl import instances
|
||||
|
||||
def top(...):
|
||||
...
|
||||
instance_1 = module_1(...)
|
||||
instance_2 = module_2(...)
|
||||
...
|
||||
instance_n = module_n(...)
|
||||
...
|
||||
return instances()
|
||||
|
||||
Function :func:`instances` uses introspection to inspect the type of the local
|
||||
variables defined by the calling function. All variables that comply with the
|
||||
definition of an instance are assembled in a list, and that list is returned.
|
||||
|
||||
|
||||
.. _model-rtl:
|
||||
|
||||
RTL modeling
|
||||
============
|
||||
|
||||
.. index:: single: modeling; RTL style
|
||||
|
||||
The present section describes how MyHDL supports RTL style modeling as is
|
||||
typically used for synthesizable models.
|
||||
|
||||
|
||||
.. _model-comb:
|
||||
|
||||
Combinatorial logic
|
||||
-------------------
|
||||
|
||||
.. index:: single: combinatorial logic
|
||||
|
||||
|
||||
.. _model-comb-templ:
|
||||
|
||||
Template
|
||||
^^^^^^^^
|
||||
|
||||
Combinatorial logic is described with a code pattern as follows::
|
||||
|
||||
def top(<parameters>):
|
||||
...
|
||||
@always_comb
|
||||
def combLogic():
|
||||
<functional code>
|
||||
...
|
||||
return combLogic, ...
|
||||
|
||||
The :func:`always_comb` decorator describes combinatorial logic. [#]_. The
|
||||
decorated function is a local function that specifies what happens when one of
|
||||
the input signals of the logic changes. The :func:`always_comb` decorator
|
||||
infers the input signals automatically. It returns a generator that is sensitive
|
||||
to all inputs, and that executes the function whenever an input changes.
|
||||
|
||||
|
||||
.. _model-comb-ex:
|
||||
|
||||
Example
|
||||
^^^^^^^
|
||||
|
||||
The following is an example of a combinatorial multiplexer::
|
||||
|
||||
from myhdl import Signal, Simulation, delay, always_comb
|
||||
|
||||
def Mux(z, a, b, sel):
|
||||
|
||||
""" Multiplexer.
|
||||
|
||||
z -- mux output
|
||||
a, b -- data inputs
|
||||
sel -- control input: select a if asserted, otherwise b
|
||||
|
||||
"""
|
||||
|
||||
@always_comb
|
||||
def muxLogic():
|
||||
if sel == 1:
|
||||
z.next = a
|
||||
else:
|
||||
z.next = b
|
||||
|
||||
return muxLogic
|
||||
|
||||
To verify it, we will simulate the logic with some random patterns. The
|
||||
``random`` module in Python's standard library comes in handy for such purposes.
|
||||
The function ``randrange(n)`` returns a random natural integer smaller than *n*.
|
||||
It is used in the test bench code to produce random input values::
|
||||
|
||||
from random import randrange
|
||||
|
||||
z, a, b, sel = [Signal(0) for i in range(4)]
|
||||
|
||||
mux_1 = Mux(z, a, b, sel)
|
||||
|
||||
def test():
|
||||
print "z a b sel"
|
||||
for i in range(8):
|
||||
a.next, b.next, sel.next = randrange(8), randrange(8), randrange(2)
|
||||
yield delay(10)
|
||||
print "%s %s %s %s" % (z, a, b, sel)
|
||||
|
||||
test_1 = test()
|
||||
|
||||
sim = Simulation(mux_1, test_1)
|
||||
sim.run()
|
||||
|
||||
Because of the randomness, the simulation output varies between runs [#]_. One
|
||||
particular run produced the following output::
|
||||
|
||||
% python mux.py
|
||||
z a b sel
|
||||
6 6 1 1
|
||||
7 7 1 1
|
||||
7 3 7 0
|
||||
1 2 1 0
|
||||
7 7 5 1
|
||||
4 7 4 0
|
||||
4 0 4 0
|
||||
3 3 5 1
|
||||
StopSimulation: No more events
|
||||
|
||||
|
||||
.. _model-seq:
|
||||
|
||||
Sequential logic
|
||||
----------------
|
||||
|
||||
.. index:: single: sequential logic
|
||||
|
||||
|
||||
.. _model-seq-templ:
|
||||
|
||||
Template
|
||||
^^^^^^^^
|
||||
|
||||
Sequential RTL models are sensitive to a clock edge. In addition, they may be
|
||||
sensitive to a reset signal. We will describe one of the most common patterns: a
|
||||
template with a rising clock edge and an asynchronous reset signal. Other
|
||||
templates are similar. ::
|
||||
|
||||
def top(<parameters>, clock, ..., reset, ...):
|
||||
...
|
||||
@always(clock.posedge, reset.negedge)
|
||||
def seqLogic():
|
||||
if reset == <active level>:
|
||||
<reset code>
|
||||
else:
|
||||
<functional code>
|
||||
...
|
||||
return seqLogic, ...
|
||||
|
||||
|
||||
.. _model-seq-ex:
|
||||
|
||||
Example
|
||||
^^^^^^^
|
||||
|
||||
The following code is a description of an incrementer with enable, and an
|
||||
asynchronous reset. ::
|
||||
|
||||
from random import randrange
|
||||
from myhdl import *
|
||||
|
||||
ACTIVE_LOW, INACTIVE_HIGH = 0, 1
|
||||
|
||||
def Inc(count, enable, clock, reset, n):
|
||||
|
||||
""" Incrementer with enable.
|
||||
|
||||
count -- output
|
||||
enable -- control input, increment when 1
|
||||
clock -- clock input
|
||||
reset -- asynchronous reset input
|
||||
n -- counter max value
|
||||
|
||||
"""
|
||||
|
||||
@always(clock.posedge, reset.negedge)
|
||||
def incLogic():
|
||||
if reset == ACTIVE_LOW:
|
||||
count.next = 0
|
||||
else:
|
||||
if enable:
|
||||
count.next = (count + 1) % n
|
||||
|
||||
return incLogic
|
||||
|
||||
For the test bench, we will use an independent clock generator, stimulus
|
||||
generator, and monitor. After applying enough stimulus patterns, we can raise
|
||||
the ``StopSimulation`` exception to stop the simulation run. The test bench for
|
||||
a small incrementer and a small number of patterns is a follows::
|
||||
|
||||
def testbench():
|
||||
count, enable, clock, reset = [Signal(intbv(0)) for i in range(4)]
|
||||
|
||||
inc_1 = Inc(count, enable, clock, reset, n=4)
|
||||
|
||||
HALF_PERIOD = delay(10)
|
||||
|
||||
@always(HALF_PERIOD)
|
||||
def clockGen():
|
||||
clock.next = not clock
|
||||
|
||||
@instance
|
||||
def stimulus():
|
||||
reset.next = ACTIVE_LOW
|
||||
yield clock.negedge
|
||||
reset.next = INACTIVE_HIGH
|
||||
for i in range(12):
|
||||
enable.next = min(1, randrange(3))
|
||||
yield clock.negedge
|
||||
raise StopSimulation
|
||||
|
||||
@instance
|
||||
def monitor():
|
||||
print "enable count"
|
||||
yield reset.posedge
|
||||
while 1:
|
||||
yield clock.posedge
|
||||
yield delay(1)
|
||||
print " %s %s" % (enable, count)
|
||||
|
||||
return clockGen, stimulus, inc_1, monitor
|
||||
|
||||
|
||||
tb = testbench()
|
||||
|
||||
def main():
|
||||
Simulation(tb).run()
|
||||
|
||||
The simulation produces the following output::
|
||||
|
||||
% python inc.py
|
||||
enable count
|
||||
0 0
|
||||
1 1
|
||||
0 1
|
||||
1 2
|
||||
1 3
|
||||
1 0
|
||||
0 0
|
||||
1 1
|
||||
0 1
|
||||
0 1
|
||||
0 1
|
||||
1 2
|
||||
StopSimulation
|
||||
|
||||
|
||||
.. _model-fsm:
|
||||
|
||||
Finite State Machine modeling
|
||||
-----------------------------
|
||||
|
||||
.. index:: single: modeling; Finite State Machine
|
||||
|
||||
Finite State Machine (FSM) modeling is very common in RTL design and therefore
|
||||
deserves special attention.
|
||||
|
||||
For code clarity, the state values are typically represented by a set of
|
||||
identifiers. A standard Python idiom for this purpose is to assign a range of
|
||||
integers to a tuple of identifiers, like so::
|
||||
|
||||
>>> SEARCH, CONFIRM, SYNC = range(3)
|
||||
>>> CONFIRM
|
||||
1
|
||||
|
||||
However, this technique has some drawbacks. Though it is clearly the intention
|
||||
that the identifiers belong together, this information is lost as soon as they
|
||||
are defined. Also, the identifiers evaluate to integers, whereas a string
|
||||
representation of the identifiers would be preferable. To solve these issues, we
|
||||
need an *enumeration type*.
|
||||
|
||||
.. index:: single: enum(); example usage
|
||||
|
||||
MyHDL supports enumeration types by providing a function :func:`enum`. The
|
||||
arguments to :func:`enum` are the string representations of the identifiers, and
|
||||
its return value is an enumeration type. The identifiers are available as
|
||||
attributes of the type. For example::
|
||||
|
||||
>>> from myhdl import enum
|
||||
>>> t_State = enum('SEARCH', 'CONFIRM', 'SYNC')
|
||||
>>> t_State
|
||||
<Enum: SEARCH, CONFIRM, SYNC>
|
||||
>>> t_State.CONFIRM
|
||||
CONFIRM
|
||||
|
||||
We can use this type to construct a state signal as follows::
|
||||
|
||||
state = Signal(t_State.SEARCH)
|
||||
|
||||
As an example, we will use a framing controller FSM. It is an imaginary
|
||||
example, but similar control structures are often found in telecommunication
|
||||
applications. Suppose that we need to find the Start Of Frame (SOF) position of
|
||||
an incoming frame of bytes. A sync pattern detector continuously looks for a
|
||||
framing pattern and indicates it to the FSM with a ``syncFlag`` signal. When
|
||||
found, the FSM moves from the initial ``SEARCH`` state to the ``CONFIRM`` state.
|
||||
When the ``syncFlag`` is confirmed on the expected position, the FSM declares
|
||||
``SYNC``, otherwise it falls back to the ``SEARCH`` state. This FSM can be
|
||||
coded as follows::
|
||||
|
||||
from myhdl import *
|
||||
|
||||
ACTIVE_LOW = 0
|
||||
FRAME_SIZE = 8
|
||||
t_State = enum('SEARCH', 'CONFIRM', 'SYNC')
|
||||
|
||||
def FramerCtrl(SOF, state, syncFlag, clk, reset_n):
|
||||
|
||||
""" Framing control FSM.
|
||||
|
||||
SOF -- start-of-frame output bit
|
||||
state -- FramerState output
|
||||
syncFlag -- sync pattern found indication input
|
||||
clk -- clock input
|
||||
reset_n -- active low reset
|
||||
|
||||
"""
|
||||
|
||||
index = Signal(0) # position in frame
|
||||
|
||||
@always(clk.posedge, reset_n.negedge)
|
||||
def FSM():
|
||||
if reset_n == ACTIVE_LOW:
|
||||
SOF.next = 0
|
||||
index.next = 0
|
||||
state.next = t_State.SEARCH
|
||||
|
||||
else:
|
||||
index.next = (index + 1) % FRAME_SIZE
|
||||
SOF.next = 0
|
||||
|
||||
if state == t_State.SEARCH:
|
||||
index.next = 1
|
||||
if syncFlag:
|
||||
state.next = t_State.CONFIRM
|
||||
|
||||
elif state == t_State.CONFIRM:
|
||||
if index == 0:
|
||||
if syncFlag:
|
||||
state.next = t_State.SYNC
|
||||
else:
|
||||
state.next = t_State.SEARCH
|
||||
|
||||
elif state == t_State.SYNC:
|
||||
if index == 0:
|
||||
if not syncFlag:
|
||||
state.next = t_State.SEARCH
|
||||
SOF.next = (index == FRAME_SIZE-1)
|
||||
|
||||
else:
|
||||
raise ValueError("Undefined state")
|
||||
|
||||
return FSM
|
||||
|
||||
.. index:: single: waveform viewing
|
||||
|
||||
At this point, we will use the example to demonstrate the MyHDL support for
|
||||
waveform viewing. During simulation, signal changes can be written to a VCD
|
||||
output file. The VCD file can then be loaded and viewed in a waveform viewer
|
||||
tool such as :program:`gtkwave`.
|
||||
|
||||
.. %
|
||||
|
||||
The user interface of this feature consists of a single function,
|
||||
:func:`traceSignals`. To explain how it works, recall that in MyHDL, an
|
||||
instance is created by assigning the result of a function call to an instance
|
||||
name. For example::
|
||||
|
||||
tb_fsm = testbench()
|
||||
|
||||
To enable VCD tracing, the instance should be created as follows instead::
|
||||
|
||||
tb_fsm = traceSignals(testbench)
|
||||
|
||||
Note that the first argument of :func:`traceSignals` consists of the uncalled
|
||||
function. By calling the function under its control, :func:`traceSignals`
|
||||
gathers information about the hierarchy and the signals to be traced. In
|
||||
addition to a function argument, :func:`traceSignals` accepts an arbitrary
|
||||
number of non-keyword and keyword arguments that will be passed to the function
|
||||
call.
|
||||
|
||||
A small test bench for our framing controller example, with signal tracing
|
||||
enabled, is shown below::
|
||||
|
||||
def testbench():
|
||||
|
||||
SOF = Signal(bool(0))
|
||||
syncFlag = Signal(bool(0))
|
||||
clk = Signal(bool(0))
|
||||
reset_n = Signal(bool(1))
|
||||
state = Signal(t_State.SEARCH)
|
||||
|
||||
framectrl = FramerCtrl(SOF, state, syncFlag, clk, reset_n)
|
||||
|
||||
@always(delay(10))
|
||||
def clkgen():
|
||||
clk.next = not clk
|
||||
|
||||
@instance
|
||||
def stimulus():
|
||||
for i in range(3):
|
||||
yield clk.posedge
|
||||
for n in (12, 8, 8, 4):
|
||||
syncFlag.next = 1
|
||||
yield clk.posedge
|
||||
syncFlag.next = 0
|
||||
for i in range(n-1):
|
||||
yield clk.posedge
|
||||
raise StopSimulation
|
||||
|
||||
return framectrl, clkgen, stimulus
|
||||
|
||||
|
||||
tb_fsm = traceSignals(testbench)
|
||||
sim = Simulation(tb_fsm)
|
||||
sim.run()
|
||||
|
||||
When we run the test bench, it generates a VCD file called
|
||||
:file:`testbench.vcd`. When we load this file into :program:`gtkwave`, we can
|
||||
view the waveforms:
|
||||
|
||||
.. image:: tbfsm.png
|
||||
|
||||
Signals are dumped in a suitable format. This format is inferred at the
|
||||
:class:`Signal` construction time, from the type of the initial value. In
|
||||
particular, :class:`bool` signals are dumped as single bits. (This only works
|
||||
starting with Python 2.3, when :class:`bool` has become a separate type).
|
||||
Likewise, :class:`intbv` signals with a defined bit width are dumped as bit
|
||||
vectors. To support the general case, other types of signals are dumped as a
|
||||
string representation, as returned by the standard :func:`str` function.
|
||||
|
||||
.. warning::
|
||||
|
||||
Support for literal string representations is not part of the VCD standard. It
|
||||
is specific to :program:`gtkwave`. To generate a standard VCD file, you need to
|
||||
use signals with a defined bit width only.
|
||||
|
||||
|
||||
.. _model-hl:
|
||||
|
||||
*******************
|
||||
High level modeling
|
||||
===================
|
||||
*******************
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
.. index:: single: modeling; high level
|
||||
|
||||
To write synthesizable models in MyHDL, you should stick to
|
||||
the RTL templates shown in :ref:`model-rtl`. However,
|
||||
modeling in MyHDL is much more powerful than that.
|
||||
Conceptually, MyHDL is a library for general event-driven
|
||||
modeling and simulation of hardware systems.
|
||||
|
||||
There are many reasons why it can be useful to model at a
|
||||
higher abstraction level than RTL. For example, you can
|
||||
use MyHDL to verify architectural features, such as system
|
||||
throughput, latency and buffer sizes. You can also write
|
||||
high level models for specialized technology-dependent cores
|
||||
that are not going through synthesis. Last but not least,
|
||||
you can use MyHDL to write test benches that verify a system
|
||||
model or a synthesizable description.
|
||||
|
||||
This chapter explores some of the options for high level
|
||||
modeling with MyHDL.
|
||||
|
||||
|
||||
.. _model-bfm:
|
||||
|
||||
Modeling with bus-functional procedures
|
||||
---------------------------------------
|
||||
=======================================
|
||||
|
||||
.. index:: single: bus-functional procedure
|
||||
|
||||
@ -916,7 +317,7 @@ Now, transmission of a new byte only starts when the previous one is received::
|
||||
.. _model-mem:
|
||||
|
||||
Modeling memories with built-in types
|
||||
-------------------------------------
|
||||
=====================================
|
||||
|
||||
.. index:: single: modeling; memories
|
||||
|
||||
@ -1014,7 +415,7 @@ to get the size of a Python object.
|
||||
.. _model-err:
|
||||
|
||||
Modeling errors using exceptions
|
||||
--------------------------------
|
||||
================================
|
||||
|
||||
In the previous section, we used Python data types for modeling. If such a type
|
||||
is used inappropriately, Python's run time error system will come into play. For
|
||||
@ -1113,7 +514,7 @@ detected by a regular check on the length of the list.
|
||||
.. _model-obj:
|
||||
|
||||
Object oriented modeling
|
||||
------------------------
|
||||
========================
|
||||
|
||||
.. index:: single: modeling; object oriented
|
||||
|
@ -12,7 +12,9 @@ Contents:
|
||||
preface
|
||||
background
|
||||
intro
|
||||
modeling
|
||||
structure
|
||||
rtl
|
||||
highlevel
|
||||
unittest
|
||||
cosimulation
|
||||
conversion
|
||||
|
@ -47,9 +47,9 @@ The first line of the script imports a number of objects from the ``myhdl``
|
||||
package. In Python we can only use identifiers that are literally defined in the
|
||||
source file [#]_.
|
||||
|
||||
Then, we define a function called :func:`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.
|
||||
Then, we define a function called :func:`HelloWorld`. In MyHDL, hardware
|
||||
modules can be modeled using classic functions. In particular, the parameter list
|
||||
is then used to define the interface. In this first example, the interface is empty.
|
||||
|
||||
.. index:: single: decorator; always
|
||||
|
||||
|
@ -80,13 +80,18 @@ Waveform tracing
|
||||
will be moved to a backup file by attaching a timestamp to it, before creating
|
||||
the new file.
|
||||
|
||||
The ``traceSignals`` callable has the following attribute:
|
||||
The ``traceSignals`` callable has the following attribute:
|
||||
|
||||
|
||||
.. attribute:: traceSignals.name
|
||||
.. attribute:: name
|
||||
|
||||
This attribute is used to overwrite the default top-level instance name and the
|
||||
basename of the VCD output filename.
|
||||
This attribute is used to overwrite the default top-level instance name and the
|
||||
basename of the VCD output filename.
|
||||
|
||||
.. attribute:: timescale
|
||||
|
||||
This attribute is used to set the timescale corresponding to unit steps,
|
||||
according to the VCD format. The default is "1ns".
|
||||
|
||||
|
||||
.. _ref-model:
|
||||
@ -641,9 +646,13 @@ Conversion
|
||||
|
||||
.. attribute:: name
|
||||
|
||||
This attribute is used to overwrite the default top-level instance name and the
|
||||
basename of the Verilog output filename.
|
||||
This attribute is used to overwrite the default top-level instance name and the
|
||||
basename of the Verilog output filename.
|
||||
|
||||
.. attribute:: timescale
|
||||
|
||||
This attribute is used to set the timescale in Verilog format. Default is
|
||||
"1ns/10ps".
|
||||
|
||||
|
||||
.. function:: toVHDL(func[, *args][, **kwargs])
|
||||
|
444
doc/source/manual/rtl.rst
Normal file
444
doc/source/manual/rtl.rst
Normal file
@ -0,0 +1,444 @@
|
||||
.. currentmodule:: myhdl
|
||||
|
||||
|
||||
.. _model-rtl:
|
||||
|
||||
************
|
||||
RTL modeling
|
||||
************
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
.. index:: single: modeling; RTL style
|
||||
|
||||
RTL (Register Transfer Level) is a modeling abstraction level that
|
||||
is typically used to write synthesizable models.
|
||||
:dfn:`Synthesis` refers to the process by which an HDL description
|
||||
is automatically compiled into an implementation for an ASIC or FPGA.
|
||||
This chapter describes how MyHDL supports it.
|
||||
|
||||
|
||||
.. _model-comb:
|
||||
|
||||
Combinatorial logic
|
||||
===================
|
||||
|
||||
.. index:: single: combinatorial logic
|
||||
|
||||
|
||||
.. _model-comb-templ:
|
||||
|
||||
Template
|
||||
--------
|
||||
|
||||
Combinatorial logic is described with a code pattern as follows::
|
||||
|
||||
def top(<parameters>):
|
||||
...
|
||||
@always_comb
|
||||
def combLogic():
|
||||
<functional code>
|
||||
...
|
||||
return combLogic, ...
|
||||
|
||||
The :func:`always_comb` decorator describes combinatorial logic. [#]_. The
|
||||
decorated function is a local function that specifies what happens when one of
|
||||
the input signals of the logic changes. The :func:`always_comb` decorator
|
||||
infers the input signals automatically. It returns a generator that is sensitive
|
||||
to all inputs, and that executes the function whenever an input changes.
|
||||
|
||||
|
||||
.. _model-comb-ex:
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
The following is an example of a combinatorial multiplexer::
|
||||
|
||||
from myhdl import Signal, Simulation, delay, always_comb
|
||||
|
||||
def Mux(z, a, b, sel):
|
||||
|
||||
""" Multiplexer.
|
||||
|
||||
z -- mux output
|
||||
a, b -- data inputs
|
||||
sel -- control input: select a if asserted, otherwise b
|
||||
|
||||
"""
|
||||
|
||||
@always_comb
|
||||
def muxLogic():
|
||||
if sel == 1:
|
||||
z.next = a
|
||||
else:
|
||||
z.next = b
|
||||
|
||||
return muxLogic
|
||||
|
||||
To verify it, we will simulate the logic with some random patterns. The
|
||||
``random`` module in Python's standard library comes in handy for such purposes.
|
||||
The function ``randrange(n)`` returns a random natural integer smaller than *n*.
|
||||
It is used in the test bench code to produce random input values::
|
||||
|
||||
from random import randrange
|
||||
|
||||
z, a, b, sel = [Signal(0) for i in range(4)]
|
||||
|
||||
mux_1 = Mux(z, a, b, sel)
|
||||
|
||||
def test():
|
||||
print "z a b sel"
|
||||
for i in range(8):
|
||||
a.next, b.next, sel.next = randrange(8), randrange(8), randrange(2)
|
||||
yield delay(10)
|
||||
print "%s %s %s %s" % (z, a, b, sel)
|
||||
|
||||
test_1 = test()
|
||||
|
||||
sim = Simulation(mux_1, test_1)
|
||||
sim.run()
|
||||
|
||||
Because of the randomness, the simulation output varies between runs [#]_. One
|
||||
particular run produced the following output::
|
||||
|
||||
% python mux.py
|
||||
z a b sel
|
||||
6 6 1 1
|
||||
7 7 1 1
|
||||
7 3 7 0
|
||||
1 2 1 0
|
||||
7 7 5 1
|
||||
4 7 4 0
|
||||
4 0 4 0
|
||||
3 3 5 1
|
||||
StopSimulation: No more events
|
||||
|
||||
|
||||
.. _model-seq:
|
||||
|
||||
Sequential logic
|
||||
================
|
||||
|
||||
.. index:: single: sequential logic
|
||||
|
||||
|
||||
.. _model-seq-templ:
|
||||
|
||||
Template
|
||||
--------
|
||||
|
||||
Sequential RTL models are sensitive to a clock edge. In addition, they may be
|
||||
sensitive to a reset signal. We will describe one of the most common patterns: a
|
||||
template with a rising clock edge and an asynchronous reset signal. Other
|
||||
templates are similar. ::
|
||||
|
||||
def top(<parameters>, clock, ..., reset, ...):
|
||||
...
|
||||
@always(clock.posedge, reset.negedge)
|
||||
def seqLogic():
|
||||
if reset == <active level>:
|
||||
<reset code>
|
||||
else:
|
||||
<functional code>
|
||||
...
|
||||
return seqLogic, ...
|
||||
|
||||
|
||||
.. _model-seq-ex:
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
The following code is a description of an incrementer with enable, and an
|
||||
asynchronous reset. ::
|
||||
|
||||
from random import randrange
|
||||
from myhdl import *
|
||||
|
||||
ACTIVE_LOW, INACTIVE_HIGH = 0, 1
|
||||
|
||||
def Inc(count, enable, clock, reset, n):
|
||||
|
||||
""" Incrementer with enable.
|
||||
|
||||
count -- output
|
||||
enable -- control input, increment when 1
|
||||
clock -- clock input
|
||||
reset -- asynchronous reset input
|
||||
n -- counter max value
|
||||
|
||||
"""
|
||||
|
||||
@always(clock.posedge, reset.negedge)
|
||||
def incLogic():
|
||||
if reset == ACTIVE_LOW:
|
||||
count.next = 0
|
||||
else:
|
||||
if enable:
|
||||
count.next = (count + 1) % n
|
||||
|
||||
return incLogic
|
||||
|
||||
For the test bench, we will use an independent clock generator, stimulus
|
||||
generator, and monitor. After applying enough stimulus patterns, we can raise
|
||||
the ``StopSimulation`` exception to stop the simulation run. The test bench for
|
||||
a small incrementer and a small number of patterns is a follows::
|
||||
|
||||
def testbench():
|
||||
count, enable, clock, reset = [Signal(intbv(0)) for i in range(4)]
|
||||
|
||||
inc_1 = Inc(count, enable, clock, reset, n=4)
|
||||
|
||||
HALF_PERIOD = delay(10)
|
||||
|
||||
@always(HALF_PERIOD)
|
||||
def clockGen():
|
||||
clock.next = not clock
|
||||
|
||||
@instance
|
||||
def stimulus():
|
||||
reset.next = ACTIVE_LOW
|
||||
yield clock.negedge
|
||||
reset.next = INACTIVE_HIGH
|
||||
for i in range(12):
|
||||
enable.next = min(1, randrange(3))
|
||||
yield clock.negedge
|
||||
raise StopSimulation
|
||||
|
||||
@instance
|
||||
def monitor():
|
||||
print "enable count"
|
||||
yield reset.posedge
|
||||
while 1:
|
||||
yield clock.posedge
|
||||
yield delay(1)
|
||||
print " %s %s" % (enable, count)
|
||||
|
||||
return clockGen, stimulus, inc_1, monitor
|
||||
|
||||
|
||||
tb = testbench()
|
||||
|
||||
def main():
|
||||
Simulation(tb).run()
|
||||
|
||||
The simulation produces the following output::
|
||||
|
||||
% python inc.py
|
||||
enable count
|
||||
0 0
|
||||
1 1
|
||||
0 1
|
||||
1 2
|
||||
1 3
|
||||
1 0
|
||||
0 0
|
||||
1 1
|
||||
0 1
|
||||
0 1
|
||||
0 1
|
||||
1 2
|
||||
StopSimulation
|
||||
|
||||
|
||||
.. _model-fsm:
|
||||
|
||||
Finite State Machine modeling
|
||||
=============================
|
||||
|
||||
.. index:: single: modeling; Finite State Machine
|
||||
|
||||
Finite State Machine (FSM) modeling is very common in RTL design and therefore
|
||||
deserves special attention.
|
||||
|
||||
For code clarity, the state values are typically represented by a set of
|
||||
identifiers. A standard Python idiom for this purpose is to assign a range of
|
||||
integers to a tuple of identifiers, like so::
|
||||
|
||||
>>> SEARCH, CONFIRM, SYNC = range(3)
|
||||
>>> CONFIRM
|
||||
1
|
||||
|
||||
However, this technique has some drawbacks. Though it is clearly the intention
|
||||
that the identifiers belong together, this information is lost as soon as they
|
||||
are defined. Also, the identifiers evaluate to integers, whereas a string
|
||||
representation of the identifiers would be preferable. To solve these issues, we
|
||||
need an *enumeration type*.
|
||||
|
||||
.. index:: single: enum(); example usage
|
||||
|
||||
MyHDL supports enumeration types by providing a function :func:`enum`. The
|
||||
arguments to :func:`enum` are the string representations of the identifiers, and
|
||||
its return value is an enumeration type. The identifiers are available as
|
||||
attributes of the type. For example::
|
||||
|
||||
>>> from myhdl import enum
|
||||
>>> t_State = enum('SEARCH', 'CONFIRM', 'SYNC')
|
||||
>>> t_State
|
||||
<Enum: SEARCH, CONFIRM, SYNC>
|
||||
>>> t_State.CONFIRM
|
||||
CONFIRM
|
||||
|
||||
We can use this type to construct a state signal as follows::
|
||||
|
||||
state = Signal(t_State.SEARCH)
|
||||
|
||||
As an example, we will use a framing controller FSM. It is an imaginary
|
||||
example, but similar control structures are often found in telecommunication
|
||||
applications. Suppose that we need to find the Start Of Frame (SOF) position of
|
||||
an incoming frame of bytes. A sync pattern detector continuously looks for a
|
||||
framing pattern and indicates it to the FSM with a ``syncFlag`` signal. When
|
||||
found, the FSM moves from the initial ``SEARCH`` state to the ``CONFIRM`` state.
|
||||
When the ``syncFlag`` is confirmed on the expected position, the FSM declares
|
||||
``SYNC``, otherwise it falls back to the ``SEARCH`` state. This FSM can be
|
||||
coded as follows::
|
||||
|
||||
from myhdl import *
|
||||
|
||||
ACTIVE_LOW = 0
|
||||
FRAME_SIZE = 8
|
||||
t_State = enum('SEARCH', 'CONFIRM', 'SYNC')
|
||||
|
||||
def FramerCtrl(SOF, state, syncFlag, clk, reset_n):
|
||||
|
||||
""" Framing control FSM.
|
||||
|
||||
SOF -- start-of-frame output bit
|
||||
state -- FramerState output
|
||||
syncFlag -- sync pattern found indication input
|
||||
clk -- clock input
|
||||
reset_n -- active low reset
|
||||
|
||||
"""
|
||||
|
||||
index = Signal(0) # position in frame
|
||||
|
||||
@always(clk.posedge, reset_n.negedge)
|
||||
def FSM():
|
||||
if reset_n == ACTIVE_LOW:
|
||||
SOF.next = 0
|
||||
index.next = 0
|
||||
state.next = t_State.SEARCH
|
||||
|
||||
else:
|
||||
index.next = (index + 1) % FRAME_SIZE
|
||||
SOF.next = 0
|
||||
|
||||
if state == t_State.SEARCH:
|
||||
index.next = 1
|
||||
if syncFlag:
|
||||
state.next = t_State.CONFIRM
|
||||
|
||||
elif state == t_State.CONFIRM:
|
||||
if index == 0:
|
||||
if syncFlag:
|
||||
state.next = t_State.SYNC
|
||||
else:
|
||||
state.next = t_State.SEARCH
|
||||
|
||||
elif state == t_State.SYNC:
|
||||
if index == 0:
|
||||
if not syncFlag:
|
||||
state.next = t_State.SEARCH
|
||||
SOF.next = (index == FRAME_SIZE-1)
|
||||
|
||||
else:
|
||||
raise ValueError("Undefined state")
|
||||
|
||||
return FSM
|
||||
|
||||
.. index:: single: waveform viewing
|
||||
|
||||
At this point, we will use the example to demonstrate the MyHDL support for
|
||||
waveform viewing. During simulation, signal changes can be written to a VCD
|
||||
output file. The VCD file can then be loaded and viewed in a waveform viewer
|
||||
tool such as :program:`gtkwave`.
|
||||
|
||||
.. %
|
||||
|
||||
The user interface of this feature consists of a single function,
|
||||
:func:`traceSignals`. To explain how it works, recall that in MyHDL, an
|
||||
instance is created by assigning the result of a function call to an instance
|
||||
name. For example::
|
||||
|
||||
tb_fsm = testbench()
|
||||
|
||||
To enable VCD tracing, the instance should be created as follows instead::
|
||||
|
||||
tb_fsm = traceSignals(testbench)
|
||||
|
||||
Note that the first argument of :func:`traceSignals` consists of the uncalled
|
||||
function. By calling the function under its control, :func:`traceSignals`
|
||||
gathers information about the hierarchy and the signals to be traced. In
|
||||
addition to a function argument, :func:`traceSignals` accepts an arbitrary
|
||||
number of non-keyword and keyword arguments that will be passed to the function
|
||||
call.
|
||||
|
||||
A small test bench for our framing controller example, with signal tracing
|
||||
enabled, is shown below::
|
||||
|
||||
def testbench():
|
||||
|
||||
SOF = Signal(bool(0))
|
||||
syncFlag = Signal(bool(0))
|
||||
clk = Signal(bool(0))
|
||||
reset_n = Signal(bool(1))
|
||||
state = Signal(t_State.SEARCH)
|
||||
|
||||
framectrl = FramerCtrl(SOF, state, syncFlag, clk, reset_n)
|
||||
|
||||
@always(delay(10))
|
||||
def clkgen():
|
||||
clk.next = not clk
|
||||
|
||||
@instance
|
||||
def stimulus():
|
||||
for i in range(3):
|
||||
yield clk.posedge
|
||||
for n in (12, 8, 8, 4):
|
||||
syncFlag.next = 1
|
||||
yield clk.posedge
|
||||
syncFlag.next = 0
|
||||
for i in range(n-1):
|
||||
yield clk.posedge
|
||||
raise StopSimulation
|
||||
|
||||
return framectrl, clkgen, stimulus
|
||||
|
||||
|
||||
tb_fsm = traceSignals(testbench)
|
||||
sim = Simulation(tb_fsm)
|
||||
sim.run()
|
||||
|
||||
When we run the test bench, it generates a VCD file called
|
||||
:file:`testbench.vcd`. When we load this file into :program:`gtkwave`, we can
|
||||
view the waveforms:
|
||||
|
||||
.. image:: tbfsm.png
|
||||
|
||||
Signals are dumped in a suitable format. This format is inferred at the
|
||||
:class:`Signal` construction time, from the type of the initial value. In
|
||||
particular, :class:`bool` signals are dumped as single bits. (This only works
|
||||
starting with Python 2.3, when :class:`bool` has become a separate type).
|
||||
Likewise, :class:`intbv` signals with a defined bit width are dumped as bit
|
||||
vectors. To support the general case, other types of signals are dumped as a
|
||||
string representation, as returned by the standard :func:`str` function.
|
||||
|
||||
.. warning::
|
||||
|
||||
Support for literal string representations is not part of the VCD standard. It
|
||||
is specific to :program:`gtkwave`. To generate a standard VCD file, you need to
|
||||
use signals with a defined bit width only.
|
||||
|
||||
|
||||
.. rubric:: Footnotes
|
||||
|
||||
.. [#] The name :func:`always_comb` refers to a construct with similar semantics in
|
||||
SystemVerilog.
|
||||
|
||||
.. [#] It also possible to have a reproducible random output, by explicitly providing a
|
||||
seed value. See the documentation of the ``random`` module.
|
||||
|
||||
|
196
doc/source/manual/structure.rst
Normal file
196
doc/source/manual/structure.rst
Normal file
@ -0,0 +1,196 @@
|
||||
.. currentmodule:: myhdl
|
||||
|
||||
|
||||
.. _model-structure:
|
||||
|
||||
*******************
|
||||
Structural modeling
|
||||
*******************
|
||||
|
||||
.. index:: single: modeling; structural
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
Hardware descriptions need to support the concepts of module instantiation and
|
||||
hierarchy. In MyHDL, an instance is recursively defined as being either a
|
||||
sequence of instances, or a generator. Hierarchy is modeled by defining
|
||||
instances in a higher-level function, and returning them. The following is a
|
||||
schematic example of the basic case. ::
|
||||
|
||||
def top(...):
|
||||
...
|
||||
instance_1 = module_1(...)
|
||||
instance_2 = module_2(...)
|
||||
...
|
||||
instance_n = module_n(...)
|
||||
...
|
||||
return instance_1, instance_2, ... , instance_n
|
||||
|
||||
Note that MyHDL uses conventional procedural techniques for modeling structure.
|
||||
This makes it straightforward to model more complex cases.
|
||||
|
||||
|
||||
.. _model-conf:
|
||||
|
||||
Conditional instantiation
|
||||
=========================
|
||||
|
||||
.. index:: single: conditional instantiation
|
||||
|
||||
To model conditional instantiation, we can select the returned instance under
|
||||
parameter control. For example::
|
||||
|
||||
SLOW, MEDIUM, FAST = range(3)
|
||||
|
||||
def top(..., speed=SLOW):
|
||||
...
|
||||
def slowAndSmall():
|
||||
...
|
||||
...
|
||||
def fastAndLarge():
|
||||
...
|
||||
if speed == SLOW:
|
||||
return slowAndSmall()
|
||||
elif speed == FAST:
|
||||
return fastAndLarge()
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
.. _model-instarray:
|
||||
|
||||
Lists of instances and signals
|
||||
------------------------------
|
||||
|
||||
.. index:: single: lists of instances and signals
|
||||
|
||||
Python lists are easy to create. We can use them to model lists of instances.
|
||||
|
||||
Suppose we have a top module that instantiates a single ``channel`` submodule,
|
||||
as follows::
|
||||
|
||||
def top(...):
|
||||
|
||||
din = Signal(0)
|
||||
dout = Signal(0)
|
||||
clk = Signal(bool(0))
|
||||
reset = Signal(bool(0))
|
||||
|
||||
channel_inst = channel(dout, din, clk, reset)
|
||||
|
||||
return channel_inst
|
||||
|
||||
If we wanted to support an arbitrary number of channels, we can use lists of
|
||||
signals and a list of instances, as follows::
|
||||
|
||||
def top(..., n=8):
|
||||
|
||||
din = [Signal(0) for i in range(n)]
|
||||
dout = [Signal(0) for in range(n)]
|
||||
clk = Signal(bool(0))
|
||||
reset = Signal(bool(0))
|
||||
channel_inst = [None for i in range(n)]
|
||||
|
||||
for i in range(n):
|
||||
channel_inst[i] = channel(dout[i], din[i], clk, reset)
|
||||
|
||||
return channel_inst
|
||||
|
||||
.. _model-shadow-signals:
|
||||
|
||||
Converting between lists of signals and bit vectors
|
||||
===================================================
|
||||
|
||||
Compared to HDLs such as VHDL and Verilog, MyHDL signals are less
|
||||
flexible for structural modeling. For example, slicing a signal
|
||||
returns a slice of the current value. For behavioral code, this is
|
||||
just fine. However, it implies that you cannot use such as slice in
|
||||
structural descriptions. In other words, a signal slice cannot be used
|
||||
as a signal.
|
||||
|
||||
In MyHDL, you can address such cases by a concept called
|
||||
shadow signals. A shadow signal is constructed out of
|
||||
other signals and follows their value changes automatically.
|
||||
For example, a :class:`_SliceSignal` follows the value of
|
||||
an index or a slice from another signal. Likewise,
|
||||
A :class:`ConcatSignal` follows the
|
||||
values of a number of signals as a concatenation.
|
||||
|
||||
As an example, suppose we have a system with N requesters that
|
||||
need arbitration. Each requester has a ``request`` output
|
||||
and a ``grant`` input. To connect them in the system, we can
|
||||
use list of signals. For example, a list of request signals
|
||||
can be constructed as follows::
|
||||
|
||||
request_list = [Signal(bool()) for i in range(M)]
|
||||
|
||||
Suppose that an arbiter module is available that is
|
||||
instantiated as follows::
|
||||
|
||||
arb = arbiter(grant_vector, request_vector, clock, reset)
|
||||
|
||||
The ``request_vector`` input is a bit vector that can have
|
||||
any of its bits asserted. The ``grant_vector`` is an output
|
||||
bit vector with just a single bit asserted, or none.
|
||||
Such a module is typically based on bit vectors because
|
||||
they are easy to process in RTL code. In MyHDL, a bit vector
|
||||
is modeled using the :class:`intbv` type.
|
||||
|
||||
We need a way to "connect" the list of signals to the
|
||||
bit vector and vice versa. Of course, we can do this with explicit
|
||||
code, but shadow signals can do this automatically. For
|
||||
example, we can construct a ``request_vector`` as a
|
||||
:class:`ConcatSignal` object::
|
||||
|
||||
request_vector = ConcatSignal(*reversed(request_list)
|
||||
|
||||
Note that we reverse the list first. This is done because the index range
|
||||
of lists is the inverse of the range of :class:`intbv` bit vectors.
|
||||
By reversing, the indices correspond to the same bit.
|
||||
|
||||
The inverse problem exist for the ``grant_vector``. It would be defined as follows::
|
||||
|
||||
grant_vector = Signal(intbv(0)[M:])
|
||||
|
||||
To construct a list of signals that are connected automatically to the
|
||||
bit vector, we can use the :class:`Signal` call interface to construct
|
||||
:class:`_SliceSignal` objects::
|
||||
|
||||
grant_list = [grant_vector(i) for i in range(M)]
|
||||
|
||||
Note the round brackets used for this type of slicing. Also, it may not be
|
||||
necessary to construct this list explicitly. You can simply use
|
||||
``grant_vector(i)`` in an instantiation.
|
||||
|
||||
To decide when to use normal or shadow signals, consider the data
|
||||
flow. Use normal signals to connect to *outputs*. Use shadow signals to
|
||||
transform these signals so that they can be used as *inputs*.
|
||||
|
||||
|
||||
.. _model-infer-instlist:
|
||||
|
||||
Inferring the list of instances
|
||||
===============================
|
||||
|
||||
In MyHDL, instances have to be returned explicitly by a top level function. It
|
||||
may be convenient to assemble the list of instances automatically. For this
|
||||
purpose, MyHDL provides the function :func:`instances`. Using the first example
|
||||
in this section, it is used as follows::
|
||||
|
||||
from myhdl import instances
|
||||
|
||||
def top(...):
|
||||
...
|
||||
instance_1 = module_1(...)
|
||||
instance_2 = module_2(...)
|
||||
...
|
||||
instance_n = module_n(...)
|
||||
...
|
||||
return instances()
|
||||
|
||||
Function :func:`instances` uses introspection to inspect the type of the local
|
||||
variables defined by the calling function. All variables that comply with the
|
||||
definition of an instance are assembled in a list, and that list is returned.
|
||||
|
||||
|
@ -400,7 +400,7 @@ Thanks to Francesco Balau for packaging MyHDL for Ubuntu.
|
||||
I would also like to thank `Easics`_ for
|
||||
the opportunity to use MyHDL in industrial projects.
|
||||
|
||||
.. _`Easics`: http:www.easics.com
|
||||
.. _`Easics`: http://www.easics.com
|
||||
|
||||
|
||||
|
||||
|
@ -29,6 +29,7 @@ def test_dff():
|
||||
return dff_inst, clkgen, stimulus
|
||||
|
||||
def simulate(timesteps):
|
||||
traceSignals.timescale = "1ps"
|
||||
tb = traceSignals(test_dff)
|
||||
sim = Simulation(tb)
|
||||
sim.run(timesteps)
|
||||
|
@ -45,10 +45,13 @@ _error.MultipleTraces = "Cannot trace multiple instances simultaneously"
|
||||
|
||||
class _TraceSignalsClass(object):
|
||||
|
||||
__slot__ = ("name", )
|
||||
__slot__ = ("name",
|
||||
"timescale",
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.name = None
|
||||
self.timescale = "1ns"
|
||||
|
||||
def __call__(self, dut, *args, **kwargs):
|
||||
global _tracing
|
||||
@ -82,7 +85,7 @@ class _TraceSignalsClass(object):
|
||||
vcdfile = open(vcdpath, 'w')
|
||||
_simulator._tracing = 1
|
||||
_simulator._tf = vcdfile
|
||||
_writeVcdHeader(vcdfile)
|
||||
_writeVcdHeader(vcdfile, self.timescale)
|
||||
_writeVcdSigs(vcdfile, h.hierarchy)
|
||||
finally:
|
||||
_tracing = 0
|
||||
@ -111,7 +114,7 @@ def _namecode(n):
|
||||
code = _codechars[r] + code
|
||||
return code
|
||||
|
||||
def _writeVcdHeader(f):
|
||||
def _writeVcdHeader(f, timescale):
|
||||
print >> f, "$date"
|
||||
print >> f, " %s" % time.asctime()
|
||||
print >> f, "$end"
|
||||
@ -119,7 +122,7 @@ def _writeVcdHeader(f):
|
||||
print >> f, " MyHDL %s" % __version__
|
||||
print >> f, "$end"
|
||||
print >> f, "$timescale"
|
||||
print >> f, " 1ns"
|
||||
print >> f, " %s" % timescale
|
||||
print >> f, "$end"
|
||||
print >> f
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user