// retroDE_ps2 — tb_sif_command_echo // // First two-actor coordination proof on the SIF seam. TB drives only the // EE side of sif_mailbox_stub; sif_mailbox_peer_stub drives the IOP side // in response. Demonstrates that the mailbox supports a real command-echo // handshake without requiring a live IOP peer. // // Scenario: // 1. TB writes MSCOM = cmd (0xFEED_FACE) // 2. TB writes MSFLG = CMD_PENDING_BIT (0x1) // 3. peer observes MSFLG, reads MSCOM, writes SMCOM = cmd, writes // SMFLG = CMD_ACK_BIT (0x2) // 4. TB reads SMCOM — expects 0xFEED_FACE // 5. TB reads SMFLG — expects CMD_ACK_BIT // // Pass criteria: // - peer done_o asserts within a bounded window // - SMCOM contains the command value written by EE // - SMFLG contains the ack bit // - trace shows mailbox writes with side_id=IOP for the peer's responses `timescale 1ns/1ps module tb_sif_command_echo; 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] CMD_VALUE = 32'hFEED_FACE; // ------------------------------------------------------------------ // Mailbox + peer // ------------------------------------------------------------------ // EE side (TB-driven) 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; // IOP side (peer-driven) 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 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) ); trace_sink_stub #(.FILENAME("sif_command_echo.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) ); // ------------------------------------------------------------------ // Counters // ------------------------------------------------------------------ int iop_side_writes; // peer responses (side_id==1, write) int ee_side_writes; // TB writes (side_id==0, write) int errors; // Capture the first two peer-originated writes so we can enforce the // exact protocol chronology (SMCOM=cmd, then SMFLG=ack), not just the // final-storage outcome. logic [7:0] captured_wr_addr [0:1]; logic [31:0] captured_wr_data [0:1]; initial begin iop_side_writes = 0; ee_side_writes = 0; errors = 0; captured_wr_addr[0] = 8'd0; captured_wr_addr[1] = 8'd0; captured_wr_data[0] = 32'd0; captured_wr_data[1] = 32'd0; 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_side_writes <= ee_side_writes + 1; if (ev_arg2[7:0] == 8'd1) begin // Latch the first two peer writes for chronology checks. if (iop_side_writes == 0) begin captured_wr_addr[0] <= ev_arg0[7:0]; captured_wr_data[0] <= ev_arg1[31:0]; end else if (iop_side_writes == 1) begin captured_wr_addr[1] <= ev_arg0[7:0]; captured_wr_data[1] <= ev_arg1[31:0]; end iop_side_writes <= iop_side_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] EE read %s: got 0x%08h valid=%0b expected 0x%08h", label, ee_rd_data, ee_rd_valid, expected); errors = errors + 1; end endtask // ------------------------------------------------------------------ // Stimulus // ------------------------------------------------------------------ int wait_cycles; 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); // 1. EE writes the command value into MSCOM. ee_write(MSCOM, CMD_VALUE); // 2. EE sets CMD_PENDING_BIT in MSFLG — the peer's doorbell. ee_write(MSFLG, CMD_PENDING_BIT); // 3. Wait for peer to complete its one-shot exchange. Bounded // wait: worst case ~10 cycles per state across 6 states plus // some polling slack. wait_cycles = 0; while (!peer_done && wait_cycles < 200) begin @(posedge clk); wait_cycles = wait_cycles + 1; end if (!peer_done) begin $error("[tb_sif_command_echo] peer did not complete within %0d cycles", wait_cycles); errors = errors + 1; end // Let the mailbox settle. repeat (2) @(posedge clk); // 4. EE reads SMCOM — must be the echoed command. ee_read_expect(SMCOM, CMD_VALUE, "SMCOM (echoed command)"); // 5. EE reads SMFLG — must be CMD_ACK_BIT. ee_read_expect(SMFLG, CMD_ACK_BIT, "SMFLG (ack)"); repeat (4) @(posedge clk); // ------------------------------------------------------------------ $display("[tb_sif_command_echo] peer_done=%0b wait_cycles=%0d ee_side_writes=%0d iop_side_writes=%0d errors=%0d", peer_done, wait_cycles, ee_side_writes, iop_side_writes, errors); if (!peer_done) $error("peer_done never asserted"); if (ee_side_writes < 2) $error("expected >= 2 EE-side writes, got %0d", ee_side_writes); if (iop_side_writes < 2) $error("expected >= 2 IOP-side writes (peer SMCOM+SMFLG), got %0d", iop_side_writes); // Protocol chronology: first peer write must be SMCOM=CMD_VALUE, // second must be SMFLG=CMD_ACK_BIT, in that order. if (captured_wr_addr[0] !== SMCOM) $error("peer write[0] expected SMCOM (0x%02h), got 0x%02h", SMCOM, captured_wr_addr[0]); if (captured_wr_data[0] !== CMD_VALUE) $error("peer write[0] SMCOM data expected 0x%08h, got 0x%08h", CMD_VALUE, captured_wr_data[0]); if (captured_wr_addr[1] !== SMFLG) $error("peer write[1] expected SMFLG (0x%02h), got 0x%02h", SMFLG, captured_wr_addr[1]); if (captured_wr_data[1] !== CMD_ACK_BIT) $error("peer write[1] SMFLG data expected 0x%08h, got 0x%08h", CMD_ACK_BIT, captured_wr_data[1]); if (errors == 0 && peer_done && ee_side_writes >= 2 && iop_side_writes >= 2 && captured_wr_addr[0] === SMCOM && captured_wr_data[0] === CMD_VALUE && captured_wr_addr[1] === SMFLG && captured_wr_data[1] === CMD_ACK_BIT) $display("[tb_sif_command_echo] PASS"); else $display("[tb_sif_command_echo] FAIL"); $finish; end initial begin #200000; $error("[tb_sif_command_echo] timeout"); $finish; end endmodule : tb_sif_command_echo