Skip to content
Draft
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,11 @@ monitor/tests/axi_output.txt
*.csv
# Allow benchmark results to be committed for CI regression testing
!benchmark_results/*.csv
# Ignore all dotfiles and dotdirectories
.*
# But include the .github directory (CI files)
!.github/
!.github/**/*
# And also include .gitignore + .python-version
!.gitignore
!.python-version
22 changes: 1 addition & 21 deletions monitor/tests/fpga-debugging/axi-stream-s2/s2_buggy.out
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,5 @@ trace {
recv(4); // [time: 937.5ns -> 962.5ns]
recv(5); // [time: 962.5ns -> 987.5ns]
recv(6); // [time: 987.5ns -> 1012.5ns]
stall(7, 0); // [time: 1012.5ns -> 1037.5ns]
stall(7, 1); // [time: 1037.5ns -> 1050ns]
}

// trace 1
trace {
reset(); // [time: 0ns -> 12.5ns]
wait_for_data(); // [time: 337.5ns -> 362.5ns]
wait_for_data(); // [time: 387.5ns -> 412.5ns]
wait_for_data(); // [time: 512.5ns -> 537.5ns]
wait_for_data(); // [time: 537.5ns -> 562.5ns]
wait_for_data(); // [time: 612.5ns -> 637.5ns]
wait_for_data(); // [time: 787.5ns -> 812.5ns]
wait_for_data(); // [time: 837.5ns -> 862.5ns]
recv(1); // [time: 862.5ns -> 887.5ns]
recv(2); // [time: 887.5ns -> 912.5ns]
recv(3); // [time: 912.5ns -> 937.5ns]
recv(4); // [time: 937.5ns -> 962.5ns]
recv(5); // [time: 962.5ns -> 987.5ns]
recv(6); // [time: 987.5ns -> 1012.5ns]
stall(7, 1); // [time: 1037.5ns -> 1050ns]
stall(7, 0); // [time: 1012.5ns -> 1050ns]
}
82 changes: 42 additions & 40 deletions monitor/tests/fpga-debugging/axi-stream-s2/s2_buggy.prot
Original file line number Diff line number Diff line change
@@ -1,111 +1,113 @@
// ARGS: --wave fpga-debugging/axi-stream-s2/s2_buggy.fst --instances TOP.testbench.UUT.axi_stream_check:AXISManager --sample-posedge TOP.testbench.UUT.axi_stream_check.i_aclk --show-waveform-time --time-unit ns
// ARGS: --wave fpga-debugging/axi-stream-s2/s2_buggy.fst --instances TOP.testbench.UUT:AXISManager --sample-posedge TOP.testbench.UUT.M_AXIS_ACLK --show-waveform-time --time-unit ns

// Source (manager) that outputs a sequence of 8 words
struct AXISManager {
// Control signals
in i_aresetn: u1, // Active-low reset
in M_AXIS_ARESETN: u1, // Active-low reset

// AXI-Stream manager outputs (from DUT perspective)
out i_tvalid: u1, // manager has valid data
out i_tdata: u32, // Data payload
out i_tstrb: u4, // Byte strobe (always 0xF)
out i_tlast: u1, // Last word in packet
out M_AXIS_TVALID: u1, // manager has valid data
out M_AXIS_TDATA: u32, // Data payload
out M_AXIS_TSTRB: u4, // Byte strobe (always 0xF)
out M_AXIS_TLAST: u1, // Last word in packet

// AXI-Stream sub-ordinate input
in i_tready: u1, // Downstream ready to accept
in M_AXIS_TREADY: u1, // Downstream ready to accept
}

// RESET: Assert reset (active-low) and wait for manager to be ready
// The manager waits C_M_START_COUNT cycles in INIT_COUNTER before sending
prot reset<DUT: AXISManager>() {
DUT.i_aresetn := 1'b0; // Assert reset (active-low)
DUT.i_tready := 1'b0;
DUT.M_AXIS_ARESETN := 1'b0; // Assert reset (active-low)
DUT.M_AXIS_TREADY := 1'b0;
step();
}

// RECV: Receive one data word from the AXI-Stream manager
// Data transfer occurs when i_tvalid and i_tready are both 1
// Only matches when data is immediately available (i_tvalid = 1)
// Data transfer occurs when M_AXIS_TVALID and M_AXIS_TREADY are both 1
// Only matches when data is immediately available (M_AXIS_TVALID = 1)
//
// Output Arguments:
// data - Expected payload from manager
prot recv<DUT: AXISManager>(
out data: u32,
) {
DUT.i_aresetn := 1'b1; // Keep out of reset
DUT.i_tready := 1'b1; // Signal ready to receive
DUT.M_AXIS_ARESETN := 1'b1; // Keep out of reset
DUT.M_AXIS_TREADY := 1'b1; // Signal ready to receive

// Only matches when data is available
// (use wait_for_data for cycles when tready=1 but tvalid=0)
assert_eq(DUT.i_tvalid, 1'b1);
assert_eq(DUT.M_AXIS_TVALID, 1'b1);

// Verify output data
assert_eq(DUT.i_tdata, data);
assert_eq(DUT.M_AXIS_TDATA, data);

// One cycle for the transfer to complete
step();
}

// RECV_LAST: Receive the last data word (with i_tlast asserted)
// This verifies both the data and that i_tlast is properly set
// Only matches when data is immediately available (i_tvalid = 1)
// RECV_LAST: Receive the last data word (with M_AXIS_TLAST asserted)
// This verifies both the data and that M_AXIS_TLAST is properly set
// Only matches when data is immediately available (M_AXIS_TVALID = 1)
//
// Output Arguments:
// data - Expected payload from manager (should be the last word)
prot recv_last<DUT: AXISManager>(
out data: u32,
) {
DUT.i_aresetn := 1'b1; // Keep out of reset
DUT.i_tready := 1'b1; // Signal ready to receive
DUT.M_AXIS_ARESETN := 1'b1; // Keep out of reset
DUT.M_AXIS_TREADY := 1'b1; // Signal ready to receive

// Only matches when data is available
assert_eq(DUT.i_tvalid, 1'b1);
assert_eq(DUT.M_AXIS_TVALID, 1'b1);

// Verify output data and i_tlast
assert_eq(DUT.i_tdata, data);
assert_eq(DUT.i_tlast, 1'b1);
// Verify output data and M_AXIS_TLAST
assert_eq(DUT.M_AXIS_TDATA, data);
assert_eq(DUT.M_AXIS_TLAST, 1'b1);

// One cycle for the transfer to complete
step();
}

// STALL: Assert backpressure (i_tready=0) while manager has valid data
// STALL: Assert backpressure (M_AXIS_TREADY=0) while manager has valid data
// AXI-Stream requires output args (tdata, tlast) to remain stable during stall
// Only matches when the waveform contains i_tvalid=1 (i.e. there is some valid data and it's available to stall)
// Only matches when the waveform contains M_AXIS_TVALID=1 (i.e. there is some valid data and it's available to stall)
prot stall<DUT: AXISManager>(out data: u32, out last: u1) {
DUT.i_aresetn := 1'b1; // Keep out of reset
DUT.i_tready := 1'b0; // Apply backpressure
DUT.M_AXIS_ARESETN := 1'b1; // Keep out of reset
DUT.M_AXIS_TREADY := 1'b0; // Apply backpressure

// Stall only applies when manager has valid data
// If i_tvalid=0, this fails and we instead have an `idle` transaction
assert_eq(DUT.i_tvalid, 1'b1);
// If M_AXIS_TVALID=0, this fails and we instead have an `idle` transaction
assert_eq(DUT.M_AXIS_TVALID, 1'b1);

// Capture output values before the stall cycle
assert_eq(DUT.i_tdata, data);
assert_eq(DUT.i_tlast, last);
assert_eq(DUT.M_AXIS_TDATA, data);
assert_eq(DUT.M_AXIS_TLAST, last);

step();

// Verify outputs remained stable during the stall
assert_eq(DUT.i_tdata, data);
assert_eq(DUT.i_tlast, last);
assert_eq(DUT.M_AXIS_TDATA, data);
assert_eq(DUT.M_AXIS_TLAST, last);

step();
}

// WAIT_FOR_DATA: Receiver is ready but no data is available
// Used when tready=1 but tvalid=0 (i.e. receiver is polling for data)
prot wait_for_data<DUT: AXISManager>() {
DUT.i_aresetn := 1'b1; // Keep out of reset
DUT.i_tready := 1'b1;
DUT.M_AXIS_ARESETN := 1'b1; // Keep out of reset
DUT.M_AXIS_TREADY := 1'b1;

// Only matches when no data is available
assert_eq(DUT.i_tvalid, 1'b0);
assert_eq(DUT.M_AXIS_TVALID, 1'b0);
step();
}

// IDLE: No transaction - i_tready is deasserted
// IDLE: No transaction - M_AXIS_TREADY is deasserted
#[idle]
prot idle<DUT: AXISManager>() {
DUT.i_aresetn := 1'b1; // Keep out of reset
DUT.i_tready := 1'b0;
DUT.M_AXIS_ARESETN := 1'b1; // Keep out of reset
DUT.M_AXIS_TREADY := 1'b0;
step();
}
24 changes: 1 addition & 23 deletions monitor/tests/fpga-debugging/axi-stream-s2/s2_fixed.out
Original file line number Diff line number Diff line change
Expand Up @@ -14,34 +14,12 @@ trace {
recv(4); // [time: 937.5ns -> 962.5ns]
recv(5); // [time: 962.5ns -> 987.5ns]
recv(6); // [time: 987.5ns -> 1012.5ns]
stall(7, 0); // [time: 1012.5ns -> 1037.5ns]
stall(7, 0); // [time: 1037.5ns -> 1062.5ns]
stall(7, 0); // [time: 1012.5ns -> 1062.5ns]
reset(); // [time: 1062.5ns -> 1087.5ns]
reset(); // [time: 1087.5ns -> 1100ns]
}

// trace 1
trace {
reset(); // [time: 0ns -> 12.5ns]
wait_for_data(); // [time: 337.5ns -> 362.5ns]
wait_for_data(); // [time: 387.5ns -> 412.5ns]
wait_for_data(); // [time: 512.5ns -> 537.5ns]
wait_for_data(); // [time: 537.5ns -> 562.5ns]
wait_for_data(); // [time: 612.5ns -> 637.5ns]
wait_for_data(); // [time: 787.5ns -> 812.5ns]
wait_for_data(); // [time: 837.5ns -> 862.5ns]
recv(1); // [time: 862.5ns -> 887.5ns]
recv(2); // [time: 887.5ns -> 912.5ns]
recv(3); // [time: 912.5ns -> 937.5ns]
recv(4); // [time: 937.5ns -> 962.5ns]
recv(5); // [time: 962.5ns -> 987.5ns]
recv(6); // [time: 987.5ns -> 1012.5ns]
stall(7, 0); // [time: 1012.5ns -> 1037.5ns]
reset(); // [time: 1062.5ns -> 1087.5ns]
reset(); // [time: 1087.5ns -> 1100ns]
}

// trace 2
trace {
reset(); // [time: 0ns -> 12.5ns]
wait_for_data(); // [time: 337.5ns -> 362.5ns]
Expand Down
84 changes: 42 additions & 42 deletions monitor/tests/fpga-debugging/axi-stream-s2/s2_fixed.prot
Original file line number Diff line number Diff line change
@@ -1,113 +1,113 @@
// ARGS: --wave fpga-debugging/axi-stream-s2/s2_fixed.fst --instances TOP.testbench.UUT.axi_stream_check:AXISManager --sample-posedge TOP.testbench.UUT.axi_stream_check.i_aclk --show-waveform-time --time-unit ns

// ARGS: --wave fpga-debugging/axi-stream-s2/s2_fixed.fst --instances TOP.testbench.UUT:AXISManager --sample-posedge TOP.testbench.UUT.M_AXIS_ACLK --show-waveform-time --time-unit ns

// Source (manager) that outputs a sequence of 8 words
struct AXISManager {
// Control signals
in i_aresetn: u1, // Active-low reset
in M_AXIS_ARESETN: u1, // Active-low reset

// AXI-Stream manager outputs (from DUT perspective)
out i_tvalid: u1, // manager has valid data
out i_tdata: u32, // Data payload
out i_tstrb: u4, // Byte strobe (always 0xF)
out i_tlast: u1, // Last word in packet
out M_AXIS_TVALID: u1, // manager has valid data
out M_AXIS_TDATA: u32, // Data payload
out M_AXIS_TSTRB: u4, // Byte strobe (always 0xF)
out M_AXIS_TLAST: u1, // Last word in packet

// AXI-Stream sub-ordinate input
in i_tready: u1, // Downstream ready to accept
in M_AXIS_TREADY: u1, // Downstream ready to accept
}

// RESET: Assert reset (active-low) and wait for manager to be ready
// The manager waits C_M_START_COUNT cycles in INIT_COUNTER before sending
prot reset<DUT: AXISManager>() {
DUT.i_aresetn := 1'b0; // Assert reset (active-low)
DUT.i_tready := 1'b0;
DUT.M_AXIS_ARESETN := 1'b0; // Assert reset (active-low)
DUT.M_AXIS_TREADY := 1'b0;
step();
}

// RECV: Receive one data word from the AXI-Stream manager
// Data transfer occurs when i_tvalid and i_tready are both 1
// Only matches when data is immediately available (i_tvalid = 1)
// Data transfer occurs when M_AXIS_TVALID and M_AXIS_TREADY are both 1
// Only matches when data is immediately available (M_AXIS_TVALID = 1)
//
// Output Arguments:
// data - Expected payload from manager
prot recv<DUT: AXISManager>(
out data: u32,
) {
DUT.i_aresetn := 1'b1; // Keep out of reset
DUT.i_tready := 1'b1; // Signal ready to receive
DUT.M_AXIS_ARESETN := 1'b1; // Keep out of reset
DUT.M_AXIS_TREADY := 1'b1; // Signal ready to receive

// Only matches when data is available
// (use wait_for_data for cycles when tready=1 but tvalid=0)
assert_eq(DUT.i_tvalid, 1'b1);
assert_eq(DUT.M_AXIS_TVALID, 1'b1);

// Verify output data
assert_eq(DUT.i_tdata, data);
assert_eq(DUT.M_AXIS_TDATA, data);

// One cycle for the transfer to complete
step();
}

// RECV_LAST: Receive the last data word (with i_tlast asserted)
// This verifies both the data and that i_tlast is properly set
// Only matches when data is immediately available (i_tvalid = 1)
// RECV_LAST: Receive the last data word (with M_AXIS_TLAST asserted)
// This verifies both the data and that M_AXIS_TLAST is properly set
// Only matches when data is immediately available (M_AXIS_TVALID = 1)
//
// Output Arguments:
// data - Expected payload from manager (should be the last word)
prot recv_last<DUT: AXISManager>(
out data: u32,
) {
DUT.i_aresetn := 1'b1; // Keep out of reset
DUT.i_tready := 1'b1; // Signal ready to receive
DUT.M_AXIS_ARESETN := 1'b1; // Keep out of reset
DUT.M_AXIS_TREADY := 1'b1; // Signal ready to receive

// Only matches when data is available
assert_eq(DUT.i_tvalid, 1'b1);
assert_eq(DUT.M_AXIS_TVALID, 1'b1);

// Verify output data and i_tlast
assert_eq(DUT.i_tdata, data);
assert_eq(DUT.i_tlast, 1'b1);
// Verify output data and M_AXIS_TLAST
assert_eq(DUT.M_AXIS_TDATA, data);
assert_eq(DUT.M_AXIS_TLAST, 1'b1);

// One cycle for the transfer to complete
step();
}

// STALL: Assert backpressure (i_tready=0) while manager has valid data
// STALL: Assert backpressure (M_AXIS_TREADY=0) while manager has valid data
// AXI-Stream requires output args (tdata, tlast) to remain stable during stall
// Only matches when the waveform contains i_tvalid=1 (i.e. there is some valid data and it's available to stall)
// Only matches when the waveform contains M_AXIS_TVALID=1 (i.e. there is some valid data and it's available to stall)
prot stall<DUT: AXISManager>(out data: u32, out last: u1) {
DUT.i_aresetn := 1'b1; // Keep out of reset
DUT.i_tready := 1'b0; // Apply backpressure
DUT.M_AXIS_ARESETN := 1'b1; // Keep out of reset
DUT.M_AXIS_TREADY := 1'b0; // Apply backpressure

// Stall only applies when manager has valid data
// If i_tvalid=0, this fails and we instead have an `idle` transaction
assert_eq(DUT.i_tvalid, 1'b1);
// If M_AXIS_TVALID=0, this fails and we instead have an `idle` transaction
assert_eq(DUT.M_AXIS_TVALID, 1'b1);

// Capture output values before the stall cycle
assert_eq(DUT.i_tdata, data);
assert_eq(DUT.i_tlast, last);
assert_eq(DUT.M_AXIS_TDATA, data);
assert_eq(DUT.M_AXIS_TLAST, last);

step();

// Verify outputs remained stable during the stall
assert_eq(DUT.i_tdata, data);
assert_eq(DUT.i_tlast, last);
assert_eq(DUT.M_AXIS_TDATA, data);
assert_eq(DUT.M_AXIS_TLAST, last);

step();
}

// WAIT_FOR_DATA: Receiver is ready but no data is available
// Used when tready=1 but tvalid=0 (i.e. receiver is polling for data)
prot wait_for_data<DUT: AXISManager>() {
DUT.i_aresetn := 1'b1; // Keep out of reset
DUT.i_tready := 1'b1;
DUT.M_AXIS_ARESETN := 1'b1; // Keep out of reset
DUT.M_AXIS_TREADY := 1'b1;

// Only matches when no data is available
assert_eq(DUT.i_tvalid, 1'b0);

assert_eq(DUT.M_AXIS_TVALID, 1'b0);
step();
}

// IDLE: No transaction - i_tready is deasserted
// IDLE: No transaction - M_AXIS_TREADY is deasserted
#[idle]
prot idle<DUT: AXISManager>() {
DUT.i_aresetn := 1'b1; // Keep out of reset
DUT.i_tready := 1'b0;
DUT.M_AXIS_ARESETN := 1'b1; // Keep out of reset
DUT.M_AXIS_TREADY := 1'b0;
step();
}
Loading