mirror of
https://github.com/myhdl/myhdl.git
synced 2024-12-14 07:44:38 +08:00
Merge remote-tracking branch 'upstream/master' into initial_value_support
This commit is contained in:
commit
4b4be7be6c
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,6 +5,7 @@
|
|||||||
*.coverage
|
*.coverage
|
||||||
build/
|
build/
|
||||||
.cache
|
.cache
|
||||||
|
.style.yapf
|
||||||
|
|
||||||
# Python
|
# Python
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
@ -6,7 +6,8 @@ cmd = "iverilog -o bin2gray.o -Dwidth=%s " + \
|
|||||||
"../../test/verilog/bin2gray.v " + \
|
"../../test/verilog/bin2gray.v " + \
|
||||||
"../../test/verilog/dut_bin2gray.v "
|
"../../test/verilog/dut_bin2gray.v "
|
||||||
|
|
||||||
def bin2gray(B, G, width):
|
def bin2gray(B, G):
|
||||||
|
width = len(B)
|
||||||
os.system(cmd % width)
|
os.system(cmd % width)
|
||||||
return Cosimulation("vvp -m ../myhdl.vpi bin2gray.o", B=B, G=G)
|
return Cosimulation("vvp -m ../myhdl.vpi bin2gray.o", B=B, G=G)
|
||||||
|
|
||||||
|
@ -23,11 +23,11 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
sys.path.append("../../test")
|
sys.path.append("../../test")
|
||||||
|
sys.path.append("../../../example/manual")
|
||||||
|
|
||||||
import test_bin2gray, test_inc, test_dff
|
import test_gray_properties, test_gray_original, test_inc, test_dff
|
||||||
|
|
||||||
modules = (test_inc, )
|
modules = (test_gray_properties, test_gray_original, test_inc, test_dff )
|
||||||
modules = (test_bin2gray, test_inc, test_dff )
|
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
46
cosimulation/icarus/test/test_gray.py
Normal file
46
cosimulation/icarus/test/test_gray.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# This file is part of the myhdl library, a Python package for using
|
||||||
|
# Python as a Hardware Description Language.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2003-2008 Jan Decaluwe
|
||||||
|
#
|
||||||
|
# The myhdl library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public License as
|
||||||
|
# published by the Free Software Foundation; either version 2.1 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
""" Run cosimulation unit tests. """
|
||||||
|
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.append("../../../example/manual")
|
||||||
|
|
||||||
|
import test_gray_properties, test_gray_original
|
||||||
|
|
||||||
|
modules = (test_gray_properties, test_gray_original)
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
tl = unittest.defaultTestLoader
|
||||||
|
def suite():
|
||||||
|
alltests = unittest.TestSuite()
|
||||||
|
for m in modules:
|
||||||
|
alltests.addTest(tl.loadTestsFromModule(m))
|
||||||
|
return alltests
|
||||||
|
|
||||||
|
def main():
|
||||||
|
unittest.main(defaultTest='suite',
|
||||||
|
testRunner=unittest.TextTestRunner(verbosity=2))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
@ -1,5 +1,4 @@
|
|||||||
import myhdl
|
from myhdl import always_comb
|
||||||
from myhdl import *
|
|
||||||
|
|
||||||
def bin2gray(B, G, width):
|
def bin2gray(B, G, width):
|
||||||
""" Gray encoder.
|
""" Gray encoder.
|
||||||
@ -15,5 +14,3 @@ def bin2gray(B, G, width):
|
|||||||
G.next[i] = B[i+1] ^ B[i]
|
G.next[i] = B[i+1] ^ B[i]
|
||||||
|
|
||||||
return logic
|
return logic
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,15 +3,7 @@ module bin2gray(B, G);
|
|||||||
parameter width = 8;
|
parameter width = 8;
|
||||||
input [width-1:0] B;
|
input [width-1:0] B;
|
||||||
output [width-1:0] G;
|
output [width-1:0] G;
|
||||||
reg [width-1:0] G;
|
|
||||||
integer i;
|
|
||||||
wire [width:0] extB;
|
|
||||||
|
|
||||||
assign extB = {1'b0, B}; // zero-extend input
|
assign G = (B >> 1) ^ B;
|
||||||
|
|
||||||
always @(extB) begin
|
|
||||||
for (i=0; i < width; i=i+1)
|
|
||||||
G[i] <= extB[i+1] ^ extB[i];
|
|
||||||
end
|
|
||||||
|
|
||||||
endmodule // bin2gray
|
endmodule // bin2gray
|
||||||
|
@ -41,22 +41,9 @@ from the previous chapters. Suppose that we want to synthesize it and write it
|
|||||||
in Verilog for that purpose. Clearly we would like to reuse our unit test
|
in Verilog for that purpose. Clearly we would like to reuse our unit test
|
||||||
verification environment.
|
verification environment.
|
||||||
|
|
||||||
To start, let's recall how the Gray encoder in MyHDL looks like::
|
To start, let's recall how the Gray encoder in MyHDL looks like:
|
||||||
|
|
||||||
def bin2gray(B, G, width):
|
.. include-example:: bin2gray.py
|
||||||
""" Gray encoder.
|
|
||||||
|
|
||||||
B -- input intbv signal, binary encoded
|
|
||||||
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
|
|
||||||
|
|
||||||
To show the co-simulation flow, we don't need the Verilog implementation yet,
|
To show the co-simulation flow, we don't need the Verilog implementation yet,
|
||||||
but only the interface. Our Gray encoder in Verilog would have the following
|
but only the interface. Our Gray encoder in Verilog would have the following
|
||||||
@ -65,7 +52,7 @@ interface::
|
|||||||
module bin2gray(B, G);
|
module bin2gray(B, G);
|
||||||
|
|
||||||
parameter width = 8;
|
parameter width = 8;
|
||||||
input [width-1:0] B;
|
input [width-1:0] B;
|
||||||
output [width-1:0] G;
|
output [width-1:0] G;
|
||||||
....
|
....
|
||||||
|
|
||||||
@ -135,11 +122,14 @@ simulator, this is done as follows::
|
|||||||
|
|
||||||
from myhdl import Cosimulation
|
from myhdl import Cosimulation
|
||||||
|
|
||||||
cmd = "iverilog -o bin2gray -Dwidth=%s bin2gray.v dut_bin2gray.v"
|
cmd = "iverilog -o bin2gray.o -Dwidth=%s " + \
|
||||||
|
"../../test/verilog/bin2gray.v " + \
|
||||||
|
"../../test/verilog/dut_bin2gray.v "
|
||||||
|
|
||||||
def bin2gray(B, G, width):
|
def bin2gray(B, G):
|
||||||
|
width = len(B)
|
||||||
os.system(cmd % width)
|
os.system(cmd % width)
|
||||||
return Cosimulation("vvp -m ./myhdl.vpi bin2gray", B=B, G=G)
|
return Cosimulation("vvp -m ../myhdl.vpi bin2gray.o", B=B, G=G)
|
||||||
|
|
||||||
After the executable command argument, the ``Cosimulation`` constructor takes an
|
After the executable command argument, the ``Cosimulation`` constructor takes an
|
||||||
arbitrary number of keyword arguments. Those arguments make the link between
|
arbitrary number of keyword arguments. Those arguments make the link between
|
||||||
@ -154,37 +144,31 @@ definition to the existing unit test.
|
|||||||
|
|
||||||
Let's try it on the Verilog design::
|
Let's try it on the Verilog design::
|
||||||
|
|
||||||
module bin2gray(B, G);
|
module bin2gray(B, G);
|
||||||
|
|
||||||
parameter width = 8;
|
parameter width = 8;
|
||||||
input [width-1:0] B;
|
input [width-1:0] B;
|
||||||
output [width-1:0] G;
|
output [width-1:0] G;
|
||||||
reg [width-1:0] G;
|
|
||||||
integer i;
|
|
||||||
wire [width:0] extB;
|
|
||||||
|
|
||||||
assign extB = {1'b0, B}; // zero-extend input
|
assign G = (B >> 1) ^ B;
|
||||||
|
|
||||||
always @(extB) begin
|
endmodule // bin2gray
|
||||||
for (i=0; i < width; i=i+1)
|
|
||||||
G[i] <= extB[i+1] ^ extB[i];
|
|
||||||
end
|
|
||||||
|
|
||||||
endmodule
|
When we run our unit tests, we get::
|
||||||
|
|
||||||
When we run our unit test, we get::
|
% python test_gray.py
|
||||||
|
testSingleBitChange (test_gray_properties.TestGrayCodeProperties)
|
||||||
% python test_bin2gray.py
|
Check that only one bit changes in successive codewords. ... ok
|
||||||
Check that only one bit changes in successive codewords ... ok
|
testUniqueCodeWords (test_gray_properties.TestGrayCodeProperties)
|
||||||
Check that all codewords occur exactly once ... ok
|
Check that all codewords occur exactly once. ... ok
|
||||||
Check that the code is an original Gray code ... ok
|
testOriginalGrayCode (test_gray_original.TestOriginalGrayCode)
|
||||||
|
Check that the code is an original Gray code. ... ok
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
Ran 3 tests in 2.729s
|
Ran 3 tests in 0.706s
|
||||||
|
|
||||||
OK
|
OK
|
||||||
|
|
||||||
|
|
||||||
.. _cosim-restr:
|
.. _cosim-restr:
|
||||||
|
|
||||||
Restrictions
|
Restrictions
|
||||||
|
@ -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` in 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()
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,27 +31,26 @@ Combinatorial logic
|
|||||||
Template
|
Template
|
||||||
--------
|
--------
|
||||||
|
|
||||||
.. testsetup:: *
|
|
||||||
|
|
||||||
from myhdl import *
|
|
||||||
|
|
||||||
|
|
||||||
Combinatorial logic is described with a code pattern as follows::
|
Combinatorial logic is described with a code pattern as follows::
|
||||||
|
|
||||||
|
from myhdl import block, always_comb
|
||||||
|
|
||||||
|
@block
|
||||||
def top(<parameters>):
|
def top(<parameters>):
|
||||||
...
|
...
|
||||||
@always_comb
|
@always_comb
|
||||||
def combLogic():
|
def comb_logic():
|
||||||
<functional code>
|
<functional code>
|
||||||
...
|
...
|
||||||
return combLogic, ...
|
return comb_logic, ...
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
|
The :func:`always_comb` decorator describes combinatorial logic. The name refers
|
||||||
|
to a similar construct in SystemVerilog. 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:
|
.. _model-comb-ex:
|
||||||
|
|
||||||
@ -60,76 +59,21 @@ Example
|
|||||||
|
|
||||||
The following is an example of a combinatorial multiplexer
|
The following is an example of a combinatorial multiplexer
|
||||||
|
|
||||||
|
.. include-example:: mux.py
|
||||||
|
|
||||||
.. testcode:: comb1
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
# Once we've created some signals...
|
|
||||||
z, a, b, sel = [Signal(intbv(0)) for i in range(4)]
|
|
||||||
|
|
||||||
# ...it can be instantiated as follows
|
|
||||||
mux_1 = Mux(z, a, b, sel)
|
|
||||||
|
|
||||||
To verify it, we will simulate the logic with some random patterns. The
|
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.
|
``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*.
|
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
|
It is used in the test bench code to produce random input values.
|
||||||
|
|
||||||
.. testcode:: comb1
|
.. include-example:: test_mux.py
|
||||||
:hide:
|
|
||||||
|
|
||||||
import random
|
It is often useful to keep the random values reproducible. This can be
|
||||||
random.seed(0xDECAFBAD)
|
accomplished by providing a seed value as in the code. The run produces the
|
||||||
|
following output:
|
||||||
|
|
||||||
.. testcode:: comb1
|
.. run-example:: test_mux.py
|
||||||
|
|
||||||
from random import randrange
|
|
||||||
|
|
||||||
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).run()
|
|
||||||
|
|
||||||
Because of the randomness, the simulation output varies between runs [#]_. One
|
|
||||||
particular run produced the following output
|
|
||||||
|
|
||||||
.. testoutput:: comb1
|
|
||||||
|
|
||||||
z a b sel
|
|
||||||
6 6 0 1
|
|
||||||
7 7 2 1
|
|
||||||
7 6 7 0
|
|
||||||
0 3 0 0
|
|
||||||
1 1 1 1
|
|
||||||
1 5 1 0
|
|
||||||
2 3 2 0
|
|
||||||
1 1 0 1
|
|
||||||
|
|
||||||
.. _model-seq:
|
.. _model-seq:
|
||||||
|
|
||||||
@ -138,7 +82,6 @@ Sequential logic
|
|||||||
|
|
||||||
.. index:: single: sequential logic
|
.. index:: single: sequential logic
|
||||||
|
|
||||||
|
|
||||||
.. _model-seq-templ:
|
.. _model-seq-templ:
|
||||||
|
|
||||||
Template
|
Template
|
||||||
@ -148,13 +91,16 @@ Sequential RTL models are sensitive to a clock edge. In addition, they may be
|
|||||||
sensitive to a reset signal. The :func:`always_seq` decorator supports this
|
sensitive to a reset signal. The :func:`always_seq` decorator supports this
|
||||||
model directly::
|
model directly::
|
||||||
|
|
||||||
|
from myhdl import block, always_seq
|
||||||
|
|
||||||
|
@instance
|
||||||
def top(<parameters>, clock, ..., reset, ...):
|
def top(<parameters>, clock, ..., reset, ...):
|
||||||
...
|
...
|
||||||
@always_seq(clock.posedge, reset=reset)
|
@always_seq(clock.posedge, reset=reset)
|
||||||
def seqLogic():
|
def seq_logic():
|
||||||
<functional code>
|
<functional code>
|
||||||
...
|
...
|
||||||
return seqLogic, ...
|
return seq_logic, ...
|
||||||
|
|
||||||
The :func:`always_seq` decorator automatically infers the reset
|
The :func:`always_seq` decorator automatically infers the reset
|
||||||
functionality. It detects which signals need to be reset, and uses their
|
functionality. It detects which signals need to be reset, and uses their
|
||||||
@ -175,33 +121,9 @@ Example
|
|||||||
-------
|
-------
|
||||||
|
|
||||||
The following code is a description of an incrementer with enable, and an
|
The following code is a description of an incrementer with enable, and an
|
||||||
asynchronous reset.
|
asynchronous reset.
|
||||||
|
|
||||||
.. testcode:: seq1
|
|
||||||
|
|
||||||
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_seq(clock.posedge, reset=reset)
|
|
||||||
def incLogic():
|
|
||||||
if enable:
|
|
||||||
count.next = (count + 1) % n
|
|
||||||
|
|
||||||
return incLogic
|
|
||||||
|
|
||||||
|
.. include-example:: inc.py
|
||||||
|
|
||||||
|
|
||||||
For the test bench, we will use an independent clock generator, stimulus
|
For the test bench, we will use an independent clock generator, stimulus
|
||||||
@ -209,70 +131,11 @@ generator, and monitor. After applying enough stimulus patterns, we can raise
|
|||||||
the :func:`StopSimulation()` exception to stop the simulation run. The test bench for
|
the :func:`StopSimulation()` exception to stop the simulation run. The test bench for
|
||||||
a small incrementer and a small number of patterns is a follows
|
a small incrementer and a small number of patterns is a follows
|
||||||
|
|
||||||
.. testcode:: seq1
|
.. include-example:: test_inc.py
|
||||||
:hide:
|
|
||||||
|
|
||||||
import random
|
|
||||||
random.seed(0xDECAFBAD)
|
|
||||||
|
|
||||||
.. testcode:: seq1
|
|
||||||
|
|
||||||
from random import randrange
|
|
||||||
|
|
||||||
def testbench():
|
|
||||||
count, enable, clock = [Signal(intbv(0)) for i in range(3)]
|
|
||||||
reset = ResetSignal(0, active=ACTIVE_LOW, async=True)
|
|
||||||
|
|
||||||
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()
|
|
||||||
Simulation(tb).run()
|
|
||||||
|
|
||||||
The simulation produces the following output
|
The simulation produces the following output
|
||||||
|
|
||||||
.. testoutput:: seq1
|
.. run-example:: test_inc.py
|
||||||
|
|
||||||
enable count
|
|
||||||
1 1
|
|
||||||
0 1
|
|
||||||
1 2
|
|
||||||
1 3
|
|
||||||
0 3
|
|
||||||
1 0
|
|
||||||
1 1
|
|
||||||
1 2
|
|
||||||
1 3
|
|
||||||
1 0
|
|
||||||
0 0
|
|
||||||
1 1
|
|
||||||
|
|
||||||
.. _mode-seq-templ-alt:
|
.. _mode-seq-templ-alt:
|
||||||
|
|
||||||
@ -283,15 +146,20 @@ The template with the :func:`always_seq` decorator is convenient
|
|||||||
as it infers the reset functionality automatically. Alternatively,
|
as it infers the reset functionality automatically. Alternatively,
|
||||||
you can use a more explicit template as follows::
|
you can use a more explicit template as follows::
|
||||||
|
|
||||||
|
from myhdl import block, always
|
||||||
|
|
||||||
|
@block
|
||||||
def top(<parameters>, clock, ..., reset, ...):
|
def top(<parameters>, clock, ..., reset, ...):
|
||||||
...
|
...
|
||||||
@always(clock.posedge, reset.negedge)
|
@always(clock.posedge, reset.negedge)
|
||||||
def seqLogic():
|
def seq_logic():
|
||||||
if not reset:
|
if not reset:
|
||||||
<reset code>
|
<reset code>
|
||||||
else:
|
else:
|
||||||
<functional code>
|
<functional code>
|
||||||
|
|
||||||
|
return seq_logic,...
|
||||||
|
|
||||||
With this template, the reset values have to be specified
|
With this template, the reset values have to be specified
|
||||||
explicitly.
|
explicitly.
|
||||||
|
|
||||||
@ -353,55 +221,7 @@ 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
|
``SYNC``, otherwise it falls back to the ``SEARCH`` state. This FSM can be
|
||||||
coded as follows
|
coded as follows
|
||||||
|
|
||||||
.. testcode:: sm1
|
.. include-example:: fsm.py
|
||||||
|
|
||||||
from myhdl import *
|
|
||||||
|
|
||||||
ACTIVE_LOW = 0
|
|
||||||
FRAME_SIZE = 8
|
|
||||||
t_State = enum('SEARCH', 'CONFIRM', 'SYNC')
|
|
||||||
|
|
||||||
def FramerCtrl(SOF, state, syncFlag, clk, reset):
|
|
||||||
|
|
||||||
""" 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_seq(clk.posedge, reset=reset)
|
|
||||||
def FSM():
|
|
||||||
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
|
.. index:: single: waveform viewing
|
||||||
|
|
||||||
@ -410,7 +230,7 @@ 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
|
output file. The VCD file can then be loaded and viewed in a waveform viewer
|
||||||
tool such as :program:`gtkwave`.
|
tool such as :program:`gtkwave`.
|
||||||
|
|
||||||
.. %
|
.. %
|
||||||
|
|
||||||
The user interface of this feature consists of a single function,
|
The user interface of this feature consists of a single function,
|
||||||
:func:`traceSignals`. To explain how it works, recall that in MyHDL, an
|
:func:`traceSignals`. To explain how it works, recall that in MyHDL, an
|
||||||
@ -433,87 +253,8 @@ call.
|
|||||||
A small test bench for our framing controller example, with signal tracing
|
A small test bench for our framing controller example, with signal tracing
|
||||||
enabled, is shown below:
|
enabled, is shown below:
|
||||||
|
|
||||||
.. testcode:: sm1
|
.. include-example:: test_fsm.py
|
||||||
|
|
||||||
def testbench():
|
|
||||||
|
|
||||||
SOF = Signal(bool(0))
|
|
||||||
syncFlag = Signal(bool(0))
|
|
||||||
clk = Signal(bool(0))
|
|
||||||
reset = ResetSignal(1, active=ACTIVE_LOW, async=True)
|
|
||||||
state = Signal(t_State.SEARCH)
|
|
||||||
|
|
||||||
framectrl = FramerCtrl(SOF, state, syncFlag, clk, reset)
|
|
||||||
|
|
||||||
@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
|
|
||||||
|
|
||||||
@always_seq(clk.posedge, reset=reset)
|
|
||||||
def output_printer():
|
|
||||||
print syncFlag, SOF, state
|
|
||||||
|
|
||||||
return framectrl, clkgen, stimulus, output_printer
|
|
||||||
|
|
||||||
tb_fsm = traceSignals(testbench)
|
|
||||||
sim = Simulation(tb_fsm)
|
|
||||||
sim.run()
|
|
||||||
|
|
||||||
.. testoutput:: sm1
|
|
||||||
:hide:
|
|
||||||
|
|
||||||
False False SEARCH
|
|
||||||
False False SEARCH
|
|
||||||
False False SEARCH
|
|
||||||
1 False SEARCH
|
|
||||||
0 False CONFIRM
|
|
||||||
0 False CONFIRM
|
|
||||||
0 False CONFIRM
|
|
||||||
0 False CONFIRM
|
|
||||||
0 False CONFIRM
|
|
||||||
0 False CONFIRM
|
|
||||||
0 False CONFIRM
|
|
||||||
0 False CONFIRM
|
|
||||||
0 False SEARCH
|
|
||||||
0 False SEARCH
|
|
||||||
0 False SEARCH
|
|
||||||
1 False SEARCH
|
|
||||||
0 False CONFIRM
|
|
||||||
0 False CONFIRM
|
|
||||||
0 False CONFIRM
|
|
||||||
0 False CONFIRM
|
|
||||||
0 False CONFIRM
|
|
||||||
0 False CONFIRM
|
|
||||||
0 False CONFIRM
|
|
||||||
1 False CONFIRM
|
|
||||||
0 False SYNC
|
|
||||||
0 False SYNC
|
|
||||||
0 False SYNC
|
|
||||||
0 False SYNC
|
|
||||||
0 False SYNC
|
|
||||||
0 False SYNC
|
|
||||||
0 False SYNC
|
|
||||||
1 True SYNC
|
|
||||||
0 False SYNC
|
|
||||||
0 False SYNC
|
|
||||||
|
|
||||||
.. testcleanup:: sm1
|
|
||||||
|
|
||||||
import os
|
|
||||||
os.remove('testbench.vcd')
|
|
||||||
|
|
||||||
When we run the test bench, it generates a VCD file called
|
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
|
:file:`testbench.vcd`. When we load this file into :program:`gtkwave`, we can
|
||||||
view the waveforms:
|
view the waveforms:
|
||||||
@ -533,14 +274,3 @@ string representation, as returned by the standard :func:`str` function.
|
|||||||
Support for literal string representations is not part of the VCD standard. It
|
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
|
is specific to :program:`gtkwave`. To generate a standard VCD file, you need to
|
||||||
use signals with a defined bit width only.
|
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.
|
|
||||||
|
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 14 KiB |
@ -19,21 +19,20 @@ to hardware design.
|
|||||||
|
|
||||||
.. index:: single: extreme programming
|
.. index:: single: extreme programming
|
||||||
|
|
||||||
One software design approach that gets a lot of attention recently is *Extreme
|
One software design approach that deserves attention is *Extreme Programming*
|
||||||
Programming* (XP). It is a fascinating set of techniques and guidelines that
|
(XP). It is a fascinating set of techniques and guidelines that often seems to
|
||||||
often seems to go against the conventional wisdom. On other occasions, XP just
|
go against the conventional wisdom. On other occasions, XP just seems to
|
||||||
seems to emphasize the common sense, which doesn't always coincide with common
|
emphasize the common sense, which doesn't always coincide with common practice.
|
||||||
practice. For example, XP stresses the importance of normal workweeks, if we are
|
For example, XP stresses the importance of normal workweeks, if we are to have
|
||||||
to have the fresh mind needed for good software development.
|
the fresh mind needed for good software development.
|
||||||
|
|
||||||
.. %
|
.. %
|
||||||
|
|
||||||
It is not my intention nor qualification to present a tutorial on Extreme
|
It is not my intention nor qualification to present a tutorial on Extreme
|
||||||
Programming. Instead, in this section I will highlight one XP concept which I
|
Programming. Instead, in this section I will highlight one XP concept which I
|
||||||
think is very relevant to hardware design: the importance and methodology of
|
think is very relevant to hardware design: the importance and methodology of
|
||||||
unit testing.
|
unit testing.
|
||||||
|
|
||||||
|
|
||||||
.. _unittest-why:
|
.. _unittest-why:
|
||||||
|
|
||||||
The importance of unit tests
|
The importance of unit tests
|
||||||
@ -81,7 +80,7 @@ of :dfn:`codewords`, where a codeword is a bit string. A code of order ``n`` has
|
|||||||
|
|
||||||
A well-known characteristic is the one that Gray codes are all about:
|
A well-known characteristic is the one that Gray codes are all about:
|
||||||
|
|
||||||
Consecutive codewords in a Gray code should differ in a single bit.
|
*Consecutive codewords in a Gray code should differ in a single bit.*
|
||||||
|
|
||||||
Is this sufficient? Not quite: suppose for example that an implementation
|
Is this sufficient? Not quite: suppose for example that an implementation
|
||||||
returns the lsb of each binary input. This would comply with the requirement,
|
returns the lsb of each binary input. This would comply with the requirement,
|
||||||
@ -122,83 +121,39 @@ to be tests of the test case.
|
|||||||
We will define a test case for the Gray code properties, and then write a test
|
We will define a test case for the Gray code properties, and then write a test
|
||||||
for each of the requirements. The outline of the test case class is as follows::
|
for each of the requirements. The outline of the test case class is as follows::
|
||||||
|
|
||||||
from unittest import TestCase
|
import unittest
|
||||||
|
|
||||||
class TestGrayCodeProperties(TestCase):
|
class TestGrayCodeProperties(unittest.TestCase):
|
||||||
|
|
||||||
def testSingleBitChange(self):
|
def testSingleBitChange(self):
|
||||||
""" Check that only one bit changes in successive codewords """
|
"""Check that only one bit changes in successive codewords."""
|
||||||
....
|
....
|
||||||
|
|
||||||
|
|
||||||
def testUniqueCodeWords(self):
|
def testUniqueCodeWords(self):
|
||||||
""" Check that all codewords occur exactly once """
|
"""Check that all codewords occur exactly once."""
|
||||||
....
|
....
|
||||||
|
|
||||||
Each method will be a small test bench that tests a single requirement. To write
|
Each method will be a small test bench that tests a single requirement. To write
|
||||||
the tests, we don't need an implementation of the Gray encoder, but we do need
|
the tests, we don't need an implementation of the Gray encoder, but we do need
|
||||||
the interface of the design. We can specify this by a dummy implementation, as
|
the interface of the design. We can specify this by a dummy implementation, as
|
||||||
follows::
|
follows:
|
||||||
|
|
||||||
def bin2gray(B, G, width):
|
.. include-example:: bin2gray_dummy.py
|
||||||
### NOT IMPLEMENTED YET! ###
|
|
||||||
yield None
|
|
||||||
|
|
||||||
For the first requirement, we will write a test bench that applies all
|
For the first requirement, we will test all consecutive input numbers, and
|
||||||
consecutive input numbers, and compares the current output with the previous one
|
compare the current output with the previous one For each input, we check that
|
||||||
for each input. Then we check that the difference is a single bit. We will test
|
the difference is exactly a single bit. For the second requirement, we will test
|
||||||
all Gray codes up to a certain order ``MAX_WIDTH``. ::
|
all input numbers and put the result in a list. The requirement implies that if
|
||||||
|
we sort the result list, we should get a range of numbers. For both
|
||||||
|
requirements, we will test all Gray codes up to a certain order ``MAX_WIDTH``.
|
||||||
|
The test code looks as follows:
|
||||||
|
|
||||||
def testSingleBitChange(self):
|
.. include-example:: test_gray_properties.py
|
||||||
""" Check that only one bit changes in successive codewords """
|
|
||||||
|
|
||||||
def test(B, G, width):
|
|
||||||
B.next = intbv(0)
|
|
||||||
yield delay(10)
|
|
||||||
for i in range(1, 2**width):
|
|
||||||
G_Z.next = G
|
|
||||||
B.next = intbv(i)
|
|
||||||
yield delay(10)
|
|
||||||
diffcode = bin(G ^ G_Z)
|
|
||||||
self.assertEqual(diffcode.count('1'), 1)
|
|
||||||
|
|
||||||
for width in range(1, MAX_WIDTH):
|
|
||||||
B = Signal(intbv(-1))
|
|
||||||
G = Signal(intbv(0))
|
|
||||||
G_Z = Signal(intbv(0))
|
|
||||||
dut = bin2gray(B, G, width)
|
|
||||||
check = test(B, G, width)
|
|
||||||
sim = Simulation(dut, check)
|
|
||||||
sim.run(quiet=1)
|
|
||||||
|
|
||||||
Note how the actual check is performed by a ``self.assertEqual`` method, defined
|
Note how the actual check is performed by a ``self.assertEqual`` method, defined
|
||||||
by the ``unittest.TestCase`` class.
|
by the ``unittest.TestCase`` class. Also, we have factored out running the
|
||||||
|
tests for all Gray codes in a separate method :func:`runTests`.
|
||||||
Similarly, we write a test bench for the second requirement. Again, we simulate
|
|
||||||
all numbers, and put the result in a list. The requirement implies that if we
|
|
||||||
sort the result list, we should get a range of numbers::
|
|
||||||
|
|
||||||
def testUniqueCodeWords(self):
|
|
||||||
""" Check that all codewords occur exactly once """
|
|
||||||
|
|
||||||
def test(B, G, width):
|
|
||||||
actual = []
|
|
||||||
for i in range(2**width):
|
|
||||||
B.next = intbv(i)
|
|
||||||
yield delay(10)
|
|
||||||
actual.append(int(G))
|
|
||||||
actual.sort()
|
|
||||||
expected = range(2**width)
|
|
||||||
self.assertEqual(actual, expected)
|
|
||||||
|
|
||||||
for width in range(1, MAX_WIDTH):
|
|
||||||
B = Signal(intbv(-1))
|
|
||||||
G = Signal(intbv(0))
|
|
||||||
dut = bin2gray(B, G, width)
|
|
||||||
check = test(B, G, width)
|
|
||||||
sim = Simulation(dut, check)
|
|
||||||
sim.run(quiet=1)
|
|
||||||
|
|
||||||
|
|
||||||
.. _unittest-impl:
|
.. _unittest-impl:
|
||||||
|
|
||||||
@ -216,66 +171,70 @@ a call to its ``main`` method at the end of the test module::
|
|||||||
|
|
||||||
Let's run the test using the dummy Gray encoder shown earlier::
|
Let's run the test using the dummy Gray encoder shown earlier::
|
||||||
|
|
||||||
% python test_gray.py -v
|
% python test_gray_properties.py
|
||||||
Check that only one bit changes in successive codewords ... FAIL
|
testSingleBitChange (__main__.TestGrayCodeProperties)
|
||||||
Check that all codewords occur exactly once ... FAIL
|
Check that only one bit changes in successive codewords. ... ERROR
|
||||||
<trace backs not shown>
|
testUniqueCodeWords (__main__.TestGrayCodeProperties)
|
||||||
|
Check that all codewords occur exactly once. ... ERROR
|
||||||
|
|
||||||
As expected, this fails completely. Let us try an incorrect implementation, that
|
As expected, this fails completely. Let us try an incorrect implementation, that
|
||||||
puts the lsb of in the input on the output::
|
puts the lsb of in the input on the output:
|
||||||
|
|
||||||
def bin2gray(B, G, width):
|
.. include-example:: bin2gray_wrong.py
|
||||||
### INCORRECT - DEMO PURPOSE ONLY! ###
|
|
||||||
|
|
||||||
@always_comb
|
|
||||||
def logic():
|
|
||||||
G.next = B[0]
|
|
||||||
|
|
||||||
return logic
|
|
||||||
|
|
||||||
Running the test produces::
|
Running the test produces::
|
||||||
|
|
||||||
% python test_gray.py -v
|
python test_gray_properties.py
|
||||||
Check that only one bit changes in successive codewords ... ok
|
testSingleBitChange (__main__.TestGrayCodeProperties)
|
||||||
Check that all codewords occur exactly once ... FAIL
|
Check that only one bit changes in successive codewords. ... ok
|
||||||
|
testUniqueCodeWords (__main__.TestGrayCodeProperties)
|
||||||
|
Check that all codewords occur exactly once. ... FAIL
|
||||||
|
|
||||||
======================================================================
|
======================================================================
|
||||||
FAIL: Check that all codewords occur exactly once
|
FAIL: testUniqueCodeWords (__main__.TestGrayCodeProperties)
|
||||||
----------------------------------------------------------------------
|
Check that all codewords occur exactly once.
|
||||||
Traceback (most recent call last):
|
----------------------------------------------------------------------
|
||||||
File "test_gray.py", line 109, in testUniqueCodeWords
|
Traceback (most recent call last):
|
||||||
sim.run(quiet=1)
|
File "test_gray_properties.py", line 42, in testUniqueCodeWords
|
||||||
...
|
self.runTests(test)
|
||||||
File "test_gray.py", line 104, in test
|
File "test_gray_properties.py", line 53, in runTests
|
||||||
self.assertEqual(actual, expected)
|
sim.run(quiet=1)
|
||||||
File "/usr/local/lib/python2.2/unittest.py", line 286, in failUnlessEqual
|
File "/home/jand/dev/myhdl/myhdl/_Simulation.py", line 154, in run
|
||||||
raise self.failureException, \
|
waiter.next(waiters, actives, exc)
|
||||||
AssertionError: [0, 0, 1, 1] != [0, 1, 2, 3]
|
File "/home/jand/dev/myhdl/myhdl/_Waiter.py", line 127, in next
|
||||||
|
clause = next(self.generator)
|
||||||
|
File "test_gray_properties.py", line 40, in test
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
AssertionError: Lists differ: [0, 0, 1, 1] != [0, 1, 2, 3]
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
First differing element 1:
|
||||||
Ran 2 tests in 0.785s
|
0
|
||||||
|
1
|
||||||
|
|
||||||
|
- [0, 0, 1, 1]
|
||||||
|
+ [0, 1, 2, 3]
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Ran 2 tests in 0.083s
|
||||||
|
|
||||||
|
FAILED (failures=1)
|
||||||
|
|
||||||
Now the test passes the first requirement, as expected, but fails the second
|
Now the test passes the first requirement, as expected, but fails the second
|
||||||
one. After the test feedback, a full traceback is shown that can help to debug
|
one. After the test feedback, a full traceback is shown that can help to debug
|
||||||
the test output.
|
the test output.
|
||||||
|
|
||||||
Finally, if we use the correct implementation as in section
|
Finally, we use a correct implementation:
|
||||||
:ref:`hwtypes-indexing`, the output is::
|
|
||||||
|
|
||||||
% python test_gray.py -v
|
.. include-example:: bin2gray.py
|
||||||
Check that only one bit changes in successive codewords ... ok
|
|
||||||
Check that all codewords occur exactly once ... ok
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
Now the tests pass:
|
||||||
Ran 2 tests in 6.364s
|
|
||||||
|
|
||||||
OK
|
|
||||||
|
|
||||||
|
.. run-example:: test_gray_properties.py
|
||||||
|
|
||||||
.. _unittest-change:
|
.. _unittest-change:
|
||||||
|
|
||||||
Changing requirements
|
Additional requirements
|
||||||
---------------------
|
-----------------------
|
||||||
|
|
||||||
In the previous section, we concentrated on the general requirements of a Gray
|
In the previous section, we concentrated on the general requirements of a Gray
|
||||||
code. It is possible to specify these without specifying the actual code. It is
|
code. It is possible to specify these without specifying the actual code. It is
|
||||||
@ -305,54 +264,19 @@ It is possible to specify these codes by a recursive algorithm, as follows:
|
|||||||
of Ln0 and Ln1.
|
of Ln0 and Ln1.
|
||||||
|
|
||||||
Python is well-known for its elegant algorithmic descriptions, and this is a
|
Python is well-known for its elegant algorithmic descriptions, and this is a
|
||||||
good example. We can write the algorithm in Python as follows::
|
good example. We can write the algorithm in Python as follows:
|
||||||
|
|
||||||
def nextLn(Ln):
|
.. include-example:: next_gray_code.py
|
||||||
""" Return Gray code Ln+1, given Ln. """
|
|
||||||
Ln0 = ['0' + codeword for codeword in Ln]
|
|
||||||
Ln1 = ['1' + codeword for codeword in Ln]
|
|
||||||
Ln1.reverse()
|
|
||||||
return Ln0 + Ln1
|
|
||||||
|
|
||||||
The code ``['0' + codeword for ...]`` is called a :dfn:`list comprehension`. It
|
The code ``['0' + codeword for ...]`` is called a :dfn:`list comprehension`. It
|
||||||
is a concise way to describe lists built by short computations in a for loop.
|
is a concise way to describe lists built by short computations in a for loop.
|
||||||
|
|
||||||
The requirement is now that the output code matches the expected code Ln. We use
|
The requirement is now that the output code matches the expected code Ln. We use
|
||||||
the ``nextLn`` function to compute the expected result. The new test case code
|
the ``nextLn`` function to compute the expected result. The new test case code
|
||||||
is as follows::
|
is as follows:
|
||||||
|
|
||||||
class TestOriginalGrayCode(TestCase):
|
.. include-example:: test_gray_original.py
|
||||||
|
|
||||||
def testOriginalGrayCode(self):
|
As it happens, our implementation is apparently an original Gray code:
|
||||||
""" Check that the code is an original Gray code """
|
|
||||||
|
|
||||||
Rn = []
|
|
||||||
|
|
||||||
def stimulus(B, G, n):
|
|
||||||
for i in range(2**n):
|
|
||||||
B.next = intbv(i)
|
|
||||||
yield delay(10)
|
|
||||||
Rn.append(bin(G, width=n))
|
|
||||||
|
|
||||||
Ln = ['0', '1'] # n == 1
|
|
||||||
for n in range(2, MAX_WIDTH):
|
|
||||||
Ln = nextLn(Ln)
|
|
||||||
del Rn[:]
|
|
||||||
B = Signal(intbv(-1))
|
|
||||||
G = Signal(intbv(0))
|
|
||||||
dut = bin2gray(B, G, n)
|
|
||||||
stim = stimulus(B, G, n)
|
|
||||||
sim = Simulation(dut, stim)
|
|
||||||
sim.run(quiet=1)
|
|
||||||
self.assertEqual(Ln, Rn)
|
|
||||||
|
|
||||||
As it happens, our implementation is apparently an original Gray code::
|
|
||||||
|
|
||||||
% python test_gray.py -v TestOriginalGrayCode
|
|
||||||
Check that the code is an original Gray code ... ok
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
Ran 1 tests in 3.091s
|
|
||||||
|
|
||||||
OK
|
|
||||||
|
|
||||||
|
.. run-example:: test_gray_original.py
|
||||||
|
@ -22,7 +22,7 @@ class RunExample(Directive):
|
|||||||
env = document.settings.env
|
env = document.settings.env
|
||||||
_ , wd = env.relfn2path(example_dir)
|
_ , wd = env.relfn2path(example_dir)
|
||||||
prog = self.arguments[0]
|
prog = self.arguments[0]
|
||||||
out = subprocess.check_output(['python', '-u', prog], cwd=wd,
|
out = subprocess.check_output(['python3', '-u', prog], cwd=wd,
|
||||||
stderr=subprocess.STDOUT,
|
stderr=subprocess.STDOUT,
|
||||||
universal_newlines=True)
|
universal_newlines=True)
|
||||||
out = '$ python {}\n{}'.format(prog, out)
|
out = '$ python {}\n{}'.format(prog, out)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// File: Array8Sorter.v
|
// File: Array8Sorter.v
|
||||||
// Generated by MyHDL 1.0dev
|
// Generated by MyHDL 1.0dev
|
||||||
// Date: Sat Mar 19 17:27:30 2016
|
// Date: Sun May 15 11:40:06 2016
|
||||||
|
|
||||||
|
|
||||||
`timescale 1ns/10ps
|
`timescale 1ns/10ps
|
||||||
|
@ -6,6 +6,6 @@ def Hello(clk, to="World!"):
|
|||||||
|
|
||||||
@always(clk.posedge)
|
@always(clk.posedge)
|
||||||
def say_hello():
|
def say_hello():
|
||||||
print "%s Hello %s" % (now(), to)
|
print("%s Hello %s" % (now(), to))
|
||||||
|
|
||||||
return say_hello
|
return say_hello
|
||||||
|
@ -1,43 +1,16 @@
|
|||||||
from myhdl import Signal, delay, Simulation, always_comb, instance, intbv, bin, traceSignals
|
from myhdl import block, always_comb
|
||||||
|
|
||||||
def bin2gray(B, G, width):
|
@block
|
||||||
|
def bin2gray(B, G):
|
||||||
""" Gray encoder.
|
""" Gray encoder.
|
||||||
|
|
||||||
B -- input intbv signal, binary encoded
|
B -- binary input
|
||||||
G -- output intbv signal, gray encoded
|
G -- Gray encoded output
|
||||||
width -- bit width
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@always_comb
|
@always_comb
|
||||||
def logic():
|
def logic():
|
||||||
for i in range(width):
|
G.next = (B>>1) ^ B
|
||||||
G.next[i] = B[i+1] ^ B[i]
|
|
||||||
|
|
||||||
return logic
|
return logic
|
||||||
|
|
||||||
|
|
||||||
def testBench(width):
|
|
||||||
|
|
||||||
B = Signal(intbv(0))
|
|
||||||
G = Signal(intbv(0))
|
|
||||||
|
|
||||||
dut = traceSignals(bin2gray, B, G, width)
|
|
||||||
|
|
||||||
@instance
|
|
||||||
def stimulus():
|
|
||||||
for i in range(2**width):
|
|
||||||
B.next = intbv(i)
|
|
||||||
yield delay(10)
|
|
||||||
print "B: " + bin(B, width) + "| G: " + bin(G, width)
|
|
||||||
|
|
||||||
return dut, stimulus
|
|
||||||
|
|
||||||
def main():
|
|
||||||
sim = Simulation(testBench(width=3))
|
|
||||||
sim.run()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
|
|
||||||
|
|
||||||
|
11
example/manual/bin2gray_dummy.py
Normal file
11
example/manual/bin2gray_dummy.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
from myhdl import block
|
||||||
|
|
||||||
|
@block
|
||||||
|
def bin2gray(B, G):
|
||||||
|
# DUMMY PLACEHOLDER
|
||||||
|
""" Gray encoder.
|
||||||
|
|
||||||
|
B -- binary input
|
||||||
|
G -- Gray encoded output
|
||||||
|
"""
|
||||||
|
pass
|
16
example/manual/bin2gray_wrong.py
Normal file
16
example/manual/bin2gray_wrong.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
from myhdl import block, always_comb
|
||||||
|
|
||||||
|
@block
|
||||||
|
def bin2gray(B, G):
|
||||||
|
# INCORRECT IMPLEMENTATION
|
||||||
|
""" Gray encoder.
|
||||||
|
|
||||||
|
B -- binary input
|
||||||
|
G -- Gray encoded output
|
||||||
|
"""
|
||||||
|
|
||||||
|
@always_comb
|
||||||
|
def logic():
|
||||||
|
G.next = B[0]
|
||||||
|
|
||||||
|
return logic
|
17
example/manual/conv_inc.py
Normal file
17
example/manual/conv_inc.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
from myhdl import toVerilog, toVHDL, Signal, ResetSignal, modbv
|
||||||
|
from inc import inc
|
||||||
|
|
||||||
|
ACTIVE_LOW, INACTIVE_HIGH = 0, 1
|
||||||
|
|
||||||
|
|
||||||
|
# conversion
|
||||||
|
m = 8
|
||||||
|
|
||||||
|
count = Signal(modbv(0)[m:])
|
||||||
|
enable = Signal(bool(0))
|
||||||
|
clock = Signal(bool(0))
|
||||||
|
reset = ResetSignal(0, active=0, async=True)
|
||||||
|
|
||||||
|
inc_inst = inc(count, enable, clock, reset)
|
||||||
|
inc_inst = toVerilog(inc, count, enable, clock, reset)
|
||||||
|
inc_inst = toVHDL(inc, count, enable, clock, reset)
|
@ -1,95 +1,54 @@
|
|||||||
import myhdl
|
from myhdl import block, always_seq, Signal, intbv, enum
|
||||||
from myhdl import *
|
|
||||||
|
|
||||||
ACTIVE_LOW = 0
|
ACTIVE_LOW = 0
|
||||||
FRAME_SIZE = 8
|
FRAME_SIZE = 8
|
||||||
t_State = enum('SEARCH', 'CONFIRM', 'SYNC')
|
t_state = enum('SEARCH', 'CONFIRM', 'SYNC')
|
||||||
|
|
||||||
@block
|
@block
|
||||||
def FramerCtrl(SOF, state, syncFlag, clk, reset_n):
|
def framer_ctrl(sof, state, sync_flag, clk, reset_n):
|
||||||
|
|
||||||
""" Framing control FSM.
|
""" Framing control FSM.
|
||||||
|
|
||||||
SOF -- start-of-frame output bit
|
sof -- start-of-frame output bit
|
||||||
state -- FramerState output
|
state -- FramerState output
|
||||||
syncFlag -- sync pattern found indication input
|
sync_flag -- sync pattern found indication input
|
||||||
clk -- clock input
|
clk -- clock input
|
||||||
reset_n -- active low reset
|
reset_n -- active low reset
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
index = Signal(0) # position in frame
|
|
||||||
|
|
||||||
@always(clk.posedge, reset_n.negedge)
|
"""
|
||||||
|
|
||||||
|
index = Signal(intbv(0, min=0, max=FRAME_SIZE)) # position in frame
|
||||||
|
|
||||||
|
@always_seq(clk.posedge, reset=reset_n)
|
||||||
def FSM():
|
def FSM():
|
||||||
if reset_n == ACTIVE_LOW:
|
if reset_n == ACTIVE_LOW:
|
||||||
SOF.next = 0
|
sof.next = 0
|
||||||
index.next = 0
|
index.next = 0
|
||||||
state.next = t_State.SEARCH
|
state.next = t_state.SEARCH
|
||||||
|
|
||||||
else:
|
else:
|
||||||
index.next = (index + 1) % FRAME_SIZE
|
index.next = (index + 1) % FRAME_SIZE
|
||||||
SOF.next = 0
|
sof.next = 0
|
||||||
|
|
||||||
if state == t_State.SEARCH:
|
if state == t_state.SEARCH:
|
||||||
index.next = 1
|
index.next = 1
|
||||||
if syncFlag:
|
if sync_flag:
|
||||||
state.next = t_State.CONFIRM
|
state.next = t_state.CONFIRM
|
||||||
|
|
||||||
elif state == t_State.CONFIRM:
|
elif state == t_state.CONFIRM:
|
||||||
if index == 0:
|
if index == 0:
|
||||||
if syncFlag:
|
if sync_flag:
|
||||||
state.next = t_State.SYNC
|
state.next = t_state.SYNC
|
||||||
else:
|
else:
|
||||||
state.next = t_State.SEARCH
|
state.next = t_state.SEARCH
|
||||||
|
|
||||||
elif state == t_State.SYNC:
|
elif state == t_state.SYNC:
|
||||||
if index == 0:
|
if index == 0:
|
||||||
if not syncFlag:
|
if not sync_flag:
|
||||||
state.next = t_State.SEARCH
|
state.next = t_state.SEARCH
|
||||||
SOF.next = (index == FRAME_SIZE-1)
|
sof.next = (index == FRAME_SIZE-1)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise ValueError("Undefined state")
|
raise ValueError("Undefined state")
|
||||||
|
|
||||||
return FSM
|
return FSM
|
||||||
|
|
||||||
|
|
||||||
@block
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
tb = testbench()
|
|
||||||
tb.config_sim(trace=True)
|
|
||||||
tb.run_sim()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
|
@ -11,7 +11,7 @@ def HelloWorld():
|
|||||||
|
|
||||||
@always(clk.posedge)
|
@always(clk.posedge)
|
||||||
def say_hello():
|
def say_hello():
|
||||||
print "%s Hello World!" % now()
|
print("%s Hello World!" % now())
|
||||||
|
|
||||||
return drive_clk, say_hello
|
return drive_clk, say_hello
|
||||||
|
|
||||||
|
@ -1,92 +1,21 @@
|
|||||||
from random import randrange
|
from myhdl import block, always_seq
|
||||||
import myhdl
|
|
||||||
from myhdl import *
|
|
||||||
|
|
||||||
ACTIVE_LOW, INACTIVE_HIGH = 0, 1
|
@block
|
||||||
|
def inc(count, enable, clock, reset):
|
||||||
|
|
||||||
def Inc(count, enable, clock, reset):
|
|
||||||
|
|
||||||
""" Incrementer with enable.
|
""" Incrementer with enable.
|
||||||
|
|
||||||
count -- output
|
count -- output
|
||||||
enable -- control input, increment when 1
|
enable -- control input, increment when 1
|
||||||
clock -- clock input
|
clock -- clock input
|
||||||
reset -- asynchronous reset input
|
reset -- asynchronous reset input
|
||||||
n -- counter max value
|
n -- counter max value
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@always_seq(clock.posedge, reset=reset)
|
@always_seq(clock.posedge, reset=reset)
|
||||||
def incLogic():
|
def seq():
|
||||||
if enable:
|
if enable:
|
||||||
count.next = count + 1
|
count.next = count + 1
|
||||||
|
|
||||||
return incLogic
|
return seq
|
||||||
|
|
||||||
|
|
||||||
def testbench():
|
|
||||||
m = 3
|
|
||||||
count = Signal(modbv(0)[m:])
|
|
||||||
enable = Signal(bool(0))
|
|
||||||
clock = Signal(bool(0))
|
|
||||||
reset = ResetSignal(0, active=0, async=True)
|
|
||||||
|
|
||||||
inc_1 = Inc(count, enable, clock, reset)
|
|
||||||
|
|
||||||
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(20):
|
|
||||||
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()
|
|
||||||
|
|
||||||
|
|
||||||
# conversion
|
|
||||||
m = 8
|
|
||||||
|
|
||||||
count = Signal(modbv(0)[m:])
|
|
||||||
enable = Signal(bool(0))
|
|
||||||
clock = Signal(bool(0))
|
|
||||||
reset = ResetSignal(0, active=0, async=True)
|
|
||||||
|
|
||||||
inc_inst = Inc(count, enable, clock, reset)
|
|
||||||
inc_inst = toVerilog(Inc, count, enable, clock, reset)
|
|
||||||
inc_inst = toVHDL(Inc, count, enable, clock, reset)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,34 +1,21 @@
|
|||||||
from myhdl import Signal, Simulation, delay
|
from myhdl import block, always_comb, Signal
|
||||||
|
|
||||||
|
@block
|
||||||
def mux(z, a, b, sel):
|
def mux(z, a, b, sel):
|
||||||
|
|
||||||
""" Multiplexer.
|
""" Multiplexer.
|
||||||
|
|
||||||
z -- mux output
|
z -- mux output
|
||||||
a, b -- data inputs
|
a, b -- data inputs
|
||||||
sel -- control input: select a if asserted, otherwise b
|
sel -- control input: select a if asserted, otherwise b
|
||||||
|
|
||||||
"""
|
"""
|
||||||
while 1:
|
|
||||||
yield a, b, sel
|
@always_comb
|
||||||
|
def comb():
|
||||||
if sel == 1:
|
if sel == 1:
|
||||||
z.next = a
|
z.next = a
|
||||||
else:
|
else:
|
||||||
z.next = b
|
z.next = b
|
||||||
|
|
||||||
from random import randrange
|
return comb
|
||||||
|
|
||||||
(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)
|
|
||||||
|
|
||||||
def main():
|
|
||||||
Simulation(MUX_1, test()).run()
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
|
6
example/manual/next_gray_code.py
Normal file
6
example/manual/next_gray_code.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
def nextLn(Ln):
|
||||||
|
""" Return Gray code Ln+1, given Ln. """
|
||||||
|
Ln0 = ['0' + codeword for codeword in Ln]
|
||||||
|
Ln1 = ['1' + codeword for codeword in Ln]
|
||||||
|
Ln1.reverse()
|
||||||
|
return Ln0 + Ln1
|
29
example/manual/test_bin2gray.py
Normal file
29
example/manual/test_bin2gray.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
from myhdl import block, Signal, intbv, delay, instance, bin
|
||||||
|
|
||||||
|
from bin2gray import bin2gray
|
||||||
|
|
||||||
|
@block
|
||||||
|
def testbench(width):
|
||||||
|
|
||||||
|
B = Signal(intbv(0)[width:])
|
||||||
|
G = Signal(intbv(0)[width:])
|
||||||
|
|
||||||
|
dut = bin2gray(B, G)
|
||||||
|
dut.config_sim(trace=True)
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
def main():
|
||||||
|
tb = testbench(width=3)
|
||||||
|
# tb.config_sim(trace=True)
|
||||||
|
tb.run_sim()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
38
example/manual/test_fsm.py
Normal file
38
example/manual/test_fsm.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import myhdl
|
||||||
|
from myhdl import block, always, instance, Signal, ResetSignal, delay, StopSimulation
|
||||||
|
from fsm import framer_ctrl, t_state
|
||||||
|
|
||||||
|
ACTIVE_LOW = 0
|
||||||
|
|
||||||
|
@block
|
||||||
|
def testbench():
|
||||||
|
|
||||||
|
sof = Signal(bool(0))
|
||||||
|
sync_flag = Signal(bool(0))
|
||||||
|
clk = Signal(bool(0))
|
||||||
|
reset_n = ResetSignal(1, active=ACTIVE_LOW, async=True)
|
||||||
|
state = Signal(t_state.SEARCH)
|
||||||
|
|
||||||
|
frame_ctrl_0 = framer_ctrl(sof, state, sync_flag, clk, reset_n)
|
||||||
|
|
||||||
|
@always(delay(10))
|
||||||
|
def clkgen():
|
||||||
|
clk.next = not clk
|
||||||
|
|
||||||
|
@instance
|
||||||
|
def stimulus():
|
||||||
|
for i in range(3):
|
||||||
|
yield clk.negedge
|
||||||
|
for n in (12, 8, 8, 4):
|
||||||
|
sync_flag.next = 1
|
||||||
|
yield clk.negedge
|
||||||
|
sync_flag.next = 0
|
||||||
|
for i in range(n-1):
|
||||||
|
yield clk.negedge
|
||||||
|
raise StopSimulation()
|
||||||
|
|
||||||
|
return frame_ctrl_0, clkgen, stimulus
|
||||||
|
|
||||||
|
tb = testbench()
|
||||||
|
tb.config_sim(trace=True)
|
||||||
|
tb.run_sim()
|
@ -1,116 +0,0 @@
|
|||||||
from __future__ import generators
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
from unittest import TestCase
|
|
||||||
|
|
||||||
from myhdl import Simulation, Signal, delay, intbv, bin
|
|
||||||
|
|
||||||
from bin2gray import bin2gray
|
|
||||||
|
|
||||||
MAX_WIDTH = 11
|
|
||||||
|
|
||||||
def nextLn(Ln):
|
|
||||||
""" Return Gray code Ln+1, given Ln. """
|
|
||||||
Ln0 = ['0' + codeword for codeword in Ln]
|
|
||||||
Ln1 = ['1' + codeword for codeword in Ln]
|
|
||||||
Ln1.reverse()
|
|
||||||
return Ln0 + Ln1
|
|
||||||
|
|
||||||
## def bin2gray(B, G, width):
|
|
||||||
## while 1:
|
|
||||||
## yield B
|
|
||||||
## G.next = B[0]
|
|
||||||
|
|
||||||
class TestOriginalGrayCode(TestCase):
|
|
||||||
|
|
||||||
def testOriginalGrayCode(self):
|
|
||||||
|
|
||||||
""" Check that the code is an original Gray code """
|
|
||||||
|
|
||||||
Rn = []
|
|
||||||
|
|
||||||
def stimulus(B, G, n):
|
|
||||||
for i in range(2**n):
|
|
||||||
B.next = intbv(i)
|
|
||||||
yield delay(10)
|
|
||||||
Rn.append(bin(G, width=n))
|
|
||||||
|
|
||||||
Ln = ['0', '1'] # n == 1
|
|
||||||
for n in range(2, MAX_WIDTH):
|
|
||||||
Ln = nextLn(Ln)
|
|
||||||
del Rn[:]
|
|
||||||
B = Signal(intbv(-1))
|
|
||||||
G = Signal(intbv(0))
|
|
||||||
dut = bin2gray(B, G, n)
|
|
||||||
stim = stimulus(B, G, n)
|
|
||||||
sim = Simulation(dut, stim)
|
|
||||||
sim.run(quiet=1)
|
|
||||||
self.assertEqual(Ln, Rn)
|
|
||||||
|
|
||||||
|
|
||||||
class TestGrayCodeProperties(TestCase):
|
|
||||||
|
|
||||||
def testSingleBitChange(self):
|
|
||||||
|
|
||||||
""" Check that only one bit changes in successive codewords """
|
|
||||||
|
|
||||||
def test(B, G, width):
|
|
||||||
B.next = intbv(0)
|
|
||||||
yield delay(10)
|
|
||||||
for i in range(1, 2**width):
|
|
||||||
G_Z.next = G
|
|
||||||
B.next = intbv(i)
|
|
||||||
yield delay(10)
|
|
||||||
diffcode = bin(G ^ G_Z)
|
|
||||||
self.assertEqual(diffcode.count('1'), 1)
|
|
||||||
|
|
||||||
for width in range(1, MAX_WIDTH):
|
|
||||||
B = Signal(intbv(-1))
|
|
||||||
G = Signal(intbv(0))
|
|
||||||
G_Z = Signal(intbv(0))
|
|
||||||
dut = bin2gray(B, G, width)
|
|
||||||
check = test(B, G, width)
|
|
||||||
sim = Simulation(dut, check)
|
|
||||||
sim.run(quiet=1)
|
|
||||||
|
|
||||||
|
|
||||||
def testUniqueCodeWords(self):
|
|
||||||
|
|
||||||
""" Check that all codewords occur exactly once """
|
|
||||||
|
|
||||||
def test(B, G, width):
|
|
||||||
actual = []
|
|
||||||
for i in range(2**width):
|
|
||||||
B.next = intbv(i)
|
|
||||||
yield delay(10)
|
|
||||||
actual.append(int(G))
|
|
||||||
actual.sort()
|
|
||||||
expected = range(2**width)
|
|
||||||
self.assertEqual(actual, expected)
|
|
||||||
|
|
||||||
for width in range(1, MAX_WIDTH):
|
|
||||||
B = Signal(intbv(-1))
|
|
||||||
G = Signal(intbv(0))
|
|
||||||
dut = bin2gray(B, G, width)
|
|
||||||
check = test(B, G, width)
|
|
||||||
sim = Simulation(dut, check)
|
|
||||||
sim.run(quiet=1)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
unittest.main()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
37
example/manual/test_gray_original.py
Normal file
37
example/manual/test_gray_original.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
from myhdl import Simulation, Signal, delay, intbv, bin
|
||||||
|
|
||||||
|
from bin2gray import bin2gray
|
||||||
|
from next_gray_code import nextLn
|
||||||
|
|
||||||
|
MAX_WIDTH = 11
|
||||||
|
|
||||||
|
class TestOriginalGrayCode(unittest.TestCase):
|
||||||
|
|
||||||
|
def testOriginalGrayCode(self):
|
||||||
|
"""Check that the code is an original Gray code."""
|
||||||
|
|
||||||
|
Rn = []
|
||||||
|
|
||||||
|
def stimulus(B, G, n):
|
||||||
|
for i in range(2**n):
|
||||||
|
B.next = intbv(i)
|
||||||
|
yield delay(10)
|
||||||
|
Rn.append(bin(G, width=n))
|
||||||
|
|
||||||
|
Ln = ['0', '1'] # n == 1
|
||||||
|
for w in range(2, MAX_WIDTH):
|
||||||
|
Ln = nextLn(Ln)
|
||||||
|
del Rn[:]
|
||||||
|
B = Signal(intbv(0)[w:])
|
||||||
|
G = Signal(intbv(0)[w:])
|
||||||
|
dut = bin2gray(B, G)
|
||||||
|
stim = stimulus(B, G, w)
|
||||||
|
sim = Simulation(dut, stim)
|
||||||
|
sim.run(quiet=1)
|
||||||
|
self.assertEqual(Ln, Rn)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
57
example/manual/test_gray_properties.py
Normal file
57
example/manual/test_gray_properties.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
from myhdl import Simulation, Signal, delay, intbv, bin
|
||||||
|
|
||||||
|
from bin2gray import bin2gray
|
||||||
|
|
||||||
|
MAX_WIDTH = 11
|
||||||
|
|
||||||
|
class TestGrayCodeProperties(unittest.TestCase):
|
||||||
|
|
||||||
|
def testSingleBitChange(self):
|
||||||
|
"""Check that only one bit changes in successive codewords."""
|
||||||
|
|
||||||
|
def test(B, G):
|
||||||
|
w = len(B)
|
||||||
|
G_Z = Signal(intbv(0)[w:])
|
||||||
|
B.next = intbv(0)
|
||||||
|
yield delay(10)
|
||||||
|
for i in range(1, 2**w):
|
||||||
|
G_Z.next = G
|
||||||
|
B.next = intbv(i)
|
||||||
|
yield delay(10)
|
||||||
|
diffcode = bin(G ^ G_Z)
|
||||||
|
self.assertEqual(diffcode.count('1'), 1)
|
||||||
|
|
||||||
|
self.runTests(test)
|
||||||
|
|
||||||
|
def testUniqueCodeWords(self):
|
||||||
|
"""Check that all codewords occur exactly once."""
|
||||||
|
|
||||||
|
def test(B, G):
|
||||||
|
w = len(B)
|
||||||
|
actual = []
|
||||||
|
for i in range(2**w):
|
||||||
|
B.next = intbv(i)
|
||||||
|
yield delay(10)
|
||||||
|
actual.append(int(G))
|
||||||
|
actual.sort()
|
||||||
|
expected = list(range(2**w))
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
self.runTests(test)
|
||||||
|
|
||||||
|
|
||||||
|
def runTests(self, test):
|
||||||
|
"""Helper method to run the actual tests."""
|
||||||
|
for w in range(1, MAX_WIDTH):
|
||||||
|
B = Signal(intbv(0)[w:])
|
||||||
|
G = Signal(intbv(0)[w:])
|
||||||
|
dut = bin2gray(B, G)
|
||||||
|
check = test(B, G)
|
||||||
|
sim = Simulation(dut, check)
|
||||||
|
sim.run(quiet=1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
49
example/manual/test_inc.py
Normal file
49
example/manual/test_inc.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import random
|
||||||
|
from myhdl import block, always, instance, Signal, modbv, \
|
||||||
|
ResetSignal, delay, StopSimulation
|
||||||
|
from inc import inc
|
||||||
|
|
||||||
|
random.seed(1)
|
||||||
|
randrange = random.randrange
|
||||||
|
|
||||||
|
ACTIVE_LOW, INACTIVE_HIGH = 0, 1
|
||||||
|
|
||||||
|
@block
|
||||||
|
def testbench():
|
||||||
|
m = 3
|
||||||
|
count = Signal(modbv(0)[m:])
|
||||||
|
enable = Signal(bool(0))
|
||||||
|
clock = Signal(bool(0))
|
||||||
|
reset = ResetSignal(0, active=0, async=True)
|
||||||
|
|
||||||
|
inc_1 = inc(count, enable, clock, reset)
|
||||||
|
|
||||||
|
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(16):
|
||||||
|
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" % (int(enable), count))
|
||||||
|
|
||||||
|
return clockGen, stimulus, inc_1, monitor
|
||||||
|
|
||||||
|
tb = testbench()
|
||||||
|
tb.run_sim()
|
26
example/manual/test_mux.py
Normal file
26
example/manual/test_mux.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import random
|
||||||
|
from myhdl import block, instance, Signal, intbv, delay
|
||||||
|
from mux import mux
|
||||||
|
|
||||||
|
random.seed(5)
|
||||||
|
randrange = random.randrange
|
||||||
|
|
||||||
|
@block
|
||||||
|
def test_mux():
|
||||||
|
|
||||||
|
z, a, b, sel = [Signal(intbv(0)) for i in range(4)]
|
||||||
|
|
||||||
|
mux_1 = mux(z, a, b, sel)
|
||||||
|
|
||||||
|
@instance
|
||||||
|
def stimulus():
|
||||||
|
print("z a b sel")
|
||||||
|
for i in range(12):
|
||||||
|
a.next, b.next, sel.next = randrange(8), randrange(8), randrange(2)
|
||||||
|
yield delay(10)
|
||||||
|
print("%s %s %s %s" % (z, a, b, sel))
|
||||||
|
|
||||||
|
return mux_1, stimulus
|
||||||
|
|
||||||
|
tb = test_mux()
|
||||||
|
tb.run_sim()
|
@ -26,6 +26,7 @@ import inspect
|
|||||||
import functools
|
import functools
|
||||||
|
|
||||||
import myhdl
|
import myhdl
|
||||||
|
from myhdl._compat import PY2
|
||||||
from myhdl import BlockError, BlockInstanceError, Cosimulation
|
from myhdl import BlockError, BlockInstanceError, Cosimulation
|
||||||
from myhdl._instance import _Instantiator
|
from myhdl._instance import _Instantiator
|
||||||
from myhdl._util import _flatten
|
from myhdl._util import _flatten
|
||||||
@ -66,8 +67,15 @@ def _getCallInfo():
|
|||||||
# caller may be undefined if instantiation from a Python module
|
# caller may be undefined if instantiation from a Python module
|
||||||
callerrec = None
|
callerrec = None
|
||||||
funcrec = stack[3]
|
funcrec = stack[3]
|
||||||
|
name = funcrec[3]
|
||||||
if len(stack) > 4:
|
if len(stack) > 4:
|
||||||
callerrec = stack[4]
|
callerrec = stack[4]
|
||||||
|
# special case for list comprehension's extra scope in PY3
|
||||||
|
if name == '<listcomp>':
|
||||||
|
if not PY2:
|
||||||
|
funcrec = stack[4]
|
||||||
|
if len(stack) > 5:
|
||||||
|
callerrec = stack[5]
|
||||||
|
|
||||||
name = funcrec[3]
|
name = funcrec[3]
|
||||||
frame = funcrec[0]
|
frame = funcrec[0]
|
||||||
@ -81,8 +89,8 @@ def _getCallInfo():
|
|||||||
return _CallInfo(name, modctxt, symdict)
|
return _CallInfo(name, modctxt, symdict)
|
||||||
|
|
||||||
|
|
||||||
class _bound_function_wrapper(object):
|
class _bound_function_wrapper(object):
|
||||||
|
|
||||||
def __init__(self, bound_func, srcfile, srcline):
|
def __init__(self, bound_func, srcfile, srcline):
|
||||||
|
|
||||||
self.srcfile = srcfile
|
self.srcfile = srcfile
|
||||||
@ -95,18 +103,18 @@ class _bound_function_wrapper(object):
|
|||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def __call__(self, *args, **kwargs):
|
||||||
self.calls += 1
|
self.calls += 1
|
||||||
return _Block(self.bound_func, self, self.srcfile,
|
return _Block(self.bound_func, self, self.srcfile,
|
||||||
self.srcline, *args, **kwargs)
|
self.srcline, *args, **kwargs)
|
||||||
|
|
||||||
class block(object):
|
class block(object):
|
||||||
def __init__(self, func):
|
def __init__(self, func):
|
||||||
self.srcfile = inspect.getsourcefile(func)
|
self.srcfile = inspect.getsourcefile(func)
|
||||||
self.srcline = inspect.getsourcelines(func)[0]
|
self.srcline = inspect.getsourcelines(func)[0]
|
||||||
|
|
||||||
self.func = func
|
self.func = func
|
||||||
functools.update_wrapper(self, func)
|
functools.update_wrapper(self, func)
|
||||||
|
|
||||||
self.calls = 0
|
self.calls = 0
|
||||||
|
|
||||||
def __get__(self, instance, owner):
|
def __get__(self, instance, owner):
|
||||||
|
|
||||||
@ -114,9 +122,9 @@ class block(object):
|
|||||||
return _bound_function_wrapper(bound_func, self.srcfile, self.srcline)
|
return _bound_function_wrapper(bound_func, self.srcfile, self.srcline)
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def __call__(self, *args, **kwargs):
|
||||||
|
|
||||||
self.calls += 1
|
self.calls += 1
|
||||||
return _Block(self.func, self, self.srcfile,
|
return _Block(self.func, self, self.srcfile,
|
||||||
self.srcline, *args, **kwargs)
|
self.srcline, *args, **kwargs)
|
||||||
|
|
||||||
#def block(func):
|
#def block(func):
|
||||||
@ -231,8 +239,8 @@ class _Block(object):
|
|||||||
`self.mod.__name__`
|
`self.mod.__name__`
|
||||||
trace(Optional[bool]): Verilog only. Whether the testbench should
|
trace(Optional[bool]): Verilog only. Whether the testbench should
|
||||||
dump all signal waveforms. Defaults to False.
|
dump all signal waveforms. Defaults to False.
|
||||||
tb (Optional[bool]): Verilog only. Specifies whether a testbench
|
testbench (Optional[bool]): Verilog only. Specifies whether a
|
||||||
should be created. Defaults to True.
|
testbench should be created. Defaults to True.
|
||||||
timescale(Optional[str]): Verilog only. Defaults to '1ns/10ps'
|
timescale(Optional[str]): Verilog only. Defaults to '1ns/10ps'
|
||||||
"""
|
"""
|
||||||
if hdl.lower() == 'vhdl':
|
if hdl.lower() == 'vhdl':
|
||||||
@ -247,7 +255,7 @@ class _Block(object):
|
|||||||
conv_attrs['name'] = kwargs.pop('name')
|
conv_attrs['name'] = kwargs.pop('name')
|
||||||
conv_attrs['directory'] = kwargs.pop('path', '')
|
conv_attrs['directory'] = kwargs.pop('path', '')
|
||||||
if hdl.lower() == 'verilog':
|
if hdl.lower() == 'verilog':
|
||||||
conv_attrs['no_testbench'] = not kwargs.pop('tb', True)
|
conv_attrs['no_testbench'] = not kwargs.pop('testbench', True)
|
||||||
conv_attrs['timescale'] = kwargs.pop('timescale', '1ns/10ps')
|
conv_attrs['timescale'] = kwargs.pop('timescale', '1ns/10ps')
|
||||||
conv_attrs['trace'] = kwargs.pop('trace', False)
|
conv_attrs['trace'] = kwargs.pop('trace', False)
|
||||||
conv_attrs.update(kwargs)
|
conv_attrs.update(kwargs)
|
||||||
@ -257,12 +265,14 @@ class _Block(object):
|
|||||||
|
|
||||||
def config_sim(self, trace=False):
|
def config_sim(self, trace=False):
|
||||||
self._config_sim['trace'] = trace
|
self._config_sim['trace'] = trace
|
||||||
|
if trace:
|
||||||
|
myhdl.traceSignals(self)
|
||||||
|
|
||||||
def run_sim(self, duration=None, quiet=0):
|
def run_sim(self, duration=None, quiet=0):
|
||||||
if self.sim is None:
|
if self.sim is None:
|
||||||
sim = self
|
sim = self
|
||||||
if self._config_sim['trace']:
|
#if self._config_sim['trace']:
|
||||||
sim = myhdl.traceSignals(self)
|
# sim = myhdl.traceSignals(self)
|
||||||
self.sim = myhdl._Simulation.Simulation(sim)
|
self.sim = myhdl._Simulation.Simulation(sim)
|
||||||
self.sim.run(duration, quiet)
|
self.sim.run(duration, quiet)
|
||||||
|
|
||||||
|
@ -32,9 +32,9 @@ import inspect
|
|||||||
from myhdl._Cosimulation import Cosimulation
|
from myhdl._Cosimulation import Cosimulation
|
||||||
from myhdl._instance import _Instantiator
|
from myhdl._instance import _Instantiator
|
||||||
|
|
||||||
|
|
||||||
def _isGenSeq(obj):
|
def _isGenSeq(obj):
|
||||||
if isinstance(obj, (Cosimulation, _Instantiator)):
|
from myhdl._block import _Block
|
||||||
|
if isinstance(obj, (Cosimulation, _Instantiator, _Block)):
|
||||||
return True
|
return True
|
||||||
if not isinstance(obj, (list, tuple, set)):
|
if not isinstance(obj, (list, tuple, set)):
|
||||||
return False
|
return False
|
||||||
|
160
myhdl/test/bugs/test_issue_167.py
Normal file
160
myhdl/test/bugs/test_issue_167.py
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
''' Bitonic sort '''
|
||||||
|
|
||||||
|
# http://www.myhdl.org/examples/bitonic/
|
||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from random import randrange
|
||||||
|
|
||||||
|
from myhdl import Signal, intbv, \
|
||||||
|
always_comb, instance, \
|
||||||
|
delay, block, StopSimulation
|
||||||
|
|
||||||
|
|
||||||
|
ASCENDING = True
|
||||||
|
DESCENDING = False
|
||||||
|
|
||||||
|
|
||||||
|
# modules
|
||||||
|
|
||||||
|
@block
|
||||||
|
def compare(a_1, a_2, z_1, z_2, direction):
|
||||||
|
""" Combinatorial circuit with two input and two output signals.
|
||||||
|
Sorting to 'direction'. """
|
||||||
|
|
||||||
|
@always_comb
|
||||||
|
def logic():
|
||||||
|
''' Combinatorial logic '''
|
||||||
|
if direction == (a_1 > a_2):
|
||||||
|
z_1.next = a_2
|
||||||
|
z_2.next = a_1
|
||||||
|
else:
|
||||||
|
z_1.next = a_1
|
||||||
|
z_2.next = a_2
|
||||||
|
|
||||||
|
return logic
|
||||||
|
|
||||||
|
|
||||||
|
@block
|
||||||
|
def feedthru(in_a, out_z):
|
||||||
|
""" Equivalent of 'doing nothing'. """
|
||||||
|
|
||||||
|
@always_comb
|
||||||
|
def logic():
|
||||||
|
''' Combinatorial logic '''
|
||||||
|
out_z.next = in_a
|
||||||
|
|
||||||
|
return logic
|
||||||
|
|
||||||
|
|
||||||
|
@block
|
||||||
|
def bitonic_merge(list_a, list_z, direction):
|
||||||
|
""" bitonicMerge:
|
||||||
|
Generates the output from the input list of signals.
|
||||||
|
Recursive. """
|
||||||
|
len_list = len(list_a)
|
||||||
|
half_len = len_list//2
|
||||||
|
width = len(list_a[0])
|
||||||
|
|
||||||
|
if len_list > 1:
|
||||||
|
tmp = [Signal(intbv(0)[width:]) for _ in range(len_list)]
|
||||||
|
|
||||||
|
comp = [compare(list_a[i], list_a[i+half_len], tmp[i], tmp[i+half_len], \
|
||||||
|
direction) for i in range(half_len)]
|
||||||
|
|
||||||
|
lo_merge = bitonic_merge( tmp[:half_len], list_z[:half_len], direction )
|
||||||
|
hi_merge = bitonic_merge( tmp[half_len:], list_z[half_len:], direction )
|
||||||
|
|
||||||
|
return comp, lo_merge, hi_merge
|
||||||
|
else:
|
||||||
|
feed = feedthru(list_a[0], list_z[0])
|
||||||
|
return feed
|
||||||
|
|
||||||
|
|
||||||
|
@block
|
||||||
|
def bitonic_sort(list_a, list_z, direction):
|
||||||
|
""" bitonicSort:
|
||||||
|
Produces a bitonic sequence.
|
||||||
|
Recursive. """
|
||||||
|
len_list = len(list_a)
|
||||||
|
half_len = len_list//2
|
||||||
|
width = len(list_a[0])
|
||||||
|
|
||||||
|
if len_list > 1:
|
||||||
|
tmp = [Signal(intbv(0)[width:]) for _ in range(len_list)]
|
||||||
|
|
||||||
|
lo_sort = bitonic_sort( list_a[:half_len], tmp[:half_len], ASCENDING )
|
||||||
|
hi_sort = bitonic_sort( list_a[half_len:], tmp[half_len:], DESCENDING )
|
||||||
|
|
||||||
|
merge = bitonic_merge( tmp, list_z, direction )
|
||||||
|
return lo_sort, hi_sort, merge
|
||||||
|
else:
|
||||||
|
feed = feedthru(list_a[0], list_z[0])
|
||||||
|
return feed
|
||||||
|
|
||||||
|
|
||||||
|
# tests
|
||||||
|
|
||||||
|
@block
|
||||||
|
def array8sorter(a_0, a_1, a_2, a_3, a_4, a_5, a_6, a_7,
|
||||||
|
z_0, z_1, z_2, z_3, z_4, z_5, z_6, z_7):
|
||||||
|
''' Sort Array with 8 values '''
|
||||||
|
|
||||||
|
list_a = [a_0, a_1, a_2, a_3, a_4, a_5, a_6, a_7]
|
||||||
|
list_z = [z_0, z_1, z_2, z_3, z_4, z_5, z_6, z_7]
|
||||||
|
|
||||||
|
sort = bitonic_sort(list_a, list_z, ASCENDING)
|
||||||
|
return sort
|
||||||
|
|
||||||
|
|
||||||
|
class TestBitonicSort(unittest.TestCase):
|
||||||
|
''' Test class for bitonic sort '''
|
||||||
|
|
||||||
|
def test_sort(self):
|
||||||
|
""" Check the functionality of the bitonic sort """
|
||||||
|
length = 8
|
||||||
|
width = 4
|
||||||
|
|
||||||
|
@block
|
||||||
|
def test_impl():
|
||||||
|
''' test implementation '''
|
||||||
|
inputs = [ Signal(intbv(0)[width:]) for _ in range(length) ]
|
||||||
|
outputs = [ Signal(intbv(0)[width:]) for _ in range(length) ]
|
||||||
|
z_0, z_1, z_2, z_3, z_4, z_5, z_6, z_7 = outputs
|
||||||
|
a_0, a_1, a_2, a_3, a_4, a_5, a_6, a_7 = inputs
|
||||||
|
|
||||||
|
inst = array8sorter(a_0, a_1, a_2, a_3, a_4, a_5, a_6, a_7,
|
||||||
|
z_0, z_1, z_2, z_3, z_4, z_5, z_6, z_7)
|
||||||
|
|
||||||
|
@instance
|
||||||
|
def check():
|
||||||
|
''' testbench input and validation '''
|
||||||
|
for i in range(100):
|
||||||
|
data = [randrange(2**width) for i in range(length)]
|
||||||
|
for i in range(length):
|
||||||
|
inputs[i].next = data[i]
|
||||||
|
yield delay(10)
|
||||||
|
data.sort()
|
||||||
|
self.assertEqual(data, outputs, 'wrong data')
|
||||||
|
raise StopSimulation
|
||||||
|
|
||||||
|
return inst, check
|
||||||
|
|
||||||
|
inst = test_impl()
|
||||||
|
inst.run_sim()
|
||||||
|
|
||||||
|
|
||||||
|
# convert
|
||||||
|
|
||||||
|
def test_issue_167():
|
||||||
|
''' Convert to VHDL '''
|
||||||
|
length = 8
|
||||||
|
width = 4
|
||||||
|
sigs = [Signal(intbv(0)[width:]) for _ in range(2*length)]
|
||||||
|
|
||||||
|
inst = array8sorter( *sigs )
|
||||||
|
inst.convert( hdl='VHDL' )
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test_issue_167()
|
@ -3,39 +3,39 @@ Test: timer
|
|||||||
=====
|
=====
|
||||||
pypy
|
pypy
|
||||||
----
|
----
|
||||||
real 36.20
|
real 37.31
|
||||||
user 36.15
|
user 37.25
|
||||||
sys 0.02
|
sys 0.03
|
||||||
|
|
||||||
Test: lfsr24
|
Test: lfsr24
|
||||||
=====
|
=====
|
||||||
pypy
|
pypy
|
||||||
----
|
----
|
||||||
real 39.08
|
real 39.90
|
||||||
user 39.00
|
user 39.86
|
||||||
sys 0.04
|
sys 0.01
|
||||||
|
|
||||||
Test: randgen
|
Test: randgen
|
||||||
=====
|
=====
|
||||||
pypy
|
pypy
|
||||||
----
|
----
|
||||||
real 21.34
|
real 20.96
|
||||||
user 21.28
|
user 20.90
|
||||||
sys 0.04
|
sys 0.04
|
||||||
|
|
||||||
Test: longdiv
|
Test: longdiv
|
||||||
=====
|
=====
|
||||||
pypy
|
pypy
|
||||||
----
|
----
|
||||||
real 28.94
|
real 30.57
|
||||||
user 28.88
|
user 30.50
|
||||||
sys 0.04
|
sys 0.04
|
||||||
|
|
||||||
Test: findmax
|
Test: findmax
|
||||||
=====
|
=====
|
||||||
pypy
|
pypy
|
||||||
----
|
----
|
||||||
real 18.89
|
real 16.29
|
||||||
user 18.85
|
user 16.24
|
||||||
sys 0.02
|
sys 0.03
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user