add simulator support for sim-only values

This commit is contained in:
Jacob Lifshay 2025-09-01 04:46:24 -07:00
parent d3dd66cbf0
commit db9b1c202c
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
52 changed files with 5441 additions and 819 deletions

View file

@ -9,7 +9,7 @@ use fayalite::{
sim::vcd::VcdWriterDecls,
util::RcWriter,
};
use std::num::NonZeroUsize;
use std::{collections::BTreeMap, num::NonZeroUsize, rc::Rc};
#[hdl_module(outline_generated)]
pub fn connect_const() {
@ -1626,3 +1626,99 @@ fn test_ripple_counter() {
panic!();
}
}
/// use `Rc` to ensure you can use `!Send + !Sync` types
type SimOnlyTestMap = BTreeMap<String, Rc<str>>;
#[hdl_module(outline_generated, extern)]
fn sim_only_connects_helper() {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let inp: SimOnly<SimOnlyTestMap> = m.input();
#[hdl]
let out: SimOnly<SimOnlyTestMap> = m.output();
m.extern_module_simulation_fn((cd, inp, out), |(cd, inp, out), mut sim| async move {
sim.write(out, SimOnlyValue::default()).await;
loop {
sim.wait_for_clock_edge(cd.clk).await;
let mut map = sim.read(inp).await;
let foo = map.get("foo").cloned().unwrap_or_default();
map.insert(String::from("bar"), foo);
map.insert(String::from("foo"), Rc::from("baz"));
sim.write(out, map).await;
}
});
}
#[hdl_module(outline_generated)]
pub fn sim_only_connects() {
#[hdl]
let cd: ClockDomain = m.input();
#[hdl]
let inp: SimOnly<SimOnlyTestMap> = m.input();
#[hdl]
let out1: SimOnly<SimOnlyTestMap> = m.output();
#[hdl]
let out2: SimOnly<SimOnlyTestMap> = m.output();
#[hdl]
let out3: SimOnly<SimOnlyTestMap> = m.output();
#[hdl]
let helper1 = instance(sim_only_connects_helper());
#[hdl]
let delay1: SimOnly<SimOnlyTestMap> = reg_builder()
.clock_domain(cd)
.reset(SimOnly::<SimOnlyTestMap>::new().uninit());
#[hdl]
let delay1_empty: Bool = reg_builder().clock_domain(cd).reset(true);
connect(helper1.cd, cd);
connect(helper1.inp, delay1);
connect(out1, delay1);
#[hdl]
if delay1_empty {
connect(helper1.inp, inp);
connect(out1, inp);
}
connect(delay1, inp);
connect(delay1_empty, false);
connect(out2, helper1.out);
#[hdl]
let helper2 = instance(sim_only_connects_helper());
connect(helper2.cd, cd);
connect(helper2.inp, out2);
connect(out3, helper2.out);
}
#[test]
fn test_sim_only_connects() {
let _n = SourceLocation::normalize_files_for_tests();
let mut sim = Simulation::new(sim_only_connects());
let mut writer = RcWriter::default();
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
sim.write(sim.io().cd.rst, true);
sim.write(
sim.io().inp,
SimOnlyValue::new(BTreeMap::from_iter([(
String::from("extra"),
Rc::from("value"),
)])),
);
for _ in 0..8 {
sim.write(sim.io().cd.clk, false);
sim.advance_time(SimDuration::from_micros(1));
sim.write(sim.io().cd.clk, true);
sim.advance_time(SimDuration::from_micros(1));
sim.write(sim.io().cd.rst, false);
}
sim.flush_traces().unwrap();
let vcd = String::from_utf8(writer.take()).unwrap();
println!("####### VCD:\n{vcd}\n#######");
if vcd != include_str!("sim/expected/sim_only_connects.vcd") {
panic!();
}
let sim_debug = format!("{sim:#?}");
println!("#######\n{sim_debug}\n#######");
if sim_debug != include_str!("sim/expected/sim_only_connects.txt") {
panic!();
}
}