1
0
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:
Henry Gomersall 2016-05-27 18:25:38 +01:00
commit 4b4be7be6c
No known key found for this signature in database
GPG Key ID: 67F4313D73CED5A6
34 changed files with 862 additions and 1043 deletions

1
.gitignore vendored
View File

@ -5,6 +5,7 @@
*.coverage
build/
.cache
.style.yapf
# Python
*.py[cod]

View File

@ -6,7 +6,8 @@ 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)
return Cosimulation("vvp -m ../myhdl.vpi bin2gray.o", B=B, G=G)

View File

@ -23,11 +23,11 @@
import sys
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_bin2gray, test_inc, test_dff )
modules = (test_gray_properties, test_gray_original, test_inc, test_dff )
import unittest

View 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()

View File

@ -1,5 +1,4 @@
import myhdl
from myhdl import *
from myhdl import always_comb
def bin2gray(B, G, width):
""" Gray encoder.
@ -15,5 +14,3 @@ def bin2gray(B, G, width):
G.next[i] = B[i+1] ^ B[i]
return logic

View File

@ -3,15 +3,7 @@ module bin2gray(B, G);
parameter width = 8;
input [width-1:0] B;
output [width-1:0] G;
reg [width-1:0] G;
integer i;
wire [width:0] extB;
assign extB = {1'b0, B}; // zero-extend input
always @(extB) begin
for (i=0; i < width; i=i+1)
G[i] <= extB[i+1] ^ extB[i];
end
assign G = (B >> 1) ^ B;
endmodule // bin2gray

View File

@ -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
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):
""" 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
.. include-example:: bin2gray.py
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
@ -65,7 +52,7 @@ interface::
module bin2gray(B, G);
parameter width = 8;
input [width-1:0] B;
input [width-1:0] B;
output [width-1:0] G;
....
@ -135,11 +122,14 @@ simulator, this is done as follows::
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)
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
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::
module bin2gray(B, G);
module bin2gray(B, G);
parameter width = 8;
input [width-1:0] B;
output [width-1:0] G;
reg [width-1:0] G;
integer i;
wire [width:0] extB;
parameter width = 8;
input [width-1:0] B;
output [width-1:0] G;
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
When we run our unit tests, we get::
When we run our unit test, we get::
% python test_bin2gray.py
Check that only one bit changes in successive codewords ... ok
Check that all codewords occur exactly once ... ok
Check that the code is an original Gray code ... ok
% python test_gray.py
testSingleBitChange (test_gray_properties.TestGrayCodeProperties)
Check that only one bit changes in successive codewords. ... ok
testUniqueCodeWords (test_gray_properties.TestGrayCodeProperties)
Check that all codewords occur exactly once. ... 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
.. _cosim-restr:
Restrictions

View File

@ -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
creation, through methods and operators such as slice assignment.
:class:`intbv` supports the same operators as :class:`int` for arithmetic.
In addition, it provides a number of features to make it
suitable for hardware design. First, the range of allowed values can
be constrained. This makes it possible to check the value at run time
during simulation. Moreover, back end tools can determine the smallest
possible bit width for representing the object.
Secondly, it supports bit level operations by providing an indexing
and slicing interface.
:class:`intbv` supports the same operators as :class:`int` for arithmetic. In
addition, it provides a number of features to make it suitable for hardware
design. First, the range of allowed values can be constrained. This makes it
possible to check the value at run time during simulation. Moreover, back end
tools can determine the smallest possible bit width for representing the object.
Secondly, it supports bit level operations by providing an indexing and slicing
interface.
: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
*max* is exclusive. Therefore, the allowed value range is *min* .. *max*-1.
Let's us look at some examples. First, an unconstrained :class:`intbv`
object is created as follows:
Let's look at some examples. An unconstrained :class:`intbv` object is created
as follows::
>>> a = intbv(24)
.. index::
.. index::
single: intbv; min
single: intbv; max
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
to determine the bit width. If we inspect the previously created
object, we get::
>>> print a.min
>>> a
intbv(24)
>>> print(a.min)
None
>>> print a.max
>>> print(a.max)
None
>>> print len(a)
>>> len(a)
0
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)
Inspecting the object now gives::
>>> a
intbv(24)
>>> a.min
0
>>> 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
required to represent the object.
Sometimes hardware engineers prefer to constrain an object by defining
its bit width directly, instead of the range of allowed values.
The following example shows how to do that::
The *min* and *max* bound attributes enable fine-grained control and error
checking of the value range. In particular, the bound values do not have to be
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:]
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
>>> 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
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:
Bit indexing
@ -145,135 +113,102 @@ Bit indexing
.. index:: single: bit indexing
As an example, we will consider the design of a Gray encoder. The following code
is a Gray encoder modeled in MyHDL::
A common requirement in hardware design is access to the individual bits. The
: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):
""" 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
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
We use the :func:`bin` function provide by MyHDL because it shows the two's
complement representation for negative values, unlike Python's builtin with the
same name. Note that lower indices correspond to less significant bits. The
following code illustrates bit index assignment::
>>> 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:
Bit slicing
===========
.. index::
.. index::
single: bit slicing
single: concat(); example usage
For a change, we will use a traditional function as an example to illustrate
slicing. The following function calculates the HEC byte of an ATM header. ::
The :class:`intbv` type also supports bit slicing, for both read access
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):
""" Return hec for an ATM header, represented as an intbv.
Both indices can be omitted from the slice. If the rightmost index is omitted,
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.
"""
hec = intbv(0)
for bit in header[32:]:
hec[8:] = concat(hec[7:2],
bit ^ hec[1] ^ hec[7],
bit ^ hec[0] ^ hec[7],
bit ^ hec[7]
)
return hec ^ COSET
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.
>>> bin(a)
'11000'
>>> bin(a[4:])
'1000'
>>> a[4:] = '0001'
>>> bin(a)
'10001'
>>> a[:] = 0b10101
>>> bin(a)
'10101'
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
``8`` bits. Likewise, the slice ``hex[7:2]`` has ``7-2=5`` bits. You can think
one-off count issues in practice. For example, the slice ``a[8:]`` has exactly
``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
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
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)
4
>>> b = a[4:]
>>> b
>>> b
intbv(6L)
>>> len(b)
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
the bit width.
The object returned by a slice is positive, even when the
original object is negative::
The object returned by a slice is positive, even when the original object is
negative::
>>> a = intbv(-3)
>>> bin(a, width=5)
@ -307,8 +242,31 @@ original object is negative::
>>> bin(b)
'11101'
The bit pattern of the two objects is identical within the bit width,
but their values have opposite sign.
In this example, the bit pattern of the two objects is identical within the bit
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:
@ -363,7 +321,7 @@ In a typical case when ``min==0``, this reduces to::
Unsigned and signed representation
==================================
.. index::
.. index::
single: intbv; intbv.signed
: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)
'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.
Naturally, for a "signed" the return value will always be identical
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()
imag.next = data_bus[4:].signed()

View File

@ -31,27 +31,26 @@ Combinatorial logic
Template
--------
.. testsetup:: *
from myhdl import *
Combinatorial logic is described with a code pattern as follows::
from myhdl import block, always_comb
@block
def top(<parameters>):
...
@always_comb
def combLogic():
def comb_logic():
<functional code>
...
return combLogic, ...
The :func:`always_comb` decorator describes combinatorial logic. [#]_. The
decorated function is a local function that specifies what happens when one of
the input signals of the logic changes. The :func:`always_comb` decorator
infers the input signals automatically. It returns a generator that is sensitive
to all inputs, and that executes the function whenever an input changes.
return comb_logic, ...
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:
@ -60,76 +59,21 @@ Example
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
``random`` module in Python's standard library comes in handy for such purposes.
The function ``randrange(n)`` returns a random natural integer smaller than *n*.
It is used in the test bench code to produce random input values
It is used in the test bench code to produce random input values.
.. testcode:: comb1
:hide:
.. include-example:: test_mux.py
import random
random.seed(0xDECAFBAD)
It is often useful to keep the random values reproducible. This can be
accomplished by providing a seed value as in the code. The run produces the
following output:
.. testcode:: comb1
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
.. run-example:: test_mux.py
.. _model-seq:
@ -138,7 +82,6 @@ Sequential logic
.. index:: single: sequential logic
.. _model-seq-templ:
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
model directly::
from myhdl import block, always_seq
@instance
def top(<parameters>, clock, ..., reset, ...):
...
@always_seq(clock.posedge, reset=reset)
def seqLogic():
def seq_logic():
<functional code>
...
return seqLogic, ...
return seq_logic, ...
The :func:`always_seq` decorator automatically infers the reset
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
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
asynchronous reset.
.. include-example:: inc.py
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
a small incrementer and a small number of patterns is a follows
.. testcode:: seq1
: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()
.. include-example:: test_inc.py
The simulation produces the following output
.. testoutput:: seq1
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
.. run-example:: test_inc.py
.. _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,
you can use a more explicit template as follows::
from myhdl import block, always
@block
def top(<parameters>, clock, ..., reset, ...):
...
@always(clock.posedge, reset.negedge)
def seqLogic():
def seq_logic():
if not reset:
<reset code>
else:
<functional code>
return seq_logic,...
With this template, the reset values have to be specified
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
coded as follows
.. testcode:: sm1
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
.. include-example:: fsm.py
.. 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
tool such as :program:`gtkwave`.
.. %
.. %
The user interface of this feature consists of a single function,
:func:`traceSignals`. To explain how it works, recall that in MyHDL, an
@ -433,87 +253,8 @@ call.
A small test bench for our framing controller example, with signal tracing
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
:file:`testbench.vcd`. When we load this file into :program:`gtkwave`, we can
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
is specific to :program:`gtkwave`. To generate a standard VCD file, you need to
use signals with a defined bit width only.
.. rubric:: Footnotes
.. [#] The name :func:`always_comb` refers to a construct with similar semantics in
SystemVerilog.
.. [#] It also possible to have a reproducible random output, by explicitly providing a
seed value. See the documentation of the ``random`` module.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -19,21 +19,20 @@ to hardware design.
.. index:: single: extreme programming
One software design approach that gets a lot of attention recently is *Extreme
Programming* (XP). It is a fascinating set of techniques and guidelines that
often seems to go against the conventional wisdom. On other occasions, XP just
seems to emphasize the common sense, which doesn't always coincide with common
practice. For example, XP stresses the importance of normal workweeks, if we are
to have the fresh mind needed for good software development.
One software design approach that deserves attention is *Extreme Programming*
(XP). It is a fascinating set of techniques and guidelines that often seems to
go against the conventional wisdom. On other occasions, XP just seems to
emphasize the common sense, which doesn't always coincide with common practice.
For example, XP stresses the importance of normal workweeks, if we are to have
the fresh mind needed for good software development.
.. %
.. %
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
think is very relevant to hardware design: the importance and methodology of
unit testing.
.. _unittest-why:
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:
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
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
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):
""" Check that only one bit changes in successive codewords """
"""Check that only one bit changes in successive codewords."""
....
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
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
follows::
follows:
def bin2gray(B, G, width):
### NOT IMPLEMENTED YET! ###
yield None
.. include-example:: bin2gray_dummy.py
For the first requirement, we will write a test bench that applies all
consecutive input numbers, and compares the current output with the previous one
for each input. Then we check that the difference is a single bit. We will test
all Gray codes up to a certain order ``MAX_WIDTH``. ::
For the first requirement, we will test all consecutive input numbers, and
compare the current output with the previous one For each input, we check that
the difference is exactly a single bit. For the second requirement, we will test
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):
""" 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)
.. include-example:: test_gray_properties.py
Note how the actual check is performed by a ``self.assertEqual`` method, defined
by the ``unittest.TestCase`` class.
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)
by the ``unittest.TestCase`` class. Also, we have factored out running the
tests for all Gray codes in a separate method :func:`runTests`.
.. _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::
% python test_gray.py -v
Check that only one bit changes in successive codewords ... FAIL
Check that all codewords occur exactly once ... FAIL
<trace backs not shown>
% python test_gray_properties.py
testSingleBitChange (__main__.TestGrayCodeProperties)
Check that only one bit changes in successive codewords. ... ERROR
testUniqueCodeWords (__main__.TestGrayCodeProperties)
Check that all codewords occur exactly once. ... ERROR
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):
### INCORRECT - DEMO PURPOSE ONLY! ###
@always_comb
def logic():
G.next = B[0]
return logic
.. include-example:: bin2gray_wrong.py
Running the test produces::
% python test_gray.py -v
Check that only one bit changes in successive codewords ... ok
Check that all codewords occur exactly once ... FAIL
python test_gray_properties.py
testSingleBitChange (__main__.TestGrayCodeProperties)
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
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_gray.py", line 109, in testUniqueCodeWords
sim.run(quiet=1)
...
File "test_gray.py", line 104, in test
self.assertEqual(actual, expected)
File "/usr/local/lib/python2.2/unittest.py", line 286, in failUnlessEqual
raise self.failureException, \
AssertionError: [0, 0, 1, 1] != [0, 1, 2, 3]
======================================================================
FAIL: testUniqueCodeWords (__main__.TestGrayCodeProperties)
Check that all codewords occur exactly once.
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_gray_properties.py", line 42, in testUniqueCodeWords
self.runTests(test)
File "test_gray_properties.py", line 53, in runTests
sim.run(quiet=1)
File "/home/jand/dev/myhdl/myhdl/_Simulation.py", line 154, in run
waiter.next(waiters, actives, exc)
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]
----------------------------------------------------------------------
Ran 2 tests in 0.785s
First differing element 1:
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
one. After the test feedback, a full traceback is shown that can help to debug
the test output.
Finally, if we use the correct implementation as in section
:ref:`hwtypes-indexing`, the output is::
Finally, we use a correct implementation:
% python test_gray.py -v
Check that only one bit changes in successive codewords ... ok
Check that all codewords occur exactly once ... ok
.. include-example:: bin2gray.py
----------------------------------------------------------------------
Ran 2 tests in 6.364s
OK
Now the tests pass:
.. run-example:: test_gray_properties.py
.. _unittest-change:
Changing requirements
---------------------
Additional requirements
-----------------------
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
@ -305,54 +264,19 @@ It is possible to specify these codes by a recursive algorithm, as follows:
of Ln0 and Ln1.
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):
""" 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
.. include-example:: next_gray_code.py
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.
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
is as follows::
is as follows:
class TestOriginalGrayCode(TestCase):
.. include-example:: test_gray_original.py
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)
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
As it happens, our implementation is apparently an original Gray code:
.. run-example:: test_gray_original.py

View File

@ -22,7 +22,7 @@ class RunExample(Directive):
env = document.settings.env
_ , wd = env.relfn2path(example_dir)
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,
universal_newlines=True)
out = '$ python {}\n{}'.format(prog, out)

View File

@ -1,6 +1,6 @@
// File: Array8Sorter.v
// Generated by MyHDL 1.0dev
// Date: Sat Mar 19 17:27:30 2016
// Date: Sun May 15 11:40:06 2016
`timescale 1ns/10ps

View File

@ -6,6 +6,6 @@ def Hello(clk, to="World!"):
@always(clk.posedge)
def say_hello():
print "%s Hello %s" % (now(), to)
print("%s Hello %s" % (now(), to))
return say_hello

View File

@ -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.
B -- input intbv signal, binary encoded
G -- output intbv signal, gray encoded
width -- bit width
B -- binary input
G -- Gray encoded output
"""
@always_comb
def logic():
for i in range(width):
G.next[i] = B[i+1] ^ B[i]
G.next = (B>>1) ^ B
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()

View 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

View 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

View 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)

View File

@ -1,95 +1,54 @@
import myhdl
from myhdl import *
from myhdl import block, always_seq, Signal, intbv, enum
ACTIVE_LOW = 0
FRAME_SIZE = 8
t_State = enum('SEARCH', 'CONFIRM', 'SYNC')
t_state = enum('SEARCH', 'CONFIRM', 'SYNC')
@block
def FramerCtrl(SOF, state, syncFlag, clk, reset_n):
def framer_ctrl(sof, state, sync_flag, clk, reset_n):
""" Framing control FSM.
SOF -- start-of-frame output bit
sof -- start-of-frame output bit
state -- FramerState output
syncFlag -- sync pattern found indication input
sync_flag -- sync pattern found indication input
clk -- clock input
reset_n -- active low reset
"""
index = Signal(0) # position in frame
@always(clk.posedge, reset_n.negedge)
"""
index = Signal(intbv(0, min=0, max=FRAME_SIZE)) # position in frame
@always_seq(clk.posedge, reset=reset_n)
def FSM():
if reset_n == ACTIVE_LOW:
SOF.next = 0
sof.next = 0
index.next = 0
state.next = t_State.SEARCH
state.next = t_state.SEARCH
else:
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
if syncFlag:
state.next = t_State.CONFIRM
if sync_flag:
state.next = t_state.CONFIRM
elif state == t_State.CONFIRM:
elif state == t_state.CONFIRM:
if index == 0:
if syncFlag:
state.next = t_State.SYNC
if sync_flag:
state.next = t_state.SYNC
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 not syncFlag:
state.next = t_State.SEARCH
SOF.next = (index == FRAME_SIZE-1)
if not sync_flag:
state.next = t_state.SEARCH
sof.next = (index == FRAME_SIZE-1)
else:
raise ValueError("Undefined state")
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()

View File

@ -11,7 +11,7 @@ def HelloWorld():
@always(clk.posedge)
def say_hello():
print "%s Hello World!" % now()
print("%s Hello World!" % now())
return drive_clk, say_hello

View File

@ -1,92 +1,21 @@
from random import randrange
import myhdl
from myhdl import *
from myhdl import block, always_seq
ACTIVE_LOW, INACTIVE_HIGH = 0, 1
@block
def inc(count, enable, clock, reset):
def Inc(count, enable, clock, reset):
""" 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():
def seq():
if enable:
count.next = count + 1
return incLogic
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()
return seq

View File

@ -1,34 +1,21 @@
from myhdl import Signal, Simulation, delay
from myhdl import block, always_comb, Signal
@block
def mux(z, a, b, sel):
""" Multiplexer.
z -- mux output
a, b -- data inputs
sel -- control input: select a if asserted, otherwise b
"""
while 1:
yield a, b, sel
@always_comb
def comb():
if sel == 1:
z.next = a
else:
z.next = b
from random import randrange
(z, a, b, sel) = [Signal(0) for i in range(4)]
MUX_1 = mux(z, a, b, sel)
def test():
print "z a b sel"
for i in range(8):
a.next, b.next, sel.next = randrange(8), randrange(8), randrange(2)
yield delay(10)
print "%s %s %s %s" % (z, a, b, sel)
def main():
Simulation(MUX_1, test()).run()
if __name__ == '__main__':
main()
return comb

View 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

View 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()

View 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()

View File

@ -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()

View 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)

View 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)

View 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()

View 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()

View File

@ -26,6 +26,7 @@ import inspect
import functools
import myhdl
from myhdl._compat import PY2
from myhdl import BlockError, BlockInstanceError, Cosimulation
from myhdl._instance import _Instantiator
from myhdl._util import _flatten
@ -66,8 +67,15 @@ def _getCallInfo():
# caller may be undefined if instantiation from a Python module
callerrec = None
funcrec = stack[3]
name = funcrec[3]
if len(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]
frame = funcrec[0]
@ -81,8 +89,8 @@ def _getCallInfo():
return _CallInfo(name, modctxt, symdict)
class _bound_function_wrapper(object):
class _bound_function_wrapper(object):
def __init__(self, bound_func, srcfile, srcline):
self.srcfile = srcfile
@ -95,18 +103,18 @@ class _bound_function_wrapper(object):
def __call__(self, *args, **kwargs):
self.calls += 1
return _Block(self.bound_func, self, self.srcfile,
return _Block(self.bound_func, self, self.srcfile,
self.srcline, *args, **kwargs)
class block(object):
def __init__(self, func):
self.srcfile = inspect.getsourcefile(func)
self.srcline = inspect.getsourcelines(func)[0]
self.func = func
functools.update_wrapper(self, func)
self.calls = 0
self.calls = 0
def __get__(self, instance, owner):
@ -114,9 +122,9 @@ class block(object):
return _bound_function_wrapper(bound_func, self.srcfile, self.srcline)
def __call__(self, *args, **kwargs):
self.calls += 1
return _Block(self.func, self, self.srcfile,
return _Block(self.func, self, self.srcfile,
self.srcline, *args, **kwargs)
#def block(func):
@ -231,8 +239,8 @@ class _Block(object):
`self.mod.__name__`
trace(Optional[bool]): Verilog only. Whether the testbench should
dump all signal waveforms. Defaults to False.
tb (Optional[bool]): Verilog only. Specifies whether a testbench
should be created. Defaults to True.
testbench (Optional[bool]): Verilog only. Specifies whether a
testbench should be created. Defaults to True.
timescale(Optional[str]): Verilog only. Defaults to '1ns/10ps'
"""
if hdl.lower() == 'vhdl':
@ -247,7 +255,7 @@ class _Block(object):
conv_attrs['name'] = kwargs.pop('name')
conv_attrs['directory'] = kwargs.pop('path', '')
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['trace'] = kwargs.pop('trace', False)
conv_attrs.update(kwargs)
@ -257,12 +265,14 @@ class _Block(object):
def config_sim(self, trace=False):
self._config_sim['trace'] = trace
if trace:
myhdl.traceSignals(self)
def run_sim(self, duration=None, quiet=0):
if self.sim is None:
sim = self
if self._config_sim['trace']:
sim = myhdl.traceSignals(self)
#if self._config_sim['trace']:
# sim = myhdl.traceSignals(self)
self.sim = myhdl._Simulation.Simulation(sim)
self.sim.run(duration, quiet)

View File

@ -32,9 +32,9 @@ import inspect
from myhdl._Cosimulation import Cosimulation
from myhdl._instance import _Instantiator
def _isGenSeq(obj):
if isinstance(obj, (Cosimulation, _Instantiator)):
from myhdl._block import _Block
if isinstance(obj, (Cosimulation, _Instantiator, _Block)):
return True
if not isinstance(obj, (list, tuple, set)):
return False

View 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()

View File

@ -3,39 +3,39 @@ Test: timer
=====
pypy
----
real 36.20
user 36.15
sys 0.02
real 37.31
user 37.25
sys 0.03
Test: lfsr24
=====
pypy
----
real 39.08
user 39.00
sys 0.04
real 39.90
user 39.86
sys 0.01
Test: randgen
=====
pypy
----
real 21.34
user 21.28
real 20.96
user 20.90
sys 0.04
Test: longdiv
=====
pypy
----
real 28.94
user 28.88
real 30.57
user 30.50
sys 0.04
Test: findmax
=====
pypy
----
real 18.89
user 18.85
sys 0.02
real 16.29
user 16.24
sys 0.03