ternary function changed to BM2 form
[libreriscv.git] / docs / gtkwave_tutorial.mdwn
1 [[!img 2020-08-15_12-04.png size="800x" ]]
2
3 This tutorial is about generating better GTKWave documents (\*.gtkw)
4 from Python. The goal is to ease analysis of traces generated by
5 unit-tests, and at the same time to better understand the inner working
6 of modules, for which we are writing such tests.
7
8 There is a FOSDEM 2022 talk about writing GTKwave documents, with style,
9 <https://fosdem.org/2022/schedule/event/gtkwavecss/>
10 <https://video.fosdem.org/2022/D.open-hardware/gtkwavecss.webm>
11
12 # Stylish GTKWave Documents
13
14 In this tutorial, you will learn how to:
15
16 1. Use individual trace colors.
17 For instance, use different color styles for input, output, debug and
18 internal traces.
19 2. Use numeric bases besides the default hex.
20 3. Create collapsible trace groups
21 Useful to hide and show, at once, groups of debug, internal and
22 sub-module traces.
23 Select the opening or closing brace, then use the T key.
24 4. Add comments in the signal names pane
25 5. Change the displayed name of a trace
26 6. Use a sane default for initial zoom level
27 7. Place markers on interesting places
28 8. Put the generating file name as a comment in the file
29
30 ## Basic trace display
31
32 First, we need a VCD file. Try:
33
34 python -m nmutil.test.example_gtkwave
35
36 Among other files, it will generate ``test_shifter.vcd``.
37
38 Lets write a simple GTKW document. First, import the function:
39
40 from nmutil.gtkw import write_gtkw
41
42 Create a list of the traces you want to see. Some hints:
43
44 1. Put all trace names in quotes.
45 2. Use the same names as you would see in the trace pane of GTKWave
46 3. If a trace is multi-bit, use array notation 'name[max:min]'
47
48 For example:
49
50 traces = [
51 'clk',
52 # prev port
53 'op__sdir', 'p_i_data[7:0]', 'p_i_shift[7:0]', 'p_i_valid', 'p_o_ready',
54 # internal signals
55 'fsm_state', 'count[3:0]', 'shift_reg[7:0]',
56 # next port
57 'n_o_data[7:0]', 'n_o_valid', 'n_i_ready'
58 ]
59
60 Now, create the document:
61
62 write_gtkw("simple.gtkw", "test_shifter.vcd", traces, module='top.shf')
63
64 Remarks:
65
66 1. ``simple.gtkw`` is the name of our GTKWave document
67 2. ``test_shifter.vcd`` is the VCD file
68 3. ``traces`` is a list of trace names
69 4. ``top.shf`` is the hierarchy path of the module
70
71 Now try:
72
73 gtkwave simple.gtkw
74
75 Notice:
76
77 1. No need to press the "zoom to fit" button. The default zoom level is
78 adequate for a 1 MHz clock.
79 2. If you made a mistake, there will be no warning. The trace will
80 simply not appear (*the properties of the dropped trace will be given to the next signal, be careful*)
81 3. The reload button will only reload the VCD file, not the GTKW document. If you regenerate the document, you need to close and open a
82 new tab, or exit GTKWave and run again: ``gtkwave simple.gtkw``
83 4. If you feel tired of seeing the GTKWave splash window every time,
84 do: ``echo splash_disable 1 >> ~/.gtkwaverc``
85 5. If you modify the document manually, better to save it with another
86 name
87
88 ## Adding color
89
90 Go back to the trace list and replace the ``'op__sdir'`` string with:
91
92 ('op__sdir', {'color': 'orange'})
93
94 Recreate the document (you can change the file name):
95
96 write_gtkw("color.gtkw", "test_shifter.vcd", traces, module='top.shf')
97
98 If you now run ``gtkwave color.gtkw``, you will see that ``op__sdir``
99 has the new color.
100
101 ## Writing GTKWave documents, with style
102
103 Let's say we want all input traces be orange, and all output traces be
104 yellow. To change them one by one, as we did with ``op__sdir``, would be
105 very tedious and verbose. Also, hardcoding the color name will make it
106 difficult to change it later.
107
108 To solve this, lets create a style specification:
109
110 style = {
111 'in': {'color': 'orange'},
112 'out': {'color': 'yellow'}
113 }
114
115 Then, change:
116
117 ('op__sdir', {'color': 'orange'})
118
119 to:
120
121 ('op__sdir', 'in')
122
123 then (notice how we add ``style``):
124
125 write_gtkw("style1.gtkw", "test_shifter.vcd", traces, style, module='top.shf')
126
127 If you now run ``gtkwave style1.gtkw``, you will see that ``op__sdir``
128 still has the new color.
129
130 Let's add more color:
131
132 traces = [
133 'clk',
134 # prev port
135 ('op__sdir', 'in'),
136 ('p_i_data[7:0]', 'in'),
137 ('p_i_shift[7:0]', 'in'),
138 ('p_i_valid', 'in'),
139 ('p_o_ready', 'out'),
140 # internal signals
141 'fsm_state',
142 'count[3:0]',
143 'shift_reg[7:0]',
144 # next port
145 ('n_o_data[7:0]', 'out'),
146 ('n_o_valid', 'out'),
147 ('n_i_ready', 'in'),
148 ]
149
150 Then
151
152 write_gtkw("style2.gtkw", "test_shifter.vcd", traces, style, module='top.shf')
153
154 If you now run ``gtkwave style2.gtkw``, you will see that input, output and internal signals have different color.
155
156 # New signals at simulation time
157
158 At simulation time, you can declare a new signal, and use it inside
159 the test case, as any other signal. By including it in the "traces"
160 parameter of Simulator.write_vcd, it is included in the trace dump
161 file.
162
163 Useful for adding "printf" style debugging for GTKWave.
164
165 # String traces
166
167 When declaring a Signal, you can pass a custom decoder that
168 translates the Signal logic level to a string. nMigen uses this
169 internally to display Enum traces, but it is available for general
170 use.
171
172 Some applications are:
173
174 1. Display a string when a signal is at high level, otherwise show a
175 single horizontal line. Useful to draw attention to a time interval.
176 2. Display the stages of a unit test
177 3. Display arbitrary debug statements along the timeline.
178
179 from the documentation for Signal:
180
181 ```
182 decoder : function or Enum
183 A function converting integer signal values to human-readable
184 strings (e.g. FSM state names). If an ``Enum`` subclass is
185 passed, it is concisely decoded using format string
186 ``"{0.name:}/{0.value:}"``, or a number if the signal value
187 is not a member of the enumeration.
188 ```
189
190 An [example how to do this](https://git.libre-soc.org/?p=nmutil.git;a=blob;f=src/nmutil/test/example_gtkwave.py;h=1b8c3b9c1b0bb5cde23c6896fc5cbde991790384;hb=HEAD#l262):
191
192 ```
193 262 # demonstrates adding extra debug signal traces
194 263 # they end up in the top module
195 264 #
196 265 zero = Signal() # mark an interesting place
197 266 #
198 267 # demonstrates string traces
199 268 #
200 269 # display a message when the signal is high
201 270 # the low level is just an horizontal line
202 271 interesting = Signal(decoder=lambda v: 'interesting!' if v else '')
203 272 # choose between alternate strings based on numerical value
204 273 test_cases = ['', '13>>2', '3<<4', '21<<0']
205 274 test_case = Signal(8, decoder=lambda v: test_cases[v])
206 275 # hack to display arbitrary strings, like debug statements
207 276 msg = Signal(decoder=lambda _: msg.str)
208 277 msg.str = ''
209 ```
210
211 Then, in the Simulation, an arbitrary debug message can be inserted at
212 exactly the right required point. remember to "waggle" the Signal itself
213 so that the Simulation knows to put the change *of the string*
214 into the VCD file:
215
216 ```
217 289 # show current operation operation
218 290 if direction:
219 291 msg.str = f'{data}>>{shift}'
220 292 else:
221 293 msg.str = f'{data}<<{shift}'
222 294 # force dump of the above message by toggling the
223 295 # underlying signal
224 296 yield msg.eq(0)
225 297 yield msg.eq(1)
226 ```
227
228 Also very important is to explicitly add the debug Signal to the
229 gtkwave list of Signals to watch for. As the debug Signal is at
230 the top-level, without them being explicitly added they will be
231 *removed* from the vcd file.
232
233 ```
234 352 sim.add_sync_process(producer)
235 353 sim.add_sync_process(consumer)
236 354 sim_writer = sim.write_vcd(
237 355 "test_shifter.vcd",
238 356 # include additional signals in the trace dump
239 357 traces=[zero, interesting, test_case, msg],
240 358 )
241 359 with sim_writer:
242 360 sim.run()
243 ```
244
245 Here is an [actual practical use](https://git.libre-soc.org/?p=soc.git;a=commitdiff;h=ca3d417a6946dfde083a7c34d76c7572d4132be0)
246 where a "debug_status" message has been added (and toggled) to
247 show the different phases as a unit test progresses. This
248 unit test (MMU Virtual Memory Page-Table fault, and PTE insertion into
249 the I-Cache) is particularly complex and so is a three-stage process
250 that needed some context in order to see which phase of the test
251 is underway.
252
253 [[!img 2021-12-09_20-21.png size="800x" ]]