From c16726cee63cd5afbf720dad14fcba7f738cf6b6 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 29 Dec 2024 00:48:15 -0800 Subject: [PATCH 01/10] fix #[hdl]/#[hdl_module] attributes getting the wrong hygiene when processing #[cfg]s --- crates/fayalite-proc-macros-impl/src/lib.rs | 27 +++++++++++++++------ crates/fayalite/src/lib.rs | 17 +++++++------ crates/fayalite/tests/module.rs | 15 ++++++------ 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/crates/fayalite-proc-macros-impl/src/lib.rs b/crates/fayalite-proc-macros-impl/src/lib.rs index 1b1b61f..6ba177b 100644 --- a/crates/fayalite-proc-macros-impl/src/lib.rs +++ b/crates/fayalite-proc-macros-impl/src/lib.rs @@ -1210,17 +1210,29 @@ fn hdl_main( attr: TokenStream, item: TokenStream, ) -> syn::Result { - let (evaluated_cfgs, attr): (_, TokenStream) = Parser::parse2( + fn parse_evaluated_cfgs_attr( + input: ParseStream, + parse_inner: impl FnOnce(ParseStream) -> syn::Result, + ) -> syn::Result { + let _: Token![#] = input.parse()?; + let bracket_content; + bracketed!(bracket_content in input); + let _: kw::__evaluated_cfgs = bracket_content.parse()?; + let paren_content; + parenthesized!(paren_content in bracket_content); + parse_inner(&paren_content) + } + let (evaluated_cfgs, item): (_, TokenStream) = Parser::parse2( |input: ParseStream| { - if input.peek(Bracket) && input.peek2(kw::__evaluated_cfgs) { - let cfgs = input.parse()?; - let _: kw::__evaluated_cfgs = input.parse()?; - Ok((Some(cfgs), input.parse()?)) + let peek = input.fork(); + if parse_evaluated_cfgs_attr(&peek, |_| Ok(())).is_ok() { + let evaluated_cfgs = parse_evaluated_cfgs_attr(input, Cfgs::::parse)?; + Ok((Some(evaluated_cfgs), input.parse()?)) } else { Ok((None, input.parse()?)) } }, - attr, + item, )?; let cfgs = if let Some(cfgs) = evaluated_cfgs { cfgs @@ -1229,12 +1241,11 @@ fn hdl_main( if cfgs.cfgs_list.is_empty() { Cfgs::default() } else { - let key = kw::__evaluated_cfgs::default(); return Ok(quote! { ::fayalite::__cfg_expansion_helper! { [] #cfgs - #[::fayalite::#kw:(#key #attr)] { #item } + {#[::fayalite::#kw(#attr)]} { #item } } }); } diff --git a/crates/fayalite/src/lib.rs b/crates/fayalite/src/lib.rs index 5b5afb4..88fe169 100644 --- a/crates/fayalite/src/lib.rs +++ b/crates/fayalite/src/lib.rs @@ -22,7 +22,8 @@ macro_rules! __cfg_expansion_helper { $cfg:ident($($expr:tt)*), $($unevaluated_cfgs:ident($($unevaluated_exprs:tt)*),)* ] - #[$after_evaluation:path:($($after_evaluation_attr_args:tt)*)] {$($after_evaluation_args:tt)*} + // pass as tt so we get right span for attribute + $after_evaluation_attr:tt $after_evaluation_body:tt ) => { #[$cfg($($expr)*)] $crate::__cfg_expansion_helper! { @@ -33,7 +34,7 @@ macro_rules! __cfg_expansion_helper { [ $($unevaluated_cfgs($($unevaluated_exprs)*),)* ] - #[$after_evaluation:($($after_evaluation_attr_args)*)] {$($after_evaluation_args)*} + $after_evaluation_attr $after_evaluation_body } #[$cfg(not($($expr)*))] $crate::__cfg_expansion_helper! { @@ -44,7 +45,7 @@ macro_rules! __cfg_expansion_helper { [ $($unevaluated_cfgs($($unevaluated_exprs)*),)* ] - #[$after_evaluation:($($after_evaluation_attr_args)*)] {$($after_evaluation_args)*} + $after_evaluation_attr $after_evaluation_body } }; ( @@ -52,12 +53,14 @@ macro_rules! __cfg_expansion_helper { $($evaluated_cfgs:ident($($evaluated_exprs:tt)*) = $evaluated_results:ident,)* ] [] - #[$after_evaluation:path:($($after_evaluation_attr_args:tt)*)] {$($after_evaluation_args:tt)*} + // don't use #[...] so we get right span for `#` and `[]` of attribute + {$($after_evaluation_attr:tt)*} {$($after_evaluation_body:tt)*} ) => { - #[$after_evaluation([ + $($after_evaluation_attr)* + #[__evaluated_cfgs([ $($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)* - ] $($after_evaluation_attr_args)*)] - $($after_evaluation_args)* + ])] + $($after_evaluation_body)* }; } diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 42cc528..4e56df4 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -4288,7 +4288,8 @@ circuit check_deduce_resets: }; } -#[hdl_module(outline_generated)] +// intentionally not outline_generated to ensure we get correct macro hygiene +#[hdl_module] pub fn check_cfgs<#[cfg(cfg_false_for_tests)] A: Type, #[cfg(cfg_true_for_tests)] B: Type>( #[cfg(cfg_false_for_tests)] a: A, #[cfg(cfg_true_for_tests)] b: B, @@ -4335,12 +4336,12 @@ fn test_cfgs() { "/test/check_cfgs.fir": r"FIRRTL version 3.2.0 circuit check_cfgs: type Ty0 = {b: UInt<8>} - module check_cfgs: @[module-XXXXXXXXXX.rs 1:1] - input i_b: UInt<8> @[module-XXXXXXXXXX.rs 2:1] - output o_b: UInt<8> @[module-XXXXXXXXXX.rs 4:1] - wire w: Ty0 @[module-XXXXXXXXXX.rs 3:1] - connect o_b, w.b @[module-XXXXXXXXXX.rs 5:1] - connect w.b, i_b @[module-XXXXXXXXXX.rs 6:1] + module check_cfgs: @[the_test_file.rs 9962:1] + input i_b: UInt<8> @[the_test_file.rs 9979:20] + output o_b: UInt<8> @[the_test_file.rs 9992:24] + wire w: Ty0 @[the_test_file.rs 9981:25] + connect o_b, w.b @[the_test_file.rs 9993:9] + connect w.b, i_b @[the_test_file.rs 9994:9] ", }; } From 31d01046a81fdb3b14b53efb767ce95aeb891868 Mon Sep 17 00:00:00 2001 From: Cesar Strauss Date: Sun, 29 Dec 2024 13:04:01 -0300 Subject: [PATCH 02/10] Initial queue formal proof based on one-entry FIFO equivalence For now, only check that the basic properties work in bounded model check mode, leave the induction proof for later. Partially replace the previously existing proof. Remove earlier assumptions and bounds that don't apply for this proof. Use parameterized types instead of hard-coded types. --- crates/fayalite/src/util/ready_valid.rs | 190 ++++++++++++++---------- 1 file changed, 112 insertions(+), 78 deletions(-) diff --git a/crates/fayalite/src/util/ready_valid.rs b/crates/fayalite/src/util/ready_valid.rs index 82d3f1e..9a81b1b 100644 --- a/crates/fayalite/src/util/ready_valid.rs +++ b/crates/fayalite/src/util/ready_valid.rs @@ -195,7 +195,7 @@ mod tests { assert_formal( format_args!("test_queue_{capacity}_{inp_ready_is_comb}_{out_valid_is_comb}"), queue_test(capacity, inp_ready_is_comb, out_valid_is_comb), - FormalMode::Prove, + FormalMode::BMC, 14, None, ExportOptions { @@ -203,6 +203,16 @@ mod tests { ..ExportOptions::default() }, ); + /// Formal verification of the FIFO queue + /// + /// The strategy derives from the observation that, if we filter its + /// input and output streams to consider just one in every N reads and + /// writes (where N is the FIFO capacity), then the FIFO effectively + /// behaves as a one-entry FIFO. + /// + /// In particular, any counterexample of the full FIFO behaving badly + /// will also be caught by one of the filtered versions (one which + /// happens to be in phase with the offending input or output). #[hdl_module] fn queue_test(capacity: NonZeroUsize, inp_ready_is_comb: bool, out_valid_is_comb: bool) { #[hdl] @@ -217,6 +227,8 @@ mod tests { rst: formal_reset().to_reset(), }, ); + + // random input data #[hdl] let inp_data: HdlOption> = wire(); #[hdl] @@ -225,16 +237,25 @@ mod tests { } else { connect(inp_data, HdlNone()); } + + // assert output ready at random #[hdl] let out_ready: Bool = wire(); connect(out_ready, any_seq(Bool)); - let index_ty: UInt<32> = UInt::TYPE; + + // The current number of elements in the FIFO ranges from zero to + // maximum capacity, inclusive. + let count_ty = UInt::range_inclusive(0..=capacity.get()); + // type for counters that wrap around at the FIFO capacity + let index_ty = UInt::range(0..capacity.get()); + + // among all entries of the FIFO internal circular memory, choose + // one at random to check #[hdl] - let index_to_check = wire(); + let index_to_check = wire(index_ty); connect(index_to_check, any_const(index_ty)); - let index_max = !index_ty.zero(); - // we saturate at index_max, so only check indexes where we properly maintain position - hdl_assume(clk, index_to_check.cmp_ne(index_max), ""); + + // instantiate and connect the queue #[hdl] let dut = instance(queue( UInt[ConstUsize::<8>], @@ -245,108 +266,121 @@ mod tests { connect(dut.cd, cd); connect(dut.inp.data, inp_data); connect(dut.out.ready, out_ready); - hdl_assume( - clk, - index_to_check.cmp_ne(!Expr::ty(index_to_check).zero()), - "", - ); + // Keep an independent count of words in the FIFO. Ensure that + // it's always correct, and never overflows. #[hdl] - let expected_count_reg = reg_builder().clock_domain(cd).reset(0u32); - #[hdl] - let next_expected_count = wire(); - connect(next_expected_count, expected_count_reg); - connect(expected_count_reg, next_expected_count); + let expected_count_reg = reg_builder().clock_domain(cd).reset(count_ty.zero()); #[hdl] if ReadyValid::firing(dut.inp) & !ReadyValid::firing(dut.out) { - connect_any(next_expected_count, expected_count_reg + 1u8); + hdl_assert(clk, expected_count_reg.cmp_ne(capacity.get()), ""); + connect_any(expected_count_reg, expected_count_reg + 1u8); } else if !ReadyValid::firing(dut.inp) & ReadyValid::firing(dut.out) { - connect_any(next_expected_count, expected_count_reg - 1u8); + hdl_assert(clk, expected_count_reg.cmp_ne(count_ty.zero()), ""); + connect_any(expected_count_reg, expected_count_reg - 1u8); } - hdl_assert(cd.clk, expected_count_reg.cmp_eq(dut.count), ""); - - #[hdl] - let prev_out_ready_reg = reg_builder().clock_domain(cd).reset(!0_hdl_u3); - connect_any( - prev_out_ready_reg, - (prev_out_ready_reg << 1) | out_ready.cast_to(UInt[1]), - ); - #[hdl] - let prev_inp_valid_reg = reg_builder().clock_domain(cd).reset(!0_hdl_u3); - connect_any( - prev_inp_valid_reg, - (prev_inp_valid_reg << 1) | HdlOption::is_some(inp_data).cast_to(UInt[1]), - ); - hdl_assume( - clk, - (prev_out_ready_reg & prev_inp_valid_reg).cmp_ne(0u8), - "", - ); + hdl_assert(clk, expected_count_reg.cmp_eq(dut.count), ""); + // keep an independent write index into the FIFO's circular buffer #[hdl] let inp_index_reg = reg_builder().clock_domain(cd).reset(index_ty.zero()); #[hdl] - let stored_inp_data_reg = reg_builder().clock_domain(cd).reset(0u8); - - #[hdl] - if let HdlSome(data) = ReadyValid::firing_data(dut.inp) { + if ReadyValid::firing(dut.inp) { #[hdl] - if inp_index_reg.cmp_lt(index_max) { + if inp_index_reg.cmp_ne(capacity.get() - 1) { connect_any(inp_index_reg, inp_index_reg + 1u8); - #[hdl] - if inp_index_reg.cmp_eq(index_to_check) { - connect(stored_inp_data_reg, data); - } + } else { + connect_any(inp_index_reg, 0_hdl_u0); } } - #[hdl] - if inp_index_reg.cmp_lt(index_to_check) { - hdl_assert(clk, stored_inp_data_reg.cmp_eq(0u8), ""); - } - + // keep an independent read index into the FIFO's circular buffer #[hdl] let out_index_reg = reg_builder().clock_domain(cd).reset(index_ty.zero()); #[hdl] - let stored_out_data_reg = reg_builder().clock_domain(cd).reset(0u8); - - #[hdl] - if let HdlSome(data) = ReadyValid::firing_data(dut.out) { + if ReadyValid::firing(dut.out) { #[hdl] - if out_index_reg.cmp_lt(index_max) { + if out_index_reg.cmp_ne(capacity.get() - 1) { connect_any(out_index_reg, out_index_reg + 1u8); - #[hdl] - if out_index_reg.cmp_eq(index_to_check) { - connect(stored_out_data_reg, data); - } + } else { + connect_any(out_index_reg, 0_hdl_u0); } } + // filter the input data stream, predicated by the read index + // matching the chosen position in the FIFO's circular buffer #[hdl] - if out_index_reg.cmp_lt(index_to_check) { - hdl_assert(clk, stored_out_data_reg.cmp_eq(0u8), ""); + let inp_index_matches = wire(); + connect(inp_index_matches, inp_index_reg.cmp_eq(index_to_check)); + #[hdl] + let inp_firing_data = wire(); + connect(inp_firing_data, HdlNone()); + #[hdl] + if inp_index_matches { + connect(inp_firing_data, ReadyValid::firing_data(dut.inp)); } - hdl_assert(clk, inp_index_reg.cmp_ge(out_index_reg), ""); - + // filter the output data stream, predicated by the write index + // matching the chosen position in the FIFO's circular buffer #[hdl] - if inp_index_reg.cmp_lt(index_max) & out_index_reg.cmp_lt(index_max) { - hdl_assert( - clk, - expected_count_reg.cmp_eq(inp_index_reg - out_index_reg), - "", - ); - } else { - hdl_assert( - clk, - expected_count_reg.cmp_ge(inp_index_reg - out_index_reg), - "", - ); + let out_index_matches = wire(); + connect(out_index_matches, out_index_reg.cmp_eq(index_to_check)); + #[hdl] + let out_firing_data = wire(); + connect(out_firing_data, HdlNone()); + #[hdl] + if out_index_matches { + connect(out_firing_data, ReadyValid::firing_data(dut.out)); } + // Implement a one-entry FIFO and ensure its equivalence to the + // filtered FIFO. + // + // the holding register for our one-entry FIFO #[hdl] - if inp_index_reg.cmp_gt(index_to_check) & out_index_reg.cmp_gt(index_to_check) { - hdl_assert(clk, stored_inp_data_reg.cmp_eq(stored_out_data_reg), ""); + let stored_reg = reg_builder().clock_domain(cd).reset(HdlNone()); + #[hdl] + match stored_reg { + // If the holding register is empty... + HdlNone => { + #[hdl] + match inp_firing_data { + // ... and we are not receiving data, then we must not + // transmit any data. + HdlNone => hdl_assert(clk, HdlOption::is_none(out_firing_data), ""), + // If we are indeed receiving some data... + HdlSome(data_in) => { + #[hdl] + match out_firing_data { + // ... and transmitting at the same time, we + // must be transmitting the input data itself, + // since the holding register is empty. + HdlSome(data_out) => hdl_assert(clk, data_out.cmp_eq(data_in), ""), + // If we are receiving, but not transmitting, + // store the received data in the holding + // register. + HdlNone => connect(stored_reg, HdlSome(data_in)), + } + } + } + } + // If there is some value stored in the holding register... + HdlSome(stored) => { + #[hdl] + match out_firing_data { + // ... and we are not transmitting it, we cannot + // receive any more data. + HdlNone => hdl_assert(clk, HdlOption::is_none(inp_firing_data), ""), + // If we are transmitting a previously stored value... + HdlSome(data_out) => { + // ... it must be the same data we stored earlier. + hdl_assert(clk, data_out.cmp_eq(stored), ""); + // Also, accept new data, if any. Otherwise, + // let the holding register become empty. + connect(stored_reg, inp_firing_data); + } + } + } } } } From dcf865caec1010082b2568de575ea0bdf7adf748 Mon Sep 17 00:00:00 2001 From: Cesar Strauss Date: Sun, 29 Dec 2024 13:12:58 -0300 Subject: [PATCH 03/10] Add assertions and debug ports in order for the FIFO to pass induction As some proofs involving memories, it is necessary to add more ports to the queue interface, to sync state. These changes are predicated on the test environment, so normal use is not affected. Since some speedup is achieved, use the saved time to test with a deeper FIFO. --- crates/fayalite/src/util/ready_valid.rs | 99 ++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 2 deletions(-) diff --git a/crates/fayalite/src/util/ready_valid.rs b/crates/fayalite/src/util/ready_valid.rs index 9a81b1b..28ea691 100644 --- a/crates/fayalite/src/util/ready_valid.rs +++ b/crates/fayalite/src/util/ready_valid.rs @@ -178,6 +178,30 @@ pub fn queue( } } } + // These debug ports expose some internal state during the Induction phase + // of Formal Verification. They are not present in normal use. + // + // TODO: gather the new debug ports in a bundle + #[cfg(test)] + { + // read the memory word currently stored at some fixed index + let debug_port = mem.new_read_port(); + #[hdl] + let dbg_index_to_check: UInt = m.input(index_ty); + #[hdl] + let dbg_stored: T = m.output(ty); + connect(debug_port.addr, dbg_index_to_check); + connect(debug_port.en, true); + connect(debug_port.clk, cd.clk); + connect(dbg_stored, debug_port.data); + // also expose the current read and write indices + #[hdl] + let dbg_inp_index: UInt = m.output(index_ty); + #[hdl] + let dbg_out_index: UInt = m.output(index_ty); + connect(dbg_inp_index, inp_index_reg); + connect(dbg_out_index, out_index_reg); + } } #[cfg(test)] @@ -195,8 +219,8 @@ mod tests { assert_formal( format_args!("test_queue_{capacity}_{inp_ready_is_comb}_{out_valid_is_comb}"), queue_test(capacity, inp_ready_is_comb, out_valid_is_comb), - FormalMode::BMC, - 14, + FormalMode::Prove, + 2, None, ExportOptions { simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts), @@ -254,6 +278,7 @@ mod tests { #[hdl] let index_to_check = wire(index_ty); connect(index_to_check, any_const(index_ty)); + hdl_assume(clk, index_to_check.cmp_lt(capacity.get()), ""); // instantiate and connect the queue #[hdl] @@ -382,6 +407,56 @@ mod tests { } } } + + // from now on, some extra assertions in order to pass induction + + // sync the holding register, when it's occupied, to the + // corresponding entry in the FIFO's circular buffer + connect(dut.dbg_index_to_check, index_to_check); + #[hdl] + if let HdlSome(stored) = stored_reg { + hdl_assert(clk, stored.cmp_eq(dut.dbg_stored), ""); + } + + // sync the read and write indices + hdl_assert(clk, inp_index_reg.cmp_eq(dut.dbg_inp_index), ""); + hdl_assert(clk, out_index_reg.cmp_eq(dut.dbg_out_index), ""); + + // the indices should never go past the capacity, but induction + // doesn't know that... + hdl_assert(clk, inp_index_reg.cmp_lt(capacity.get()), ""); + hdl_assert(clk, out_index_reg.cmp_lt(capacity.get()), ""); + + // strongly constrain the state of the holding register + // + // The holding register is full if and only if the corresponding + // FIFO entry was written to and not yet read. In other words, if + // the number of pending reads until the chosen entry is read out + // is greater than the current FIFO count, then the entry couldn't + // be in the FIFO in the first place. + #[hdl] + let pending_reads: UInt = wire(index_ty); + // take care of wrap-around when subtracting indices, add the + // capacity amount to keep the result positive if necessary + #[hdl] + if index_to_check.cmp_ge(out_index_reg) { + connect(pending_reads, index_to_check - out_index_reg); + } else { + connect( + pending_reads, + index_to_check + capacity.get() - out_index_reg, + ); + } + // check whether the chosen entry is in the FIFO + #[hdl] + let expected_stored: Bool = wire(); + connect(expected_stored, pending_reads.cmp_lt(dut.count)); + // sync with the state of the holding register + hdl_assert( + clk, + expected_stored.cmp_eq(HdlOption::is_some(stored_reg)), + "", + ); } } @@ -464,4 +539,24 @@ mod tests { fn test_4_true_true() { test_queue(NonZero::new(4).unwrap(), true, true); } + + #[test] + fn test_many_false_false() { + test_queue(NonZero::new((2 << 16) - 5).unwrap(), false, false); + } + + #[test] + fn test_many_false_true() { + test_queue(NonZero::new((2 << 16) - 5).unwrap(), false, true); + } + + #[test] + fn test_many_true_false() { + test_queue(NonZero::new((2 << 16) - 5).unwrap(), true, false); + } + + #[test] + fn test_many_true_true() { + test_queue(NonZero::new((2 << 16) - 5).unwrap(), true, true); + } } From 3771cea78e34fd28af6ef4a76c1013eb2406de1d Mon Sep 17 00:00:00 2001 From: Cesar Strauss Date: Sun, 29 Dec 2024 13:17:24 -0300 Subject: [PATCH 04/10] Gather the FIFO debug ports in a bundle --- crates/fayalite/src/util/ready_valid.rs | 40 ++++++++++++++----------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/crates/fayalite/src/util/ready_valid.rs b/crates/fayalite/src/util/ready_valid.rs index 28ea691..ac08a64 100644 --- a/crates/fayalite/src/util/ready_valid.rs +++ b/crates/fayalite/src/util/ready_valid.rs @@ -49,6 +49,18 @@ impl ReadyValid { } } +/// This debug port is only meant to assist the formal proof of the queue. +#[cfg(test)] +#[doc(hidden)] +#[hdl] +pub struct QueueDebugPort { + #[hdl(flip)] + index_to_check: Index, + stored: Element, + inp_index: Index, + out_index: Index, +} + #[hdl_module] pub fn queue( ty: T, @@ -180,27 +192,19 @@ pub fn queue( } // These debug ports expose some internal state during the Induction phase // of Formal Verification. They are not present in normal use. - // - // TODO: gather the new debug ports in a bundle #[cfg(test)] { + #[hdl] + let dbg: QueueDebugPort = m.output(QueueDebugPort[ty][index_ty]); // read the memory word currently stored at some fixed index let debug_port = mem.new_read_port(); - #[hdl] - let dbg_index_to_check: UInt = m.input(index_ty); - #[hdl] - let dbg_stored: T = m.output(ty); - connect(debug_port.addr, dbg_index_to_check); + connect(debug_port.addr, dbg.index_to_check); connect(debug_port.en, true); connect(debug_port.clk, cd.clk); - connect(dbg_stored, debug_port.data); + connect(dbg.stored, debug_port.data); // also expose the current read and write indices - #[hdl] - let dbg_inp_index: UInt = m.output(index_ty); - #[hdl] - let dbg_out_index: UInt = m.output(index_ty); - connect(dbg_inp_index, inp_index_reg); - connect(dbg_out_index, out_index_reg); + connect(dbg.inp_index, inp_index_reg); + connect(dbg.out_index, out_index_reg); } } @@ -412,15 +416,15 @@ mod tests { // sync the holding register, when it's occupied, to the // corresponding entry in the FIFO's circular buffer - connect(dut.dbg_index_to_check, index_to_check); + connect(dut.dbg.index_to_check, index_to_check); #[hdl] if let HdlSome(stored) = stored_reg { - hdl_assert(clk, stored.cmp_eq(dut.dbg_stored), ""); + hdl_assert(clk, stored.cmp_eq(dut.dbg.stored), ""); } // sync the read and write indices - hdl_assert(clk, inp_index_reg.cmp_eq(dut.dbg_inp_index), ""); - hdl_assert(clk, out_index_reg.cmp_eq(dut.dbg_out_index), ""); + hdl_assert(clk, inp_index_reg.cmp_eq(dut.dbg.inp_index), ""); + hdl_assert(clk, out_index_reg.cmp_eq(dut.dbg.out_index), ""); // the indices should never go past the capacity, but induction // doesn't know that... From e3a2ccd41c89eb659dde86d3d7f0581a17d45509 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 9 Jan 2025 22:52:22 -0800 Subject: [PATCH 05/10] properly handle duplicate names in vcd --- crates/fayalite/src/sim/vcd.rs | 376 +++++++++++++----- crates/fayalite/tests/sim.rs | 30 ++ .../tests/sim/expected/duplicate_names.txt | 153 +++++++ .../tests/sim/expected/duplicate_names.vcd | 11 + .../fayalite/tests/sim/expected/memories.vcd | 32 +- .../fayalite/tests/sim/expected/memories2.vcd | 10 +- .../fayalite/tests/sim/expected/memories3.vcd | 16 +- 7 files changed, 499 insertions(+), 129 deletions(-) create mode 100644 crates/fayalite/tests/sim/expected/duplicate_names.txt create mode 100644 crates/fayalite/tests/sim/expected/duplicate_names.vcd diff --git a/crates/fayalite/src/sim/vcd.rs b/crates/fayalite/src/sim/vcd.rs index b8248e3..fde30be 100644 --- a/crates/fayalite/src/sim/vcd.rs +++ b/crates/fayalite/src/sim/vcd.rs @@ -5,6 +5,7 @@ use crate::{ enum_::{Enum, EnumType}, expr::Flow, int::UInt, + intern::{Intern, Interned}, sim::{ time::{SimDuration, SimInstant}, TraceArray, TraceAsyncReset, TraceBool, TraceBundle, TraceClock, TraceDecl, @@ -15,12 +16,73 @@ use crate::{ }, }; use bitvec::{order::Lsb0, slice::BitSlice}; +use hashbrown::{hash_map::Entry, HashMap}; use std::{ - fmt, - io::{self, Write}, - mem, + fmt::{self, Write as _}, + io, mem, }; +#[derive(Default)] +struct Scope { + last_inserted: HashMap, usize>, +} + +#[derive(Copy, Clone)] +struct VerilogIdentifier { + unescaped_name: Interned, +} + +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) -> 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 { writer: W, timescale: SimDuration, @@ -97,14 +159,20 @@ impl fmt::Debug for VcdWriterDecls { } } +/// pass in scope to ensure it's not available in child scope fn write_vcd_scope( writer: &mut W, scope_type: &str, - scope_name: &str, - f: impl FnOnce(&mut W) -> io::Result, + scope_name: Interned, + scope: &mut Scope, + f: impl FnOnce(&mut W, &mut Scope) -> io::Result, ) -> io::Result { - writeln!(writer, "$scope {scope_type} {scope_name} $end")?; - let retval = f(writer)?; + writeln!( + writer, + "$scope {scope_type} {} $end", + scope.new_identifier(scope_name), + )?; + let retval = f(writer, &mut Scope::default())?; writeln!(writer, "$upscope $end")?; Ok(retval) } @@ -143,24 +211,28 @@ trait_arg! { struct ArgModule<'a> { properties: &'a mut VcdWriterProperties, + scope: &'a mut Scope, } impl<'a> ArgModule<'a> { fn reborrow(&mut self) -> ArgModule<'_> { ArgModule { properties: self.properties, + scope: self.scope, } } } struct ArgModuleBody<'a> { properties: &'a mut VcdWriterProperties, + scope: &'a mut Scope, } impl<'a> ArgModuleBody<'a> { fn reborrow(&mut self) -> ArgModuleBody<'_> { ArgModuleBody { properties: self.properties, + scope: self.scope, } } } @@ -170,6 +242,7 @@ struct ArgInType<'a> { sink_var_type: &'static str, duplex_var_type: &'static str, properties: &'a mut VcdWriterProperties, + scope: &'a mut Scope, } impl<'a> ArgInType<'a> { @@ -179,6 +252,7 @@ impl<'a> ArgInType<'a> { sink_var_type: self.sink_var_type, duplex_var_type: self.duplex_var_type, properties: self.properties, + scope: self.scope, } } } @@ -226,55 +300,42 @@ fn write_vcd_id(writer: &mut W, mut id: usize) -> io::Result<()> { Ok(()) } -fn write_escaped(writer: &mut W, value: impl fmt::Display) -> io::Result<()> { - // escaping rules from function GTKWave uses to decode VCD strings: - // https://github.com/gtkwave/gtkwave/blob/491f24d7e8619cfc1fcc65704ee5c967d1083c18/lib/libfst/fstapi.c#L7090 - struct Wrapper(W); - impl io::Write for Wrapper { - fn write(&mut self, buf: &[u8]) -> io::Result { - if buf.is_empty() { - return self.0.write(buf); - } - let mut retval = 0; - for &byte in buf { - match byte { - b'\\' | b'\'' | b'"' | b'?' => self.0.write_all(&[b'\\', byte])?, - b'\n' => self.0.write_all(br"\n")?, - b'\r' => self.0.write_all(br"\r")?, - b'\t' => self.0.write_all(br"\t")?, - 0x7 => self.0.write_all(br"\a")?, - 0x8 => self.0.write_all(br"\b")?, - 0xC => self.0.write_all(br"\f")?, - 0xB => self.0.write_all(br"\v")?, - _ => { - if byte.is_ascii_graphic() { - self.0.write_all(&[byte])?; - } else { - write!(self.0, r"\x{byte:02x}")?; +struct Escaped(T); + +impl fmt::Display for Escaped { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // escaping rules from function GTKWave uses to decode VCD strings: + // https://github.com/gtkwave/gtkwave/blob/491f24d7e8619cfc1fcc65704ee5c967d1083c18/lib/libfst/fstapi.c#L7090 + struct Wrapper(W); + impl fmt::Write for Wrapper { + fn write_str(&mut self, s: &str) -> fmt::Result { + for byte in s.bytes() { + match byte { + b'\\' | b'\'' | b'"' | b'?' => { + self.0.write_str("\\")?; + self.0.write_char(byte as char)?; + } + b'\n' => self.0.write_str(r"\n")?, + b'\r' => self.0.write_str(r"\r")?, + b'\t' => self.0.write_str(r"\t")?, + 0x7 => self.0.write_str(r"\a")?, + 0x8 => self.0.write_str(r"\b")?, + 0xC => self.0.write_str(r"\f")?, + 0xB => self.0.write_str(r"\v")?, + _ => { + if byte.is_ascii_graphic() { + 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( @@ -284,7 +345,7 @@ fn write_vcd_var( var_type: &str, size: usize, location: TraceLocation, - name: &str, + name: VerilogIdentifier, ) -> io::Result<()> { let id = match location { TraceLocation::Scalar(id) => id.as_usize(), @@ -319,12 +380,7 @@ fn write_vcd_var( }; write!(writer, "$var {var_type} {size} ")?; write_vcd_id(writer, id)?; - writer.write_all(b" ")?; - if !is_unescaped_verilog_identifier(name) { - writer.write_all(b"\\")?; - } - write_escaped(writer, name)?; - writer.write_all(b" $end\n") + writeln!(writer, " {name} $end") } impl WriteTrace for TraceUInt { @@ -334,6 +390,7 @@ impl WriteTrace for TraceUInt { sink_var_type, duplex_var_type, properties, + scope, } = arg.in_type(); let Self { location, @@ -356,7 +413,7 @@ impl WriteTrace for TraceUInt { var_type, ty.width(), location, - &name, + scope.new_identifier(name), ) } } @@ -421,6 +478,7 @@ impl WriteTrace for TraceEnumDiscriminant { sink_var_type: _, duplex_var_type: _, properties, + scope, } = arg.in_type(); let Self { location, @@ -435,7 +493,7 @@ impl WriteTrace for TraceEnumDiscriminant { "string", 1, location, - &name, + scope.new_identifier(name), ) } } @@ -507,11 +565,11 @@ impl WriteTrace for TraceScope { impl WriteTrace for TraceModule { fn write_trace(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; - write_vcd_scope(writer, "module", &name, |writer| { + write_vcd_scope(writer, "module", name, scope, |writer, scope| { for child in children { - child.write_trace(writer, ArgModuleBody { properties })?; + child.write_trace(writer, ArgModuleBody { properties, scope })?; } Ok(()) }) @@ -520,7 +578,7 @@ impl WriteTrace for TraceModule { impl WriteTrace for TraceInstance { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgModuleBody { properties } = arg.module_body(); + let ArgModuleBody { properties, scope } = arg.module_body(); let Self { name: _, instance_io, @@ -534,15 +592,16 @@ impl WriteTrace for TraceInstance { sink_var_type: "wire", duplex_var_type: "wire", properties, + scope, }, )?; - module.write_trace(writer, ArgModule { properties }) + module.write_trace(writer, ArgModule { properties, scope }) } } impl WriteTrace for TraceMem { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgModuleBody { properties } = arg.module_body(); + let ArgModuleBody { properties, scope } = arg.module_body(); let Self { id, name, @@ -551,27 +610,41 @@ impl WriteTrace for TraceMem { ports, array_type, } = self; - write_vcd_scope(writer, "struct", &*name, |writer| { - write_vcd_scope(writer, "struct", "contents", |writer| { - for element_index in 0..array_type.len() { - write_vcd_scope(writer, "struct", &format!("[{element_index}]"), |writer| { - properties.memory_properties[id.as_usize()].element_index = element_index; - properties.memory_properties[id.as_usize()].element_part_index = 0; - element_type.write_trace( + write_vcd_scope(writer, "struct", name, scope, |writer, scope| { + write_vcd_scope( + writer, + "struct", + "contents".intern(), + scope, + |writer, scope| { + for element_index in 0..array_type.len() { + write_vcd_scope( writer, - ArgInType { - source_var_type: "reg", - sink_var_type: "reg", - duplex_var_type: "reg", - properties, + "struct", + Intern::intern_owned(format!("[{element_index}]")), + scope, + |writer, scope| { + 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 { - port.write_trace(writer, ArgModuleBody { properties })?; + port.write_trace(writer, ArgModuleBody { properties, scope })?; } Ok(()) }) @@ -580,7 +653,7 @@ impl WriteTrace for TraceMem { impl WriteTrace for TraceMemPort { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgModuleBody { properties } = arg.module_body(); + let ArgModuleBody { properties, scope } = arg.module_body(); let Self { name: _, bundle, @@ -593,6 +666,7 @@ impl WriteTrace for TraceMemPort { sink_var_type: "wire", duplex_var_type: "wire", properties, + scope, }, ) } @@ -600,7 +674,7 @@ impl WriteTrace for TraceMemPort { impl WriteTrace for TraceWire { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgModuleBody { properties } = arg.module_body(); + let ArgModuleBody { properties, scope } = arg.module_body(); let Self { name: _, child, @@ -613,6 +687,7 @@ impl WriteTrace for TraceWire { sink_var_type: "wire", duplex_var_type: "wire", properties, + scope, }, ) } @@ -620,7 +695,7 @@ impl WriteTrace for TraceWire { impl WriteTrace for TraceReg { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgModuleBody { properties } = arg.module_body(); + let ArgModuleBody { properties, scope } = arg.module_body(); let Self { name: _, child, @@ -633,6 +708,7 @@ impl WriteTrace for TraceReg { sink_var_type: "reg", duplex_var_type: "reg", properties, + scope, }, ) } @@ -640,7 +716,7 @@ impl WriteTrace for TraceReg { impl WriteTrace for TraceModuleIO { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgModuleBody { properties } = arg.module_body(); + let ArgModuleBody { properties, scope } = arg.module_body(); let Self { name: _, child, @@ -654,6 +730,7 @@ impl WriteTrace for TraceModuleIO { sink_var_type: "wire", duplex_var_type: "wire", properties, + scope, }, ) } @@ -661,16 +738,31 @@ impl WriteTrace for TraceModuleIO { impl WriteTrace for TraceBundle { fn write_trace(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 { name, fields, ty: _, flow: _, } = self; - write_vcd_scope(writer, "struct", &name, |writer| { + write_vcd_scope(writer, "struct", name, scope, |writer, scope| { 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(()) }) @@ -679,16 +771,31 @@ impl WriteTrace for TraceBundle { impl WriteTrace for TraceArray { fn write_trace(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 { name, elements, ty: _, flow: _, } = self; - write_vcd_scope(writer, "struct", &name, |writer| { + write_vcd_scope(writer, "struct", name, scope, |writer, scope| { 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(()) }) @@ -697,7 +804,13 @@ impl WriteTrace for TraceArray { impl WriteTrace for TraceEnumWithFields { fn write_trace(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 { name, discriminant, @@ -705,10 +818,28 @@ impl WriteTrace for TraceEnumWithFields { ty: _, flow: _, } = self; - write_vcd_scope(writer, "struct", &name, |writer| { - discriminant.write_trace(writer, arg.reborrow())?; + write_vcd_scope(writer, "struct", name, scope, |writer, scope| { + discriminant.write_trace( + writer, + ArgInType { + source_var_type, + sink_var_type, + duplex_var_type, + properties, + scope, + }, + )?; 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(()) }) @@ -744,6 +875,7 @@ impl TraceWriterDecls for VcdWriterDecls { &mut writer, ArgModule { properties: &mut properties, + scope: &mut Scope::default(), }, )?; writeln!(writer, "$enddefinitions $end")?; @@ -798,9 +930,7 @@ fn write_string_value_change( value: impl fmt::Display, id: usize, ) -> io::Result<()> { - writer.write_all(b"s")?; - write_escaped(writer, value)?; - writer.write_all(b" ")?; + write!(writer, "s{} ", Escaped(value))?; write_vcd_id(writer, id)?; writer.write_all(b"\n") } @@ -946,3 +1076,49 @@ impl fmt::Debug for VcdWriter { .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}"), + ); + } + } +} diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index a249235..13e84eb 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -1246,3 +1246,33 @@ fn test_memories3() { 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!(); + } +} diff --git a/crates/fayalite/tests/sim/expected/duplicate_names.txt b/crates/fayalite/tests/sim/expected/duplicate_names.txt new file mode 100644 index 0000000..8a59861 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/duplicate_names.txt @@ -0,0 +1,153 @@ +Simulation { + state: State { + insns: Insns { + state_layout: StateLayout { + ty: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + 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 { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + insns: [ + // at: module-XXXXXXXXXX.rs:1:1 + 0: Const { + dest: StatePartIndex(3), // (0x6) SlotDebugData { name: "", ty: UInt<8> }, + value: 0x6, + }, + // at: module-XXXXXXXXXX.rs:5:1 + 1: Copy { + dest: StatePartIndex(2), // (0x6) SlotDebugData { name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w", ty: UInt<8> }, + src: StatePartIndex(3), // (0x6) SlotDebugData { name: "", ty: UInt<8> }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 2: Const { + dest: StatePartIndex(1), // (0x5) SlotDebugData { name: "", ty: UInt<8> }, + value: 0x5, + }, + // at: module-XXXXXXXXXX.rs:3:1 + 3: Copy { + dest: StatePartIndex(0), // (0x5) SlotDebugData { name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w", ty: UInt<8> }, + src: StatePartIndex(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: ::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(0), + ty: UInt<8>, + }, + state: 0x05, + last_state: 0x05, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigUInt { + index: StatePartIndex(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: [], + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/duplicate_names.vcd b/crates/fayalite/tests/sim/expected/duplicate_names.vcd new file mode 100644 index 0000000..1e9f6c6 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/duplicate_names.vcd @@ -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 diff --git a/crates/fayalite/tests/sim/expected/memories.vcd b/crates/fayalite/tests/sim/expected/memories.vcd index 72af410..bedc354 100644 --- a/crates/fayalite/tests/sim/expected/memories.vcd +++ b/crates/fayalite/tests/sim/expected/memories.vcd @@ -24,97 +24,97 @@ $upscope $end $upscope $end $scope struct mem $end $scope struct contents $end -$scope struct [0] $end +$scope struct \[0] $end $scope struct mem $end $var reg 8 9 \0 $end $var reg 8 I \1 $end $upscope $end $upscope $end -$scope struct [1] $end +$scope struct \[1] $end $scope struct mem $end $var reg 8 : \0 $end $var reg 8 J \1 $end $upscope $end $upscope $end -$scope struct [2] $end +$scope struct \[2] $end $scope struct mem $end $var reg 8 ; \0 $end $var reg 8 K \1 $end $upscope $end $upscope $end -$scope struct [3] $end +$scope struct \[3] $end $scope struct mem $end $var reg 8 < \0 $end $var reg 8 L \1 $end $upscope $end $upscope $end -$scope struct [4] $end +$scope struct \[4] $end $scope struct mem $end $var reg 8 = \0 $end $var reg 8 M \1 $end $upscope $end $upscope $end -$scope struct [5] $end +$scope struct \[5] $end $scope struct mem $end $var reg 8 > \0 $end $var reg 8 N \1 $end $upscope $end $upscope $end -$scope struct [6] $end +$scope struct \[6] $end $scope struct mem $end $var reg 8 ? \0 $end $var reg 8 O \1 $end $upscope $end $upscope $end -$scope struct [7] $end +$scope struct \[7] $end $scope struct mem $end $var reg 8 @ \0 $end $var reg 8 P \1 $end $upscope $end $upscope $end -$scope struct [8] $end +$scope struct \[8] $end $scope struct mem $end $var reg 8 A \0 $end $var reg 8 Q \1 $end $upscope $end $upscope $end -$scope struct [9] $end +$scope struct \[9] $end $scope struct mem $end $var reg 8 B \0 $end $var reg 8 R \1 $end $upscope $end $upscope $end -$scope struct [10] $end +$scope struct \[10] $end $scope struct mem $end $var reg 8 C \0 $end $var reg 8 S \1 $end $upscope $end $upscope $end -$scope struct [11] $end +$scope struct \[11] $end $scope struct mem $end $var reg 8 D \0 $end $var reg 8 T \1 $end $upscope $end $upscope $end -$scope struct [12] $end +$scope struct \[12] $end $scope struct mem $end $var reg 8 E \0 $end $var reg 8 U \1 $end $upscope $end $upscope $end -$scope struct [13] $end +$scope struct \[13] $end $scope struct mem $end $var reg 8 F \0 $end $var reg 8 V \1 $end $upscope $end $upscope $end -$scope struct [14] $end +$scope struct \[14] $end $scope struct mem $end $var reg 8 G \0 $end $var reg 8 W \1 $end $upscope $end $upscope $end -$scope struct [15] $end +$scope struct \[15] $end $scope struct mem $end $var reg 8 H \0 $end $var reg 8 X \1 $end diff --git a/crates/fayalite/tests/sim/expected/memories2.vcd b/crates/fayalite/tests/sim/expected/memories2.vcd index bd48f24..4039754 100644 --- a/crates/fayalite/tests/sim/expected/memories2.vcd +++ b/crates/fayalite/tests/sim/expected/memories2.vcd @@ -11,31 +11,31 @@ $var wire 1 ' wmask $end $upscope $end $scope struct mem $end $scope struct contents $end -$scope struct [0] $end +$scope struct \[0] $end $scope struct mem $end $var string 1 1 \$tag $end $var reg 1 6 HdlSome $end $upscope $end $upscope $end -$scope struct [1] $end +$scope struct \[1] $end $scope struct mem $end $var string 1 2 \$tag $end $var reg 1 7 HdlSome $end $upscope $end $upscope $end -$scope struct [2] $end +$scope struct \[2] $end $scope struct mem $end $var string 1 3 \$tag $end $var reg 1 8 HdlSome $end $upscope $end $upscope $end -$scope struct [3] $end +$scope struct \[3] $end $scope struct mem $end $var string 1 4 \$tag $end $var reg 1 9 HdlSome $end $upscope $end $upscope $end -$scope struct [4] $end +$scope struct \[4] $end $scope struct mem $end $var string 1 5 \$tag $end $var reg 1 : HdlSome $end diff --git a/crates/fayalite/tests/sim/expected/memories3.vcd b/crates/fayalite/tests/sim/expected/memories3.vcd index 328fcaa..5768560 100644 --- a/crates/fayalite/tests/sim/expected/memories3.vcd +++ b/crates/fayalite/tests/sim/expected/memories3.vcd @@ -42,7 +42,7 @@ $upscope $end $upscope $end $scope struct mem $end $scope struct contents $end -$scope struct [0] $end +$scope struct \[0] $end $scope struct mem $end $var reg 8 ] \[0] $end $var reg 8 e \[1] $end @@ -54,7 +54,7 @@ $var reg 8 /" \[6] $end $var reg 8 7" \[7] $end $upscope $end $upscope $end -$scope struct [1] $end +$scope struct \[1] $end $scope struct mem $end $var reg 8 ^ \[0] $end $var reg 8 f \[1] $end @@ -66,7 +66,7 @@ $var reg 8 0" \[6] $end $var reg 8 8" \[7] $end $upscope $end $upscope $end -$scope struct [2] $end +$scope struct \[2] $end $scope struct mem $end $var reg 8 _ \[0] $end $var reg 8 g \[1] $end @@ -78,7 +78,7 @@ $var reg 8 1" \[6] $end $var reg 8 9" \[7] $end $upscope $end $upscope $end -$scope struct [3] $end +$scope struct \[3] $end $scope struct mem $end $var reg 8 ` \[0] $end $var reg 8 h \[1] $end @@ -90,7 +90,7 @@ $var reg 8 2" \[6] $end $var reg 8 :" \[7] $end $upscope $end $upscope $end -$scope struct [4] $end +$scope struct \[4] $end $scope struct mem $end $var reg 8 a \[0] $end $var reg 8 i \[1] $end @@ -102,7 +102,7 @@ $var reg 8 3" \[6] $end $var reg 8 ;" \[7] $end $upscope $end $upscope $end -$scope struct [5] $end +$scope struct \[5] $end $scope struct mem $end $var reg 8 b \[0] $end $var reg 8 j \[1] $end @@ -114,7 +114,7 @@ $var reg 8 4" \[6] $end $var reg 8 <" \[7] $end $upscope $end $upscope $end -$scope struct [6] $end +$scope struct \[6] $end $scope struct mem $end $var reg 8 c \[0] $end $var reg 8 k \[1] $end @@ -126,7 +126,7 @@ $var reg 8 5" \[6] $end $var reg 8 =" \[7] $end $upscope $end $upscope $end -$scope struct [7] $end +$scope struct \[7] $end $scope struct mem $end $var reg 8 d \[0] $end $var reg 8 l \[1] $end From 404a2ee043b851e00e7571b5779714812accc5f7 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 12 Jan 2025 21:36:54 -0800 Subject: [PATCH 06/10] tests/sim: add test_array_rw --- crates/fayalite/tests/sim.rs | 131 + .../fayalite/tests/sim/expected/array_rw.txt | 2859 +++++++++++++++++ .../fayalite/tests/sim/expected/array_rw.vcd | 283 ++ 3 files changed, 3273 insertions(+) create mode 100644 crates/fayalite/tests/sim/expected/array_rw.txt create mode 100644 crates/fayalite/tests/sim/expected/array_rw.vcd diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index 13e84eb..cae08de 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -1276,3 +1276,134 @@ fn test_duplicate_names() { panic!(); } } + +#[hdl_module(outline_generated)] +pub fn array_rw() { + #[hdl] + let array_in: Array, 16> = m.input(); + #[hdl] + let array_out: Array, 16> = m.output(); + #[hdl] + let read_index: UInt<8> = m.input(); + #[hdl] + let read_data: UInt<8> = m.output(); + #[hdl] + let write_index: UInt<8> = m.input(); + #[hdl] + let write_data: UInt<8> = m.input(); + #[hdl] + let write_en: Bool = m.input(); + #[hdl] + let array_wire = wire(); + connect(array_wire, array_in); + connect(array_out, array_wire); + #[hdl] + if write_en { + connect(array_wire[write_index], write_data); + } + connect(read_data, array_wire[read_index]); +} + +#[test] +fn test_array_rw() { + let _n = SourceLocation::normalize_files_for_tests(); + let mut sim = Simulation::new(array_rw()); + let mut writer = RcWriter::default(); + sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); + #[derive(Debug, PartialEq)] + struct State { + array_in: [u8; 16], + array_out: [u8; 16], + read_index: u8, + read_data: u8, + write_index: u8, + write_data: u8, + write_en: bool, + } + let mut states = Vec::new(); + let array_in = [ + 0xFFu8, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, // + 0x00u8, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, + ]; + for i in 0..=16 { + states.push(State { + array_in, + array_out: array_in, + read_index: i, + read_data: array_in.get(i as usize).copied().unwrap_or(0), + write_index: 0, + write_data: 0, + write_en: false, + }); + } + for i in 0..=16u8 { + let mut array_out = array_in; + let write_data = i.wrapping_mul(i); + if let Some(v) = array_out.get_mut(i as usize) { + *v = write_data; + } + states.push(State { + array_in, + array_out, + read_index: 0, + read_data: array_out[0], + write_index: i, + write_data, + write_en: true, + }); + } + for (cycle, expected) in states.into_iter().enumerate() { + let State { + array_in, + array_out: _, + read_index, + read_data: _, + write_index, + write_data, + write_en, + } = expected; + sim.write(sim.io().array_in, array_in); + sim.write(sim.io().read_index, read_index); + sim.write(sim.io().write_index, write_index); + sim.write(sim.io().write_data, write_data); + sim.write(sim.io().write_en, write_en); + sim.advance_time(SimDuration::from_micros(1)); + let array_out = std::array::from_fn(|index| { + sim.read_bool_or_int(sim.io().array_out[index]) + .to_bigint() + .try_into() + .expect("known to be in range") + }); + let read_data = sim + .read_bool_or_int(sim.io().read_data) + .to_bigint() + .try_into() + .expect("known to be in range"); + let state = State { + array_in, + array_out, + read_index, + read_data, + write_index, + write_data, + write_en, + }; + assert_eq!( + state, + expected, + "vcd:\n{}\ncycle: {cycle}", + String::from_utf8(writer.take()).unwrap(), + ); + } + sim.flush_traces().unwrap(); + let vcd = String::from_utf8(writer.take()).unwrap(); + println!("####### VCD:\n{vcd}\n#######"); + if vcd != include_str!("sim/expected/array_rw.vcd") { + panic!(); + } + let sim_debug = format!("{sim:#?}"); + println!("#######\n{sim_debug}\n#######"); + if sim_debug != include_str!("sim/expected/array_rw.txt") { + panic!(); + } +} diff --git a/crates/fayalite/tests/sim/expected/array_rw.txt b/crates/fayalite/tests/sim/expected/array_rw.txt new file mode 100644 index 0000000..f016e72 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/array_rw.txt @@ -0,0 +1,2859 @@ +Simulation { + state: State { + insns: Insns { + state_layout: StateLayout { + ty: TypeLayout { + small_slots: StatePartLayout { + len: 2, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + big_slots: StatePartLayout { + len: 54, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[2]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[3]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[4]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[5]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[6]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[7]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[8]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[9]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[10]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[11]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[12]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[13]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[14]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[15]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[2]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[3]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[4]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[5]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[6]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[7]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[8]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[9]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[10]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[11]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[12]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[13]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[14]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[15]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::read_index", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::read_data", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::write_index", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::write_data", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::write_en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[2]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[3]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[4]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[5]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[6]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[7]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[8]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[9]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[10]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[11]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[12]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[13]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[14]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[15]", + ty: UInt<8>, + }, + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + memories: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + insns: [ + // at: module-XXXXXXXXXX.rs:1:1 + 0: CastBigToArrayIndex { + dest: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: UInt<8> }, + src: StatePartIndex(32), // (0x0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::read_index", ty: UInt<8> }, + }, + 1: CastBigToArrayIndex { + dest: StatePartIndex(0), // (0x10 16) SlotDebugData { name: "", ty: UInt<8> }, + src: StatePartIndex(34), // (0x10) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::write_index", ty: UInt<8> }, + }, + // at: module-XXXXXXXXXX.rs:10:1 + 2: Copy { + dest: StatePartIndex(37), // (0xff) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[0]", ty: UInt<8> }, + src: StatePartIndex(0), // (0xff) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[0]", ty: UInt<8> }, + }, + 3: Copy { + dest: StatePartIndex(38), // (0x7f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[1]", ty: UInt<8> }, + src: StatePartIndex(1), // (0x7f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[1]", ty: UInt<8> }, + }, + 4: Copy { + dest: StatePartIndex(39), // (0x3f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[2]", ty: UInt<8> }, + src: StatePartIndex(2), // (0x3f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[2]", ty: UInt<8> }, + }, + 5: Copy { + dest: StatePartIndex(40), // (0x1f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[3]", ty: UInt<8> }, + src: StatePartIndex(3), // (0x1f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[3]", ty: UInt<8> }, + }, + 6: Copy { + dest: StatePartIndex(41), // (0xf) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[4]", ty: UInt<8> }, + src: StatePartIndex(4), // (0xf) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[4]", ty: UInt<8> }, + }, + 7: Copy { + dest: StatePartIndex(42), // (0x7) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[5]", ty: UInt<8> }, + src: StatePartIndex(5), // (0x7) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[5]", ty: UInt<8> }, + }, + 8: Copy { + dest: StatePartIndex(43), // (0x3) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[6]", ty: UInt<8> }, + src: StatePartIndex(6), // (0x3) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[6]", ty: UInt<8> }, + }, + 9: Copy { + dest: StatePartIndex(44), // (0x1) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[7]", ty: UInt<8> }, + src: StatePartIndex(7), // (0x1) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[7]", ty: UInt<8> }, + }, + 10: Copy { + dest: StatePartIndex(45), // (0x0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[8]", ty: UInt<8> }, + src: StatePartIndex(8), // (0x0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[8]", ty: UInt<8> }, + }, + 11: Copy { + dest: StatePartIndex(46), // (0x80) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[9]", ty: UInt<8> }, + src: StatePartIndex(9), // (0x80) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[9]", ty: UInt<8> }, + }, + 12: Copy { + dest: StatePartIndex(47), // (0xc0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[10]", ty: UInt<8> }, + src: StatePartIndex(10), // (0xc0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[10]", ty: UInt<8> }, + }, + 13: Copy { + dest: StatePartIndex(48), // (0xe0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[11]", ty: UInt<8> }, + src: StatePartIndex(11), // (0xe0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[11]", ty: UInt<8> }, + }, + 14: Copy { + dest: StatePartIndex(49), // (0xf0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[12]", ty: UInt<8> }, + src: StatePartIndex(12), // (0xf0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[12]", ty: UInt<8> }, + }, + 15: Copy { + dest: StatePartIndex(50), // (0xf8) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[13]", ty: UInt<8> }, + src: StatePartIndex(13), // (0xf8) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[13]", ty: UInt<8> }, + }, + 16: Copy { + dest: StatePartIndex(51), // (0xfc) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[14]", ty: UInt<8> }, + src: StatePartIndex(14), // (0xfc) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[14]", ty: UInt<8> }, + }, + 17: Copy { + dest: StatePartIndex(52), // (0xfe) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[15]", ty: UInt<8> }, + src: StatePartIndex(15), // (0xfe) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[15]", ty: UInt<8> }, + }, + // at: module-XXXXXXXXXX.rs:12:1 + 18: BranchIfZero { + target: 20, + value: StatePartIndex(36), // (0x1) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::write_en", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:13:1 + 19: WriteIndexed { + dest: StatePartIndex(37) /* (0xff) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[0]", ty: UInt<8> } */ [StatePartIndex(0) /* (0x10 16) SlotDebugData { name: "", ty: UInt<8> } */ , len=16, stride=1],, + src: StatePartIndex(35), // (0x0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::write_data", ty: UInt<8> }, + }, + // at: module-XXXXXXXXXX.rs:11:1 + 20: Copy { + dest: StatePartIndex(16), // (0xff) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[0]", ty: UInt<8> }, + src: StatePartIndex(37), // (0xff) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[0]", ty: UInt<8> }, + }, + 21: Copy { + dest: StatePartIndex(17), // (0x7f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[1]", ty: UInt<8> }, + src: StatePartIndex(38), // (0x7f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[1]", ty: UInt<8> }, + }, + 22: Copy { + dest: StatePartIndex(18), // (0x3f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[2]", ty: UInt<8> }, + src: StatePartIndex(39), // (0x3f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[2]", ty: UInt<8> }, + }, + 23: Copy { + dest: StatePartIndex(19), // (0x1f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[3]", ty: UInt<8> }, + src: StatePartIndex(40), // (0x1f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[3]", ty: UInt<8> }, + }, + 24: Copy { + dest: StatePartIndex(20), // (0xf) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[4]", ty: UInt<8> }, + src: StatePartIndex(41), // (0xf) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[4]", ty: UInt<8> }, + }, + 25: Copy { + dest: StatePartIndex(21), // (0x7) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[5]", ty: UInt<8> }, + src: StatePartIndex(42), // (0x7) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[5]", ty: UInt<8> }, + }, + 26: Copy { + dest: StatePartIndex(22), // (0x3) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[6]", ty: UInt<8> }, + src: StatePartIndex(43), // (0x3) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[6]", ty: UInt<8> }, + }, + 27: Copy { + dest: StatePartIndex(23), // (0x1) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[7]", ty: UInt<8> }, + src: StatePartIndex(44), // (0x1) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[7]", ty: UInt<8> }, + }, + 28: Copy { + dest: StatePartIndex(24), // (0x0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[8]", ty: UInt<8> }, + src: StatePartIndex(45), // (0x0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[8]", ty: UInt<8> }, + }, + 29: Copy { + dest: StatePartIndex(25), // (0x80) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[9]", ty: UInt<8> }, + src: StatePartIndex(46), // (0x80) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[9]", ty: UInt<8> }, + }, + 30: Copy { + dest: StatePartIndex(26), // (0xc0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[10]", ty: UInt<8> }, + src: StatePartIndex(47), // (0xc0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[10]", ty: UInt<8> }, + }, + 31: Copy { + dest: StatePartIndex(27), // (0xe0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[11]", ty: UInt<8> }, + src: StatePartIndex(48), // (0xe0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[11]", ty: UInt<8> }, + }, + 32: Copy { + dest: StatePartIndex(28), // (0xf0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[12]", ty: UInt<8> }, + src: StatePartIndex(49), // (0xf0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[12]", ty: UInt<8> }, + }, + 33: Copy { + dest: StatePartIndex(29), // (0xf8) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[13]", ty: UInt<8> }, + src: StatePartIndex(50), // (0xf8) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[13]", ty: UInt<8> }, + }, + 34: Copy { + dest: StatePartIndex(30), // (0xfc) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[14]", ty: UInt<8> }, + src: StatePartIndex(51), // (0xfc) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[14]", ty: UInt<8> }, + }, + 35: Copy { + dest: StatePartIndex(31), // (0xfe) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[15]", ty: UInt<8> }, + src: StatePartIndex(52), // (0xfe) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[15]", ty: UInt<8> }, + }, + // at: module-XXXXXXXXXX.rs:14:1 + 36: ReadIndexed { + dest: StatePartIndex(53), // (0xff) SlotDebugData { name: "", ty: UInt<8> }, + src: StatePartIndex(37) /* (0xff) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[0]", ty: UInt<8> } */ [StatePartIndex(1) /* (0x0 0) SlotDebugData { name: "", ty: UInt<8> } */ , len=16, stride=1],, + }, + 37: Copy { + dest: StatePartIndex(33), // (0xff) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::read_data", ty: UInt<8> }, + src: StatePartIndex(53), // (0xff) SlotDebugData { name: "", ty: UInt<8> }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 38: Return, + ], + .. + }, + pc: 38, + memory_write_log: [], + memories: StatePart { + value: [], + }, + small_slots: StatePart { + value: [ + 16, + 0, + ], + }, + big_slots: StatePart { + value: [ + 255, + 127, + 63, + 31, + 15, + 7, + 3, + 1, + 0, + 128, + 192, + 224, + 240, + 248, + 252, + 254, + 255, + 127, + 63, + 31, + 15, + 7, + 3, + 1, + 0, + 128, + 192, + 224, + 240, + 248, + 252, + 254, + 0, + 255, + 16, + 0, + 1, + 255, + 127, + 63, + 31, + 15, + 7, + 3, + 1, + 0, + 128, + 192, + 224, + 240, + 248, + 252, + 254, + 255, + ], + }, + }, + io: Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }, + uninitialized_inputs: {}, + io_targets: { + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in: CompiledValue { + layout: CompiledTypeLayout { + ty: Array, 16>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 16, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[2]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[3]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[4]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[5]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[6]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[7]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[8]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[9]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[10]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[11]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[12]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[13]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[14]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[15]", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Array { + element: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + }, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 0, len: 16 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[0]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 0, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[10]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 10, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[11]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 11, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[12]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 12, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[13]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 13, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[14]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 14, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[15]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 15, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[1]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 1, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[2]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 2, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[3]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 3, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[4]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 4, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[5]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 5, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[6]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 6, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[7]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 7, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[8]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 8, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[9]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 9, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out: CompiledValue { + layout: CompiledTypeLayout { + ty: Array, 16>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 16, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[2]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[3]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[4]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[5]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[6]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[7]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[8]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[9]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[10]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[11]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[12]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[13]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[14]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[15]", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Array { + element: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + }, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 16, len: 16 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[0]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 16, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[10]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 26, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[11]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 27, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[12]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 28, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[13]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 29, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[14]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 30, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[15]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 31, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[1]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 17, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[2]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 18, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[3]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 19, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[4]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 20, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[5]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 21, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[6]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 22, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[7]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 23, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[8]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 24, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[9]: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 25, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.read_data: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::read_data", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 33, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.read_index: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::read_index", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 32, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.write_data: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::write_data", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 35, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.write_en: CompiledValue { + layout: CompiledTypeLayout { + ty: Bool, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::write_en", + ty: Bool, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 36, len: 1 }, + }, + write: None, + }, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.write_index: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::write_index", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 34, len: 1 }, + }, + write: None, + }, + }, + made_initial_step: true, + needs_settle: false, + trace_decls: TraceModule { + name: "array_rw", + children: [ + TraceModuleIO { + name: "array_in", + child: TraceArray { + name: "array_in", + elements: [ + TraceUInt { + location: TraceScalarId(0), + name: "[0]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(1), + name: "[1]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(2), + name: "[2]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(3), + name: "[3]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(4), + name: "[4]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(5), + name: "[5]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(6), + name: "[6]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(7), + name: "[7]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(8), + name: "[8]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(9), + name: "[9]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(10), + name: "[10]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(11), + name: "[11]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(12), + name: "[12]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(13), + name: "[13]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(14), + name: "[14]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(15), + name: "[15]", + ty: UInt<8>, + flow: Source, + }, + ], + ty: Array, 16>, + flow: Source, + }, + ty: Array, 16>, + flow: Source, + }, + TraceModuleIO { + name: "array_out", + child: TraceArray { + name: "array_out", + elements: [ + TraceUInt { + location: TraceScalarId(16), + name: "[0]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(17), + name: "[1]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(18), + name: "[2]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(19), + name: "[3]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(20), + name: "[4]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(21), + name: "[5]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(22), + name: "[6]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(23), + name: "[7]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(24), + name: "[8]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(25), + name: "[9]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(26), + name: "[10]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(27), + name: "[11]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(28), + name: "[12]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(29), + name: "[13]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(30), + name: "[14]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(31), + name: "[15]", + ty: UInt<8>, + flow: Sink, + }, + ], + ty: Array, 16>, + flow: Sink, + }, + ty: Array, 16>, + flow: Sink, + }, + TraceModuleIO { + name: "read_index", + child: TraceUInt { + location: TraceScalarId(32), + name: "read_index", + ty: UInt<8>, + flow: Source, + }, + ty: UInt<8>, + flow: Source, + }, + TraceModuleIO { + name: "read_data", + child: TraceUInt { + location: TraceScalarId(33), + name: "read_data", + ty: UInt<8>, + flow: Sink, + }, + ty: UInt<8>, + flow: Sink, + }, + TraceModuleIO { + name: "write_index", + child: TraceUInt { + location: TraceScalarId(34), + name: "write_index", + ty: UInt<8>, + flow: Source, + }, + ty: UInt<8>, + flow: Source, + }, + TraceModuleIO { + name: "write_data", + child: TraceUInt { + location: TraceScalarId(35), + name: "write_data", + ty: UInt<8>, + flow: Source, + }, + ty: UInt<8>, + flow: Source, + }, + TraceModuleIO { + name: "write_en", + child: TraceBool { + location: TraceScalarId(36), + name: "write_en", + flow: Source, + }, + ty: Bool, + flow: Source, + }, + TraceWire { + name: "array_wire", + child: TraceArray { + name: "array_wire", + elements: [ + TraceUInt { + location: TraceScalarId(37), + name: "[0]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(38), + name: "[1]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(39), + name: "[2]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(40), + name: "[3]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(41), + name: "[4]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(42), + name: "[5]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(43), + name: "[6]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(44), + name: "[7]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(45), + name: "[8]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(46), + name: "[9]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(47), + name: "[10]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(48), + name: "[11]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(49), + name: "[12]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(50), + name: "[13]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(51), + name: "[14]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(52), + name: "[15]", + ty: UInt<8>, + flow: Duplex, + }, + ], + ty: Array, 16>, + flow: Duplex, + }, + ty: Array, 16>, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigUInt { + index: StatePartIndex(0), + ty: UInt<8>, + }, + state: 0xff, + last_state: 0xff, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigUInt { + index: StatePartIndex(1), + ty: UInt<8>, + }, + state: 0x7f, + last_state: 0x7f, + }, + SimTrace { + id: TraceScalarId(2), + kind: BigUInt { + index: StatePartIndex(2), + ty: UInt<8>, + }, + state: 0x3f, + last_state: 0x3f, + }, + SimTrace { + id: TraceScalarId(3), + kind: BigUInt { + index: StatePartIndex(3), + ty: UInt<8>, + }, + state: 0x1f, + last_state: 0x1f, + }, + SimTrace { + id: TraceScalarId(4), + kind: BigUInt { + index: StatePartIndex(4), + ty: UInt<8>, + }, + state: 0x0f, + last_state: 0x0f, + }, + SimTrace { + id: TraceScalarId(5), + kind: BigUInt { + index: StatePartIndex(5), + ty: UInt<8>, + }, + state: 0x07, + last_state: 0x07, + }, + SimTrace { + id: TraceScalarId(6), + kind: BigUInt { + index: StatePartIndex(6), + ty: UInt<8>, + }, + state: 0x03, + last_state: 0x03, + }, + SimTrace { + id: TraceScalarId(7), + kind: BigUInt { + index: StatePartIndex(7), + ty: UInt<8>, + }, + state: 0x01, + last_state: 0x01, + }, + SimTrace { + id: TraceScalarId(8), + kind: BigUInt { + index: StatePartIndex(8), + ty: UInt<8>, + }, + state: 0x00, + last_state: 0x00, + }, + SimTrace { + id: TraceScalarId(9), + kind: BigUInt { + index: StatePartIndex(9), + ty: UInt<8>, + }, + state: 0x80, + last_state: 0x80, + }, + SimTrace { + id: TraceScalarId(10), + kind: BigUInt { + index: StatePartIndex(10), + ty: UInt<8>, + }, + state: 0xc0, + last_state: 0xc0, + }, + SimTrace { + id: TraceScalarId(11), + kind: BigUInt { + index: StatePartIndex(11), + ty: UInt<8>, + }, + state: 0xe0, + last_state: 0xe0, + }, + SimTrace { + id: TraceScalarId(12), + kind: BigUInt { + index: StatePartIndex(12), + ty: UInt<8>, + }, + state: 0xf0, + last_state: 0xf0, + }, + SimTrace { + id: TraceScalarId(13), + kind: BigUInt { + index: StatePartIndex(13), + ty: UInt<8>, + }, + state: 0xf8, + last_state: 0xf8, + }, + SimTrace { + id: TraceScalarId(14), + kind: BigUInt { + index: StatePartIndex(14), + ty: UInt<8>, + }, + state: 0xfc, + last_state: 0xfc, + }, + SimTrace { + id: TraceScalarId(15), + kind: BigUInt { + index: StatePartIndex(15), + ty: UInt<8>, + }, + state: 0xfe, + last_state: 0xfe, + }, + SimTrace { + id: TraceScalarId(16), + kind: BigUInt { + index: StatePartIndex(16), + ty: UInt<8>, + }, + state: 0xff, + last_state: 0xff, + }, + SimTrace { + id: TraceScalarId(17), + kind: BigUInt { + index: StatePartIndex(17), + ty: UInt<8>, + }, + state: 0x7f, + last_state: 0x7f, + }, + SimTrace { + id: TraceScalarId(18), + kind: BigUInt { + index: StatePartIndex(18), + ty: UInt<8>, + }, + state: 0x3f, + last_state: 0x3f, + }, + SimTrace { + id: TraceScalarId(19), + kind: BigUInt { + index: StatePartIndex(19), + ty: UInt<8>, + }, + state: 0x1f, + last_state: 0x1f, + }, + SimTrace { + id: TraceScalarId(20), + kind: BigUInt { + index: StatePartIndex(20), + ty: UInt<8>, + }, + state: 0x0f, + last_state: 0x0f, + }, + SimTrace { + id: TraceScalarId(21), + kind: BigUInt { + index: StatePartIndex(21), + ty: UInt<8>, + }, + state: 0x07, + last_state: 0x07, + }, + SimTrace { + id: TraceScalarId(22), + kind: BigUInt { + index: StatePartIndex(22), + ty: UInt<8>, + }, + state: 0x03, + last_state: 0x03, + }, + SimTrace { + id: TraceScalarId(23), + kind: BigUInt { + index: StatePartIndex(23), + ty: UInt<8>, + }, + state: 0x01, + last_state: 0x01, + }, + SimTrace { + id: TraceScalarId(24), + kind: BigUInt { + index: StatePartIndex(24), + ty: UInt<8>, + }, + state: 0x00, + last_state: 0x00, + }, + SimTrace { + id: TraceScalarId(25), + kind: BigUInt { + index: StatePartIndex(25), + ty: UInt<8>, + }, + state: 0x80, + last_state: 0x80, + }, + SimTrace { + id: TraceScalarId(26), + kind: BigUInt { + index: StatePartIndex(26), + ty: UInt<8>, + }, + state: 0xc0, + last_state: 0xc0, + }, + SimTrace { + id: TraceScalarId(27), + kind: BigUInt { + index: StatePartIndex(27), + ty: UInt<8>, + }, + state: 0xe0, + last_state: 0xe0, + }, + SimTrace { + id: TraceScalarId(28), + kind: BigUInt { + index: StatePartIndex(28), + ty: UInt<8>, + }, + state: 0xf0, + last_state: 0xf0, + }, + SimTrace { + id: TraceScalarId(29), + kind: BigUInt { + index: StatePartIndex(29), + ty: UInt<8>, + }, + state: 0xf8, + last_state: 0xf8, + }, + SimTrace { + id: TraceScalarId(30), + kind: BigUInt { + index: StatePartIndex(30), + ty: UInt<8>, + }, + state: 0xfc, + last_state: 0xfc, + }, + SimTrace { + id: TraceScalarId(31), + kind: BigUInt { + index: StatePartIndex(31), + ty: UInt<8>, + }, + state: 0xfe, + last_state: 0xe1, + }, + SimTrace { + id: TraceScalarId(32), + kind: BigUInt { + index: StatePartIndex(32), + ty: UInt<8>, + }, + state: 0x00, + last_state: 0x00, + }, + SimTrace { + id: TraceScalarId(33), + kind: BigUInt { + index: StatePartIndex(33), + ty: UInt<8>, + }, + state: 0xff, + last_state: 0xff, + }, + SimTrace { + id: TraceScalarId(34), + kind: BigUInt { + index: StatePartIndex(34), + ty: UInt<8>, + }, + state: 0x10, + last_state: 0x0f, + }, + SimTrace { + id: TraceScalarId(35), + kind: BigUInt { + index: StatePartIndex(35), + ty: UInt<8>, + }, + state: 0x00, + last_state: 0xe1, + }, + SimTrace { + id: TraceScalarId(36), + kind: BigBool { + index: StatePartIndex(36), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(37), + kind: BigUInt { + index: StatePartIndex(37), + ty: UInt<8>, + }, + state: 0xff, + last_state: 0xff, + }, + SimTrace { + id: TraceScalarId(38), + kind: BigUInt { + index: StatePartIndex(38), + ty: UInt<8>, + }, + state: 0x7f, + last_state: 0x7f, + }, + SimTrace { + id: TraceScalarId(39), + kind: BigUInt { + index: StatePartIndex(39), + ty: UInt<8>, + }, + state: 0x3f, + last_state: 0x3f, + }, + SimTrace { + id: TraceScalarId(40), + kind: BigUInt { + index: StatePartIndex(40), + ty: UInt<8>, + }, + state: 0x1f, + last_state: 0x1f, + }, + SimTrace { + id: TraceScalarId(41), + kind: BigUInt { + index: StatePartIndex(41), + ty: UInt<8>, + }, + state: 0x0f, + last_state: 0x0f, + }, + SimTrace { + id: TraceScalarId(42), + kind: BigUInt { + index: StatePartIndex(42), + ty: UInt<8>, + }, + state: 0x07, + last_state: 0x07, + }, + SimTrace { + id: TraceScalarId(43), + kind: BigUInt { + index: StatePartIndex(43), + ty: UInt<8>, + }, + state: 0x03, + last_state: 0x03, + }, + SimTrace { + id: TraceScalarId(44), + kind: BigUInt { + index: StatePartIndex(44), + ty: UInt<8>, + }, + state: 0x01, + last_state: 0x01, + }, + SimTrace { + id: TraceScalarId(45), + kind: BigUInt { + index: StatePartIndex(45), + ty: UInt<8>, + }, + state: 0x00, + last_state: 0x00, + }, + SimTrace { + id: TraceScalarId(46), + kind: BigUInt { + index: StatePartIndex(46), + ty: UInt<8>, + }, + state: 0x80, + last_state: 0x80, + }, + SimTrace { + id: TraceScalarId(47), + kind: BigUInt { + index: StatePartIndex(47), + ty: UInt<8>, + }, + state: 0xc0, + last_state: 0xc0, + }, + SimTrace { + id: TraceScalarId(48), + kind: BigUInt { + index: StatePartIndex(48), + ty: UInt<8>, + }, + state: 0xe0, + last_state: 0xe0, + }, + SimTrace { + id: TraceScalarId(49), + kind: BigUInt { + index: StatePartIndex(49), + ty: UInt<8>, + }, + state: 0xf0, + last_state: 0xf0, + }, + SimTrace { + id: TraceScalarId(50), + kind: BigUInt { + index: StatePartIndex(50), + ty: UInt<8>, + }, + state: 0xf8, + last_state: 0xf8, + }, + SimTrace { + id: TraceScalarId(51), + kind: BigUInt { + index: StatePartIndex(51), + ty: UInt<8>, + }, + state: 0xfc, + last_state: 0xfc, + }, + SimTrace { + id: TraceScalarId(52), + kind: BigUInt { + index: StatePartIndex(52), + ty: UInt<8>, + }, + state: 0xfe, + last_state: 0xe1, + }, + ], + trace_memories: {}, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + instant: 34 μs, + clocks_triggered: [], + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/array_rw.vcd b/crates/fayalite/tests/sim/expected/array_rw.vcd new file mode 100644 index 0000000..8ede394 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/array_rw.vcd @@ -0,0 +1,283 @@ +$timescale 1 ps $end +$scope module array_rw $end +$scope struct array_in $end +$var wire 8 ! \[0] $end +$var wire 8 " \[1] $end +$var wire 8 # \[2] $end +$var wire 8 $ \[3] $end +$var wire 8 % \[4] $end +$var wire 8 & \[5] $end +$var wire 8 ' \[6] $end +$var wire 8 ( \[7] $end +$var wire 8 ) \[8] $end +$var wire 8 * \[9] $end +$var wire 8 + \[10] $end +$var wire 8 , \[11] $end +$var wire 8 - \[12] $end +$var wire 8 . \[13] $end +$var wire 8 / \[14] $end +$var wire 8 0 \[15] $end +$upscope $end +$scope struct array_out $end +$var wire 8 1 \[0] $end +$var wire 8 2 \[1] $end +$var wire 8 3 \[2] $end +$var wire 8 4 \[3] $end +$var wire 8 5 \[4] $end +$var wire 8 6 \[5] $end +$var wire 8 7 \[6] $end +$var wire 8 8 \[7] $end +$var wire 8 9 \[8] $end +$var wire 8 : \[9] $end +$var wire 8 ; \[10] $end +$var wire 8 < \[11] $end +$var wire 8 = \[12] $end +$var wire 8 > \[13] $end +$var wire 8 ? \[14] $end +$var wire 8 @ \[15] $end +$upscope $end +$var wire 8 A read_index $end +$var wire 8 B read_data $end +$var wire 8 C write_index $end +$var wire 8 D write_data $end +$var wire 1 E write_en $end +$scope struct array_wire $end +$var wire 8 F \[0] $end +$var wire 8 G \[1] $end +$var wire 8 H \[2] $end +$var wire 8 I \[3] $end +$var wire 8 J \[4] $end +$var wire 8 K \[5] $end +$var wire 8 L \[6] $end +$var wire 8 M \[7] $end +$var wire 8 N \[8] $end +$var wire 8 O \[9] $end +$var wire 8 P \[10] $end +$var wire 8 Q \[11] $end +$var wire 8 R \[12] $end +$var wire 8 S \[13] $end +$var wire 8 T \[14] $end +$var wire 8 U \[15] $end +$upscope $end +$upscope $end +$enddefinitions $end +$dumpvars +b11111111 ! +b1111111 " +b111111 # +b11111 $ +b1111 % +b111 & +b11 ' +b1 ( +b0 ) +b10000000 * +b11000000 + +b11100000 , +b11110000 - +b11111000 . +b11111100 / +b11111110 0 +b11111111 1 +b1111111 2 +b111111 3 +b11111 4 +b1111 5 +b111 6 +b11 7 +b1 8 +b0 9 +b10000000 : +b11000000 ; +b11100000 < +b11110000 = +b11111000 > +b11111100 ? +b11111110 @ +b0 A +b11111111 B +b0 C +b0 D +0E +b11111111 F +b1111111 G +b111111 H +b11111 I +b1111 J +b111 K +b11 L +b1 M +b0 N +b10000000 O +b11000000 P +b11100000 Q +b11110000 R +b11111000 S +b11111100 T +b11111110 U +$end +#1000000 +b1 A +b1111111 B +#2000000 +b10 A +b111111 B +#3000000 +b11 A +b11111 B +#4000000 +b100 A +b1111 B +#5000000 +b101 A +b111 B +#6000000 +b110 A +b11 B +#7000000 +b111 A +b1 B +#8000000 +b1000 A +b0 B +#9000000 +b1001 A +b10000000 B +#10000000 +b1010 A +b11000000 B +#11000000 +b1011 A +b11100000 B +#12000000 +b1100 A +b11110000 B +#13000000 +b1101 A +b11111000 B +#14000000 +b1110 A +b11111100 B +#15000000 +b1111 A +b11111110 B +#16000000 +b10000 A +b0 B +#17000000 +b0 1 +b0 A +1E +b0 F +#18000000 +b11111111 1 +b1 2 +b11111111 B +b1 C +b1 D +b11111111 F +b1 G +#19000000 +b1111111 2 +b100 3 +b10 C +b100 D +b1111111 G +b100 H +#20000000 +b111111 3 +b1001 4 +b11 C +b1001 D +b111111 H +b1001 I +#21000000 +b11111 4 +b10000 5 +b100 C +b10000 D +b11111 I +b10000 J +#22000000 +b1111 5 +b11001 6 +b101 C +b11001 D +b1111 J +b11001 K +#23000000 +b111 6 +b100100 7 +b110 C +b100100 D +b111 K +b100100 L +#24000000 +b11 7 +b110001 8 +b111 C +b110001 D +b11 L +b110001 M +#25000000 +b1 8 +b1000000 9 +b1000 C +b1000000 D +b1 M +b1000000 N +#26000000 +b0 9 +b1010001 : +b1001 C +b1010001 D +b0 N +b1010001 O +#27000000 +b10000000 : +b1100100 ; +b1010 C +b1100100 D +b10000000 O +b1100100 P +#28000000 +b11000000 ; +b1111001 < +b1011 C +b1111001 D +b11000000 P +b1111001 Q +#29000000 +b11100000 < +b10010000 = +b1100 C +b10010000 D +b11100000 Q +b10010000 R +#30000000 +b11110000 = +b10101001 > +b1101 C +b10101001 D +b11110000 R +b10101001 S +#31000000 +b11111000 > +b11000100 ? +b1110 C +b11000100 D +b11111000 S +b11000100 T +#32000000 +b11111100 ? +b11100001 @ +b1111 C +b11100001 D +b11111100 T +b11100001 U +#33000000 +b11111110 @ +b10000 C +b0 D +b11111110 U +#34000000 From d4ea8260512e54c5187c1c59bd9bac47a319f86d Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 15 Jan 2025 19:04:40 -0800 Subject: [PATCH 07/10] sim: fix "label address not set" bug when the last Assignment is conditional --- crates/fayalite/src/sim.rs | 3 + crates/fayalite/tests/sim.rs | 36 ++++ .../expected/conditional_assignment_last.txt | 189 ++++++++++++++++++ .../expected/conditional_assignment_last.vcd | 14 ++ 4 files changed, 242 insertions(+) create mode 100644 crates/fayalite/tests/sim/expected/conditional_assignment_last.txt create mode 100644 crates/fayalite/tests/sim/expected/conditional_assignment_last.vcd diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index cb6228d..f630f5a 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -4773,6 +4773,9 @@ impl Compiler { } self.insns.extend(insns.iter().copied(), *source_location); } + for CondStackEntry { cond: _, end_label } in cond_stack { + self.insns.define_label_at_next_insn(end_label); + } } fn process_clocks(&mut self) -> Interned<[StatePartIndex]> { mem::take(&mut self.clock_triggers) diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index cae08de..8c8a10f 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -1407,3 +1407,39 @@ fn test_array_rw() { panic!(); } } + +#[hdl_module(outline_generated)] +pub fn conditional_assignment_last() { + #[hdl] + let i: Bool = m.input(); + #[hdl] + let w = wire(); + connect(w, true); + #[hdl] + if i { + connect(w, false); + } +} + +#[test] +fn test_conditional_assignment_last() { + let _n = SourceLocation::normalize_files_for_tests(); + let mut sim = Simulation::new(conditional_assignment_last()); + let mut writer = RcWriter::default(); + sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); + sim.write(sim.io().i, false); + sim.advance_time(SimDuration::from_micros(1)); + sim.write(sim.io().i, true); + 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/conditional_assignment_last.vcd") { + panic!(); + } + let sim_debug = format!("{sim:#?}"); + println!("#######\n{sim_debug}\n#######"); + if sim_debug != include_str!("sim/expected/conditional_assignment_last.txt") { + panic!(); + } +} diff --git a/crates/fayalite/tests/sim/expected/conditional_assignment_last.txt b/crates/fayalite/tests/sim/expected/conditional_assignment_last.txt new file mode 100644 index 0000000..186e5a5 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/conditional_assignment_last.txt @@ -0,0 +1,189 @@ +Simulation { + state: State { + insns: Insns { + state_layout: StateLayout { + ty: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 4, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::i", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::w", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + ], + .. + }, + }, + memories: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + insns: [ + // at: module-XXXXXXXXXX.rs:1:1 + 0: Const { + dest: StatePartIndex(3), // (0x0) SlotDebugData { name: "", ty: Bool }, + value: 0x0, + }, + 1: Const { + dest: StatePartIndex(2), // (0x1) SlotDebugData { name: "", ty: Bool }, + value: 0x1, + }, + // at: module-XXXXXXXXXX.rs:4:1 + 2: Copy { + dest: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::w", ty: Bool }, + src: StatePartIndex(2), // (0x1) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:5:1 + 3: BranchIfZero { + target: 5, + value: StatePartIndex(0), // (0x1) SlotDebugData { name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::i", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 4: Copy { + dest: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::w", ty: Bool }, + src: StatePartIndex(3), // (0x0) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 5: Return, + ], + .. + }, + pc: 5, + memory_write_log: [], + memories: StatePart { + value: [], + }, + small_slots: StatePart { + value: [], + }, + big_slots: StatePart { + value: [ + 1, + 0, + 1, + 0, + ], + }, + }, + io: Instance { + name: ::conditional_assignment_last, + instantiated: Module { + name: conditional_assignment_last, + .. + }, + }, + uninitialized_inputs: {}, + io_targets: { + Instance { + name: ::conditional_assignment_last, + instantiated: Module { + name: conditional_assignment_last, + .. + }, + }.i: CompiledValue { + layout: CompiledTypeLayout { + ty: Bool, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::i", + ty: Bool, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 0, len: 1 }, + }, + write: None, + }, + }, + made_initial_step: true, + needs_settle: false, + trace_decls: TraceModule { + name: "conditional_assignment_last", + children: [ + TraceModuleIO { + name: "i", + child: TraceBool { + location: TraceScalarId(0), + name: "i", + flow: Source, + }, + ty: Bool, + flow: Source, + }, + TraceWire { + name: "w", + child: TraceBool { + location: TraceScalarId(1), + name: "w", + flow: Duplex, + }, + ty: Bool, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigBool { + index: StatePartIndex(0), + }, + state: 0x1, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigBool { + index: StatePartIndex(1), + }, + state: 0x0, + last_state: 0x1, + }, + ], + trace_memories: {}, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + instant: 2 μs, + clocks_triggered: [], + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/conditional_assignment_last.vcd b/crates/fayalite/tests/sim/expected/conditional_assignment_last.vcd new file mode 100644 index 0000000..dd9a85a --- /dev/null +++ b/crates/fayalite/tests/sim/expected/conditional_assignment_last.vcd @@ -0,0 +1,14 @@ +$timescale 1 ps $end +$scope module conditional_assignment_last $end +$var wire 1 ! i $end +$var wire 1 " w $end +$upscope $end +$enddefinitions $end +$dumpvars +0! +1" +$end +#1000000 +1! +0" +#2000000 From 209d5b5fe1b16652dacbd9a72156764a2f4138eb Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 10 Feb 2025 22:49:16 -0800 Subject: [PATCH 08/10] fix broken doc links --- crates/fayalite/src/int.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index b49ca3f..5d10b29 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -567,12 +567,12 @@ impl_prim_int!(i64, SInt<64>); impl_prim_int!(i128, SInt<128>); impl_prim_int!( - /// for portability reasons, [`usize`] always translates to [`UInt<64>`] + /// for portability reasons, [`usize`] always translates to [`UInt<64>`][type@UInt] usize, UInt<64> ); impl_prim_int!( - /// for portability reasons, [`isize`] always translates to [`SInt<64>`] + /// for portability reasons, [`isize`] always translates to [`SInt<64>`][type@SInt] isize, SInt<64> ); From 86a1bb46be4690e854470cf4a1a6e90c92268391 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 10 Feb 2025 22:49:41 -0800 Subject: [PATCH 09/10] add #[hdl] let destructuring and, while at it, tuple patterns --- .../src/module/transform_body.rs | 25 +- .../src/module/transform_body/expand_match.rs | 330 +++++++++++++++++- .../module_bodies/hdl_let_statements.rs | 1 + .../hdl_let_statements/destructuring.rs | 33 ++ .../module_bodies/hdl_match_statements.rs | 2 +- crates/fayalite/tests/module.rs | 76 ++++ 6 files changed, 454 insertions(+), 13 deletions(-) create mode 100644 crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/destructuring.rs diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body.rs index 6e99e87..c67f8dc 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body.rs @@ -1109,7 +1109,7 @@ fn parse_quote_let_pat>( } } -fn wrap_ty_with_expr(ty: impl ToTokens) -> Type { +pub(crate) fn wrap_ty_with_expr(ty: impl ToTokens) -> Type { parse_quote_spanned! {ty.span()=> ::fayalite::expr::Expr<#ty> } @@ -1586,7 +1586,7 @@ impl Visitor<'_> { } } -fn empty_let() -> Local { +pub(crate) fn empty_let() -> Local { Local { attrs: vec![], let_token: Default::default(), @@ -1672,7 +1672,7 @@ impl Fold for Visitor<'_> { } } - fn fold_local(&mut self, let_stmt: Local) -> Local { + fn fold_local(&mut self, mut let_stmt: Local) -> Local { match self .errors .ok(HdlAttr::::parse_and_leave_attr( @@ -1682,6 +1682,25 @@ impl Fold for Visitor<'_> { Some(None) => return fold_local(self, let_stmt), Some(Some(HdlAttr { .. })) => {} }; + let mut pat = &let_stmt.pat; + if let Pat::Type(pat_type) = pat { + pat = &pat_type.pat; + } + let Pat::Ident(syn::PatIdent { + attrs: _, + by_ref: None, + mutability: _, + ident: _, + subpat: None, + }) = pat + else { + let hdl_attr = HdlAttr::::parse_and_take_attr(&mut let_stmt.attrs) + .ok() + .flatten() + .expect("already checked above"); + let let_stmt = fold_local(self, let_stmt); + return self.process_hdl_let_pat(hdl_attr, let_stmt); + }; let hdl_let = syn::parse2::>>(let_stmt.into_token_stream()); let Some(hdl_let) = self.errors.ok(hdl_let) else { return empty_let(); diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs index 1d53104..f1ff2c2 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs @@ -3,22 +3,111 @@ use crate::{ fold::{impl_fold, DoFold}, kw, - module::transform_body::{with_debug_clone_and_fold, Visitor}, + module::transform_body::{empty_let, with_debug_clone_and_fold, wrap_ty_with_expr, Visitor}, Errors, HdlAttr, PairsIterExt, }; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote_spanned, ToTokens, TokenStreamExt}; +use std::collections::BTreeSet; use syn::{ - fold::{fold_arm, fold_expr_match, fold_pat, Fold}, + fold::{fold_arm, fold_expr_match, fold_local, fold_pat, Fold}, parse::Nothing, parse_quote_spanned, punctuated::Punctuated, spanned::Spanned, token::{Brace, Paren}, - Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Member, Pat, PatIdent, PatOr, PatParen, - PatPath, PatRest, PatStruct, PatTupleStruct, PatWild, Path, PathSegment, Token, TypePath, + Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Local, Member, Pat, PatIdent, PatOr, + PatParen, PatPath, PatRest, PatStruct, PatTuple, PatTupleStruct, PatWild, Path, PathSegment, + Token, TypePath, }; +macro_rules! visit_trait { + ( + $($vis:vis fn $fn:ident($state:ident: _, $value:ident: &$Value:ty) $block:block)* + ) => { + trait VisitMatchPat<'a> { + $(fn $fn(&mut self, $value: &'a $Value) { + $fn(self, $value); + })* + } + + $($vis fn $fn<'a>($state: &mut (impl ?Sized + VisitMatchPat<'a>), $value: &'a $Value) $block)* + }; +} + +visit_trait! { + fn visit_match_pat_binding(_state: _, v: &MatchPatBinding) { + let MatchPatBinding { ident: _ } = v; + } + fn visit_match_pat_wild(_state: _, v: &MatchPatWild) { + let MatchPatWild { underscore_token: _ } = v; + } + fn visit_match_pat_rest(_state: _, v: &MatchPatRest) { + let MatchPatRest { dot2_token: _ } = v; + } + fn visit_match_pat_paren(state: _, v: &MatchPatParen) { + let MatchPatParen { paren_token: _, pat } = v; + state.visit_match_pat(pat); + } + fn visit_match_pat_paren_simple(state: _, v: &MatchPatParen) { + let MatchPatParen { paren_token: _, pat } = v; + state.visit_match_pat_simple(pat); + } + fn visit_match_pat_or(state: _, v: &MatchPatOr) { + let MatchPatOr { leading_vert: _, cases } = v; + for v in cases { + state.visit_match_pat(v); + } + } + fn visit_match_pat_or_simple(state: _, v: &MatchPatOr) { + let MatchPatOr { leading_vert: _, cases } = v; + for v in cases { + state.visit_match_pat_simple(v); + } + } + fn visit_match_pat_struct_field(state: _, v: &MatchPatStructField) { + let MatchPatStructField { field_name: _, colon_token: _, pat } = v; + state.visit_match_pat_simple(pat); + } + fn visit_match_pat_struct(state: _, v: &MatchPatStruct) { + let MatchPatStruct { match_span: _, path: _, brace_token: _, fields, rest: _ } = v; + for v in fields { + state.visit_match_pat_struct_field(v); + } + } + fn visit_match_pat_tuple(state: _, v: &MatchPatTuple) { + let MatchPatTuple { paren_token: _, fields } = v; + for v in fields { + state.visit_match_pat_simple(v); + } + } + fn visit_match_pat_enum_variant(state: _, v: &MatchPatEnumVariant) { + let MatchPatEnumVariant {match_span:_, variant_path: _, enum_path: _, variant_name: _, field } = v; + if let Some((_, v)) = field { + state.visit_match_pat_simple(v); + } + } + fn visit_match_pat_simple(state: _, v: &MatchPatSimple) { + match v { + MatchPatSimple::Paren(v) => state.visit_match_pat_paren_simple(v), + MatchPatSimple::Or(v) => state.visit_match_pat_or_simple(v), + MatchPatSimple::Binding(v) => state.visit_match_pat_binding(v), + MatchPatSimple::Wild(v) => state.visit_match_pat_wild(v), + MatchPatSimple::Rest(v) => state.visit_match_pat_rest(v), + } + } + fn visit_match_pat(state: _, v: &MatchPat) { + match v { + MatchPat::Simple(v) => state.visit_match_pat_simple(v), + MatchPat::Or(v) => state.visit_match_pat_or(v), + MatchPat::Paren(v) => state.visit_match_pat_paren(v), + MatchPat::Struct(v) => state.visit_match_pat_struct(v), + MatchPat::Tuple(v) => state.visit_match_pat_tuple(v), + MatchPat::EnumVariant(v) => state.visit_match_pat_enum_variant(v), + } + } +} + with_debug_clone_and_fold! { struct MatchPatBinding<> { ident: Ident, @@ -53,6 +142,15 @@ with_debug_clone_and_fold! { } } +impl

MatchPatOr

{ + /// returns the first `|` between two patterns + fn first_inner_vert(&self) -> Option { + let mut pairs = self.cases.pairs(); + pairs.next_back(); + pairs.next().and_then(|v| v.into_tuple().1.copied()) + } +} + impl ToTokens for MatchPatOr

{ fn to_tokens(&self, tokens: &mut TokenStream) { let Self { @@ -77,6 +175,19 @@ impl ToTokens for MatchPatWild { } } +with_debug_clone_and_fold! { + struct MatchPatRest<> { + dot2_token: Token![..], + } +} + +impl ToTokens for MatchPatRest { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { dot2_token } = self; + dot2_token.to_tokens(tokens); + } +} + with_debug_clone_and_fold! { struct MatchPatStructField<> { field_name: Ident, @@ -159,6 +270,25 @@ impl ToTokens for MatchPatStruct { } } +with_debug_clone_and_fold! { + struct MatchPatTuple<> { + paren_token: Paren, + fields: Punctuated, + } +} + +impl ToTokens for MatchPatTuple { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + paren_token, + fields, + } = self; + paren_token.surround(tokens, |tokens| { + fields.to_tokens(tokens); + }) + } +} + with_debug_clone_and_fold! { struct MatchPatEnumVariant<> { match_span: Span, @@ -194,6 +324,7 @@ enum MatchPatSimple { Or(MatchPatOr), Binding(MatchPatBinding), Wild(MatchPatWild), + Rest(MatchPatRest), } impl_fold! { @@ -202,6 +333,7 @@ impl_fold! { Or(MatchPatOr), Binding(MatchPatBinding), Wild(MatchPatWild), + Rest(MatchPatRest), } } @@ -212,6 +344,7 @@ impl ToTokens for MatchPatSimple { Self::Paren(v) => v.to_tokens(tokens), Self::Binding(v) => v.to_tokens(tokens), Self::Wild(v) => v.to_tokens(tokens), + Self::Rest(v) => v.to_tokens(tokens), } } } @@ -278,6 +411,7 @@ trait ParseMatchPat: Sized { fn or(v: MatchPatOr) -> Self; fn paren(v: MatchPatParen) -> Self; fn struct_(state: &mut HdlMatchParseState<'_>, v: MatchPatStruct) -> Result; + fn tuple(state: &mut HdlMatchParseState<'_>, v: MatchPatTuple) -> Result; fn enum_variant(state: &mut HdlMatchParseState<'_>, v: MatchPatEnumVariant) -> Result; fn parse(state: &mut HdlMatchParseState<'_>, pat: Pat) -> Result { @@ -462,7 +596,34 @@ trait ParseMatchPat: Sized { }) => Ok(Self::simple(MatchPatSimple::Wild(MatchPatWild { underscore_token, }))), - Pat::Tuple(_) | Pat::Slice(_) | Pat::Const(_) | Pat::Lit(_) | Pat::Range(_) => { + Pat::Tuple(PatTuple { + attrs: _, + paren_token, + elems, + }) => { + let fields = elems + .into_pairs() + .filter_map_pair_value(|field_pat| { + if let Pat::Rest(PatRest { + attrs: _, + dot2_token, + }) = field_pat + { + Some(MatchPatSimple::Rest(MatchPatRest { dot2_token })) + } else { + MatchPatSimple::parse(state, field_pat).ok() + } + }) + .collect(); + Self::tuple( + state, + MatchPatTuple { + paren_token, + fields, + }, + ) + } + Pat::Slice(_) | Pat::Const(_) | Pat::Lit(_) | Pat::Range(_) => { state .errors .error(pat, "not yet implemented in #[hdl] patterns"); @@ -497,6 +658,14 @@ impl ParseMatchPat for MatchPatSimple { Err(()) } + fn tuple(state: &mut HdlMatchParseState<'_>, v: MatchPatTuple) -> Result { + state.errors.push(syn::Error::new( + v.paren_token.span.open(), + "matching tuples is not yet implemented inside structs/enums in #[hdl] patterns", + )); + Err(()) + } + fn enum_variant( state: &mut HdlMatchParseState<'_>, v: MatchPatEnumVariant, @@ -515,6 +684,7 @@ enum MatchPat { Or(MatchPatOr), Paren(MatchPatParen), Struct(MatchPatStruct), + Tuple(MatchPatTuple), EnumVariant(MatchPatEnumVariant), } @@ -524,6 +694,7 @@ impl_fold! { Or(MatchPatOr), Paren(MatchPatParen), Struct(MatchPatStruct), + Tuple(MatchPatTuple), EnumVariant(MatchPatEnumVariant), } } @@ -545,6 +716,10 @@ impl ParseMatchPat for MatchPat { Ok(Self::Struct(v)) } + fn tuple(_state: &mut HdlMatchParseState<'_>, v: MatchPatTuple) -> Result { + Ok(Self::Tuple(v)) + } + fn enum_variant( _state: &mut HdlMatchParseState<'_>, v: MatchPatEnumVariant, @@ -560,6 +735,7 @@ impl ToTokens for MatchPat { Self::Or(v) => v.to_tokens(tokens), Self::Paren(v) => v.to_tokens(tokens), Self::Struct(v) => v.to_tokens(tokens), + Self::Tuple(v) => v.to_tokens(tokens), Self::EnumVariant(v) => v.to_tokens(tokens), } } @@ -622,10 +798,6 @@ struct RewriteAsCheckMatch { } impl Fold for RewriteAsCheckMatch { - fn fold_field_pat(&mut self, mut i: FieldPat) -> FieldPat { - i.colon_token = Some(Token![:](i.member.span())); - i - } fn fold_pat(&mut self, pat: Pat) -> Pat { match pat { Pat::Ident(mut pat_ident) => match parse_enum_ident(pat_ident.ident) { @@ -740,6 +912,30 @@ impl Fold for RewriteAsCheckMatch { // don't recurse into expressions i } + fn fold_local(&mut self, mut let_stmt: Local) -> Local { + if let Some(syn::LocalInit { + eq_token, + expr: _, + diverge, + }) = let_stmt.init.take() + { + let_stmt.init = Some(syn::LocalInit { + eq_token, + expr: parse_quote_spanned! {self.span=> + __match_value + }, + diverge: diverge.map(|(else_, _expr)| { + ( + else_, + parse_quote_spanned! {self.span=> + match __infallible {} + }, + ) + }), + }); + } + fold_local(self, let_stmt) + } } struct HdlMatchParseState<'a> { @@ -747,7 +943,123 @@ struct HdlMatchParseState<'a> { errors: &'a mut Errors, } +struct HdlLetPatVisitState<'a> { + errors: &'a mut Errors, + bindings: BTreeSet<&'a Ident>, +} + +impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> { + fn visit_match_pat_binding(&mut self, v: &'a MatchPatBinding) { + self.bindings.insert(&v.ident); + } + + fn visit_match_pat_or(&mut self, v: &'a MatchPatOr) { + if let Some(first_inner_vert) = v.first_inner_vert() { + self.errors.error( + first_inner_vert, + "or-patterns are not supported in let statements", + ); + } + visit_match_pat_or(self, v); + } + + fn visit_match_pat_or_simple(&mut self, v: &'a MatchPatOr) { + if let Some(first_inner_vert) = v.first_inner_vert() { + self.errors.error( + first_inner_vert, + "or-patterns are not supported in let statements", + ); + } + visit_match_pat_or_simple(self, v); + } + + fn visit_match_pat_enum_variant(&mut self, v: &'a MatchPatEnumVariant) { + self.errors.error(v, "refutable pattern in let statement"); + } +} + impl Visitor<'_> { + pub(crate) fn process_hdl_let_pat( + &mut self, + _hdl_attr: HdlAttr, + mut let_stmt: Local, + ) -> Local { + let span = let_stmt.let_token.span(); + if let Pat::Type(pat) = &mut let_stmt.pat { + *pat.ty = wrap_ty_with_expr((*pat.ty).clone()); + } + let check_let_stmt = RewriteAsCheckMatch { span }.fold_local(let_stmt.clone()); + let Local { + attrs: _, + let_token, + pat, + init, + semi_token, + } = let_stmt; + self.require_normal_module_or_fn(let_token); + let Some(syn::LocalInit { + eq_token, + expr, + diverge, + }) = init + else { + self.errors + .error(let_token, "#[hdl] let must be assigned a value"); + return empty_let(); + }; + if let Some((else_, _)) = diverge { + // TODO: implement let-else + self.errors + .error(else_, "#[hdl] let ... else { ... } is not implemented"); + return empty_let(); + } + let Ok(pat) = MatchPat::parse( + &mut HdlMatchParseState { + match_span: span, + errors: &mut self.errors, + }, + pat, + ) else { + return empty_let(); + }; + let mut state = HdlLetPatVisitState { + errors: &mut self.errors, + bindings: BTreeSet::new(), + }; + state.visit_match_pat(&pat); + let HdlLetPatVisitState { + errors: _, + bindings, + } = state; + let retval = parse_quote_spanned! {span=> + let (#(#bindings,)* __scope,) = { + type __MatchTy = ::MatchVariant; + let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr)); + ::fayalite::expr::check_match_expr(__match_expr, |__match_value, __infallible| { + #[allow(unused_variables)] + #check_let_stmt + match __infallible {} + }); + let mut __match_iter = ::fayalite::module::match_(__match_expr); + let ::fayalite::__std::option::Option::Some(__match_variant) = ::fayalite::__std::iter::Iterator::next(&mut __match_iter) else { + ::fayalite::__std::unreachable!("#[hdl] let with uninhabited type"); + }; + let ::fayalite::__std::option::Option::None = ::fayalite::__std::iter::Iterator::next(&mut __match_iter) else { + ::fayalite::__std::unreachable!("#[hdl] let with refutable pattern"); + }; + let (__match_variant, __scope) = + ::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope( + __match_variant, + ); + #let_token #pat #eq_token __match_variant #semi_token + (#(#bindings,)* __scope,) + }; + }; + match retval { + syn::Stmt::Local(retval) => retval, + _ => unreachable!(), + } + } pub(crate) fn process_hdl_match( &mut self, _hdl_attr: HdlAttr, diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements.rs index 61d29b5..229871b 100644 --- a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements.rs +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements.rs @@ -2,6 +2,7 @@ // See Notices.txt for copyright information //! ## `#[hdl] let` statements +pub mod destructuring; pub mod inputs_outputs; pub mod instances; pub mod memories; diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/destructuring.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/destructuring.rs new file mode 100644 index 0000000..1fc4705 --- /dev/null +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/destructuring.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information +//! ### Destructuring Let +//! +//! You can use `#[hdl] let` to destructure types, similarly to Rust `let` statements with non-trivial patterns. +//! +//! `#[hdl] let` statements can only match one level of struct/tuple pattern for now, +//! e.g. you can match with the pattern `MyStruct { a, b }`, but not `MyStruct { a, b: Struct2 { v } }`. +//! +//! ``` +//! # use fayalite::prelude::*; +//! #[hdl] +//! struct MyStruct { +//! a: UInt<8>, +//! b: Bool, +//! } +//! +//! #[hdl_module] +//! fn my_module() { +//! #[hdl] +//! let my_input: MyStruct = m.input(); +//! #[hdl] +//! let my_output: UInt<8> = m.input(); +//! #[hdl] +//! let MyStruct { a, b } = my_input; +//! #[hdl] +//! if b { +//! connect(my_output, a); +//! } else { +//! connect(my_output, 0_hdl_u8); +//! } +//! } +//! ``` diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_match_statements.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_match_statements.rs index 9e6c511..6df70f1 100644 --- a/crates/fayalite/src/_docs/modules/module_bodies/hdl_match_statements.rs +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_match_statements.rs @@ -7,5 +7,5 @@ //! //! `#[hdl] match` statements' bodies must evaluate to type `()` for now. //! -//! `#[hdl] match` statements can only match one level of struct/enum pattern for now, +//! `#[hdl] match` statements can only match one level of struct/tuple/enum pattern for now, //! e.g. you can match with the pattern `HdlSome(v)`, but not `HdlSome(HdlSome(_))`. diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 4e56df4..49f5689 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -4345,3 +4345,79 @@ circuit check_cfgs: ", }; } + +#[hdl_module(outline_generated)] +pub fn check_let_patterns() { + #[hdl] + let tuple_in: (UInt<1>, SInt<1>, Bool) = m.input(); + #[hdl] + let (tuple_0, tuple_1, tuple_2) = tuple_in; + #[hdl] + let tuple_0_out: UInt<1> = m.output(); + connect(tuple_0_out, tuple_0); + #[hdl] + let tuple_1_out: SInt<1> = m.output(); + connect(tuple_1_out, tuple_1); + #[hdl] + let tuple_2_out: Bool = m.output(); + connect(tuple_2_out, tuple_2); + + #[hdl] + let test_struct_in: TestStruct> = m.input(); + #[hdl] + let TestStruct::<_> { a, b } = test_struct_in; + #[hdl] + let test_struct_a_out: SInt<8> = m.output(); + connect(test_struct_a_out, a); + #[hdl] + let test_struct_b_out: UInt<8> = m.output(); + connect(test_struct_b_out, b); + + #[hdl] + let test_struct_2_in: TestStruct2 = m.input(); + #[hdl] + let TestStruct2 { v } = test_struct_2_in; + #[hdl] + let test_struct_2_v_out: UInt<8> = m.output(); + connect(test_struct_2_v_out, v); + + #[hdl] + let test_struct_3_in: TestStruct3 = m.input(); + #[hdl] + let TestStruct3 {} = test_struct_3_in; +} + +#[test] +fn test_let_patterns() { + let _n = SourceLocation::normalize_files_for_tests(); + let m = check_let_patterns(); + dbg!(m); + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m => + "/test/check_let_patterns.fir": r"FIRRTL version 3.2.0 +circuit check_let_patterns: + type Ty0 = {`0`: UInt<1>, `1`: SInt<1>, `2`: UInt<1>} + type Ty1 = {a: SInt<8>, b: UInt<8>} + type Ty2 = {v: UInt<8>} + type Ty3 = {} + module check_let_patterns: @[module-XXXXXXXXXX.rs 1:1] + input tuple_in: Ty0 @[module-XXXXXXXXXX.rs 2:1] + output tuple_0_out: UInt<1> @[module-XXXXXXXXXX.rs 4:1] + output tuple_1_out: SInt<1> @[module-XXXXXXXXXX.rs 6:1] + output tuple_2_out: UInt<1> @[module-XXXXXXXXXX.rs 8:1] + input test_struct_in: Ty1 @[module-XXXXXXXXXX.rs 10:1] + output test_struct_a_out: SInt<8> @[module-XXXXXXXXXX.rs 12:1] + output test_struct_b_out: UInt<8> @[module-XXXXXXXXXX.rs 14:1] + input test_struct_2_in: Ty2 @[module-XXXXXXXXXX.rs 16:1] + output test_struct_2_v_out: UInt<8> @[module-XXXXXXXXXX.rs 18:1] + input test_struct_3_in: Ty3 @[module-XXXXXXXXXX.rs 20:1] + connect tuple_0_out, tuple_in.`0` @[module-XXXXXXXXXX.rs 5:1] + connect tuple_1_out, tuple_in.`1` @[module-XXXXXXXXXX.rs 7:1] + connect tuple_2_out, tuple_in.`2` @[module-XXXXXXXXXX.rs 9:1] + connect test_struct_a_out, test_struct_in.a @[module-XXXXXXXXXX.rs 13:1] + connect test_struct_b_out, test_struct_in.b @[module-XXXXXXXXXX.rs 15:1] + connect test_struct_2_v_out, test_struct_2_in.v @[module-XXXXXXXXXX.rs 19:1] +", + }; +} From cdd84953d076cd9a83db50e9d11a63b6181b0976 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 13 Feb 2025 18:35:30 -0800 Subject: [PATCH 10/10] support unknown trait bounds in type parameters --- .../src/hdl_type_common.rs | 130 +++++++++++++++--- crates/fayalite/tests/module.rs | 6 +- 2 files changed, 119 insertions(+), 17 deletions(-) diff --git a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs index 6193dc3..3b2e1ec 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -2069,11 +2069,16 @@ macro_rules! impl_bounds { $( $Variant:ident, )* + $( + #[unknown] + $Unknown:ident, + )? } ) => { #[derive(Clone, Debug)] $vis enum $enum_type { $($Variant(known_items::$Variant),)* + $($Unknown(syn::TypeParamBound),)? } $(impl From for $enum_type { @@ -2086,28 +2091,54 @@ macro_rules! impl_bounds { fn to_tokens(&self, tokens: &mut TokenStream) { match self { $(Self::$Variant(v) => v.to_tokens(tokens),)* + $(Self::$Unknown(v) => v.to_tokens(tokens),)? } } } impl $enum_type { $vis fn parse_path(path: Path) -> Result { + #![allow(unreachable_code)] $(let path = match known_items::$Variant::parse_path(path) { Ok(v) => return Ok(Self::$Variant(v)), Err(path) => path, };)* + $(return Ok(Self::$Unknown(syn::TraitBound { + paren_token: None, + modifier: syn::TraitBoundModifier::None, + lifetimes: None, + path, + }.into()));)? Err(path) } + $vis fn parse_type_param_bound(mut type_param_bound: syn::TypeParamBound) -> Result { + #![allow(unreachable_code)] + if let syn::TypeParamBound::Trait(mut trait_bound) = type_param_bound { + if let syn::TraitBound { + paren_token: _, + modifier: syn::TraitBoundModifier::None, + lifetimes: None, + path: _, + } = trait_bound { + match Self::parse_path(trait_bound.path) { + Ok(retval) => return Ok(retval), + Err(path) => trait_bound.path = path, + } + } + type_param_bound = trait_bound.into(); + } + $(return Ok(Self::$Unknown(type_param_bound));)? + Err(type_param_bound) + } } impl Parse for $enum_type { fn parse(input: ParseStream) -> syn::Result { - Self::parse_path(Path::parse_mod_style(input)?).map_err(|path| { - syn::Error::new_spanned( - path, + Self::parse_type_param_bound(input.parse()?) + .map_err(|type_param_bound| syn::Error::new_spanned( + type_param_bound, format_args!("expected one of: {}", [$(stringify!($Variant)),*].join(", ")), - ) - }) + )) } } @@ -2115,6 +2146,7 @@ macro_rules! impl_bounds { #[allow(non_snake_case)] $vis struct $struct_type { $($vis $Variant: Option,)* + $($vis $Unknown: Vec,)? } impl ToTokens for $struct_type { @@ -2126,42 +2158,63 @@ macro_rules! impl_bounds { separator = Some(::default()); v.to_tokens(tokens); })* + $(for v in &self.$Unknown { + separator.to_tokens(tokens); + separator = Some(::default()); + v.to_tokens(tokens); + })* } } const _: () = { #[derive(Clone, Debug)] - $vis struct Iter($vis $struct_type); + #[allow(non_snake_case)] + $vis struct Iter { + $($Variant: Option,)* + $($Unknown: std::vec::IntoIter,)? + } impl IntoIterator for $struct_type { type Item = $enum_type; type IntoIter = Iter; fn into_iter(self) -> Self::IntoIter { - Iter(self) + Iter { + $($Variant: self.$Variant,)* + $($Unknown: self.$Unknown.into_iter(),)? + } } } impl Iterator for Iter { type Item = $enum_type; - fn next(&mut self) -> Option { $( - if let Some(value) = self.0.$Variant.take() { + if let Some(value) = self.$Variant.take() { return Some($enum_type::$Variant(value)); } )* + $( + if let Some(value) = self.$Unknown.next() { + return Some($enum_type::$Unknown(value)); + } + )? None } #[allow(unused_mut, unused_variables)] fn fold B>(mut self, mut init: B, mut f: F) -> B { $( - if let Some(value) = self.0.$Variant.take() { + if let Some(value) = self.$Variant.take() { init = f(init, $enum_type::$Variant(value)); } )* + $( + if let Some(value) = self.$Unknown.next() { + init = f(init, $enum_type::$Unknown(value)); + } + )? init } } @@ -2173,6 +2226,9 @@ macro_rules! impl_bounds { $($enum_type::$Variant(v) => { self.$Variant = Some(v); })* + $($enum_type::$Unknown(v) => { + self.$Unknown.push(v); + })? }); } } @@ -2191,6 +2247,7 @@ macro_rules! impl_bounds { $(if let Some(v) = v.$Variant { self.$Variant = Some(v); })* + $(self.$Unknown.extend(v.$Unknown);)* }); } } @@ -2244,6 +2301,8 @@ impl_bounds! { Size, StaticType, Type, + #[unknown] + Unknown, } } @@ -2257,6 +2316,8 @@ impl_bounds! { ResetType, StaticType, Type, + #[unknown] + Unknown, } } @@ -2270,6 +2331,7 @@ impl From for ParsedBound { ParsedTypeBound::ResetType(v) => ParsedBound::ResetType(v), ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v), ParsedTypeBound::Type(v) => ParsedBound::Type(v), + ParsedTypeBound::Unknown(v) => ParsedBound::Unknown(v), } } } @@ -2284,6 +2346,7 @@ impl From for ParsedBounds { ResetType, StaticType, Type, + Unknown, } = value; Self { BoolOrIntType, @@ -2295,6 +2358,7 @@ impl From for ParsedBounds { Size: None, StaticType, Type, + Unknown, } } } @@ -2330,6 +2394,7 @@ impl ParsedTypeBound { ParsedTypeBound::Type(known_items::Type(span)), ]), Self::Type(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::from(v)]), + Self::Unknown(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::Unknown(v)]), } } } @@ -2364,6 +2429,7 @@ impl From for ParsedBounds { Size, StaticType: None, Type: None, + Unknown: vec![], } } } @@ -2391,6 +2457,7 @@ impl ParsedBounds { fn categorize(self, errors: &mut Errors, span: Span) -> ParsedBoundsCategory { let mut type_bounds = None; let mut size_type_bounds = None; + let mut unknown_bounds = vec![]; self.into_iter().for_each(|bound| match bound.categorize() { ParsedBoundCategory::Type(bound) => { type_bounds @@ -2402,15 +2469,37 @@ impl ParsedBounds { .get_or_insert_with(ParsedSizeTypeBounds::default) .extend([bound]); } + ParsedBoundCategory::Unknown(bound) => unknown_bounds.push(bound), }); - match (type_bounds, size_type_bounds) { - (None, None) => ParsedBoundsCategory::Type(ParsedTypeBounds { + match (type_bounds, size_type_bounds, unknown_bounds.is_empty()) { + (None, None, true) => ParsedBoundsCategory::Type(ParsedTypeBounds { Type: Some(known_items::Type(span)), ..Default::default() }), - (None, Some(bounds)) => ParsedBoundsCategory::SizeType(bounds), - (Some(bounds), None) => ParsedBoundsCategory::Type(bounds), - (Some(type_bounds), Some(size_type_bounds)) => { + (None, None, false) => { + errors.error( + unknown_bounds.remove(0), + "unknown bounds: must use at least one known bound (such as `Type`) with any unknown bounds", + ); + ParsedBoundsCategory::Type(ParsedTypeBounds { + Unknown: unknown_bounds, + ..Default::default() + }) + } + (None, Some(bounds), true) => ParsedBoundsCategory::SizeType(bounds), + (None, Some(bounds), false) => { + // TODO: implement + errors.error( + unknown_bounds.remove(0), + "unknown bounds with `Size` bounds are not implemented", + ); + ParsedBoundsCategory::SizeType(bounds) + } + (Some(bounds), None, _) => ParsedBoundsCategory::Type(ParsedTypeBounds { + Unknown: unknown_bounds, + ..bounds + }), + (Some(type_bounds), Some(size_type_bounds), _) => { errors.error( size_type_bounds .Size @@ -2427,6 +2516,7 @@ impl ParsedBounds { pub(crate) enum ParsedBoundCategory { Type(ParsedTypeBound), SizeType(ParsedSizeTypeBound), + Unknown(syn::TypeParamBound), } impl ParsedBound { @@ -2441,12 +2531,14 @@ impl ParsedBound { Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)), Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)), Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)), + Self::Unknown(v) => ParsedBoundCategory::Unknown(v), } } fn implied_bounds(self) -> ParsedBounds { match self.categorize() { ParsedBoundCategory::Type(v) => v.implied_bounds().into(), ParsedBoundCategory::SizeType(v) => v.implied_bounds().into(), + ParsedBoundCategory::Unknown(v) => ParsedBounds::from_iter([ParsedBound::Unknown(v)]), } } } @@ -3325,7 +3417,7 @@ impl ParsedGenerics { | ParsedTypeBound::EnumType(_) | ParsedTypeBound::IntType(_) | ParsedTypeBound::ResetType(_) => { - errors.error(bound, "bound on mask type not implemented"); + errors.error(bound, "bounds on mask types are not implemented"); } ParsedTypeBound::StaticType(bound) => { if bounds.StaticType.is_none() { @@ -3337,6 +3429,12 @@ impl ParsedGenerics { } } ParsedTypeBound::Type(_) => {} + ParsedTypeBound::Unknown(_) => { + errors.error( + bound, + "unknown bounds on mask types are not implemented", + ); + } } } bounds.add_implied_bounds(); diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 49f5689..2f93fa5 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -191,10 +191,14 @@ circuit check_array_repeat: }; } +pub trait UnknownTrait {} + +impl UnknownTrait for T {} + #[hdl_module(outline_generated)] pub fn check_skipped_generics(v: U) where - T: StaticType, + T: StaticType + UnknownTrait, ConstUsize: KnownSize, U: std::fmt::Display, {