1use crate::docs::vcd2svg::display_metrics::DisplayMetrics;
2use crate::docs::vcd2svg::symbols;
3use crate::docs::vcd2svg::text_frame::TextFrame;
4use crate::docs::vcd2svg::timed_value::{changes, SignalType, TimedValue};
5use crate::docs::vcd2svg::utils::{time_label, value_to_bigint, value_to_bool};
6use num_bigint::BigInt;
7use std::clone::Clone;
8use std::collections::HashMap;
9use std::fmt::Debug;
10use std::fs::File;
11use std::iter::Iterator;
12use std::string::ToString;
13use svg::Document;
14use vcd::IdCode;
15
16type StringTrace = Vec<TimedValue<String>>;
17type VectorTrace = Vec<TimedValue<BigInt>>;
18type BinaryTrace = Vec<TimedValue<bool>>;
19
20#[derive(Clone, PartialEq, Eq)]
22enum WaveformElement {
23 Low,
24 High,
25 Value(String),
26 Transition,
27 RisingEdge,
28 FallingEdge,
29 Invalid,
30 LowDensity,
31 MediumDensity,
32 HighDensity,
33}
34
35impl WaveformElement {
36 pub fn to_symbols(&self) -> (char, char, char) {
37 match self {
38 WaveformElement::Low => (symbols::BLANK, symbols::BLANK, symbols::HORIZONTAL),
39 WaveformElement::High => (symbols::HORIZONTAL, symbols::BLANK, symbols::BLANK),
40 WaveformElement::Value(_) => (symbols::HORIZONTAL, symbols::BLANK, symbols::HORIZONTAL),
41 WaveformElement::RisingEdge => {
42 (symbols::TOP_LEFT, symbols::VERTICAL, symbols::BOTTOM_RIGHT)
43 }
44 WaveformElement::FallingEdge => {
45 (symbols::TOP_RIGHT, symbols::VERTICAL, symbols::BOTTOM_LEFT)
46 }
47 WaveformElement::Transition => (
48 symbols::HORIZONTAL_DOWN,
49 symbols::VERTICAL,
50 symbols::HORIZONTAL_UP,
51 ),
52 WaveformElement::Invalid => (symbols::FULL_LOWER, symbols::FULL, symbols::FULL_UPPER),
53 WaveformElement::LowDensity => {
54 (symbols::LIGHT_LOWER, symbols::LIGHT, symbols::LIGHT_UPPER)
55 }
56 WaveformElement::MediumDensity => (
57 symbols::MEDIUM_LOWER,
58 symbols::MEDIUM,
59 symbols::MEDIUM_UPPER,
60 ),
61 WaveformElement::HighDensity => {
62 (symbols::FULL_LOWER, symbols::FULL, symbols::FULL_UPPER)
63 }
64 }
65 }
66}
67
68#[derive(Clone, Default, Debug)]
69struct SignalBin<T: SignalType> {
70 start_time: u64,
71 end_time: u64,
72 values: Vec<TimedValue<T>>,
73 before: Option<T>,
74 after: Option<T>,
75 changes: usize,
76}
77
78fn bin_edges(first_time: u64, last_time: u64, num_bins: usize) -> Vec<(u64, u64)> {
79 let delta = (last_time - first_time) as f64 / (num_bins as f64);
80 (0..num_bins)
81 .map(|x| {
82 (
83 (first_time + ((x as f64) * delta) as u64),
84 (first_time + ((x as f64 + 1.0) * delta) as u64),
85 )
86 })
87 .collect()
88}
89
90fn bin_trace<T: SignalType>(trace: &Vec<TimedValue<T>>, timing: &BinTimes) -> Vec<SignalBin<T>> {
91 let num_bins = timing.count();
92 let mut bins: Vec<SignalBin<T>> = vec![Default::default(); num_bins];
93 trace.iter().for_each(|val| {
94 if let Some(bin) = timing.to_bin(val.time) {
95 bins[bin].values.push(val.clone());
96 }
97 });
98 let mut signal_value = None;
100 for ndx in 0..num_bins {
101 let mut bin = &mut bins[ndx];
102 (bin.start_time, bin.end_time) = timing.bracket(ndx);
103 bin.before = signal_value.clone();
104 signal_value = if let Some(x) = bin.values.last() {
105 Some(x.value.clone())
106 } else {
107 signal_value
108 };
109 bin.after = signal_value.clone();
110 bin.changes = bin.values.len();
111 }
112 bins
113}
114
115pub struct TraceCollection {
116 pub signal_names: Vec<(IdCode, String)>,
117 pub string_valued: HashMap<IdCode, StringTrace>,
118 pub vector_valued: HashMap<IdCode, VectorTrace>,
119 pub scalar_valued: HashMap<IdCode, BinaryTrace>,
120}
121
122impl TraceCollection {
123 pub fn parse(signals: &[&str], mut file: File) -> anyhow::Result<Self> {
124 let mut parser = vcd::Parser::new(&mut file);
125 let header = parser.parse_header()?;
126 let mut string_valued = HashMap::new();
127 let mut vector_valued = HashMap::new();
128 let mut scalar_valued = HashMap::new();
129 let mut signal_names = Vec::new();
130 for signal in signals {
131 let path = signal.split(".").collect::<Vec<_>>();
132 let sig = header
133 .find_var(&path)
134 .ok_or_else(|| anyhow::Error::msg(format!("cannot resolve signal {}", signal)))?;
135 if sig.size == 0 {
136 string_valued.insert(sig.code, StringTrace::new());
137 } else if sig.size == 1 {
138 scalar_valued.insert(sig.code, BinaryTrace::new());
139 } else {
140 vector_valued.insert(sig.code, VectorTrace::new());
141 }
142 signal_names.push((sig.code, signal.to_string()));
143 }
144 let mut timestamp = 0_u64;
145 for command_result in parser {
146 let command = command_result?;
147 match command {
148 vcd::Command::Timestamp(x) => {
149 timestamp = x;
150 }
151 vcd::Command::ChangeScalar(i, v) => {
152 if let Some(s) = scalar_valued.get_mut(&i) {
153 s.push(TimedValue {
154 time: timestamp,
155 value: value_to_bool(&v)?,
156 })
157 }
158 }
159 vcd::Command::ChangeVector(i, v) => {
160 if let Some(s) = vector_valued.get_mut(&i) {
161 s.push(TimedValue {
162 time: timestamp,
163 value: value_to_bigint(&v)?,
164 })
165 }
166 }
167 vcd::Command::ChangeString(i, v) => {
168 if let Some(s) = string_valued.get_mut(&i) {
169 s.push(TimedValue {
170 time: timestamp,
171 value: v.clone(),
172 })
173 }
174 }
175 _ => {}
176 }
177 }
178 Ok(Self {
179 signal_names,
180 string_valued,
181 vector_valued,
182 scalar_valued,
183 })
184 }
185
186 pub fn as_svg(&self, metrics: &DisplayMetrics) -> anyhow::Result<Document> {
187 let document = Document::new()
188 .set(
189 "viewBox",
190 (0, 0, metrics.canvas_width, metrics.canvas_height),
191 )
192 .add(metrics.background_rect());
193
194 let mut document = document
196 .add(metrics.signal_rect())
197 .add(metrics.timescale_header_rect())
198 .add(metrics.timescale_midline());
199
200 document = metrics.timescale(document);
201
202 for (index, details) in self.signal_names.iter().enumerate() {
203 document = document
204 .add(metrics.signal_label(index, &details.1))
205 .add(metrics.signal_line(index));
206 document = metrics.horiz_grid_line(index, document);
207 if let Some(s) = self.scalar_valued.get(&details.0) {
208 document = document.add(metrics.bit_signal_plot(index, s));
209 } else if let Some(s) = self.vector_valued.get(&details.0) {
210 document = metrics.vector_signal_plot(index, s, document);
211 } else if let Some(s) = self.string_valued.get(&details.0) {
212 document = metrics.vector_signal_plot(index, s, document);
213 } else {
214 anyhow::bail!("Unable to find signal {} in the trace...", details.1)
215 }
216 }
217 Ok(document)
218 }
219 pub fn as_string(
220 &self,
221 first_time: u64,
222 last_time: u64,
223 max_columns: usize,
224 ) -> anyhow::Result<String> {
225 let mut frame = TextFrame::new(max_columns);
226 let sig_columns = self
227 .signal_names
228 .iter()
229 .map(|(_, x)| x.len())
230 .max()
231 .unwrap_or(0)
232 .max(4);
233 let num_bins = max_columns - sig_columns - 1;
234 let timing = BinTimes::new(first_time, last_time, num_bins);
235 draw_symbols(
236 &render_multibit(&clock_trace(&timing)),
237 &mut frame,
238 0,
239 sig_columns + 1,
240 );
241 frame.write(1, sig_columns - 4, "time");
242 for (index, details) in self.signal_names.iter().enumerate() {
243 let index = index + 1;
244 frame.write(index * 3 + 1, sig_columns - details.1.len(), &details.1);
245 if let Some(s) = self.scalar_valued.get(&details.0) {
246 let bins = bin_trace(&changes(s), &timing);
247 draw_symbols(&render_bool(&bins), &mut frame, index * 3, sig_columns + 1);
248 } else if let Some(s) = self.vector_valued.get(&details.0) {
249 let bins = bin_trace(&changes(s), &timing);
250 draw_symbols(
251 &render_multibit(&bins),
252 &mut frame,
253 index * 3,
254 sig_columns + 1,
255 );
256 } else if let Some(s) = self.string_valued.get(&details.0) {
257 let bins = bin_trace(&changes(s), &timing);
258 draw_symbols(
259 &render_multibit(&bins),
260 &mut frame,
261 index * 3,
262 sig_columns + 1,
263 );
264 } else {
265 anyhow::bail!("Unable to find signal {} in the trace...", details.1)
266 }
267 }
268 Ok(frame.to_string())
269 }
270}
271
272struct BinTimes {
273 first_time: u64,
274 last_time: u64,
275 num_bins: usize,
276 delta: f64,
277}
278
279impl BinTimes {
280 fn new(first_time: u64, last_time: u64, num_bins: usize) -> Self {
281 Self {
282 first_time,
283 last_time,
284 num_bins,
285 delta: (last_time - first_time) as f64 / (num_bins as f64),
286 }
287 }
288 fn bracket(&self, ndx: usize) -> (u64, u64) {
289 (
290 (self.first_time as f64 + self.delta * (ndx as f64)).floor() as u64,
291 (self.first_time as f64 + self.delta * (ndx as f64 + 1.0)).floor() as u64,
292 )
293 }
294 fn to_bin(&self, time: u64) -> Option<usize> {
295 let bin = ((time as f64 - self.first_time as f64) / self.delta).floor() as i32;
296 if bin >= 0 && bin < self.num_bins as i32 {
297 Some(bin as usize)
298 } else {
299 None
300 }
301 }
302 fn count(&self) -> usize {
303 self.num_bins
304 }
305 fn first(&self) -> u64 {
306 self.first_time
307 }
308 fn last(&self) -> u64 {
309 self.last_time
310 }
311 fn span(&self) -> u64 {
312 self.last_time - self.first_time
313 }
314}
315
316#[test]
317fn test_bin_times() {
318 let times = BinTimes::new(0, 250, 80);
319 (0..=times.count()).for_each(|x| println!("{:?}", times.bracket(x)));
320}
321
322fn clock_trace(timing: &BinTimes) -> Vec<SignalBin<String>> {
323 let major_tick_delta = compute_major_tick_delta_t(timing.span());
324 let first_index = (timing.first() as f64 / major_tick_delta as f64).floor() as u64;
325 let last_index = (timing.last() as f64 / major_tick_delta as f64).ceil() as u64;
326 let clock = (first_index..=last_index)
327 .map(|ndx| ndx * major_tick_delta)
328 .map(|x| TimedValue {
329 time: x,
330 value: time_label(x),
331 })
332 .collect::<Vec<_>>();
333 bin_trace(&changes(&clock), timing)
334}
335
336fn compute_major_tick_delta_t(span: u64) -> u64 {
337 let delta_t = span as f64;
338 let s = delta_t.log10() - 1.0;
339 let x = s.floor();
340 let e = s - x;
341 let d0 = (e - 0.0).abs();
342 let d1 = (e - 2.0_f64.log10()).abs();
343 let d2 = (e - 5.0_f64.log10()).abs();
344 let value = if d0 <= d1 && d0 <= d2 {
345 (10.0_f64.powf(x)) as u64
346 } else if d1 <= d0 && d1 <= d2 {
347 (2.0_f64 * 10.0_f64.powf(x)) as u64
348 } else {
349 (5.0_f64 * 10.0_f64.powf(x)) as u64
350 };
351 value
352}
353
354fn render_multibit<T: SignalType>(bins: &[SignalBin<T>]) -> Vec<WaveformElement> {
355 bins.iter()
356 .map(|bin| {
357 if bin.after.is_none() {
358 WaveformElement::Invalid
359 } else if bin.changes == 0 || (bin.changes == 1 && bin.before.is_none()) {
360 if let Some(b) = bin.before.as_ref() {
361 WaveformElement::Value(b.render())
362 } else {
363 WaveformElement::Value("?".to_string())
364 }
365 } else if bin.changes == 1 {
366 WaveformElement::Transition
367 } else if bin.changes <= 3 {
368 WaveformElement::LowDensity
369 } else if bin.changes <= 10 {
370 WaveformElement::MediumDensity
371 } else {
372 WaveformElement::HighDensity
373 }
374 })
375 .collect()
376}
377
378fn render_bool(bins: &[SignalBin<bool>]) -> Vec<WaveformElement> {
379 bins.iter()
380 .map(|bin| {
381 if bin.after.is_none() {
382 WaveformElement::Invalid
383 } else if bin.changes == 0 || (bin.changes == 1 && bin.before.is_none()) {
384 if !bin.after.unwrap() {
385 WaveformElement::Low
386 } else {
387 WaveformElement::High
388 }
389 } else if bin.changes == 1 {
390 if let Some(x) = bin.before {
391 if !x {
392 WaveformElement::RisingEdge
393 } else {
394 WaveformElement::FallingEdge
395 }
396 } else {
397 WaveformElement::Invalid
398 }
399 } else if bin.changes <= 3 {
400 WaveformElement::LowDensity
401 } else if bin.changes <= 10 {
402 WaveformElement::MediumDensity
403 } else {
404 WaveformElement::HighDensity
405 }
406 })
407 .collect()
408}
409
410fn draw_symbols(symbols: &[WaveformElement], frame: &mut TextFrame, row: usize, start_col: usize) {
411 symbols.iter().enumerate().for_each(|(ndx, element)| {
412 let symbol = element.to_symbols();
413 frame.put(row, start_col + ndx, symbol.0);
414 frame.put(row + 1, start_col + ndx, symbol.1);
415 frame.put(row + 2, start_col + ndx, symbol.2);
416 });
417 let mut elmts = symbols[1..].iter().enumerate();
419 loop {
420 let mut free_space = 0;
421 let mut value = "";
422 let mut elmt = elmts.next();
423 let offset = if let Some((i, _)) = elmt { i } else { break };
424
425 while let Some((_, WaveformElement::Value(v))) = elmt {
426 free_space += 1;
427 value = v;
428 elmt = elmts.next();
429 }
430
431 for (i, c) in value.chars().enumerate() {
432 if i >= free_space {
433 break;
434 }
435
436 let r = &c.to_string();
437 let symbol = if i >= free_space - 1 && i + 1 < value.len() {
438 "…"
439 } else {
440 r
441 };
442
443 frame.write(row + 1, start_col + (offset + i + 1), symbol);
444 }
445 }
446}