c242e255a6c0efe9d0e0c7e3b009fd268baec1d9
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_data_i``: value to be shifted
19 * ``p_shift_i``: shift amount
21 * ``op__sdir``: shift direction (0 = left, 1 = right)
23 * ``p_i_valid`` and ``p_o_ready``: handshake
27 * ``n_data_o``: shifted value
29 * ``n_o_valid`` and ``n_i_ready``: handshake
31 def __init__(self
, width
):
34 self
.p_data_i
= Signal(width
)
35 self
.p_shift_i
= Signal(width
)
36 self
.op__sdir
= Signal()
37 self
.p_i_valid
= Signal()
38 self
.p_o_ready
= Signal()
39 self
.n_data_o
= Signal(width
)
40 self
.n_o_valid
= Signal()
41 self
.n_i_ready
= Signal()
43 def elaborate(self
, _
):
51 shift_in
= Signal(self
.width
)
52 shift_left_by_1
= Signal(self
.width
)
53 shift_right_by_1
= Signal(self
.width
)
54 next_shift
= Signal(self
.width
)
56 shift_reg
= Signal(self
.width
, reset_less
=True)
59 # connect input and output
60 shift_in
.eq(self
.p_data_i
),
61 self
.n_data_o
.eq(shift_reg
),
62 # generate shifted views of the register
63 shift_left_by_1
.eq(Cat(0, shift_reg
[:-1])),
64 shift_right_by_1
.eq(Cat(shift_reg
[1:], 0)),
66 # choose the next value of the register according to the
68 # default is no change
69 m
.d
.comb
+= next_shift
.eq(shift_reg
)
71 m
.d
.comb
+= next_shift
.eq(shift_in
)
74 m
.d
.comb
+= next_shift
.eq(shift_right_by_1
)
76 m
.d
.comb
+= next_shift
.eq(shift_left_by_1
)
78 # register the next value
79 m
.d
.sync
+= shift_reg
.eq(next_shift
)
82 shift_width
= int(log2(self
.width
)) + 1
83 next_count
= Signal(shift_width
)
84 count
= Signal(shift_width
, reset_less
=True)
85 m
.d
.sync
+= count
.eq(next_count
)
90 # keep p.o_ready active on IDLE
92 # keep loading the shift register and shift count
94 next_count
.eq(self
.p_shift_i
),
96 # capture the direction bit as well
97 m
.d
.sync
+= direction
.eq(self
.op__sdir
)
98 with m
.If(self
.p_i_valid
):
99 # Leave IDLE when data arrives
100 with m
.If(next_count
== 0):
101 # short-circuit for zero shift
105 with m
.State("SHIFT"):
107 # keep shifting, while counter is not zero
109 # decrement the shift counter
110 next_count
.eq(count
- 1),
112 with m
.If(next_count
== 0):
113 # exit when shift counter goes to zero
115 with m
.State("DONE"):
116 # keep n_o_valid active while the data is not accepted
117 m
.d
.comb
+= self
.n_o_valid
.eq(1)
118 with m
.If(self
.n_i_ready
):
119 # go back to IDLE when the data is accepted
138 def write_gtkw_direct():
139 """Write a formatted GTKWave "save" file, using vcd.gtkw directly"""
140 # hierarchy path, to prepend to signal names
143 style_input
= GTKWColor
.orange
144 style_output
= GTKWColor
.yellow
145 style_debug
= GTKWColor
.red
146 with
open("test_shifter_direct.gtkw", "wt") as gtkw_file
:
147 gtkw
= GTKWSave(gtkw_file
)
148 gtkw
.comment("Auto-generated by " + __file__
)
149 gtkw
.dumpfile("test_shifter.vcd")
150 # set a reasonable zoom level
151 # also, move the marker to an interesting place
152 gtkw
.zoom_markers(-22.9, 10500000)
153 gtkw
.trace(dut
+ "clk")
154 # place a comment in the signal names panel
155 gtkw
.blank("Shifter Demonstration")
156 with gtkw
.group("prev port"):
157 gtkw
.trace(dut
+ "op__sdir", color
=style_input
)
158 # demonstrates using decimal base (default is hex)
159 gtkw
.trace(dut
+ "p_data_i[7:0]", color
=style_input
,
161 gtkw
.trace(dut
+ "p_shift_i[7:0]", color
=style_input
,
163 gtkw
.trace(dut
+ "p_i_valid", color
=style_input
)
164 gtkw
.trace(dut
+ "p_o_ready", color
=style_output
)
165 with gtkw
.group("debug"):
166 gtkw
.blank("Some debug statements")
167 # change the displayed name in the panel
168 gtkw
.trace("top.zero", alias
='zero delay shift',
170 gtkw
.trace("top.interesting", color
=style_debug
)
171 gtkw
.trace("top.test_case", alias
="test case", color
=style_debug
)
172 gtkw
.trace("top.msg", color
=style_debug
)
173 with gtkw
.group("internal"):
174 gtkw
.trace(dut
+ "fsm_state")
175 gtkw
.trace(dut
+ "count[3:0]")
176 gtkw
.trace(dut
+ "shift_reg[7:0]", datafmt
='dec')
177 with gtkw
.group("next port"):
178 gtkw
.trace(dut
+ "n_data_o[7:0]", color
=style_output
,
180 gtkw
.trace(dut
+ "n_o_valid", color
=style_output
)
181 gtkw
.trace(dut
+ "n_i_ready", color
=style_input
)
185 """Simulate the Shifter to generate some traces,
186 as well as the GTKWave documents"""
188 m
.submodules
.shf
= dut
= Shifter(8)
189 print("Shifter port names:")
191 print("-", port
.name
)
193 # try "proc; show" in yosys to check the data path
194 il
= rtlil
.convert(dut
, ports
=dut
.ports())
195 with
open("test_shifter.il", "w") as f
:
198 # write the GTKWave project file, directly
201 # Describe a GTKWave document
203 # Style for signals, classes and groups
205 # Root selector. Gives default attributes for every signal.
207 # color the traces, according to class
208 # class names are not hardcoded, they are just strings
209 'in': {'color': 'orange'},
210 'out': {'color': 'yellow'},
211 # signals in the debug group have a common color and module path
212 'debug': {'module': 'top', 'color': 'red'},
213 # display a different string replacing the signal name
214 'test_case': {'display': 'test case'},
217 # DOM style description for the trace pane
219 # simple signal, without a class
220 # even so, it inherits the top-level root attributes
223 {'comment': 'Shifter Demonstration'},
224 # collapsible signal group
226 # attach a class style for each signal
228 ('p_data_i[7:0]', 'in'),
229 ('p_shift_i[7:0]', 'in'),
231 ('p_o_ready', 'out'),
233 # Signals in a signal group inherit the group attributes.
234 # In this case, a different module path and color.
236 {'comment': 'Some debug statements'},
237 # inline attributes, instead of a class name
238 ('zero', {'display': 'zero delay shift'}),
249 ('n_data_o[7:0]', 'out'),
250 ('n_o_valid', 'out'),
255 write_gtkw("test_shifter.gtkw", "test_shifter.vcd",
256 gtkwave_desc
, gtkwave_style
,
257 module
="top.shf", loc
=__file__
, marker
=10500000)
262 # demonstrates adding extra debug signal traces
263 # they end up in the top module
265 zero
= Signal() # mark an interesting place
267 # demonstrates string traces
269 # display a message when the signal is high
270 # the low level is just an horizontal line
271 interesting
= Signal(decoder
=lambda v
: 'interesting!' if v
else '')
272 # choose between alternate strings based on numerical value
273 test_cases
= ['', '13>>2', '3<<4', '21<<0']
274 test_case
= Signal(8, decoder
=lambda v
: test_cases
[v
])
275 # hack to display arbitrary strings, like debug statements
276 msg
= Signal(decoder
=lambda _
: msg
.str)
279 def send(data
, shift
, direction
):
280 # present input data and assert i_valid
281 yield dut
.p_data_i
.eq(data
)
282 yield dut
.p_shift_i
.eq(shift
)
283 yield dut
.op__sdir
.eq(direction
)
284 yield dut
.p_i_valid
.eq(1)
286 # wait for p.o_ready to be asserted
287 while not (yield dut
.p_o_ready
):
289 # show current operation operation
291 msg
.str = f
'{data}>>{shift}'
293 msg
.str = f
'{data}<<{shift}'
294 # force dump of the above message by toggling the
298 # clear input data and negate p.i_valid
299 yield dut
.p_i_valid
.eq(0)
300 yield dut
.p_data_i
.eq(0)
301 yield dut
.p_shift_i
.eq(0)
302 yield dut
.op__sdir
.eq(0)
304 def receive(expected
):
305 # signal readiness to receive data
306 yield dut
.n_i_ready
.eq(1)
308 # wait for n.o_valid to be asserted
309 while not (yield dut
.n_o_valid
):
312 result
= yield dut
.n_data_o
314 yield dut
.n_i_ready
.eq(0)
316 assert result
== expected
317 # finish displaying the current operation
324 yield from send(13, 2, 1)
326 yield from send(3, 4, 0)
328 # use a debug signal to mark an interesting operation
329 # in this case, it is a shift by zero
330 yield interesting
.eq(1)
331 yield from send(21, 0, 0)
332 yield interesting
.eq(0)
335 # the consumer is not in step with the producer, but the
336 # order of the results are preserved
338 yield test_case
.eq(1)
339 yield from receive(3)
341 yield test_case
.eq(2)
342 yield from receive(48)
344 yield test_case
.eq(3)
345 # you can look for the rising edge of this signal to quickly
346 # locate this point in the traces
348 yield from receive(21)
350 yield test_case
.eq(0)
352 sim
.add_sync_process(producer
)
353 sim
.add_sync_process(consumer
)
354 sim_writer
= sim
.write_vcd(
356 # include additional signals in the trace dump
357 traces
=[zero
, interesting
, test_case
, msg
],
363 if __name__
== "__main__":