1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
// Copyright (c) 2017-2021 Fabian Schuiki
//! A Value Change Dump tracer.
use crate::{
state::{Scope, SignalRef, State},
tracer::Tracer,
value::Value,
};
use num::{traits::Pow, BigInt, BigRational, FromPrimitive};
use std::{
cell::RefCell,
collections::{HashMap, HashSet},
};
/// A tracer that emits the simulation trace as VCD.
pub struct VcdTracer<T> {
writer: RefCell<T>,
abbrevs: HashMap<SignalRef, Vec<(String, String, usize)>>,
time: BigRational,
pending: HashMap<SignalRef, Value>,
precision: BigRational,
}
impl<T> VcdTracer<T>
where
T: std::io::Write,
{
/// Create a new VCD tracer which will write its VCD to `writer`.
pub fn new(writer: T) -> Self {
VcdTracer {
writer: RefCell::new(writer),
abbrevs: HashMap::new(),
time: num::zero(),
pending: HashMap::new(),
// Hard-code the precision to ps for now. Later on, we might want to
// make this configurable or automatically determined by the module.
precision: BigInt::from_usize(10).unwrap().pow(12usize).into(), // ps
}
}
/// Write the value of all signals that have changed since the last flush.
/// Clears the `pending` set.
fn flush(&mut self) {
let time = (&self.time * &self.precision).trunc();
write!(self.writer.borrow_mut(), "#{}\n", time).unwrap();
for (signal, value) in std::mem::replace(&mut self.pending, HashMap::new()) {
for &(ref abbrev, _, offset) in &self.abbrevs[&signal] {
self.flush_signal(signal, offset, &value, abbrev);
}
}
}
/// Write the value of a signal. Called at the beginning of the simulation
/// to perform a variable dump, and during flush once for each signal that
/// changed.
fn flush_signal(&self, signal: SignalRef, offset: usize, value: &Value, abbrev: &str) {
match value {
Value::Void => (),
Value::Int(v) => {
assert_eq!(offset, 0);
write!(self.writer.borrow_mut(), "b{:b} {}\n", v.value, abbrev).unwrap();
}
Value::Time(_) => (),
Value::Array(v) => {
let elems = &v.0;
self.flush_signal(
signal,
offset / elems.len(),
&elems[offset % elems.len()],
abbrev,
);
}
Value::Struct(v) => {
let fields = &v.0;
self.flush_signal(
signal,
offset / fields.len(),
&fields[offset % fields.len()],
abbrev,
);
}
// _ => panic!(
// "flush non-const/non-aggregate signal {:?} with value {:?}",
// signal, value
// ),
};
}
/// Allocate short names and emit `$scope` statement.
fn prepare_scope(&mut self, state: &State, scope: &Scope, index: &mut usize) {
write!(
self.writer.borrow_mut(),
"$scope module {} $end\n",
scope.name.replace('.', "_")
)
.unwrap();
let mut probed_signals: Vec<_> = scope.probes.keys().cloned().collect();
probed_signals.sort();
for sigref in probed_signals {
for name in &scope.probes[&sigref] {
self.prepare_signal(
state,
sigref,
state[sigref].ty().unwrap_signal(),
name,
index,
0,
1,
);
}
}
for subscope in scope.subscopes.iter() {
self.prepare_scope(state, subscope, index);
}
write!(self.writer.borrow_mut(), "$upscope $end\n").unwrap();
}
/// Expand signals and allocate short names.
fn prepare_signal(
&mut self,
state: &State,
sigref: SignalRef,
ty: &llhd::Type,
name: &str,
index: &mut usize,
offset: usize,
stride: usize,
) {
match **ty {
llhd::IntType(width) => {
// Allocate short name for the probed signal.
let mut idx = *index;
let mut abbrev = String::new();
loop {
abbrev.push((33 + idx % 94) as u8 as char);
idx /= 94;
if idx == 0 {
break;
}
}
*index += 1;
// Write the abbreviations for this signal.
let abbrevs_for_signal = self.abbrevs.entry(sigref).or_insert_with(Vec::new);
write!(
self.writer.borrow_mut(),
"$var wire {} {} {} $end\n",
width,
abbrev,
name
)
.unwrap();
abbrevs_for_signal.push((abbrev, name.to_owned(), offset));
}
llhd::ArrayType(width, ref subty) => {
for i in 0..width {
self.prepare_signal(
state,
sigref,
subty,
&format!("{}[{}]", name, i),
index,
offset + i * stride,
stride * width,
);
}
}
llhd::StructType(ref fields) => {
for (i, subty) in fields.iter().enumerate() {
self.prepare_signal(
state,
sigref,
subty,
&format!("{}.{}", name, i),
index,
offset + i * stride,
stride * fields.len(),
);
}
}
// _ => (),
ref x => panic!("signal of type {} not supported in VCD", x),
}
}
}
impl<T> Tracer for VcdTracer<T>
where
T: std::io::Write,
{
fn init(&mut self, state: &State) {
// Dump the VCD header.
write!(
self.writer.borrow_mut(),
"$version\nllhd-sim {}\n$end\n",
clap::crate_version!()
)
.unwrap();
write!(self.writer.borrow_mut(), "$timescale 1ps $end\n").unwrap();
self.prepare_scope(state, &state.scope, &mut 0);
write!(self.writer.borrow_mut(), "$enddefinitions $end\n").unwrap();
// Dump the variables.
write!(self.writer.borrow_mut(), "$dumpvars\n").unwrap();
for &signal in state.probes.keys() {
if let Some(abbrevs) = self.abbrevs.get(&signal) {
for &(ref abbrev, _, offset) in abbrevs {
self.flush_signal(signal, offset, state[signal].value(), abbrev);
}
}
}
write!(self.writer.borrow_mut(), "$end\n").unwrap();
}
fn step(&mut self, state: &State, changed: &HashSet<SignalRef>) {
// If the physical time in seconds of the simulation changed, flush the
// aggregated pending changes and update the time.
if self.time != *state.time.time() {
self.flush();
self.time = state.time.time().clone();
}
// Mark the changed signals for consideration during the next flush.
self.pending.extend(
changed
.iter()
.map(|&signal| (signal, state[signal].value().clone())),
);
}
fn finish(&mut self, _: &State) {
self.flush();
}
}