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 *.coverage
build/ build/
.cache .cache
.style.yapf
# Python # Python
*.py[cod] *.py[cod]

View File

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

View File

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

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

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. """ 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()

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

View File

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

View File

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

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

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

View File

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

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