1 """Generation of GTKWave documents with nmutil.gtkw"""
3 from nmigen
import Elaboratable
, Signal
, Module
, Cat
4 from nmigen
.back
.pysim
import Simulator
5 from nmigen
.cli
import rtlil
8 from vcd
.gtkw
import GTKWSave
, GTKWColor
9 from nmutil
.gtkw
import write_gtkw
12 class Shifter(Elaboratable
):
13 """Simple sequential shifter
17 * ``p_i_data``: value to be shifted
19 * ``p_i_shift``: shift amount
21 * ``op__sdir``: shift direction (0 = left, 1 = right)
23 * ``p_i_valid`` and ``p_o_ready``: handshake
27 * ``n_o_data``: shifted value
29 * ``n_o_valid`` and ``n_i_ready``: handshake
32 def __init__(self
, width
):
35 self
.p_i_data
= Signal(width
)
36 self
.p_i_shift
= Signal(width
)
37 self
.op__sdir
= Signal()
38 self
.p_i_valid
= Signal()
39 self
.p_o_ready
= Signal()
40 self
.n_o_data
= Signal(width
)
41 self
.n_o_valid
= Signal()
42 self
.n_i_ready
= Signal()
44 def elaborate(self
, _
):
52 shift_in
= Signal(self
.width
)
53 shift_left_by_1
= Signal(self
.width
)
54 shift_right_by_1
= Signal(self
.width
)
55 next_shift
= Signal(self
.width
)
57 shift_reg
= Signal(self
.width
, reset_less
=True)
60 # connect input and output
61 shift_in
.eq(self
.p_i_data
),
62 self
.n_o_data
.eq(shift_reg
),
63 # generate shifted views of the register
64 shift_left_by_1
.eq(Cat(0, shift_reg
[:-1])),
65 shift_right_by_1
.eq(Cat(shift_reg
[1:], 0)),
67 # choose the next value of the register according to the
69 # default is no change
70 m
.d
.comb
+= next_shift
.eq(shift_reg
)
72 m
.d
.comb
+= next_shift
.eq(shift_in
)
75 m
.d
.comb
+= next_shift
.eq(shift_right_by_1
)
77 m
.d
.comb
+= next_shift
.eq(shift_left_by_1
)
79 # register the next value
80 m
.d
.sync
+= shift_reg
.eq(next_shift
)
83 shift_width
= int(log2(self
.width
)) + 1
84 next_count
= Signal(shift_width
)
85 count
= Signal(shift_width
, reset_less
=True)
86 m
.d
.sync
+= count
.eq(next_count
)
91 # keep p.o_ready active on IDLE
93 # keep loading the shift register and shift count
95 next_count
.eq(self
.p_i_shift
),
97 # capture the direction bit as well
98 m
.d
.sync
+= direction
.eq(self
.op__sdir
)
99 with m
.If(self
.p_i_valid
):
100 # Leave IDLE when data arrives
101 with m
.If(next_count
== 0):
102 # short-circuit for zero shift
106 with m
.State("SHIFT"):
108 # keep shifting, while counter is not zero
110 # decrement the shift counter
111 next_count
.eq(count
- 1),
113 with m
.If(next_count
== 0):
114 # exit when shift counter goes to zero
116 with m
.State("DONE"):
117 # keep n_o_valid active while the data is not accepted
118 m
.d
.comb
+= self
.n_o_valid
.eq(1)
119 with m
.If(self
.n_i_ready
):
120 # go back to IDLE when the data is accepted
139 def write_gtkw_direct():
140 """Write a formatted GTKWave "save" file, using vcd.gtkw directly"""
141 # hierarchy path, to prepend to signal names
144 style_input
= GTKWColor
.orange
145 style_output
= GTKWColor
.yellow
146 style_debug
= GTKWColor
.red
147 with
open("test_shifter_direct.gtkw", "wt") as gtkw_file
:
148 gtkw
= GTKWSave(gtkw_file
)
149 gtkw
.comment("Auto-generated by " + __file__
)
150 gtkw
.dumpfile("test_shifter.vcd")
151 # set a reasonable zoom level
152 # also, move the marker to an interesting place
153 gtkw
.zoom_markers(-22.9, 10500000)
154 gtkw
.trace(dut
+ "clk")
155 # place a comment in the signal names panel
156 gtkw
.blank("Shifter Demonstration")
157 with gtkw
.group("prev port"):
158 gtkw
.trace(dut
+ "op__sdir", color
=style_input
)
159 # demonstrates using decimal base (default is hex)
160 gtkw
.trace(dut
+ "p_i_data[7:0]", color
=style_input
,
162 gtkw
.trace(dut
+ "p_i_shift[7:0]", color
=style_input
,
164 gtkw
.trace(dut
+ "p_i_valid", color
=style_input
)
165 gtkw
.trace(dut
+ "p_o_ready", color
=style_output
)
166 with gtkw
.group("debug"):
167 gtkw
.blank("Some debug statements")
168 # change the displayed name in the panel
169 gtkw
.trace("top.zero", alias
='zero delay shift',
171 gtkw
.trace("top.interesting", color
=style_debug
)
172 gtkw
.trace("top.test_case", alias
="test case", color
=style_debug
)
173 gtkw
.trace("top.msg", color
=style_debug
)
174 with gtkw
.group("internal"):
175 gtkw
.trace(dut
+ "fsm_state")
176 gtkw
.trace(dut
+ "count[3:0]")
177 gtkw
.trace(dut
+ "shift_reg[7:0]", datafmt
='dec')
178 with gtkw
.group("next port"):
179 gtkw
.trace(dut
+ "n_o_data[7:0]", color
=style_output
,
181 gtkw
.trace(dut
+ "n_o_valid", color
=style_output
)
182 gtkw
.trace(dut
+ "n_i_ready", color
=style_input
)
186 """Simulate the Shifter to generate some traces,
187 as well as the GTKWave documents"""
189 m
.submodules
.shf
= dut
= Shifter(8)
190 print("Shifter port names:")
192 print("-", port
.name
)
194 # try "proc; show" in yosys to check the data path
195 il
= rtlil
.convert(dut
, ports
=dut
.ports())
196 with
open("test_shifter.il", "w") as f
:
199 # write the GTKWave project file, directly
202 # Describe a GTKWave document
204 # Style for signals, classes and groups
206 # Root selector. Gives default attributes for every signal.
208 # color the traces, according to class
209 # class names are not hardcoded, they are just strings
210 'in': {'color': 'orange'},
211 'out': {'color': 'yellow'},
212 # signals in the debug group have a common color and module path
213 'debug': {'module': 'top', 'color': 'red'},
214 # display a different string replacing the signal name
215 'test_case': {'display': 'test case'},
218 # DOM style description for the trace pane
220 # simple signal, without a class
221 # even so, it inherits the top-level root attributes
224 {'comment': 'Shifter Demonstration'},
225 # collapsible signal group
227 # attach a class style for each signal
229 ('p_i_data[7:0]', 'in'),
230 ('p_i_shift[7:0]', 'in'),
232 ('p_o_ready', 'out'),
234 # Signals in a signal group inherit the group attributes.
235 # In this case, a different module path and color.
237 {'comment': 'Some debug statements'},
238 # inline attributes, instead of a class name
239 ('zero', {'display': 'zero delay shift'}),
250 ('n_o_data[7:0]', 'out'),
251 ('n_o_valid', 'out'),
256 write_gtkw("test_shifter.gtkw", "test_shifter.vcd",
257 gtkwave_desc
, gtkwave_style
,
258 module
="top.shf", loc
=__file__
, marker
=10500000)
263 # demonstrates adding extra debug signal traces
264 # they end up in the top module
266 zero
= Signal() # mark an interesting place
268 # demonstrates string traces
270 # display a message when the signal is high
271 # the low level is just an horizontal line
272 interesting
= Signal(decoder
=lambda v
: 'interesting!' if v
else '')
273 # choose between alternate strings based on numerical value
274 test_cases
= ['', '13>>2', '3<<4', '21<<0']
275 test_case
= Signal(8, decoder
=lambda v
: test_cases
[v
])
276 # hack to display arbitrary strings, like debug statements
277 msg
= Signal(decoder
=lambda _
: msg
.str)
280 def send(data
, shift
, direction
):
281 # present input data and assert i_valid
282 yield dut
.p_i_data
.eq(data
)
283 yield dut
.p_i_shift
.eq(shift
)
284 yield dut
.op__sdir
.eq(direction
)
285 yield dut
.p_i_valid
.eq(1)
287 # wait for p.o_ready to be asserted
288 while not (yield dut
.p_o_ready
):
290 # show current operation operation
292 msg
.str = f
'{data}>>{shift}'
294 msg
.str = f
'{data}<<{shift}'
295 # force dump of the above message by toggling the
299 # clear input data and negate p.i_valid
300 yield dut
.p_i_valid
.eq(0)
301 yield dut
.p_i_data
.eq(0)
302 yield dut
.p_i_shift
.eq(0)
303 yield dut
.op__sdir
.eq(0)
305 def receive(expected
):
306 # signal readiness to receive data
307 yield dut
.n_i_ready
.eq(1)
309 # wait for n.o_valid to be asserted
310 while not (yield dut
.n_o_valid
):
313 result
= yield dut
.n_o_data
315 yield dut
.n_i_ready
.eq(0)
317 assert result
== expected
318 # finish displaying the current operation
325 yield from send(13, 2, 1)
327 yield from send(3, 4, 0)
329 # use a debug signal to mark an interesting operation
330 # in this case, it is a shift by zero
331 yield interesting
.eq(1)
332 yield from send(21, 0, 0)
333 yield interesting
.eq(0)
336 # the consumer is not in step with the producer, but the
337 # order of the results are preserved
339 yield test_case
.eq(1)
340 yield from receive(3)
342 yield test_case
.eq(2)
343 yield from receive(48)
345 yield test_case
.eq(3)
346 # you can look for the rising edge of this signal to quickly
347 # locate this point in the traces
349 yield from receive(21)
351 yield test_case
.eq(0)
353 sim
.add_sync_process(producer
)
354 sim
.add_sync_process(consumer
)
355 sim_writer
= sim
.write_vcd(
357 # include additional signals in the trace dump
358 traces
=[zero
, interesting
, test_case
, msg
],
364 if __name__
== "__main__":