Skip to content

Commit 2fe17a0

Browse files
committed
Add RFC for structured VCD output
1 parent 2979ebf commit 2fe17a0

File tree

1 file changed

+212
-0
lines changed

1 file changed

+212
-0
lines changed

text/0000-structured-vcd.md

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
- Start Date: (fill in with date at which the RFC is merged, YYYY-MM-DD)
2+
- RFC PR: [amaranth-lang/rfcs#0000](https://github.com/amaranth-lang/rfcs/pull/0000)
3+
- Amaranth Issue: [amaranth-lang/amaranth#0000](https://github.com/amaranth-lang/amaranth/issues/0000)
4+
5+
# Structured VCD Output
6+
7+
## Summary
8+
[summary]: #summary
9+
10+
When generating VCD files, use the `scope` keyword to distinguish aggregate signals.
11+
12+
## Motivation
13+
[motivation]: #motivation
14+
15+
These changes are intended to make it easier for waveform viewers to recover information about aggregate signals from VCD files.
16+
This is partially motivated by an issue with VCD parsing that occurs in Surfer (see [ekiwi/wellen#36](https://github.com/ekiwi/wellen/issues/36)).
17+
Ideally, this leads to a better user experience when inspecting simulated Amaranth designs with a waveform viewer.
18+
19+
## Guide-level explanation
20+
[guide-level-explanation]: #guide-level-explanation
21+
22+
This RFC describes changes to the way Amaranth generates a VCD file.
23+
When reading a VCD file, waveform viewers like [Surfer](https://surfer-project.org/) and [GTKWave](https://gtkwave.sourceforge.net/) support use of the `scope` keyword for organizing variables into groups.
24+
Currently, when writing a VCD file, Amaranth uses the `module` scope to distinguish between signals belonging to different modules.
25+
For instance:
26+
27+
```
28+
$scope module top $end # top
29+
$var wire 1 <id> clk $end # top.clk
30+
$var wire 1 <id> rst $end # top.rst
31+
$var wire 32 <id> in $end # top.in
32+
$var wire 32 <id> out $end # top.out
33+
$scope module submodule $end # top.submodule
34+
$var wire 1 <id> clk $end # top.submodule.clk
35+
$var wire 1 <id> rst $end # top.submodule.rst
36+
$var wire 5 <id> in $end # top.submodule.in
37+
$var wire 5 <id> out $end # top.submodule.out
38+
$upscope $end
39+
$upscope $end
40+
```
41+
42+
However, it does *not* attempt to use scopes for organizing the members of aggregate signals with array-like or struct-like datatypes.
43+
Instead, when creating VCD variables for aggregate signals, members are distinguished only by appending to the name of the parent signal.
44+
For instance, a signal `top.submodule.my_array` with the type `ArrayLayout(unsigned(32), 4)` is currently represented as:
45+
46+
```
47+
$scope module top $end # top
48+
...
49+
$scope module submodule $end # top.submodule
50+
...
51+
$var wire 128 <id> my_array $end # top.submodule.my_array
52+
$var wire 32 <id> my_array[0] $end # top.submodule.my_array[0]
53+
$var wire 32 <id> my_array[1] $end # top.submodule.my_array[1]
54+
$var wire 32 <id> my_array[2] $end # top.submodule.my_array[2]
55+
$var wire 32 <id> my_array[3] $end # top.submodule.my_array[3]
56+
$upscope $end
57+
$upscope $end
58+
```
59+
60+
This is not sufficient for explicitly conveying the relationship between aggregate signals and their members to a waveform viewer.
61+
In this case, `my_array` does not explicitly *contain* its members, and waveform viewers may only *infer* this relationship by attempting to recover it from the names.
62+
63+
This RFC proposes the use of `scope vhdl_array` and `scope vhdl_record` to pass information about these relationships to a waveform viewer.
64+
After this change, the VCD output above would become:
65+
66+
```
67+
$scope module top $end # top
68+
...
69+
$scope module submodule $end # top.submodule
70+
...
71+
$comment Flattened representation of 'my_array' $end
72+
$var wire 128 <id> my_array $end # top.submodule.my_array
73+
74+
$comment Hierarchical representation of 'my_array' members $end
75+
$scope vhdl_array my_array $end # top.submodule.my_array
76+
$var wire 32 <id> 0 $end # top.submodule.my_array.0
77+
$var wire 32 <id> 1 $end # top.submodule.my_array.1
78+
$var wire 32 <id> 2 $end # top.submodule.my_array.2
79+
$var wire 32 <id> 3 $end # top.submodule.my_array.3
80+
$upscope $end
81+
82+
$upscope $end
83+
$upscope $end
84+
```
85+
86+
## Reference-level explanation
87+
[reference-level-explanation]: #reference-level-explanation
88+
89+
Currently, Amaranth represents aggregate signals in the VCD by appending the names of elements/members to the name of the signal.
90+
When simulator output is written to a VCD file, we intend that signals with aggregate datatypes are explicitly given their own scope and split into multiple VCD variables.
91+
We propose the following conventions:
92+
93+
- The `module` scope defines a group of signals belonging to the same `Module`
94+
- The `vhdl_array` scope defines a group of signals belonging to an array (such as a signal with `ArrayLayout`)
95+
- The `vhdl_record` scope defines a structured group of signals (such as a signal with `StructLayout`)
96+
97+
> **NOTE**: (Implementation goes here)
98+
> ...
99+
100+
101+
### Expected Output: Array-like
102+
103+
```python
104+
from amaranth import *
105+
from amaranth.lib.data import *
106+
107+
foo = Signal(ArrayLayout(unsigned(32), 4))
108+
```
109+
110+
For `ArrayLayout`, each element in the array should become a VCD variable whose name is integer array index.
111+
In this case, the signal `foo` is split into `foo.0`, `foo.1`, `foo.2`, and `foo.3`:
112+
113+
```
114+
$scope vhdl_array foo $end
115+
$var wire 32 <id> 0 $end
116+
$var wire 32 <id> 1 $end
117+
$var wire 32 <id> 2 $end
118+
$var wire 32 <id> 3 $end
119+
$upscope $end
120+
```
121+
122+
### Expected Output: Struct-like
123+
124+
```python
125+
from amaranth import *
126+
from amaranth.lib.data import *
127+
128+
foo = Signal(StructLayout({
129+
"a": unsigned(1),
130+
"b": unsigned(4),
131+
"c": unsigned(32),
132+
}))
133+
```
134+
135+
For `StructLayout`, each member should become a VCD variable with the member name.
136+
In this case, the signal `bar` is split into `bar.a`, `bar.b`, and `bar.c`:
137+
138+
```
139+
$scope vhdl_record bar $end
140+
$var wire 1 id a $end
141+
$var wire 4 id b $end
142+
$var wire 32 id c $end
143+
$upscope $end
144+
```
145+
146+
### Expected Output: Nested Aggregates
147+
148+
```python
149+
from amaranth import *
150+
from amaranth.lib.data import *
151+
152+
class MyStruct(Struct):
153+
a: unsigned(1)
154+
b: unsigned(4)
155+
c: ArrayLayout(unsigned(32), 4),
156+
157+
# A struct-like signal
158+
foo = Signal(MyStruct)
159+
160+
```
161+
162+
For signals with nested aggregate types, the scopes are nested in the same way that `scope module` is already used for nested modules.
163+
In this case, the signal `foo` is split like this:
164+
165+
```
166+
$scope vhdl_record bar $end
167+
$var wire 1 id a $end
168+
$var wire 4 id b $end
169+
$scope vhdl_array c $end
170+
$var wire 32 id 0 $end
171+
$var wire 32 id 1 $end
172+
$var wire 32 id 2 $end
173+
$var wire 32 id 3 $end
174+
$upscope $end
175+
$upscope $end
176+
```
177+
178+
## Drawbacks
179+
[drawbacks]: #drawbacks
180+
181+
- VCD is not a particularly efficient format, and this adds even more bytes to generated VCD files.
182+
183+
- This breaks any existing compatibility with waveform viewers that do not handle the `vhdl_record` and `vhdl_array` scope types.
184+
Since these are not part of the VCD specification, some waveform viewers may fail to handle this.
185+
186+
## Rationale and alternatives
187+
[rationale-and-alternatives]: #rationale-and-alternatives
188+
189+
- The choice of `scope vhdl_array` and `scope vhdl_record` are suggested due to known compatibility with Surfer and GTKWave.
190+
However, note that these are not part of the VCD specification (which is somewhat old and under-defined).
191+
192+
- One alternative would be to include support for a different waveform format that has better-defined support for variables with composite datatypes.
193+
194+
## Prior art
195+
[prior-art]: #prior-art
196+
197+
- As far as I can tell, use of the `vhdl_record` and `vhdl_array` scope types comes from the [ghdl/ghdl](https://github.com/ghdl/ghdl) simulator.
198+
- [verilator/verilator](https://github.com/verilator/verilator) makes no attempt to use these scope types
199+
- [steveicarus/iverilog](https://github.com/steveicarus/iverilog) makes no attempt to use these scopes types
200+
201+
## Unresolved questions
202+
[unresolved-questions]: #unresolved-questions
203+
204+
- Should we continue to include the "flattened" [pure bit-vector] representation of aggregate signals in the VCD?
205+
- Does this feature need to be gated/opt-in by default?
206+
207+
## Future possibilities
208+
[future-possibilities]: #future-possibilities
209+
210+
Since this adds more overhead to VCD output, it's worth mentioning that VCD may be unsuitable for testing very large designs.
211+
This RFC may be a stepping stone to considering support for alternative waveform formats in the future.
212+

0 commit comments

Comments
 (0)