1
0
mirror of https://github.com/myhdl/myhdl.git synced 2024-12-14 07:44:38 +08:00

Use myhdldoctools instead of doctest

This commit is contained in:
Jan Decaluwe 2016-05-11 12:08:15 +02:00
parent 3b5ad3b471
commit f272f8fba4
12 changed files with 211 additions and 476 deletions

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
@ -177,31 +123,7 @@ 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 .. include-example:: inc.py
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
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
@ -433,86 +253,7 @@ 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
@ -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

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

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

@ -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 index = Signal(intbv(0, min=0, max=FRAME_SIZE)) # position in frame
@always(clk.posedge, reset_n.negedge) @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,10 +1,7 @@
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.
@ -17,76 +14,8 @@ def Inc(count, enable, clock, reset):
""" """
@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,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

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