mirror of
https://github.com/myhdl/myhdl.git
synced 2024-12-14 07:44:38 +08:00
370 lines
12 KiB
ReStructuredText
370 lines
12 KiB
ReStructuredText
.. currentmodule:: myhdl
|
|
|
|
.. _new08:
|
|
|
|
***********************
|
|
What's new in MyHDL 0.8
|
|
***********************
|
|
|
|
Modular bit vector types
|
|
========================
|
|
|
|
Rationale
|
|
---------
|
|
In hardware modeling, there is often a need for the elegant
|
|
modeling of wrap-around behavior. :class:`intbv` instances
|
|
don't provide this automatically, because they assert
|
|
that any assigned value is within the bound constraints.
|
|
Therefore, one has currently has to use other language
|
|
features for wrap-around modeling.
|
|
|
|
Often, this is straightforward. For example, the wrap-around
|
|
condition for a counter is often decoded explicitly, as it
|
|
is needed for other purposes. Also, the modulo
|
|
operator provides an elegant one-liner in most scenarios.
|
|
|
|
However, in some important cases the current solution is
|
|
not satisfactory. For example, we would like to describe
|
|
a free running counter using a variable and augmented
|
|
assignment as follows::
|
|
|
|
count += 1
|
|
|
|
This is not possible with the :class:`intbv` type, as we
|
|
cannot add the modulo behavior to this description. A similar
|
|
problem exist of for a left shift as follows::
|
|
|
|
shifter <<= 4
|
|
|
|
These operations can only supported directly with a new type.
|
|
For these reasons, it was felt that this would be a useful
|
|
addition to MyHDL.
|
|
|
|
Solution
|
|
--------
|
|
|
|
The proposed solution is to borrow the idea behind Ada modular types.
|
|
These are natural integer types with wrap-around behaviour.
|
|
|
|
The wrap-around behavior of modular types is based on the sound
|
|
mathematical concept of modulo arithmetic. Therefore, the modulus is
|
|
not limited to powers of 2.
|
|
|
|
Ada's modular type is called :class:`mod`. In MyHDL, we want also
|
|
want to give it "bit-vector" support, like :class:`intbv`. Therefore,
|
|
proposed MyHDL type is called :class:`modbv`.
|
|
|
|
Implementation
|
|
--------------
|
|
|
|
:class:`modbv` is implemented as a subclass of :class:`intbv`.
|
|
The two classes have an identical interface and work together
|
|
in a straightforward way for arithmetic operations.
|
|
|
|
The only difference is how the bounds are handled: out-of-bound values
|
|
result in an error with :class:`intbv`, and in wrap-around with
|
|
:class:`modbv`. The Wrap-around behavior would be defined as follows, with val
|
|
denoting the current value and min/max the bounds::
|
|
|
|
val = (val - min) % (max - min) + min
|
|
|
|
|
|
Interface
|
|
---------
|
|
|
|
.. class:: modbv([val=0] [, min=None] [, max=None])
|
|
:noindex:
|
|
|
|
The :class:`modbv` class implements modular bit vector types.
|
|
|
|
It is implemented as a subclass of :class:`intbv`
|
|
and supports the same parameters and operators.
|
|
The difference is in the handling of the *min* and *max* boundaries.
|
|
Instead of throwing an exception when those constraints are exceeded,
|
|
the value of :class:`modbv` objects wraps around according to the
|
|
following formula::
|
|
|
|
val = (val - min) % (max - min) + min
|
|
|
|
This formula is a generalization of modulo wrap-around behavior that
|
|
is often useful when describing hardware system behavior.
|
|
|
|
Conversion
|
|
----------
|
|
|
|
:dfn:`Full-range` :class:`modbv` objects are those where the max bound is
|
|
a power of 2, and the min bound is 0 or the negative of the max bound.
|
|
For these objects, conversion worked out-of-the-box because this
|
|
corresponds to the target types in Verilog and VHDL.
|
|
|
|
Currently, conversion is restricted to full-range :class:`modbv`
|
|
objects. It may be possible to support conversion of the modulo
|
|
behavior of more general cases, but this requires more sophistication
|
|
in the converter. This may be considered in the future.
|
|
|
|
See also
|
|
--------
|
|
For a more in-depth discussion, see `mep-106`_.
|
|
|
|
.. _mep-106: http://www.myhdl.org/doku.php/meps:mep-106
|
|
|
|
|
|
``always_seq`` decorator
|
|
========================
|
|
|
|
Rationale
|
|
---------
|
|
|
|
In classical synthesizable RTL coding, the reset behavior is described
|
|
explicitly. A typical template is as follows::
|
|
|
|
@always(clock.posedge, reset.negedge)
|
|
def seq():
|
|
if reset == 0:
|
|
<reset code>
|
|
else:
|
|
<functional code>
|
|
|
|
The reset behavior is described using a the top-level if-else
|
|
structure with a number of assignments under the if. A significant
|
|
piece of code at a prominent location is therefore dedicated to
|
|
non-functional behavior.
|
|
|
|
Reset behavior coding is error-prone. For a proper gate-level
|
|
implementation, most if not all registers should typically be
|
|
reset. However, it is easy to forget some reset assignments. Such bugs
|
|
are not necessarily easily detected during RTL or gate-level
|
|
simulations.
|
|
|
|
In the template, the edge that asserts reset is in the sensitivity
|
|
list. It is easy to forget this, and in that case the reset will not
|
|
behave asynchronously as intended but synchronously. Note also that it
|
|
is somewhat strange to specify an edge sensitivity when describing
|
|
asynchronous behavior.
|
|
|
|
Solution
|
|
--------
|
|
The proposed solution is to infer the reset structure automatically.
|
|
The main idea is to use the initial values of signals as the
|
|
specification of reset values. This is possible in MyHDL, because
|
|
all objects are constructed with an initial value. The assumption
|
|
is that the initial value also specifies the desired reset value.
|
|
|
|
The solution is implemented with two new MyHDL constructs. The first
|
|
one is a new decorator called :func:`always_seq`. Using this
|
|
decorator, code with identical behavior as in the previous section can
|
|
be described as follows::
|
|
|
|
@always_seq(clock.posedge, reset=reset)
|
|
<functional code>
|
|
|
|
The :func:`always_seq` decorator takes two arguments: a clock edge
|
|
and a reset signal. It inspects the code to find the registers, and
|
|
uses the initial values to construct the reset behavior.
|
|
|
|
The second construct is a specialized signal subclass called
|
|
:class:`ResetSignal`. It is used as follows::
|
|
|
|
reset = ResetSignal(1, active=0, async=True)
|
|
|
|
The ResetSignal constructor has three arguments: the initial value as
|
|
usual, an *active* argument with the active level, and an *async* argument
|
|
that specifies whether the reset style is asynchronous or synchronous.
|
|
|
|
The proposed solution has some very desirable features.
|
|
|
|
Explicit reset behavior coding is no longer necessary. Code reviewers
|
|
are thus no longer distracted by non-functional code. It is
|
|
sufficient to check the initial values to verify whether the reset
|
|
value is correctly specified. Moreover, one indentation level is saved
|
|
for functional coding.
|
|
|
|
Even more importantly, the reset structure is correct by
|
|
construction. All registers are automatically included in the reset
|
|
behavior, and the sensitivity list is automatically correct according
|
|
to the reset style.
|
|
|
|
Traditionally, the reset behavior is spread out over all sequential
|
|
processes. Therefore, it has to be debugged by investigating all those
|
|
processes. Even worse, if a change in style or active level is
|
|
required, all processes are affected. In contrast, with the proposed
|
|
technique all reset features are specified at single location in the
|
|
:class:`ResetSignal` constructor. Changes are trivial. For example, to
|
|
change to an active high synchronous reset one merely has to change
|
|
the constructor as follows::
|
|
|
|
reset = ResetSignal(1, active=1, async=False)
|
|
|
|
Occasionally, it is useful to have registers without reset at
|
|
all. The proposed technique is also useful in that case. In
|
|
particular, the :func:`always_seq` decorator accepts ``None`` as the reset
|
|
argument::
|
|
|
|
@always_seq(clock.posedge, reset=None)
|
|
|
|
A reviewer will have no doubt what the intention is. In contrast, in
|
|
the case of a traditional always block, the reviewer may think that
|
|
the designer has delayed the detailed reset coding for later and then
|
|
forgotten about it.
|
|
|
|
Interface
|
|
---------
|
|
|
|
.. function:: always_seq(edge, reset)
|
|
:noindex:
|
|
|
|
The :func:`always_seq` decorator is used to describe sequential (clocked) logic.
|
|
|
|
The *edge* parameter should be a clock edge (``clock.posedge`` or ``clock.negedge``).
|
|
The *reset* parameter should a :class:`ResetSignal` object.
|
|
|
|
.. class:: ResetSignal(val, active, async)
|
|
:noindex:
|
|
|
|
This :class:`Signal` subclass defines reset signals. *val*, *active*, and *async*
|
|
are mandatory arguments.
|
|
*val* is a boolean value that specifies the intial value,
|
|
*active* is a boolean value that specifies the active level.
|
|
*async* is a boolean value that specifies the reset style:
|
|
asynchronous (``True``) or asynchronous (``False``).
|
|
|
|
|
|
Conversion
|
|
----------
|
|
|
|
As modeling the reset behavior is a typical task in synthesizable RTL
|
|
coding, the proposed technique is fully convertible to Verilog and
|
|
VHDL.
|
|
|
|
Limitations
|
|
-----------
|
|
|
|
All registers in a process are reset
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
All registers in a process are automatically included in the reset
|
|
behavior. If it is the intention that some registers should not be
|
|
reset, those registers and the corresponding code should be factored
|
|
out in a separate process.
|
|
|
|
Actually, this is not really a limitation but a feature. If some
|
|
registers in a process are reset and others not, a synthesis tool may
|
|
generate undesirable feedback loops that are active during the reset
|
|
condition. This is not good practice and probably not the intention.
|
|
|
|
Register inferencing from variables is not supported
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
An important limitation is that the proposed technique is limited to
|
|
registers inferred from signals. Registers inferred from variables are
|
|
not supported, because such state variables cannot be described in
|
|
classic functions (in particular the functions required by MyHDL
|
|
decorators such as :func:`always_seq` and :func:`always`).
|
|
|
|
In fact, the reason is a Python2 limitation. Currently, to infer
|
|
registers from variables, one has to use the :func:`instance` decorator and
|
|
declare the state variables outside an infinite ``while True`` loop.
|
|
|
|
In Python3, this limitation can be lifted with the introduction of the
|
|
``nonlocal`` declaration. This will make it possible for functions to
|
|
modify variables in the enclosing scope. It should be possible to
|
|
adapt the :func:`always_seq` and :func:`always` decorators to support such
|
|
variables.
|
|
|
|
|
|
See also
|
|
--------
|
|
|
|
For a more in-depth discussion, see `mep-109`_.
|
|
|
|
.. _mep-109: http://www.myhdl.org/doku.php/meps:mep-109
|
|
|
|
Other improvements
|
|
==================
|
|
|
|
Conversion of top-level class methods
|
|
-------------------------------------
|
|
|
|
Often it is desirable to embed an HDL module description in
|
|
a class. Previous versions would only convert a class method
|
|
if it was not the top-level. This release adds the conversion
|
|
of top-level class methods.
|
|
|
|
Example
|
|
^^^^^^^^
|
|
|
|
.. code-block:: python
|
|
|
|
class DFilter(object):
|
|
def __init__(self,delay_length=3,fs=1):
|
|
<init code>
|
|
def nulls(self):
|
|
<support method code>
|
|
def m_top(self,clock,reset,x,y):
|
|
<myhdl module code>
|
|
|
|
clock = Signal(bool(0))
|
|
reset = ResetSignal(0,active=0,async=True)
|
|
x = Signal(intbv(0, min=-8, max=8))
|
|
y = Signal(intbv(0, min=-64, max=64))
|
|
|
|
filt = DFilter()
|
|
toVerilog(filt.m_top,clock,reset,x,y)
|
|
toVHDL(filt.m_top,clock,reset,x,y)
|
|
|
|
See also
|
|
^^^^^^^^
|
|
For a more in-depth discussion, see `mep-108`_.
|
|
|
|
.. _mep-108: http://www.myhdl.org/doku.php/meps:mep-108
|
|
|
|
|
|
|
|
Tracing lists of signals
|
|
------------------------
|
|
Tracing lists of signals is now supported.
|
|
Contributed by Frederik Teichtert, http://teichert-ing.de .
|
|
The following shows how the list of signals are displayed
|
|
in a waveform viewer
|
|
|
|
.. code-block:: python
|
|
|
|
delay_taps = [Signal(intbv(0,min=-8,max=8)) for ii in range(3)]
|
|
|
|
|
|
.. image:: los.png
|
|
:align: center
|
|
|
|
|
|
``library`` attribute for :func:`toVHDL`
|
|
----------------------------------------
|
|
:func:`toVHDL` now has a ``library`` function that
|
|
can be used to set the library name in the
|
|
VHDL output. The assigned value should be a string.
|
|
The default library is "work".
|
|
|
|
``timescale`` attribute for :func:`traceSignals`
|
|
------------------------------------------------
|
|
:func:`traceSignals` now has a ``timescale`` attribute
|
|
that can be used to set the timescale
|
|
in the VCD output. The assigned value should be a string.
|
|
The default timescale is "1ns".
|
|
|
|
Acknowledgments
|
|
===============
|
|
|
|
Several people have contributed to MyHDL 0.8 by giving feedback,
|
|
making suggestions, fixing bugs and implementing features.
|
|
In particular, I would like to thank
|
|
Christopher Felton and Frederik Teichert.
|
|
|
|
I would also like to thank `Easics`_ for
|
|
the opportunity to use MyHDL in industrial projects.
|
|
|
|
.. _`Easics`: http://www.easics.com
|
|
|
|
|
|
|
|
|
|
|
|
|