mirror of
https://github.com/myhdl/myhdl.git
synced 2025-01-24 21:52:56 +08:00
Rewrote chapter completely, example code snippets only
This commit is contained in:
parent
18d0fe0989
commit
095eb040e7
@ -29,14 +29,13 @@ representation for bitwise operations. However, unlike :class:`int`, it is
|
|||||||
a mutable type. This means that its value can be changed after object
|
a mutable type. This means that its value can be changed after object
|
||||||
creation, through methods and operators such as slice assignment.
|
creation, through methods and operators such as slice assignment.
|
||||||
|
|
||||||
:class:`intbv` supports the same operators as :class:`int` for arithmetic.
|
:class:`intbv` supports the same operators as :class:`int` for arithmetic. In
|
||||||
In addition, it provides a number of features to make it
|
addition, it provides a number of features to make it suitable for hardware
|
||||||
suitable for hardware design. First, the range of allowed values can
|
design. First, the range of allowed values can be constrained. This makes it
|
||||||
be constrained. This makes it possible to check the value at run time
|
possible to check the value at run time during simulation. Moreover, back end
|
||||||
during simulation. Moreover, back end tools can determine the smallest
|
tools can determine the smallest possible bit width for representing the object.
|
||||||
possible bit width for representing the object.
|
Secondly, it supports bit level operations by providing an indexing and slicing
|
||||||
Secondly, it supports bit level operations by providing an indexing
|
interface.
|
||||||
and slicing interface.
|
|
||||||
|
|
||||||
:class:`intbv` objects are constructed in general as follows::
|
:class:`intbv` objects are constructed in general as follows::
|
||||||
|
|
||||||
@ -46,12 +45,12 @@ and slicing interface.
|
|||||||
the value. Following the Python conventions, *min* is inclusive, and
|
the value. Following the Python conventions, *min* is inclusive, and
|
||||||
*max* is exclusive. Therefore, the allowed value range is *min* .. *max*-1.
|
*max* is exclusive. Therefore, the allowed value range is *min* .. *max*-1.
|
||||||
|
|
||||||
Let's us look at some examples. First, an unconstrained :class:`intbv`
|
Let's look at some examples. An unconstrained :class:`intbv` object is created
|
||||||
object is created as follows:
|
as follows::
|
||||||
|
|
||||||
>>> a = intbv(24)
|
>>> a = intbv(24)
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
single: intbv; min
|
single: intbv; min
|
||||||
single: intbv; max
|
single: intbv; max
|
||||||
single: intbv; bit width
|
single: intbv; bit width
|
||||||
@ -60,12 +59,14 @@ After object creation, *min* and *max* are available as attributes for
|
|||||||
inspection. Also, the standard Python function :func:`len` can be used
|
inspection. Also, the standard Python function :func:`len` can be used
|
||||||
to determine the bit width. If we inspect the previously created
|
to determine the bit width. If we inspect the previously created
|
||||||
object, we get::
|
object, we get::
|
||||||
|
|
||||||
>>> print a.min
|
>>> a
|
||||||
|
intbv(24)
|
||||||
|
>>> print(a.min)
|
||||||
None
|
None
|
||||||
>>> print a.max
|
>>> print(a.max)
|
||||||
None
|
None
|
||||||
>>> print len(a)
|
>>> len(a)
|
||||||
0
|
0
|
||||||
|
|
||||||
As the instantiation was unconstrained, the *min* and *max* attributes
|
As the instantiation was unconstrained, the *min* and *max* attributes
|
||||||
@ -76,9 +77,10 @@ A constrained :class:`intbv` object is created as follows:
|
|||||||
|
|
||||||
>>> a = intbv(24, min=0, max=25)
|
>>> a = intbv(24, min=0, max=25)
|
||||||
|
|
||||||
|
|
||||||
Inspecting the object now gives::
|
Inspecting the object now gives::
|
||||||
|
|
||||||
|
>>> a
|
||||||
|
intbv(24)
|
||||||
>>> a.min
|
>>> a.min
|
||||||
0
|
0
|
||||||
>>> a.max
|
>>> a.max
|
||||||
@ -89,55 +91,21 @@ Inspecting the object now gives::
|
|||||||
We see that the allowed value range is 0 .. 24, and that 5 bits are
|
We see that the allowed value range is 0 .. 24, and that 5 bits are
|
||||||
required to represent the object.
|
required to represent the object.
|
||||||
|
|
||||||
Sometimes hardware engineers prefer to constrain an object by defining
|
The *min* and *max* bound attributes enable fine-grained control and error
|
||||||
its bit width directly, instead of the range of allowed values.
|
checking of the value range. In particular, the bound values do not have to be
|
||||||
The following example shows how to do that::
|
symmetric or powers of 2. In all cases, the bit width is set appropriately to
|
||||||
|
represent the values in the range. For example::
|
||||||
|
|
||||||
>>> a = intbv(24)[5:]
|
>>> a = intbv(6, min=0, max=7)
|
||||||
|
>>> len(a)
|
||||||
What actually happens here is that first an unconstrained :class:`intbv`
|
3
|
||||||
is created, which is then sliced. Slicing an :class:`intbv` returns a new
|
>>> a = intbv(6, min=-3, max=7)
|
||||||
:class:`intbv` with the constraints set up appropriately.
|
>>> len(a)
|
||||||
Inspecting the object now shows::
|
4
|
||||||
|
>>> a = intbv(6, min=-13, max=7)
|
||||||
>>> a.min
|
|
||||||
0
|
|
||||||
>>> a.max
|
|
||||||
32
|
|
||||||
>>> len(a)
|
>>> len(a)
|
||||||
5
|
5
|
||||||
|
|
||||||
|
|
||||||
Note that the *max* attribute is 32, as with 5 bits it is possible to represent
|
|
||||||
the range 0 .. 31. Creating an :class:`intbv` this way has the disadvantage
|
|
||||||
that only positive value ranges can be specified. Slicing is described in more
|
|
||||||
detail in :ref:`hwtypes-slicing`.
|
|
||||||
|
|
||||||
To summarize, there are two ways to constrain an :class:`intbv` object: by
|
|
||||||
defining its bit width, or by defining its value range. The bit
|
|
||||||
width method is more traditional in hardware design. However, there
|
|
||||||
are two reasons to use the range method instead: to represent
|
|
||||||
negative values as observed above, and for fine-grained control over the
|
|
||||||
value range.
|
|
||||||
|
|
||||||
Fine-grained control over the value range permits better error
|
|
||||||
checking, as there is no need for the *min* and *max* bounds
|
|
||||||
to be symmetric or powers of 2. In all cases, the bit width
|
|
||||||
is set appropriately to represent all values in
|
|
||||||
the range. For example::
|
|
||||||
|
|
||||||
|
|
||||||
>>> a = intbv(6, min=0, max=7)
|
|
||||||
>>> len(a)
|
|
||||||
3
|
|
||||||
>>> a = intbv(6, min=-3, max=7)
|
|
||||||
>>> len(a)
|
|
||||||
4
|
|
||||||
>>> a = intbv(6, min=-13, max=7)
|
|
||||||
>>> len(a)
|
|
||||||
5
|
|
||||||
|
|
||||||
|
|
||||||
.. _hwtypes-indexing:
|
.. _hwtypes-indexing:
|
||||||
|
|
||||||
Bit indexing
|
Bit indexing
|
||||||
@ -145,135 +113,102 @@ Bit indexing
|
|||||||
|
|
||||||
.. index:: single: bit indexing
|
.. index:: single: bit indexing
|
||||||
|
|
||||||
As an example, we will consider the design of a Gray encoder. The following code
|
A common requirement in hardware design is access to the individual bits. The
|
||||||
is a Gray encoder modeled in MyHDL::
|
:class:`intbv` class implements an indexing interface that provides access to
|
||||||
|
the bits of the underlying two's complement representation. The following
|
||||||
|
illustrates bit index read access::
|
||||||
|
|
||||||
from myhdl import Signal, delay, Simulation, always_comb, instance, intbv, bin
|
>>> from myhdl import bin
|
||||||
|
>>> a = intbv(24)
|
||||||
|
>>> bin(a)
|
||||||
|
'11000'
|
||||||
|
>>> int(a[0])
|
||||||
|
0
|
||||||
|
>>> int(a[3])
|
||||||
|
1
|
||||||
|
>>> b = intbv(-23)
|
||||||
|
>>> bin(b)
|
||||||
|
'101001'
|
||||||
|
>>> int(b[0])
|
||||||
|
1
|
||||||
|
>>> int(b[3])
|
||||||
|
1
|
||||||
|
>>> int(b[4])
|
||||||
|
0
|
||||||
|
|
||||||
def bin2gray(B, G, width):
|
We use the :func:`bin` function provide by MyHDL because it shows the two's
|
||||||
""" Gray encoder.
|
complement representation for negative values, unlike Python's builtin with the
|
||||||
|
same name. Note that lower indices correspond to less significant bits. The
|
||||||
B -- input intbv signal, binary encoded
|
following code illustrates bit index assignment::
|
||||||
G -- output intbv signal, gray encoded
|
|
||||||
width -- bit width
|
|
||||||
"""
|
|
||||||
|
|
||||||
@always_comb
|
|
||||||
def logic():
|
|
||||||
for i in range(width):
|
|
||||||
G.next[i] = B[i+1] ^ B[i]
|
|
||||||
|
|
||||||
return logic
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
.. index::
|
|
||||||
single: decorator; always_comb
|
|
||||||
single: wait; for a signal value change
|
|
||||||
single: combinatorial logic
|
|
||||||
|
|
||||||
Furthermore, we introduce a third decorator: :func:`always_comb`. It is used
|
|
||||||
with a classic function and specifies that the resulting generator should wait
|
|
||||||
for a value change on any input signal. This is typically used to describe
|
|
||||||
combinatorial logic. The :func:`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 ``0``.
|
|
||||||
|
|
||||||
To verify the Gray encoder, we write a test bench that prints input and output
|
|
||||||
for all possible input values::
|
|
||||||
|
|
||||||
def testBench(width):
|
|
||||||
|
|
||||||
B = Signal(intbv(0))
|
|
||||||
G = Signal(intbv(0))
|
|
||||||
|
|
||||||
dut = 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
|
|
||||||
|
|
||||||
We use the conversion function :func:`bin` to get a binary string representation of
|
|
||||||
the signal values. This function is exported by the :mod:`myhdl` package and
|
|
||||||
supplements the standard Python :func:`hex` and :func:`oct` conversion functions.
|
|
||||||
|
|
||||||
As a demonstration, we set up a simulation for a small width::
|
|
||||||
|
|
||||||
sim = Simulation(testBench(width=3))
|
|
||||||
sim.run()
|
|
||||||
|
|
||||||
The simulation produces the following output::
|
|
||||||
|
|
||||||
% python bin2gray.py
|
|
||||||
B: 000 | G: 000
|
|
||||||
B: 001 | G: 001
|
|
||||||
B: 010 | G: 011
|
|
||||||
B: 011 | G: 010
|
|
||||||
B: 100 | G: 110
|
|
||||||
B: 101 | G: 111
|
|
||||||
B: 110 | G: 101
|
|
||||||
B: 111 | G: 100
|
|
||||||
StopSimulation: No more events
|
|
||||||
|
|
||||||
|
>>> bin(a)
|
||||||
|
'11000'
|
||||||
|
>>> a[3] = 0
|
||||||
|
>>> bin(a)
|
||||||
|
'10000'
|
||||||
|
>>> a
|
||||||
|
intbv(16)
|
||||||
|
>>> b
|
||||||
|
intbv(-23)
|
||||||
|
>>> bin(b)
|
||||||
|
'101001'
|
||||||
|
>>> b[3] = 0
|
||||||
|
>>> bin(b)
|
||||||
|
'100001'
|
||||||
|
>>> b
|
||||||
|
intbv(-31)
|
||||||
|
|
||||||
.. _hwtypes-slicing:
|
.. _hwtypes-slicing:
|
||||||
|
|
||||||
Bit slicing
|
Bit slicing
|
||||||
===========
|
===========
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
single: bit slicing
|
single: bit slicing
|
||||||
single: concat(); example usage
|
|
||||||
|
|
||||||
For a change, we will use a traditional function as an example to illustrate
|
The :class:`intbv` type also supports bit slicing, for both read access
|
||||||
slicing. The following function calculates the HEC byte of an ATM header. ::
|
assignment. For example::
|
||||||
|
|
||||||
from myhdl import intbv, concat
|
>>> a = intbv(24)
|
||||||
|
>>> bin(a)
|
||||||
|
'11000'
|
||||||
|
>>> a[4:1]
|
||||||
|
intbv(4)
|
||||||
|
>>> bin(a[4:1])
|
||||||
|
'100'
|
||||||
|
>>> a[4:1] = 0b001
|
||||||
|
>>> bin(a)
|
||||||
|
'10010'
|
||||||
|
>>> a
|
||||||
|
intbv(18)
|
||||||
|
|
||||||
COSET = 0x55
|
In accordance with the most common hardware convention, and unlike standard
|
||||||
|
Python, slicing ranges are downward. As in standard Python, the slicing range
|
||||||
|
is half-open: the highest index bit is not included. Unlike standard Python
|
||||||
|
however, this index corresponds to the *leftmost* item.
|
||||||
|
|
||||||
def calculateHec(header):
|
Both indices can be omitted from the slice. If the rightmost index is omitted,
|
||||||
""" Return hec for an ATM header, represented as an intbv.
|
it is ``0`` by default. If the leftmost index is omitted, the meaning is to
|
||||||
|
access "all" higher order bits. For example::
|
||||||
|
|
||||||
The hec polynomial is 1 + x + x**2 + x**8.
|
>>> bin(a)
|
||||||
"""
|
'11000'
|
||||||
hec = intbv(0)
|
>>> bin(a[4:])
|
||||||
for bit in header[32:]:
|
'1000'
|
||||||
hec[8:] = concat(hec[7:2],
|
>>> a[4:] = '0001'
|
||||||
bit ^ hec[1] ^ hec[7],
|
>>> bin(a)
|
||||||
bit ^ hec[0] ^ hec[7],
|
'10001'
|
||||||
bit ^ hec[7]
|
>>> a[:] = 0b10101
|
||||||
)
|
>>> bin(a)
|
||||||
return hec ^ COSET
|
'10101'
|
||||||
|
|
||||||
The code shows how slicing access and assignment is supported on the
|
|
||||||
:class:`intbv` data type. In accordance with the most common hardware
|
|
||||||
convention, and unlike standard Python, slicing ranges are downward. The code
|
|
||||||
also demonstrates concatenation of :class:`intbv` objects.
|
|
||||||
|
|
||||||
As in standard Python, the slicing range is half-open: the highest index bit is
|
|
||||||
not included. Unlike standard Python however, this index corresponds to the
|
|
||||||
*leftmost* item. Both indices can be omitted from the slice. If the leftmost
|
|
||||||
index is omitted, the meaning is to access "all" higher order bits. If the
|
|
||||||
rightmost index is omitted, it is ``0`` by default.
|
|
||||||
|
|
||||||
The half-openness of a slice may seem awkward at first, but it helps to avoid
|
The half-openness of a slice may seem awkward at first, but it helps to avoid
|
||||||
one-off count issues in practice. For example, the slice ``hex[8:]`` has exactly
|
one-off count issues in practice. For example, the slice ``a[8:]`` has exactly
|
||||||
``8`` bits. Likewise, the slice ``hex[7:2]`` has ``7-2=5`` bits. You can think
|
``8`` bits. Likewise, the slice ``a[7:2]`` has ``7-2=5`` bits. You can think
|
||||||
about it as follows: for a slice ``[i:j]``, only bits below index ``i`` are
|
about it as follows: for a slice ``[i:j]``, only bits below index ``i`` are
|
||||||
included, and the bit with index ``j`` is the last bit included.
|
included, and the bit with index ``j`` is the last bit included.
|
||||||
|
|
||||||
When an :class:`intbv` object is sliced, a new :class:`intbv` object is returned.
|
When an :class:`intbv` object is sliced, a new :class:`intbv` object is returned.
|
||||||
This new :class:`intbv` object is always positive, and the value bounds are
|
This new :class:`intbv` object is always positive, and the value bounds are
|
||||||
set up in accordance with the bit width specified by the slice. For example::
|
set up in accordance with the bit width specified by the slice. For example::
|
||||||
|
|
||||||
@ -281,7 +216,7 @@ set up in accordance with the bit width specified by the slice. For example::
|
|||||||
>>> len(a)
|
>>> len(a)
|
||||||
4
|
4
|
||||||
>>> b = a[4:]
|
>>> b = a[4:]
|
||||||
>>> b
|
>>> b
|
||||||
intbv(6L)
|
intbv(6L)
|
||||||
>>> len(b)
|
>>> len(b)
|
||||||
4
|
4
|
||||||
@ -295,8 +230,8 @@ The returned object has the same value and bit width, but its value
|
|||||||
range consists of all positive values that can be represented by
|
range consists of all positive values that can be represented by
|
||||||
the bit width.
|
the bit width.
|
||||||
|
|
||||||
The object returned by a slice is positive, even when the
|
The object returned by a slice is positive, even when the original object is
|
||||||
original object is negative::
|
negative::
|
||||||
|
|
||||||
>>> a = intbv(-3)
|
>>> a = intbv(-3)
|
||||||
>>> bin(a, width=5)
|
>>> bin(a, width=5)
|
||||||
@ -307,8 +242,31 @@ original object is negative::
|
|||||||
>>> bin(b)
|
>>> bin(b)
|
||||||
'11101'
|
'11101'
|
||||||
|
|
||||||
The bit pattern of the two objects is identical within the bit width,
|
In this example, the bit pattern of the two objects is identical within the bit
|
||||||
but their values have opposite sign.
|
width, but their values have opposite sign.
|
||||||
|
|
||||||
|
Sometimes hardware engineers prefer to constrain an object by defining its bit
|
||||||
|
width directly, instead of the range of allowed values. Using the slicing
|
||||||
|
properties of the :class:`intbv` class one can do that as follows::
|
||||||
|
|
||||||
|
>>> a = intbv(24)[5:]
|
||||||
|
|
||||||
|
What actually happens here is that first an unconstrained :class:`intbv`
|
||||||
|
is created, which is then sliced. Slicing an :class:`intbv` returns a new
|
||||||
|
:class:`intbv` with the constraints set up appropriately.
|
||||||
|
Inspecting the object now shows::
|
||||||
|
|
||||||
|
>>> a.min
|
||||||
|
0
|
||||||
|
>>> a.max
|
||||||
|
32
|
||||||
|
>>> len(a)
|
||||||
|
5
|
||||||
|
|
||||||
|
Note that the *max* attribute is 32, as with 5 bits it is possible to represent
|
||||||
|
the range 0 .. 31. Creating an :class:`intbv` this way is convenient but has
|
||||||
|
the disadvantage that only positive value ranges between 0 and a power of 2 can
|
||||||
|
be specified.
|
||||||
|
|
||||||
.. _hwtypes-modbv:
|
.. _hwtypes-modbv:
|
||||||
|
|
||||||
@ -363,7 +321,7 @@ In a typical case when ``min==0``, this reduces to::
|
|||||||
Unsigned and signed representation
|
Unsigned and signed representation
|
||||||
==================================
|
==================================
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
single: intbv; intbv.signed
|
single: intbv; intbv.signed
|
||||||
|
|
||||||
:class:`intbv` is designed to be as high level as possible. The underlying
|
:class:`intbv` is designed to be as high level as possible. The underlying
|
||||||
@ -402,7 +360,7 @@ bit width. For this purpose, :class:`intbv` provides the
|
|||||||
>>> bin(b, width=4)
|
>>> bin(b, width=4)
|
||||||
'1100'
|
'1100'
|
||||||
|
|
||||||
:meth:`intbv.signed` extends the msb bit into the higher-order bits of the
|
:meth:`intbv.signed` extends the msb bit into the higher-order bits of the
|
||||||
underlying object value, and returns the result as an integer.
|
underlying object value, and returns the result as an integer.
|
||||||
Naturally, for a "signed" the return value will always be identical
|
Naturally, for a "signed" the return value will always be identical
|
||||||
to the original value, as it has the sign bit already.
|
to the original value, as it has the sign bit already.
|
||||||
@ -420,5 +378,3 @@ the data bus and convert them as follows::
|
|||||||
|
|
||||||
real.next = data_bus[8:4].signed()
|
real.next = data_bus[8:4].signed()
|
||||||
imag.next = data_bus[4:].signed()
|
imag.next = data_bus[4:].signed()
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user