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.
This commit is contained in:
parent
31d01046a8
commit
dcf865caec
|
@ -178,6 +178,30 @@ pub fn queue<T: Type>(
|
|||
}
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue