// retroDE_ps2 — tb_sif_command_echo_rearm // // A'' lifecycle proof: two consecutive command echoes through the SIF // mailbox with an explicit TB-driven re-arm between them. Validates that // the peer responds exactly once per doorbell assertion and does not // re-fire while the doorbell stays high. // // Scenario: // 1. EE writes MSCOM = cmd1 (0xFEED_FACE) // 2. EE writes MSFLG = CMD_PENDING_BIT // 3. wait for ack_count_o == 1 // 4. verify SMCOM == cmd1, SMFLG == CMD_ACK_BIT // 5. spin for a while with MSFLG still high; ack_count_o must stay at 1 // 6. TB clears MSFLG (write 0) — explicit re-arm // 7. TB clears SMFLG (write 0) // 8. EE writes MSCOM = cmd2 (0xDEAD_BEEF) // 9. EE writes MSFLG = CMD_PENDING_BIT (second doorbell rising edge) // 10. wait for ack_count_o == 2 // 11. verify SMCOM == cmd2 (NOT stale cmd1), SMFLG == CMD_ACK_BIT // // Key properties enforced: // - one ack per doorbell rising edge // - no re-fire while doorbell stays asserted // - second exchange picks up cmd2, not stale cmd1 // - peer captures exactly four writes in order: // [0] SMCOM = cmd1 // [1] SMFLG = ACK // [2] SMCOM = cmd2 // [3] SMFLG = ACK `timescale 1ns/1ps module tb_sif_command_echo_rearm; logic clk; logic rst_n; initial clk = 1'b0; always #5 clk = ~clk; localparam logic [7:0] MSCOM = 8'h00; localparam logic [7:0] SMCOM = 8'h10; localparam logic [7:0] MSFLG = 8'h20; localparam logic [7:0] SMFLG = 8'h30; localparam logic [31:0] CMD_PENDING_BIT = 32'h0000_0001; localparam logic [31:0] CMD_ACK_BIT = 32'h0000_0002; localparam logic [31:0] CMD1_VALUE = 32'hFEED_FACE; localparam logic [31:0] CMD2_VALUE = 32'hDEAD_BEEF; // ------------------------------------------------------------------ // Mailbox + peer // ------------------------------------------------------------------ logic ee_wr_en, ee_rd_en; logic [7:0] ee_addr; logic [31:0] ee_wr_data; logic [31:0] ee_rd_data; logic ee_rd_valid; logic peer_rd_en; logic [7:0] peer_rd_addr; logic [31:0] peer_rd_data; logic peer_rd_valid; logic peer_wr_en; logic [7:0] peer_wr_addr_w; logic [31:0] peer_wr_data_w; logic peer_done; logic [31:0] peer_ack_count; logic ev_valid; trace_pkg::subsys_e ev_subsys; trace_pkg::event_e ev_event; logic [63:0] ev_arg0, ev_arg1, ev_arg2, ev_arg3; logic [31:0] ev_flags; sif_mailbox_stub u_mailbox ( .clk(clk), .rst_n(rst_n), .ee_wr_en(ee_wr_en), .ee_rd_en(ee_rd_en), .ee_addr(ee_addr), .ee_wr_data(ee_wr_data), .ee_rd_data(ee_rd_data), .ee_rd_valid(ee_rd_valid), .iop_wr_en(peer_wr_en), .iop_rd_en(peer_rd_en), .iop_addr(peer_wr_en ? peer_wr_addr_w : peer_rd_addr), .iop_wr_data(peer_wr_data_w), .iop_rd_data(peer_rd_data), .iop_rd_valid(peer_rd_valid), .ev_valid(ev_valid), .ev_subsys(ev_subsys), .ev_event(ev_event), .ev_arg0(ev_arg0), .ev_arg1(ev_arg1), .ev_arg2(ev_arg2), .ev_arg3(ev_arg3), .ev_flags(ev_flags) ); sif_mailbox_peer_stub u_peer ( .clk(clk), .rst_n(rst_n), .obs_rd_en(peer_rd_en), .obs_rd_addr(peer_rd_addr), .obs_rd_data(peer_rd_data), .obs_rd_valid(peer_rd_valid), .resp_wr_en(peer_wr_en), .resp_wr_addr(peer_wr_addr_w), .resp_wr_data(peer_wr_data_w), .done_o(peer_done), .ack_count_o(peer_ack_count) ); trace_sink_stub #(.FILENAME("sif_command_echo_rearm.trace"), .SINK_LABEL("sif")) u_trace_sif ( .clk(clk), .rst_n(rst_n), .ev_valid(ev_valid), .ev_subsys(ev_subsys), .ev_event(ev_event), .ev_arg0(ev_arg0), .ev_arg1(ev_arg1), .ev_arg2(ev_arg2), .ev_arg3(ev_arg3), .ev_flags(ev_flags) ); // ------------------------------------------------------------------ // Capture peer-originated writes in order (up to 4 expected) // ------------------------------------------------------------------ int iop_writes; int ee_writes; logic [7:0] captured_addr [0:3]; logic [31:0] captured_data [0:3]; int errors; initial begin iop_writes = 0; ee_writes = 0; for (int i = 0; i < 4; i++) begin captured_addr[i] = 8'd0; captured_data[i] = 32'd0; end errors = 0; end always_ff @(posedge clk) begin if (rst_n && ev_valid && ev_event == trace_pkg::EV_WRITE) begin if (ev_arg2[7:0] == 8'd0) ee_writes <= ee_writes + 1; if (ev_arg2[7:0] == 8'd1) begin if (iop_writes < 4) begin captured_addr[iop_writes] <= ev_arg0[7:0]; captured_data[iop_writes] <= ev_arg1[31:0]; end iop_writes <= iop_writes + 1; end end end // ------------------------------------------------------------------ // EE-side helpers // ------------------------------------------------------------------ task automatic ee_write(input logic [7:0] addr, input logic [31:0] data); @(negedge clk); ee_wr_en = 1'b1; ee_addr = addr; ee_wr_data = data; @(negedge clk); ee_wr_en = 1'b0; ee_addr = 8'd0; ee_wr_data = 32'd0; endtask task automatic ee_read_expect(input logic [7:0] addr, input logic [31:0] expected, input string label); @(negedge clk); ee_rd_en = 1'b1; ee_addr = addr; @(negedge clk); ee_rd_en = 1'b0; ee_addr = 8'd0; if (ee_rd_data !== expected || ee_rd_valid !== 1'b1) begin $error("[tb_sif_command_echo_rearm] EE read %s: got 0x%08h valid=%0b expected 0x%08h", label, ee_rd_data, ee_rd_valid, expected); errors = errors + 1; end endtask task automatic wait_for_ack(input int target, input string label); int spin; spin = 0; while (peer_ack_count < target && spin < 200) begin @(posedge clk); spin = spin + 1; end if (peer_ack_count < target) begin $error("[tb_sif_command_echo_rearm] peer did not reach ack_count=%0d (%s) within %0d cycles", target, label, spin); errors = errors + 1; end endtask // ------------------------------------------------------------------ // Stimulus // ------------------------------------------------------------------ int before_rearm_count; initial begin rst_n = 1'b0; ee_wr_en = 1'b0; ee_rd_en = 1'b0; ee_addr = 8'd0; ee_wr_data = 32'd0; repeat (4) @(posedge clk); rst_n = 1'b1; repeat (2) @(posedge clk); // ---- Exchange 1 ---- ee_write(MSCOM, CMD1_VALUE); ee_write(MSFLG, CMD_PENDING_BIT); wait_for_ack(1, "first echo"); repeat (2) @(posedge clk); ee_read_expect(SMCOM, CMD1_VALUE, "SMCOM after cmd1"); ee_read_expect(SMFLG, CMD_ACK_BIT, "SMFLG after cmd1"); // Spin with MSFLG still high — ack count must not advance. before_rearm_count = peer_ack_count; repeat (40) @(posedge clk); if (peer_ack_count !== before_rearm_count) begin $error("[tb_sif_command_echo_rearm] peer re-fired while doorbell still high: count went %0d → %0d", before_rearm_count, peer_ack_count); errors = errors + 1; end // ---- Explicit re-arm (TB owns lifecycle) ---- ee_write(MSFLG, 32'd0); ee_write(SMFLG, 32'd0); repeat (4) @(posedge clk); // ---- Exchange 2 ---- ee_write(MSCOM, CMD2_VALUE); ee_write(MSFLG, CMD_PENDING_BIT); wait_for_ack(2, "second echo"); repeat (2) @(posedge clk); ee_read_expect(SMCOM, CMD2_VALUE, "SMCOM after cmd2 (must not be stale cmd1)"); ee_read_expect(SMFLG, CMD_ACK_BIT, "SMFLG after cmd2"); repeat (4) @(posedge clk); // ------------------------------------------------------------------ $display("[tb_sif_command_echo_rearm] ack_count=%0d ee_writes=%0d iop_writes=%0d captured={SMCOM=0x%08h, SMFLG=0x%08h, SMCOM=0x%08h, SMFLG=0x%08h} errors=%0d", peer_ack_count, ee_writes, iop_writes, captured_data[0], captured_data[1], captured_data[2], captured_data[3], errors); if (peer_ack_count !== 32'd2) $error("expected ack_count=2, got %0d", peer_ack_count); // Peer-write chronology across both exchanges if (captured_addr[0] !== SMCOM || captured_data[0] !== CMD1_VALUE) $error("write[0] expected SMCOM=0x%08h, got addr=0x%02h data=0x%08h", CMD1_VALUE, captured_addr[0], captured_data[0]); if (captured_addr[1] !== SMFLG || captured_data[1] !== CMD_ACK_BIT) $error("write[1] expected SMFLG=0x%08h, got addr=0x%02h data=0x%08h", CMD_ACK_BIT, captured_addr[1], captured_data[1]); if (captured_addr[2] !== SMCOM || captured_data[2] !== CMD2_VALUE) $error("write[2] expected SMCOM=0x%08h (cmd2), got addr=0x%02h data=0x%08h", CMD2_VALUE, captured_addr[2], captured_data[2]); if (captured_addr[3] !== SMFLG || captured_data[3] !== CMD_ACK_BIT) $error("write[3] expected SMFLG=0x%08h, got addr=0x%02h data=0x%08h", CMD_ACK_BIT, captured_addr[3], captured_data[3]); if (iop_writes !== 4) $error("expected exactly 4 IOP-side writes across two exchanges, got %0d", iop_writes); if (errors == 0 && peer_ack_count == 32'd2 && iop_writes == 4 && captured_addr[0] === SMCOM && captured_data[0] === CMD1_VALUE && captured_addr[1] === SMFLG && captured_data[1] === CMD_ACK_BIT && captured_addr[2] === SMCOM && captured_data[2] === CMD2_VALUE && captured_addr[3] === SMFLG && captured_data[3] === CMD_ACK_BIT) $display("[tb_sif_command_echo_rearm] PASS"); else $display("[tb_sif_command_echo_rearm] FAIL"); $finish; end initial begin #400000; $error("[tb_sif_command_echo_rearm] timeout"); $finish; end endmodule : tb_sif_command_echo_rearm