properly handle duplicate names in vcd
All checks were successful
/ deps (pull_request) Successful in 14s
/ test (pull_request) Successful in 3m43s
/ deps (push) Successful in 15s
/ test (push) Successful in 4m8s

This commit is contained in:
Jacob Lifshay 2025-01-09 22:52:22 -08:00
parent 3771cea78e
commit e3a2ccd41c
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
7 changed files with 499 additions and 129 deletions

View file

@ -5,6 +5,7 @@ use crate::{
enum_::{Enum, EnumType}, enum_::{Enum, EnumType},
expr::Flow, expr::Flow,
int::UInt, int::UInt,
intern::{Intern, Interned},
sim::{ sim::{
time::{SimDuration, SimInstant}, time::{SimDuration, SimInstant},
TraceArray, TraceAsyncReset, TraceBool, TraceBundle, TraceClock, TraceDecl, TraceArray, TraceAsyncReset, TraceBool, TraceBundle, TraceClock, TraceDecl,
@ -15,12 +16,73 @@ use crate::{
}, },
}; };
use bitvec::{order::Lsb0, slice::BitSlice}; use bitvec::{order::Lsb0, slice::BitSlice};
use hashbrown::{hash_map::Entry, HashMap};
use std::{ use std::{
fmt, fmt::{self, Write as _},
io::{self, Write}, io, mem,
mem,
}; };
#[derive(Default)]
struct Scope {
last_inserted: HashMap<Interned<str>, usize>,
}
#[derive(Copy, Clone)]
struct VerilogIdentifier {
unescaped_name: Interned<str>,
}
impl VerilogIdentifier {
fn needs_escape(self) -> bool {
// we only allow ascii, so we can just check bytes
let Some((&first, rest)) = self.unescaped_name.as_bytes().split_first() else {
unreachable!("Scope::new_identifier guarantees a non-empty name");
};
if !first.is_ascii_alphabetic() && first != b'_' {
true
} else {
rest.iter()
.any(|&ch| !ch.is_ascii_alphanumeric() && ch != b'_' && ch != b'$')
}
}
}
impl fmt::Display for VerilogIdentifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.needs_escape() {
f.write_str("\\")?;
}
write!(f, "{}", Escaped(self.unescaped_name))
}
}
impl Scope {
fn new_identifier(&mut self, unescaped_name: Interned<str>) -> VerilogIdentifier {
let next_disambiguator = match self.last_inserted.entry(unescaped_name) {
Entry::Vacant(entry) => {
entry.insert(1);
return VerilogIdentifier { unescaped_name };
}
Entry::Occupied(entry) => entry.get() + 1,
};
let mut disambiguated_name = String::from(&*unescaped_name);
for disambiguator in next_disambiguator.. {
disambiguated_name.truncate(unescaped_name.len());
write!(disambiguated_name, "_{disambiguator}").expect("can't fail");
if let Entry::Vacant(entry) = self.last_inserted.entry((*disambiguated_name).intern()) {
let retval = VerilogIdentifier {
unescaped_name: *entry.key(),
};
entry.insert(1);
// speed up future searches
self.last_inserted.insert(unescaped_name, disambiguator);
return retval;
}
}
panic!("too many names");
}
}
pub struct VcdWriterDecls<W: io::Write + 'static> { pub struct VcdWriterDecls<W: io::Write + 'static> {
writer: W, writer: W,
timescale: SimDuration, timescale: SimDuration,
@ -97,14 +159,20 @@ impl<W: io::Write> fmt::Debug for VcdWriterDecls<W> {
} }
} }
/// pass in scope to ensure it's not available in child scope
fn write_vcd_scope<W: io::Write, R>( fn write_vcd_scope<W: io::Write, R>(
writer: &mut W, writer: &mut W,
scope_type: &str, scope_type: &str,
scope_name: &str, scope_name: Interned<str>,
f: impl FnOnce(&mut W) -> io::Result<R>, scope: &mut Scope,
f: impl FnOnce(&mut W, &mut Scope) -> io::Result<R>,
) -> io::Result<R> { ) -> io::Result<R> {
writeln!(writer, "$scope {scope_type} {scope_name} $end")?; writeln!(
let retval = f(writer)?; writer,
"$scope {scope_type} {} $end",
scope.new_identifier(scope_name),
)?;
let retval = f(writer, &mut Scope::default())?;
writeln!(writer, "$upscope $end")?; writeln!(writer, "$upscope $end")?;
Ok(retval) Ok(retval)
} }
@ -143,24 +211,28 @@ trait_arg! {
struct ArgModule<'a> { struct ArgModule<'a> {
properties: &'a mut VcdWriterProperties, properties: &'a mut VcdWriterProperties,
scope: &'a mut Scope,
} }
impl<'a> ArgModule<'a> { impl<'a> ArgModule<'a> {
fn reborrow(&mut self) -> ArgModule<'_> { fn reborrow(&mut self) -> ArgModule<'_> {
ArgModule { ArgModule {
properties: self.properties, properties: self.properties,
scope: self.scope,
} }
} }
} }
struct ArgModuleBody<'a> { struct ArgModuleBody<'a> {
properties: &'a mut VcdWriterProperties, properties: &'a mut VcdWriterProperties,
scope: &'a mut Scope,
} }
impl<'a> ArgModuleBody<'a> { impl<'a> ArgModuleBody<'a> {
fn reborrow(&mut self) -> ArgModuleBody<'_> { fn reborrow(&mut self) -> ArgModuleBody<'_> {
ArgModuleBody { ArgModuleBody {
properties: self.properties, properties: self.properties,
scope: self.scope,
} }
} }
} }
@ -170,6 +242,7 @@ struct ArgInType<'a> {
sink_var_type: &'static str, sink_var_type: &'static str,
duplex_var_type: &'static str, duplex_var_type: &'static str,
properties: &'a mut VcdWriterProperties, properties: &'a mut VcdWriterProperties,
scope: &'a mut Scope,
} }
impl<'a> ArgInType<'a> { impl<'a> ArgInType<'a> {
@ -179,6 +252,7 @@ impl<'a> ArgInType<'a> {
sink_var_type: self.sink_var_type, sink_var_type: self.sink_var_type,
duplex_var_type: self.duplex_var_type, duplex_var_type: self.duplex_var_type,
properties: self.properties, properties: self.properties,
scope: self.scope,
} }
} }
} }
@ -226,55 +300,42 @@ fn write_vcd_id<W: io::Write>(writer: &mut W, mut id: usize) -> io::Result<()> {
Ok(()) Ok(())
} }
fn write_escaped<W: io::Write>(writer: &mut W, value: impl fmt::Display) -> io::Result<()> { struct Escaped<T: fmt::Display>(T);
// escaping rules from function GTKWave uses to decode VCD strings:
// https://github.com/gtkwave/gtkwave/blob/491f24d7e8619cfc1fcc65704ee5c967d1083c18/lib/libfst/fstapi.c#L7090 impl<T: fmt::Display> fmt::Display for Escaped<T> {
struct Wrapper<W>(W); fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
impl<W: io::Write> io::Write for Wrapper<W> { // escaping rules from function GTKWave uses to decode VCD strings:
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { // https://github.com/gtkwave/gtkwave/blob/491f24d7e8619cfc1fcc65704ee5c967d1083c18/lib/libfst/fstapi.c#L7090
if buf.is_empty() { struct Wrapper<W>(W);
return self.0.write(buf); impl<W: fmt::Write> fmt::Write for Wrapper<W> {
} fn write_str(&mut self, s: &str) -> fmt::Result {
let mut retval = 0; for byte in s.bytes() {
for &byte in buf { match byte {
match byte { b'\\' | b'\'' | b'"' | b'?' => {
b'\\' | b'\'' | b'"' | b'?' => self.0.write_all(&[b'\\', byte])?, self.0.write_str("\\")?;
b'\n' => self.0.write_all(br"\n")?, self.0.write_char(byte as char)?;
b'\r' => self.0.write_all(br"\r")?, }
b'\t' => self.0.write_all(br"\t")?, b'\n' => self.0.write_str(r"\n")?,
0x7 => self.0.write_all(br"\a")?, b'\r' => self.0.write_str(r"\r")?,
0x8 => self.0.write_all(br"\b")?, b'\t' => self.0.write_str(r"\t")?,
0xC => self.0.write_all(br"\f")?, 0x7 => self.0.write_str(r"\a")?,
0xB => self.0.write_all(br"\v")?, 0x8 => self.0.write_str(r"\b")?,
_ => { 0xC => self.0.write_str(r"\f")?,
if byte.is_ascii_graphic() { 0xB => self.0.write_str(r"\v")?,
self.0.write_all(&[byte])?; _ => {
} else { if byte.is_ascii_graphic() {
write!(self.0, r"\x{byte:02x}")?; self.0.write_char(byte as char)?;
} else {
write!(self.0, r"\x{byte:02x}")?;
}
} }
} }
} }
retval += 1; Ok(())
} }
Ok(retval)
}
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
} }
write!(Wrapper(f), "{}", self.0)
} }
write!(Wrapper(writer), "{value}")
}
fn is_unescaped_verilog_identifier(ident: &str) -> bool {
// we only allow ascii, so we can just check bytes
let Some((&first, rest)) = ident.as_bytes().split_first() else {
return false; // empty string is not an identifier
};
(first.is_ascii_alphabetic() || first == b'_')
&& rest
.iter()
.all(|&ch| ch.is_ascii_alphanumeric() || ch == b'_' || ch == b'$')
} }
fn write_vcd_var<W: io::Write>( fn write_vcd_var<W: io::Write>(
@ -284,7 +345,7 @@ fn write_vcd_var<W: io::Write>(
var_type: &str, var_type: &str,
size: usize, size: usize,
location: TraceLocation, location: TraceLocation,
name: &str, name: VerilogIdentifier,
) -> io::Result<()> { ) -> io::Result<()> {
let id = match location { let id = match location {
TraceLocation::Scalar(id) => id.as_usize(), TraceLocation::Scalar(id) => id.as_usize(),
@ -319,12 +380,7 @@ fn write_vcd_var<W: io::Write>(
}; };
write!(writer, "$var {var_type} {size} ")?; write!(writer, "$var {var_type} {size} ")?;
write_vcd_id(writer, id)?; write_vcd_id(writer, id)?;
writer.write_all(b" ")?; writeln!(writer, " {name} $end")
if !is_unescaped_verilog_identifier(name) {
writer.write_all(b"\\")?;
}
write_escaped(writer, name)?;
writer.write_all(b" $end\n")
} }
impl WriteTrace for TraceUInt { impl WriteTrace for TraceUInt {
@ -334,6 +390,7 @@ impl WriteTrace for TraceUInt {
sink_var_type, sink_var_type,
duplex_var_type, duplex_var_type,
properties, properties,
scope,
} = arg.in_type(); } = arg.in_type();
let Self { let Self {
location, location,
@ -356,7 +413,7 @@ impl WriteTrace for TraceUInt {
var_type, var_type,
ty.width(), ty.width(),
location, location,
&name, scope.new_identifier(name),
) )
} }
} }
@ -421,6 +478,7 @@ impl WriteTrace for TraceEnumDiscriminant {
sink_var_type: _, sink_var_type: _,
duplex_var_type: _, duplex_var_type: _,
properties, properties,
scope,
} = arg.in_type(); } = arg.in_type();
let Self { let Self {
location, location,
@ -435,7 +493,7 @@ impl WriteTrace for TraceEnumDiscriminant {
"string", "string",
1, 1,
location, location,
&name, scope.new_identifier(name),
) )
} }
} }
@ -507,11 +565,11 @@ impl WriteTrace for TraceScope {
impl WriteTrace for TraceModule { impl WriteTrace for TraceModule {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgModule { properties } = arg.module(); let ArgModule { properties, scope } = arg.module();
let Self { name, children } = self; let Self { name, children } = self;
write_vcd_scope(writer, "module", &name, |writer| { write_vcd_scope(writer, "module", name, scope, |writer, scope| {
for child in children { for child in children {
child.write_trace(writer, ArgModuleBody { properties })?; child.write_trace(writer, ArgModuleBody { properties, scope })?;
} }
Ok(()) Ok(())
}) })
@ -520,7 +578,7 @@ impl WriteTrace for TraceModule {
impl WriteTrace for TraceInstance { impl WriteTrace for TraceInstance {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgModuleBody { properties } = arg.module_body(); let ArgModuleBody { properties, scope } = arg.module_body();
let Self { let Self {
name: _, name: _,
instance_io, instance_io,
@ -534,15 +592,16 @@ impl WriteTrace for TraceInstance {
sink_var_type: "wire", sink_var_type: "wire",
duplex_var_type: "wire", duplex_var_type: "wire",
properties, properties,
scope,
}, },
)?; )?;
module.write_trace(writer, ArgModule { properties }) module.write_trace(writer, ArgModule { properties, scope })
} }
} }
impl WriteTrace for TraceMem { impl WriteTrace for TraceMem {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgModuleBody { properties } = arg.module_body(); let ArgModuleBody { properties, scope } = arg.module_body();
let Self { let Self {
id, id,
name, name,
@ -551,27 +610,41 @@ impl WriteTrace for TraceMem {
ports, ports,
array_type, array_type,
} = self; } = self;
write_vcd_scope(writer, "struct", &*name, |writer| { write_vcd_scope(writer, "struct", name, scope, |writer, scope| {
write_vcd_scope(writer, "struct", "contents", |writer| { write_vcd_scope(
for element_index in 0..array_type.len() { writer,
write_vcd_scope(writer, "struct", &format!("[{element_index}]"), |writer| { "struct",
properties.memory_properties[id.as_usize()].element_index = element_index; "contents".intern(),
properties.memory_properties[id.as_usize()].element_part_index = 0; scope,
element_type.write_trace( |writer, scope| {
for element_index in 0..array_type.len() {
write_vcd_scope(
writer, writer,
ArgInType { "struct",
source_var_type: "reg", Intern::intern_owned(format!("[{element_index}]")),
sink_var_type: "reg", scope,
duplex_var_type: "reg", |writer, scope| {
properties, properties.memory_properties[id.as_usize()].element_index =
element_index;
properties.memory_properties[id.as_usize()].element_part_index = 0;
element_type.write_trace(
writer,
ArgInType {
source_var_type: "reg",
sink_var_type: "reg",
duplex_var_type: "reg",
properties,
scope,
},
)
}, },
) )?;
})?; }
} Ok(())
Ok(()) },
})?; )?;
for port in ports { for port in ports {
port.write_trace(writer, ArgModuleBody { properties })?; port.write_trace(writer, ArgModuleBody { properties, scope })?;
} }
Ok(()) Ok(())
}) })
@ -580,7 +653,7 @@ impl WriteTrace for TraceMem {
impl WriteTrace for TraceMemPort { impl WriteTrace for TraceMemPort {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgModuleBody { properties } = arg.module_body(); let ArgModuleBody { properties, scope } = arg.module_body();
let Self { let Self {
name: _, name: _,
bundle, bundle,
@ -593,6 +666,7 @@ impl WriteTrace for TraceMemPort {
sink_var_type: "wire", sink_var_type: "wire",
duplex_var_type: "wire", duplex_var_type: "wire",
properties, properties,
scope,
}, },
) )
} }
@ -600,7 +674,7 @@ impl WriteTrace for TraceMemPort {
impl WriteTrace for TraceWire { impl WriteTrace for TraceWire {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgModuleBody { properties } = arg.module_body(); let ArgModuleBody { properties, scope } = arg.module_body();
let Self { let Self {
name: _, name: _,
child, child,
@ -613,6 +687,7 @@ impl WriteTrace for TraceWire {
sink_var_type: "wire", sink_var_type: "wire",
duplex_var_type: "wire", duplex_var_type: "wire",
properties, properties,
scope,
}, },
) )
} }
@ -620,7 +695,7 @@ impl WriteTrace for TraceWire {
impl WriteTrace for TraceReg { impl WriteTrace for TraceReg {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgModuleBody { properties } = arg.module_body(); let ArgModuleBody { properties, scope } = arg.module_body();
let Self { let Self {
name: _, name: _,
child, child,
@ -633,6 +708,7 @@ impl WriteTrace for TraceReg {
sink_var_type: "reg", sink_var_type: "reg",
duplex_var_type: "reg", duplex_var_type: "reg",
properties, properties,
scope,
}, },
) )
} }
@ -640,7 +716,7 @@ impl WriteTrace for TraceReg {
impl WriteTrace for TraceModuleIO { impl WriteTrace for TraceModuleIO {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgModuleBody { properties } = arg.module_body(); let ArgModuleBody { properties, scope } = arg.module_body();
let Self { let Self {
name: _, name: _,
child, child,
@ -654,6 +730,7 @@ impl WriteTrace for TraceModuleIO {
sink_var_type: "wire", sink_var_type: "wire",
duplex_var_type: "wire", duplex_var_type: "wire",
properties, properties,
scope,
}, },
) )
} }
@ -661,16 +738,31 @@ impl WriteTrace for TraceModuleIO {
impl WriteTrace for TraceBundle { impl WriteTrace for TraceBundle {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let mut arg = arg.in_type(); let ArgInType {
source_var_type,
sink_var_type,
duplex_var_type,
properties,
scope,
} = arg.in_type();
let Self { let Self {
name, name,
fields, fields,
ty: _, ty: _,
flow: _, flow: _,
} = self; } = self;
write_vcd_scope(writer, "struct", &name, |writer| { write_vcd_scope(writer, "struct", name, scope, |writer, scope| {
for field in fields { for field in fields {
field.write_trace(writer, arg.reborrow())?; field.write_trace(
writer,
ArgInType {
source_var_type,
sink_var_type,
duplex_var_type,
properties,
scope,
},
)?;
} }
Ok(()) Ok(())
}) })
@ -679,16 +771,31 @@ impl WriteTrace for TraceBundle {
impl WriteTrace for TraceArray { impl WriteTrace for TraceArray {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let mut arg = arg.in_type(); let ArgInType {
source_var_type,
sink_var_type,
duplex_var_type,
properties,
scope,
} = arg.in_type();
let Self { let Self {
name, name,
elements, elements,
ty: _, ty: _,
flow: _, flow: _,
} = self; } = self;
write_vcd_scope(writer, "struct", &name, |writer| { write_vcd_scope(writer, "struct", name, scope, |writer, scope| {
for element in elements { for element in elements {
element.write_trace(writer, arg.reborrow())?; element.write_trace(
writer,
ArgInType {
source_var_type,
sink_var_type,
duplex_var_type,
properties,
scope,
},
)?;
} }
Ok(()) Ok(())
}) })
@ -697,7 +804,13 @@ impl WriteTrace for TraceArray {
impl WriteTrace for TraceEnumWithFields { impl WriteTrace for TraceEnumWithFields {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let mut arg = arg.in_type(); let ArgInType {
source_var_type,
sink_var_type,
duplex_var_type,
properties,
scope,
} = arg.in_type();
let Self { let Self {
name, name,
discriminant, discriminant,
@ -705,10 +818,28 @@ impl WriteTrace for TraceEnumWithFields {
ty: _, ty: _,
flow: _, flow: _,
} = self; } = self;
write_vcd_scope(writer, "struct", &name, |writer| { write_vcd_scope(writer, "struct", name, scope, |writer, scope| {
discriminant.write_trace(writer, arg.reborrow())?; discriminant.write_trace(
writer,
ArgInType {
source_var_type,
sink_var_type,
duplex_var_type,
properties,
scope,
},
)?;
for field in non_empty_fields { for field in non_empty_fields {
field.write_trace(writer, arg.reborrow())?; field.write_trace(
writer,
ArgInType {
source_var_type,
sink_var_type,
duplex_var_type,
properties,
scope,
},
)?;
} }
Ok(()) Ok(())
}) })
@ -744,6 +875,7 @@ impl<W: io::Write> TraceWriterDecls for VcdWriterDecls<W> {
&mut writer, &mut writer,
ArgModule { ArgModule {
properties: &mut properties, properties: &mut properties,
scope: &mut Scope::default(),
}, },
)?; )?;
writeln!(writer, "$enddefinitions $end")?; writeln!(writer, "$enddefinitions $end")?;
@ -798,9 +930,7 @@ fn write_string_value_change(
value: impl fmt::Display, value: impl fmt::Display,
id: usize, id: usize,
) -> io::Result<()> { ) -> io::Result<()> {
writer.write_all(b"s")?; write!(writer, "s{} ", Escaped(value))?;
write_escaped(writer, value)?;
writer.write_all(b" ")?;
write_vcd_id(writer, id)?; write_vcd_id(writer, id)?;
writer.write_all(b"\n") writer.write_all(b"\n")
} }
@ -946,3 +1076,49 @@ impl<W: io::Write> fmt::Debug for VcdWriter<W> {
.finish_non_exhaustive() .finish_non_exhaustive()
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_scope() {
let mut scope = Scope::default();
assert_eq!(&*scope.new_identifier("foo".intern()).unescaped_name, "foo");
assert_eq!(
&*scope.new_identifier("foo_0".intern()).unescaped_name,
"foo_0"
);
assert_eq!(
&*scope.new_identifier("foo_1".intern()).unescaped_name,
"foo_1"
);
assert_eq!(
&*scope.new_identifier("foo_3".intern()).unescaped_name,
"foo_3"
);
assert_eq!(
&*scope.new_identifier("foo".intern()).unescaped_name,
"foo_2"
);
assert_eq!(
&*scope.new_identifier("foo".intern()).unescaped_name,
"foo_4"
);
assert_eq!(
&*scope.new_identifier("foo_0".intern()).unescaped_name,
"foo_0_2"
);
assert_eq!(
&*scope.new_identifier("foo_1".intern()).unescaped_name,
"foo_1_2"
);
for i in 5..1000u64 {
// verify it actually picks the next available identifier with no skips or duplicates
assert_eq!(
*scope.new_identifier("foo".intern()).unescaped_name,
format!("foo_{i}"),
);
}
}
}

View file

@ -1246,3 +1246,33 @@ fn test_memories3() {
panic!(); panic!();
} }
} }
#[hdl_module(outline_generated)]
pub fn duplicate_names() {
#[hdl]
let w = wire();
connect(w, 5u8);
#[hdl]
let w = wire();
connect(w, 6u8);
}
#[test]
fn test_duplicate_names() {
let _n = SourceLocation::normalize_files_for_tests();
let mut sim = Simulation::new(duplicate_names());
let mut writer = RcWriter::default();
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
sim.advance_time(SimDuration::from_micros(1));
sim.flush_traces().unwrap();
let vcd = String::from_utf8(writer.take()).unwrap();
println!("####### VCD:\n{vcd}\n#######");
if vcd != include_str!("sim/expected/duplicate_names.vcd") {
panic!();
}
let sim_debug = format!("{sim:#?}");
println!("#######\n{sim_debug}\n#######");
if sim_debug != include_str!("sim/expected/duplicate_names.txt") {
panic!();
}
}

View file

@ -0,0 +1,153 @@
Simulation {
state: State {
insns: Insns {
state_layout: StateLayout {
ty: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 4,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w",
ty: UInt<8>,
},
SlotDebugData {
name: "",
ty: UInt<8>,
},
SlotDebugData {
name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w",
ty: UInt<8>,
},
SlotDebugData {
name: "",
ty: UInt<8>,
},
],
..
},
},
memories: StatePartLayout<Memories> {
len: 0,
debug_data: [],
layout_data: [],
..
},
},
insns: [
// at: module-XXXXXXXXXX.rs:1:1
0: Const {
dest: StatePartIndex<BigSlots>(3), // (0x6) SlotDebugData { name: "", ty: UInt<8> },
value: 0x6,
},
// at: module-XXXXXXXXXX.rs:5:1
1: Copy {
dest: StatePartIndex<BigSlots>(2), // (0x6) SlotDebugData { name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w", ty: UInt<8> },
src: StatePartIndex<BigSlots>(3), // (0x6) SlotDebugData { name: "", ty: UInt<8> },
},
// at: module-XXXXXXXXXX.rs:1:1
2: Const {
dest: StatePartIndex<BigSlots>(1), // (0x5) SlotDebugData { name: "", ty: UInt<8> },
value: 0x5,
},
// at: module-XXXXXXXXXX.rs:3:1
3: Copy {
dest: StatePartIndex<BigSlots>(0), // (0x5) SlotDebugData { name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w", ty: UInt<8> },
src: StatePartIndex<BigSlots>(1), // (0x5) SlotDebugData { name: "", ty: UInt<8> },
},
// at: module-XXXXXXXXXX.rs:1:1
4: Return,
],
..
},
pc: 4,
memory_write_log: [],
memories: StatePart {
value: [],
},
small_slots: StatePart {
value: [],
},
big_slots: StatePart {
value: [
5,
5,
6,
6,
],
},
},
io: Instance {
name: <simulator>::duplicate_names,
instantiated: Module {
name: duplicate_names,
..
},
},
uninitialized_inputs: {},
io_targets: {},
made_initial_step: true,
needs_settle: false,
trace_decls: TraceModule {
name: "duplicate_names",
children: [
TraceWire {
name: "w",
child: TraceUInt {
location: TraceScalarId(0),
name: "w",
ty: UInt<8>,
flow: Duplex,
},
ty: UInt<8>,
},
TraceWire {
name: "w",
child: TraceUInt {
location: TraceScalarId(1),
name: "w",
ty: UInt<8>,
flow: Duplex,
},
ty: UInt<8>,
},
],
},
traces: [
SimTrace {
id: TraceScalarId(0),
kind: BigUInt {
index: StatePartIndex<BigSlots>(0),
ty: UInt<8>,
},
state: 0x05,
last_state: 0x05,
},
SimTrace {
id: TraceScalarId(1),
kind: BigUInt {
index: StatePartIndex<BigSlots>(2),
ty: UInt<8>,
},
state: 0x06,
last_state: 0x06,
},
],
trace_memories: {},
trace_writers: [
Running(
VcdWriter {
finished_init: true,
timescale: 1 ps,
..
},
),
],
instant: 1 μs,
clocks_triggered: [],
..
}

View file

@ -0,0 +1,11 @@
$timescale 1 ps $end
$scope module duplicate_names $end
$var wire 8 ! w $end
$var wire 8 " w_2 $end
$upscope $end
$enddefinitions $end
$dumpvars
b101 !
b110 "
$end
#1000000

View file

@ -24,97 +24,97 @@ $upscope $end
$upscope $end $upscope $end
$scope struct mem $end $scope struct mem $end
$scope struct contents $end $scope struct contents $end
$scope struct [0] $end $scope struct \[0] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 9 \0 $end $var reg 8 9 \0 $end
$var reg 8 I \1 $end $var reg 8 I \1 $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [1] $end $scope struct \[1] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 : \0 $end $var reg 8 : \0 $end
$var reg 8 J \1 $end $var reg 8 J \1 $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [2] $end $scope struct \[2] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 ; \0 $end $var reg 8 ; \0 $end
$var reg 8 K \1 $end $var reg 8 K \1 $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [3] $end $scope struct \[3] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 < \0 $end $var reg 8 < \0 $end
$var reg 8 L \1 $end $var reg 8 L \1 $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [4] $end $scope struct \[4] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 = \0 $end $var reg 8 = \0 $end
$var reg 8 M \1 $end $var reg 8 M \1 $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [5] $end $scope struct \[5] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 > \0 $end $var reg 8 > \0 $end
$var reg 8 N \1 $end $var reg 8 N \1 $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [6] $end $scope struct \[6] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 ? \0 $end $var reg 8 ? \0 $end
$var reg 8 O \1 $end $var reg 8 O \1 $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [7] $end $scope struct \[7] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 @ \0 $end $var reg 8 @ \0 $end
$var reg 8 P \1 $end $var reg 8 P \1 $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [8] $end $scope struct \[8] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 A \0 $end $var reg 8 A \0 $end
$var reg 8 Q \1 $end $var reg 8 Q \1 $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [9] $end $scope struct \[9] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 B \0 $end $var reg 8 B \0 $end
$var reg 8 R \1 $end $var reg 8 R \1 $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [10] $end $scope struct \[10] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 C \0 $end $var reg 8 C \0 $end
$var reg 8 S \1 $end $var reg 8 S \1 $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [11] $end $scope struct \[11] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 D \0 $end $var reg 8 D \0 $end
$var reg 8 T \1 $end $var reg 8 T \1 $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [12] $end $scope struct \[12] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 E \0 $end $var reg 8 E \0 $end
$var reg 8 U \1 $end $var reg 8 U \1 $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [13] $end $scope struct \[13] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 F \0 $end $var reg 8 F \0 $end
$var reg 8 V \1 $end $var reg 8 V \1 $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [14] $end $scope struct \[14] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 G \0 $end $var reg 8 G \0 $end
$var reg 8 W \1 $end $var reg 8 W \1 $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [15] $end $scope struct \[15] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 H \0 $end $var reg 8 H \0 $end
$var reg 8 X \1 $end $var reg 8 X \1 $end

View file

@ -11,31 +11,31 @@ $var wire 1 ' wmask $end
$upscope $end $upscope $end
$scope struct mem $end $scope struct mem $end
$scope struct contents $end $scope struct contents $end
$scope struct [0] $end $scope struct \[0] $end
$scope struct mem $end $scope struct mem $end
$var string 1 1 \$tag $end $var string 1 1 \$tag $end
$var reg 1 6 HdlSome $end $var reg 1 6 HdlSome $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [1] $end $scope struct \[1] $end
$scope struct mem $end $scope struct mem $end
$var string 1 2 \$tag $end $var string 1 2 \$tag $end
$var reg 1 7 HdlSome $end $var reg 1 7 HdlSome $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [2] $end $scope struct \[2] $end
$scope struct mem $end $scope struct mem $end
$var string 1 3 \$tag $end $var string 1 3 \$tag $end
$var reg 1 8 HdlSome $end $var reg 1 8 HdlSome $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [3] $end $scope struct \[3] $end
$scope struct mem $end $scope struct mem $end
$var string 1 4 \$tag $end $var string 1 4 \$tag $end
$var reg 1 9 HdlSome $end $var reg 1 9 HdlSome $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [4] $end $scope struct \[4] $end
$scope struct mem $end $scope struct mem $end
$var string 1 5 \$tag $end $var string 1 5 \$tag $end
$var reg 1 : HdlSome $end $var reg 1 : HdlSome $end

View file

@ -42,7 +42,7 @@ $upscope $end
$upscope $end $upscope $end
$scope struct mem $end $scope struct mem $end
$scope struct contents $end $scope struct contents $end
$scope struct [0] $end $scope struct \[0] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 ] \[0] $end $var reg 8 ] \[0] $end
$var reg 8 e \[1] $end $var reg 8 e \[1] $end
@ -54,7 +54,7 @@ $var reg 8 /" \[6] $end
$var reg 8 7" \[7] $end $var reg 8 7" \[7] $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [1] $end $scope struct \[1] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 ^ \[0] $end $var reg 8 ^ \[0] $end
$var reg 8 f \[1] $end $var reg 8 f \[1] $end
@ -66,7 +66,7 @@ $var reg 8 0" \[6] $end
$var reg 8 8" \[7] $end $var reg 8 8" \[7] $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [2] $end $scope struct \[2] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 _ \[0] $end $var reg 8 _ \[0] $end
$var reg 8 g \[1] $end $var reg 8 g \[1] $end
@ -78,7 +78,7 @@ $var reg 8 1" \[6] $end
$var reg 8 9" \[7] $end $var reg 8 9" \[7] $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [3] $end $scope struct \[3] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 ` \[0] $end $var reg 8 ` \[0] $end
$var reg 8 h \[1] $end $var reg 8 h \[1] $end
@ -90,7 +90,7 @@ $var reg 8 2" \[6] $end
$var reg 8 :" \[7] $end $var reg 8 :" \[7] $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [4] $end $scope struct \[4] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 a \[0] $end $var reg 8 a \[0] $end
$var reg 8 i \[1] $end $var reg 8 i \[1] $end
@ -102,7 +102,7 @@ $var reg 8 3" \[6] $end
$var reg 8 ;" \[7] $end $var reg 8 ;" \[7] $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [5] $end $scope struct \[5] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 b \[0] $end $var reg 8 b \[0] $end
$var reg 8 j \[1] $end $var reg 8 j \[1] $end
@ -114,7 +114,7 @@ $var reg 8 4" \[6] $end
$var reg 8 <" \[7] $end $var reg 8 <" \[7] $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [6] $end $scope struct \[6] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 c \[0] $end $var reg 8 c \[0] $end
$var reg 8 k \[1] $end $var reg 8 k \[1] $end
@ -126,7 +126,7 @@ $var reg 8 5" \[6] $end
$var reg 8 =" \[7] $end $var reg 8 =" \[7] $end
$upscope $end $upscope $end
$upscope $end $upscope $end
$scope struct [7] $end $scope struct \[7] $end
$scope struct mem $end $scope struct mem $end
$var reg 8 d \[0] $end $var reg 8 d \[0] $end
$var reg 8 l \[1] $end $var reg 8 l \[1] $end