1use crate::id_lists::IdInternal;
2use crate::SimulatorData;
3use std::num::{NonZeroU16, NonZeroU8};
4
5#[derive(Debug, Clone, Copy)]
6enum TimescaleUnit {
7 Seconds,
8 Milliseconds,
9 Microseconds,
10 Nanoseconds,
11 Picoseconds,
12}
13
14impl std::fmt::Display for TimescaleUnit {
15 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16 let s = match self {
17 TimescaleUnit::Seconds => "s",
18 TimescaleUnit::Milliseconds => "ms",
19 TimescaleUnit::Microseconds => "us",
20 TimescaleUnit::Nanoseconds => "ns",
21 TimescaleUnit::Picoseconds => "ps",
22 };
23
24 f.write_str(s)
25 }
26}
27
28#[derive(Debug, Clone, Copy)]
30pub struct Timescale {
31 unit: TimescaleUnit,
32 value: NonZeroU16,
33}
34
35impl std::default::Default for Timescale {
36 fn default() -> Self {
37 Self {
38 unit: TimescaleUnit::Nanoseconds,
39 value: NonZeroU16::MIN,
40 }
41 }
42}
43
44impl std::fmt::Display for Timescale {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 write!(f, "{}{}", self.value, self.unit)
47 }
48}
49
50#[allow(missing_docs)]
51impl Timescale {
52 pub const fn seconds(seconds: NonZeroU16) -> Self {
53 Self {
54 unit: TimescaleUnit::Seconds,
55 value: seconds,
56 }
57 }
58
59 pub const fn milliseconds(milliseconds: NonZeroU16) -> Self {
60 Self {
61 unit: TimescaleUnit::Milliseconds,
62 value: milliseconds,
63 }
64 }
65
66 pub const fn microseconds(microseconds: NonZeroU16) -> Self {
67 Self {
68 unit: TimescaleUnit::Microseconds,
69 value: microseconds,
70 }
71 }
72
73 pub const fn nanoseconds(nanoseconds: NonZeroU16) -> Self {
74 Self {
75 unit: TimescaleUnit::Nanoseconds,
76 value: nanoseconds,
77 }
78 }
79
80 pub const fn picoseconds(picoseconds: NonZeroU16) -> Self {
81 Self {
82 unit: TimescaleUnit::Picoseconds,
83 value: picoseconds,
84 }
85 }
86}
87
88pub(crate) fn write_vcd_header<VCD: std::io::Write>(
89 data: &SimulatorData,
90 vcd: &mut VCD,
91 timescale: Timescale,
92) -> std::io::Result<()> {
93 use cow_utils::CowUtils;
94
95 const VERSION: &str = env!("CARGO_PKG_VERSION");
96 let now = chrono::Local::now().format("%A, %B %e %Y, %X");
97 writeln!(vcd, "$version Gsim {VERSION} $end")?;
98 writeln!(vcd, "$date {now} $end")?;
99 writeln!(vcd, "$timescale {timescale} $end")?;
100 writeln!(vcd, "$scope module SIM $end")?;
101 for (&wire_id, wire_name) in &data.wire_names {
102 let wire_name = wire_name.cow_replace(char::is_whitespace, "_");
103 let wire_width = data.get_wire_width(wire_id).unwrap();
104 let ident = wire_id.to_u32();
105 writeln!(vcd, " $var wire {wire_width} W{ident} {wire_name} $end")?;
106 }
107 writeln!(vcd, "$upscope $end")?;
108 writeln!(vcd, "$enddefinitions $end")?;
109
110 Ok(())
111}
112
113pub(crate) fn trace_vcd<VCD: std::io::Write>(
114 data: &SimulatorData,
115 vcd: &mut VCD,
116 time: u64,
117) -> std::io::Result<()> {
118 writeln!(vcd, "#{time}")?;
119 for &wire_id in data.wire_names.keys() {
120 let wire_width = data.get_wire_width(wire_id).unwrap();
121 let wire_state = data.get_wire_state(wire_id).unwrap();
122 let ident = wire_id.to_u32();
123 if wire_width > NonZeroU8::MIN {
124 writeln!(vcd, "b{} W{ident}", wire_state.display_string(wire_width))?;
125 } else {
126 writeln!(vcd, "{}W{ident}", wire_state.get_bit_state(0))?;
127 }
128 }
129
130 Ok(())
131}