ec82764bef
RTL (GS rasterizer, EE core stub, platform bridge, LPDDR4B path), sim regression (272 TBs), docs, and tooling. Copyrighted PS2 content (BIOS, game code, GS dumps, and all dump-derived textures/traces) is excluded via .gitignore and stays local. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
182 lines
6.3 KiB
Systemverilog
182 lines
6.3 KiB
Systemverilog
// retroDE_ps2 — sif_dma_ack_peer_stub
|
|
//
|
|
// Protocol combiner for the first combined control+data SIF milestone.
|
|
// Observes a mailbox command doorbell on one seam and the SIF DMA receive
|
|
// endpoint's payload-complete indication on the other; only issues the
|
|
// mailbox ack sequence once BOTH are true.
|
|
//
|
|
// Explicitly NOT an IOP. This module has no code execution, no bus master,
|
|
// and no capability beyond composing two existing SIF primitives. Kept
|
|
// under `rtl/sif/` with the other SIF scaffolding.
|
|
//
|
|
// Contract refs:
|
|
// docs/contracts/sif.md
|
|
//
|
|
// Layering:
|
|
// sif_mailbox_stub — storage primitive
|
|
// sif_mailbox_peer_stub — mailbox-only active peer (no DMA awareness)
|
|
// sif_dma_stub — data-plane receive endpoint
|
|
// sif_dma_ack_peer_stub — THIS module. Wires the two together.
|
|
//
|
|
// Protocol (one-shot):
|
|
// 1. EE writes MSCOM = cmd
|
|
// 2. EE writes MSFLG = CMD_PENDING_BIT (request doorbell)
|
|
// 3. DMAC transfers bounded payload into sif_dma_stub
|
|
// 4. sif_dma_stub asserts last_seen once the final beat arrives
|
|
// 5. this peer observes (MSFLG & CMD_PENDING_BIT) AND last_seen
|
|
// 6. peer reads MSCOM
|
|
// 7. peer writes SMCOM = cmd
|
|
// 8. peer writes SMFLG = CMD_ACK_BIT
|
|
// 9. terminal DONE (one-shot for this milestone)
|
|
//
|
|
// The peer does NOT clear MSFLG or SMFLG — lifecycle is the TB's
|
|
// responsibility, consistent with sif_mailbox_peer_stub's guardrail.
|
|
//
|
|
// Ports connect to:
|
|
// obs_* → sif_mailbox_stub iop_rd_* (peer reads MSFLG, then MSCOM)
|
|
// resp_* → sif_mailbox_stub iop_wr_* (peer writes SMCOM, then SMFLG)
|
|
// payload_complete ← sif_dma_stub.last_seen
|
|
|
|
`timescale 1ns/1ps
|
|
|
|
module sif_dma_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 (IOP-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 (IOP-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_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 MSFLG
|
|
S_POLL_WAIT = 3'd1, // wait for rd_valid, gate on BOTH conditions
|
|
S_MSCOM_REQ = 3'd2, // pulse rd_en for MSCOM
|
|
S_MSCOM_WAIT = 3'd3, // wait for rd_valid, latch cmd
|
|
S_WRITE_SMCOM = 3'd4, // drive wr_en, addr=SMCOM, data=cmd
|
|
S_WRITE_SMFLG = 3'd5, // drive wr_en, addr=SMFLG, 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 MSCOM_REQ only when MSFLG 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_MSCOM_REQ;
|
|
else
|
|
state <= S_POLL_REQ; // keep polling
|
|
end
|
|
end
|
|
|
|
S_MSCOM_REQ: state <= S_MSCOM_WAIT;
|
|
|
|
S_MSCOM_WAIT: begin
|
|
if (obs_rd_valid) begin
|
|
latched_cmd <= obs_rd_data;
|
|
state <= S_WRITE_SMCOM;
|
|
end
|
|
end
|
|
|
|
S_WRITE_SMCOM: state <= S_WRITE_SMFLG;
|
|
|
|
S_WRITE_SMFLG: 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 = MSFLG_OFF;
|
|
end
|
|
S_MSCOM_REQ: begin
|
|
obs_rd_en = 1'b1;
|
|
obs_rd_addr = MSCOM_OFF;
|
|
end
|
|
S_WRITE_SMCOM: begin
|
|
resp_wr_en = 1'b1;
|
|
resp_wr_addr = SMCOM_OFF;
|
|
resp_wr_data = latched_cmd;
|
|
end
|
|
S_WRITE_SMFLG: begin
|
|
resp_wr_en = 1'b1;
|
|
resp_wr_addr = SMFLG_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_SMFLG) begin
|
|
// S_WRITE_SMFLG is a single-cycle state: unconditionally
|
|
// transitions to S_DONE on the next edge, so this observes
|
|
// exactly one completion.
|
|
ack_count_o <= ack_count_o + 32'd1;
|
|
done_o <= 1'b1;
|
|
end
|
|
end
|
|
|
|
endmodule : sif_dma_ack_peer_stub
|