// retroDE_ps2 — sif_dma_ee_ack_peer_stub // // Protocol combiner for the first reverse-direction (IOP→EE) combined // control+data SIF milestone. Mirror of sif_dma_ack_peer_stub with // polarity swapped to observe the IOP's doorbell from the EE side and // respond back IOP-ward through the mailbox. // // Explicitly NOT an EE. This module has no code execution, no bus // master, no capability beyond composing two existing SIF primitives. // Kept under `rtl/sif/` with the other protocol scaffolding so it // does not get misread as EE maturity progress. // // Contract refs: // docs/contracts/sif.md // // Layering: // sif_mailbox_stub — storage primitive // sif_dma_ee_ram_bridge_stub — reverse-direction data-plane landing // sif_dma_ee_ack_peer_stub — THIS module. Ties them together on // the EE side. // // Protocol (one-shot, reverse direction): // 1. IOP writes SMCOM = cmd (what the IOP wants to say) // 2. IOP writes SMFLG = CMD_PENDING_BIT (doorbell IOP→EE) // 3. IOP DMAC ch9 transfers bounded payload through the SIF egress // bridge, which lands qwords in EE RAM. Bridge's last_seen_o rises // on the final beat and stays high. // 4. this peer observes (SMFLG & CMD_PENDING_BIT) AND payload_complete // 5. peer reads SMCOM (captures the command) // 6. peer writes MSCOM = cmd (echo back IOP-ward) // 7. peer writes MSFLG = CMD_ACK_BIT (ack back IOP-ward) // 8. terminal DONE (one-shot for this milestone) // // The peer does NOT clear SMFLG or MSFLG — lifecycle is the TB's // responsibility, consistent with sif_mailbox_peer_stub's guardrail. // // Ordering guarantee: the load-bearing behaviour is that the peer will // not advance to the ack write sequence unless BOTH the doorbell AND // payload_complete are observed simultaneously. The milestone is about // verifying that the ack is gated on data arriving, not just control. // // Ports connect to: // obs_* → sif_mailbox_stub ee_rd_* (peer reads SMFLG, then SMCOM) // resp_* → sif_mailbox_stub ee_wr_* (peer writes MSCOM, then MSFLG) // payload_complete ← sif_dma_ee_ram_bridge_stub.last_seen_o `timescale 1ns/1ps module sif_dma_ee_ack_peer_stub #( parameter logic [7:0] MSCOM_OFF = 8'h00, parameter logic [7:0] SMCOM_OFF = 8'h10, parameter logic [7:0] MSFLG_OFF = 8'h20, parameter logic [7:0] SMFLG_OFF = 8'h30, parameter logic [31:0] CMD_PENDING_BIT = 32'h0000_0001, parameter logic [31:0] CMD_ACK_BIT = 32'h0000_0002 ) ( input logic clk, input logic rst_n, // Mailbox observation (EE-side read port) output logic obs_rd_en, output logic [7:0] obs_rd_addr, input logic [31:0] obs_rd_data, input logic obs_rd_valid, // Mailbox response (EE-side write port) output logic resp_wr_en, output logic [7:0] resp_wr_addr, output logic [31:0] resp_wr_data, // Payload completion indication from sif_dma_ee_ram_bridge_stub (level) input logic payload_complete, // Status output logic done_o, output logic [31:0] ack_count_o ); typedef enum logic [2:0] { S_POLL_REQ = 3'd0, // pulse rd_en for SMFLG S_POLL_WAIT = 3'd1, // wait for rd_valid, gate on BOTH conditions S_SMCOM_REQ = 3'd2, // pulse rd_en for SMCOM S_SMCOM_WAIT = 3'd3, // wait for rd_valid, latch cmd S_WRITE_MSCOM = 3'd4, // drive wr_en, addr=MSCOM, data=cmd S_WRITE_MSFLG = 3'd5, // drive wr_en, addr=MSFLG, data=ACK S_DONE = 3'd6 // terminal (one-shot for this milestone) } state_e; state_e state; logic [31:0] latched_cmd; // ------------------------------------------------------------------ // State machine — advance to SMCOM_REQ only when SMFLG pending is set // AND payload_complete is observed simultaneously. This is the // load-bearing guarantee of the whole combiner. // ------------------------------------------------------------------ always_ff @(posedge clk) begin if (!rst_n) begin state <= S_POLL_REQ; latched_cmd <= 32'd0; end else begin unique case (state) S_POLL_REQ: state <= S_POLL_WAIT; S_POLL_WAIT: begin if (obs_rd_valid) begin if (((obs_rd_data & CMD_PENDING_BIT) != 32'd0) && payload_complete) state <= S_SMCOM_REQ; else state <= S_POLL_REQ; // keep polling end end S_SMCOM_REQ: state <= S_SMCOM_WAIT; S_SMCOM_WAIT: begin if (obs_rd_valid) begin latched_cmd <= obs_rd_data; state <= S_WRITE_MSCOM; end end S_WRITE_MSCOM: state <= S_WRITE_MSFLG; S_WRITE_MSFLG: state <= S_DONE; S_DONE: state <= S_DONE; default: state <= S_POLL_REQ; endcase end end // ------------------------------------------------------------------ // Output drive (combinational, one-hot on state) // ------------------------------------------------------------------ always_comb begin obs_rd_en = 1'b0; obs_rd_addr = 8'd0; resp_wr_en = 1'b0; resp_wr_addr = 8'd0; resp_wr_data = 32'd0; unique case (state) S_POLL_REQ: begin obs_rd_en = 1'b1; obs_rd_addr = SMFLG_OFF; end S_SMCOM_REQ: begin obs_rd_en = 1'b1; obs_rd_addr = SMCOM_OFF; end S_WRITE_MSCOM: begin resp_wr_en = 1'b1; resp_wr_addr = MSCOM_OFF; resp_wr_data = latched_cmd; end S_WRITE_MSFLG: begin resp_wr_en = 1'b1; resp_wr_addr = MSFLG_OFF; resp_wr_data = CMD_ACK_BIT; end default: ; endcase end // ------------------------------------------------------------------ // Ack bookkeeping // ------------------------------------------------------------------ always_ff @(posedge clk) begin if (!rst_n) begin ack_count_o <= 32'd0; done_o <= 1'b0; end else if (state == S_WRITE_MSFLG) begin ack_count_o <= ack_count_o + 32'd1; done_o <= 1'b1; end end endmodule : sif_dma_ee_ack_peer_stub