mirror of
https://github.com/myhdl/myhdl.git
synced 2024-12-14 07:44:38 +08:00
399 lines
13 KiB
ReStructuredText
399 lines
13 KiB
ReStructuredText
=======================
|
||
What’s New in MyHDL 0.3
|
||
=======================
|
||
|
||
:Author: Jan Decaluwe
|
||
|
||
VCD output for waveform viewing
|
||
===============================
|
||
|
||
|image|
|
||
|
||
MyHDL now has support for waveform viewing. During simulation, signal
|
||
changes can be written to a VCD output file that can be loaded into a
|
||
waveform viewer tool such as **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)
|
||
|
||
All signals in the instance hierarchy will be traced in a VCD file
|
||
called . Note that first the 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.
|
||
|
||
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 **gtkwave**. To generate a standard VCD
|
||
file, you need to use signals with a defined bit width only.
|
||
|
||
Enumeration types
|
||
=================
|
||
|
||
It is often desirable to define 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*.
|
||
|
||
MyHDL 0.3 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
|
||
|
||
Enumeration types are often used for the state variable in a finite
|
||
state machine. In the waveform in
|
||
Section \ `1 <#vcd-output-for-waveform-viewing>`__, you see a
|
||
:class:`Signal` called ``state``. Note how the waveforms show the string
|
||
representation of the enumeration type identifiers The ``state`` signal
|
||
has been constructed with an enumeration type identifier as its initial
|
||
value, as follows:
|
||
|
||
::
|
||
|
||
state = Signal(t_State.SEARCH)
|
||
|
||
Inferring the sensitivity list for combinatorial logic
|
||
======================================================
|
||
|
||
In MyHDL, combinatorial logic is described by a generator function with
|
||
a sensitivity list that contains all inputs signals (the signals that
|
||
are read inside the function).
|
||
|
||
It may be easy to forget some input signals, especially it there are a
|
||
lot of them or if the code is being modified. There are various ways to
|
||
solve this. One way is to use a sophisticated editor. Another way is
|
||
direct language support. For example, recent versions of Verilog have
|
||
the ``always @*`` construct, that infers all input signals. The
|
||
SystemVerilog 3.1 standard improves on this by introducing the
|
||
``always_comb`` block with slightly enhanced semantics.
|
||
|
||
MyHDL 0.3 provides a function called :func:`always_comb()` which is
|
||
named and modeled after the SystemVerilog counterpart.
|
||
:func:`always_comb()` takes a classic local function as its argument.
|
||
This function should specify the combinatorial logic behavior.
|
||
:func:`always_comb()` returns a generator that is sensitive to all
|
||
inputs, and that will run the function whenever an input changes.
|
||
|
||
For example, suppose that we have a mux module described as follows:
|
||
|
||
::
|
||
|
||
def mux(z, a, b, sel):
|
||
""" Multiplexer.
|
||
|
||
z -- mux output
|
||
a, b -- data inputs
|
||
sel -- control input
|
||
|
||
"""
|
||
def logic()
|
||
while 1:
|
||
yield a, b, sel
|
||
if sel == 1:
|
||
z.next = a
|
||
else:
|
||
z.next = b
|
||
mux_logic = logic()
|
||
return mux_logic
|
||
|
||
Using :func:`always_comb()`, we can describe it as follows instead:
|
||
|
||
::
|
||
|
||
def mux(z, a, b, sel):
|
||
""" Multiplexer.
|
||
|
||
z -- mux output
|
||
a, b -- data inputs
|
||
sel -- control input
|
||
|
||
"""
|
||
def logic()
|
||
if sel == 1:
|
||
z.next = a
|
||
else:
|
||
z.next = b
|
||
mux_logic = always_comb(logic)
|
||
return mux_logic
|
||
|
||
Note that in the first version, the sensitivity list is at the beginning
|
||
of the generator function code. This is traditionally done in
|
||
synthesizable RTL style modeling. However, the semantics of this style
|
||
are not entirely correct: at the start of the simulation, the
|
||
combinatorial output will not reflect the initial state of the inputs.
|
||
:func:`always_comb()` solves this by putting the sensitivity list at the
|
||
end of the code.
|
||
|
||
Inferring the list of instances
|
||
===============================
|
||
|
||
In MyHDL, the instances defined in a top level function need to be
|
||
returned explicitly. The following is a schematic example:
|
||
|
||
::
|
||
|
||
def top(...):
|
||
...
|
||
instance_1 = module_1(...)
|
||
instance_2 = module_2(...)
|
||
...
|
||
instance_n = module_n(...)
|
||
...
|
||
return instance_1, instance_2, ... , instance_n
|
||
|
||
It may be convenient to assemble the list of instances automatically,
|
||
especially if there are many instances. For this purpose, MyHDL 0.3
|
||
provides the function :func:`instances()`. 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.
|
||
|
||
Inferring the list of processes
|
||
===============================
|
||
|
||
In addition to instances, a top level function may also define local
|
||
generators functions, which I will call *processes* because of the
|
||
analogy with VHDL. Like instances, processes need to be returned
|
||
explicitly, with the qualification that they have to be called first to
|
||
turn them into generators. The following is a schematic example:
|
||
|
||
::
|
||
|
||
def top(...):
|
||
...
|
||
def process_1():
|
||
...
|
||
def process_2():
|
||
...
|
||
...
|
||
def process_n():
|
||
...
|
||
...
|
||
return process_1(), process_2(), ..., process_n()
|
||
|
||
As for instances, it may be more convenient to assemble the list of
|
||
processes automatically. One option is to turn each process into an
|
||
instance by calling it and assigning the returned generator to a local
|
||
variable. Those instances will then be found by the :func:`instances()`
|
||
function described in
|
||
Section \ `4 <#inferring-the-list-of-instances>`__.
|
||
|
||
Another option is to use the function :func:`processes()` provided by
|
||
MyHDL 0.3. This function uses introspection to find the processes, calls
|
||
each of them, and assembles the returned generators into a list. It can
|
||
be used as follows:
|
||
|
||
::
|
||
|
||
from myhdl import processes
|
||
|
||
def top(...):
|
||
...
|
||
def process_1():
|
||
...
|
||
def process_2():
|
||
...
|
||
...
|
||
def process_n():
|
||
...
|
||
...
|
||
return processes()
|
||
|
||
To conclude, a top level function with both instances and processes can
|
||
use the following idiomatic code to return all of them:
|
||
|
||
::
|
||
|
||
return instances(), processes()
|
||
|
||
Class :class:`intbv` enhancements
|
||
=================================
|
||
|
||
Class :class:`intbv` has been enhanced with new features.
|
||
|
||
It is now possible to leave the left index of a slicing operation
|
||
unspecified. The meaning is to access “all” higher order bits. For
|
||
example:
|
||
|
||
::
|
||
|
||
>>> from myhdl import intbv
|
||
>>> n = intbv()
|
||
>>> hex(n)
|
||
'0x0'
|
||
>>> n[:] = 0xde
|
||
>>> hex(n)
|
||
'0xde'
|
||
>>> n[:8] = 0xfa
|
||
>>> hex(n)
|
||
'0xfade'
|
||
>>> n[8:] = 0xb4
|
||
>>> hex(n)
|
||
'0xfab4'
|
||
|
||
:class:`intbv` objects now have ``min`` and ``max`` attributes that can
|
||
be specified at construction time. The meaning is that only values
|
||
within ``range(min, max)`` are permitted. The default value for these
|
||
attributes is ``None``, meaning “infinite”. For example (traceback
|
||
output shortened for clarity):
|
||
|
||
::
|
||
|
||
>>> n = intbv(min=-17, max=53)
|
||
>>> n
|
||
intbv(0)
|
||
>>> n.min
|
||
-17
|
||
>>> n.max
|
||
53
|
||
>>> n[:] = 28
|
||
>>> n
|
||
intbv(28)
|
||
>>> n[:] = -18
|
||
Traceback (most recent call last):
|
||
....
|
||
ValueError: intbv value -18 < minimum -17
|
||
>>> n[:] = 53
|
||
Traceback (most recent call last):
|
||
....
|
||
ValueError: intbv value 53 >= maximum 53
|
||
|
||
When a slice is taken from an :class:`intbv` object, the return value is
|
||
a new :class:`intbv` object with a defined bit width. As in Verilog, the
|
||
value of the new :class:`intbv` object is always positive, regardless of
|
||
the sign of the original value. In addition, the ``min`` and ``max``
|
||
attributes are set implicitly:
|
||
|
||
::
|
||
|
||
>>> v = intbv()[6:]
|
||
>>> v
|
||
intbv(0)
|
||
>>> v.min
|
||
0
|
||
>>> v.max
|
||
64
|
||
|
||
Lastly, a small change was implemented with regard to binary operations.
|
||
In previous versions, both numeric and bit-wise operations always
|
||
returned a new :class:`intbv` object, even in mixed-mode operations with
|
||
:class:`int` objects. This has changed: numeric operations return an
|
||
:class:`int`, and bitwise operations return a :class:`intbv`. In this
|
||
way, the return value corresponds better to the nature of the operation.
|
||
|
||
Function :func:`concat()`
|
||
==========================
|
||
|
||
In previous versions, the :class:`intbv` class provided a method. This
|
||
method is no longer available. Instead, there is now a :func:`concat()`
|
||
function that supports a much broader range of objects.
|
||
|
||
A function is more natural because MyHDL objects of various types can be
|
||
concatenated: :class:`intbv` objects with a defined bit width,
|
||
:class:`bool` objects, the corresponding signal objects, and bit
|
||
strings. All these objects have a defined bit width. Moreover, the first
|
||
argument doesn’t need to have a defined bit width. It can also be an
|
||
unsized :class:`intbv`, an :class:`int`, a :class:`long`, or a
|
||
corresponding signal object. Function :func:`concat()` returns an
|
||
:class:`intbv` object.
|
||
|
||
Python 2.3 support
|
||
==================
|
||
|
||
Python 2.3 was released on July 29, 2003, and as of this writing, it is
|
||
the latest stable Python release. MyHDL 0.3 works with both Python 2.2
|
||
and Python 2.3. In good Python tradition, MyHDL code developed with
|
||
Python 2.2 should run without changes or problems in Python 2.3.
|
||
|
||
In general, I am not that keen on early upgrading. However, as it
|
||
happens, the evolution of Python enables features that are really
|
||
important or even crucial to MyHDL. Python 2.2 generators are the best
|
||
example: they are the cornerstone of MyHDL. But Python 2.3 also has
|
||
significant benefits, which I will summarize below.
|
||
|
||
First, generators and the ``yield`` statement are a default Python 2.3
|
||
feature. This means that statements are no longer required.
|
||
|
||
Second, Python 2.3 has a :class:`bool` type, which is implemented as a
|
||
subtype of :class:`int`. For general Python use, the implications are
|
||
rather limited - the main difference is that logical result values will
|
||
print as ``False`` and ``True`` instead of ``0`` and ``1``. However, in
|
||
MyHDL, I can use the :class:`bool` type to infer a bit width. If a
|
||
:class:`Signal` is constructed with a :class:`bool` value, it is a
|
||
single bit :class:`Signal`. One application is waveform viewing as in
|
||
Section \ `1 <#vcd-output-for-waveform-viewing>`__ In the waveform, note
|
||
how single bit signals are displayed as level changes. With Python 2.2,
|
||
the waveforms of these signals would only show value changes, which is
|
||
not as clear for single bits.
|
||
|
||
Finally, Python 2.3 is significantly faster. MyHDL code runs 25–35%
|
||
faster in Python 2.3. This is a very nice speedup compared to the small
|
||
burden of a straightforward upgrade.
|
||
|
||
Python is a very stable language, so upgrading to Python 2.3 is
|
||
virtually risk free. Given the additional benefits, I recommend
|
||
MyHDL users to do so as soon as possible. For the next major
|
||
MyHDLrelease, the new features will become required and only Python 2.3
|
||
(and higher) will be supported.
|
||
|
||
.. |image| image:: ../manual/tbfsm.png
|