mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
Integrate the SPI-P2 shared-DMA ABI into the Tiny product: SPI0 at 0x1001D000 with IRQ25 and RCU target16, the four frozen Gen1 ALT routes, latched four-bit source qualification through the fabric and APB CDC, GPIO readiness/ownership guard, and the bounded rs_spi_* HAL with the TINY_SPI_P3_ACCEPTANCE ci_smoke image. Tiny DMA enables V2.2 requests 16/17 with exact fixed FIFO MMIO checks; Mini remains default-disabled and unchanged. Review closure: the DMA AXI4 master stages the origin channel with each burst so final and contended SPI alias bursts stay qualified, the RCU spare feedback bit is tied off for four-state simulators, and the clock-switch quiescence now includes the SPI session. Validation: full Pytest 2097 passed, 0 failed, 1 skipped (optional pypdf); both Tiny profiles pass the SPI-P3 Verilator acceptance (SIM_TEST_PASS cycles=1030088); R2-P7 Verilator acceptance, Mini DMA formal prove and cover, Mini/Tiny lint, Tiny Yosys synthesis, netsim-boot and OpenSTA pass; C format/policy/host tests and RTL format/style checks clean. Evidence and remaining obligations are recorded in docs/ip/spi-verification.md. Tiny Icarus behavioral remains blocked by the pre-existing observer/sv2v defect (routed to the R2 DV owners); Mini smoke synthesis host timeout is skipped per maintainer decision. Human phase acceptance and SPI-P4/P5 remain outstanding; phase_accepted stays false.
169 lines
5.1 KiB
Systemverilog
169 lines
5.1 KiB
Systemverilog
// Copyright (c) 2026 Yuchi Miao
|
|
// SPDX-License-Identifier: MulanPSL-2.0
|
|
|
|
// One outstanding SYS control request. Either endpoint reset flushes both
|
|
// mailboxes. A destination reset aborts the accepted source transfer with an
|
|
// error; it never replays it when the destination returns. The SYS endpoint
|
|
// remains clocked so a stopped/disabled peripheral is still reachable.
|
|
module tiny_apb_async_bridge (
|
|
input logic src_clk_i,
|
|
input logic src_rst_n_i,
|
|
input logic dst_clk_i,
|
|
input logic dst_rst_n_i,
|
|
input logic disabled_i,
|
|
input logic [1:0] src_origin_i,
|
|
input logic [2:0] src_channel_i,
|
|
output logic idle_o,
|
|
output logic ready_o,
|
|
output logic [1:0] dst_origin_o,
|
|
output logic [2:0] dst_channel_o,
|
|
apb4_if.slave src_apb4,
|
|
apb4_if.master dst_apb4
|
|
);
|
|
typedef enum logic [1:0] {
|
|
SrcIdle,
|
|
SrcSend,
|
|
SrcWait,
|
|
SrcResponse
|
|
} src_state_e;
|
|
typedef enum logic [1:0] {
|
|
DstIdle,
|
|
DstSetup,
|
|
DstAccess,
|
|
DstResponse
|
|
} dst_state_e;
|
|
typedef struct packed {
|
|
src_state_e state;
|
|
logic [76:0] req;
|
|
logic [32:0] resp;
|
|
} src_t;
|
|
typedef struct packed {
|
|
dst_state_e state;
|
|
logic [76:0] req;
|
|
logic [32:0] resp;
|
|
} dst_t;
|
|
src_t s_src_d, s_src_q;
|
|
dst_t s_dst_d, s_dst_q;
|
|
logic s_link_src_rst_n, s_link_dst_rst_n;
|
|
logic s_req_ready, s_req_valid, s_resp_ready, s_resp_valid;
|
|
logic [76:0] s_req_data;
|
|
logic [32:0] s_resp_data;
|
|
|
|
cdc_reset_barrier #(
|
|
.STAGES(5)
|
|
) u_barrier (
|
|
.clk_a_i (src_clk_i),
|
|
.clk_b_i (dst_clk_i),
|
|
.rst_n_i (src_rst_n_i && dst_rst_n_i),
|
|
.release_i(1'b1),
|
|
.rst_a_n_o(s_link_src_rst_n),
|
|
.rst_b_n_o(s_link_dst_rst_n)
|
|
);
|
|
cdc_2phase #(
|
|
.DATA_WIDTH(77)
|
|
) u_req_cdc (
|
|
.src_clk_i (src_clk_i),
|
|
.src_rst_n_i(s_link_src_rst_n),
|
|
.src_data_i (s_src_q.req),
|
|
.src_valid_i(s_src_q.state == SrcSend),
|
|
.src_ready_o(s_req_ready),
|
|
.dst_clk_i (dst_clk_i),
|
|
.dst_rst_n_i(s_link_dst_rst_n),
|
|
.dst_data_o (s_req_data),
|
|
.dst_valid_o(s_req_valid),
|
|
.dst_ready_i(s_dst_q.state == DstIdle)
|
|
);
|
|
cdc_2phase #(
|
|
.DATA_WIDTH(33)
|
|
) u_resp_cdc (
|
|
.src_clk_i (dst_clk_i),
|
|
.src_rst_n_i(s_link_dst_rst_n),
|
|
.src_data_i (s_dst_q.resp),
|
|
.src_valid_i(s_dst_q.state == DstResponse),
|
|
.src_ready_o(s_resp_ready),
|
|
.dst_clk_i (src_clk_i),
|
|
.dst_rst_n_i(s_link_src_rst_n),
|
|
.dst_data_o (s_resp_data),
|
|
.dst_valid_o(s_resp_valid),
|
|
.dst_ready_i(s_src_q.state == SrcWait)
|
|
);
|
|
assign ready_o = s_link_src_rst_n && s_req_ready;
|
|
assign idle_o = (s_src_q.state == SrcIdle) && s_req_ready && !s_resp_valid;
|
|
assign src_apb4.pready = s_src_q.state == SrcResponse;
|
|
assign src_apb4.pslverr = s_src_q.resp[32];
|
|
assign src_apb4.prdata = s_src_q.resp[31:0];
|
|
assign {dst_apb4.paddr, dst_apb4.pprot, dst_apb4.pwrite,
|
|
dst_apb4.pwdata, dst_apb4.pstrb} = s_dst_q.req[71:0];
|
|
assign dst_apb4.psel = s_link_dst_rst_n &&
|
|
((s_dst_q.state == DstSetup) || (s_dst_q.state == DstAccess));
|
|
assign dst_apb4.penable = s_link_dst_rst_n && (s_dst_q.state == DstAccess);
|
|
assign dst_origin_o = s_dst_q.req[76:75];
|
|
assign dst_channel_o = s_dst_q.req[74:72];
|
|
|
|
always_comb begin
|
|
s_src_d = s_src_q;
|
|
unique case (s_src_q.state)
|
|
SrcIdle:
|
|
if (src_apb4.psel && src_apb4.penable) begin
|
|
s_src_d.req = {
|
|
src_origin_i,
|
|
src_channel_i,
|
|
src_apb4.paddr,
|
|
src_apb4.pprot,
|
|
src_apb4.pwrite,
|
|
src_apb4.pwdata,
|
|
src_apb4.pstrb
|
|
};
|
|
s_src_d.resp = {1'b1, 32'd0};
|
|
s_src_d.state = disabled_i ? SrcResponse : SrcSend;
|
|
end
|
|
SrcSend: if (s_req_ready) s_src_d.state = SrcWait;
|
|
SrcWait:
|
|
if (s_resp_valid) begin
|
|
s_src_d.resp = s_resp_data;
|
|
s_src_d.state = SrcResponse;
|
|
end
|
|
SrcResponse: s_src_d.state = SrcIdle;
|
|
default: s_src_d.state = SrcIdle;
|
|
endcase
|
|
if (((s_src_q.state == SrcSend) || (s_src_q.state == SrcWait)) && !s_link_src_rst_n) begin
|
|
s_src_d.resp = {1'b1, 32'd0};
|
|
s_src_d.state = SrcResponse;
|
|
end
|
|
end
|
|
always_comb begin
|
|
s_dst_d = s_dst_q;
|
|
unique case (s_dst_q.state)
|
|
DstIdle:
|
|
if (s_req_valid) begin
|
|
s_dst_d.req = s_req_data;
|
|
s_dst_d.state = DstSetup;
|
|
end
|
|
DstSetup: s_dst_d.state = DstAccess;
|
|
DstAccess:
|
|
if (dst_apb4.pready) begin
|
|
s_dst_d.resp = {dst_apb4.pslverr, dst_apb4.prdata};
|
|
s_dst_d.state = DstResponse;
|
|
end
|
|
DstResponse: if (s_resp_ready) s_dst_d.state = DstIdle;
|
|
default: s_dst_d.state = DstIdle;
|
|
endcase
|
|
end
|
|
dffr #(
|
|
.DATA_WIDTH($bits(src_t))
|
|
) u_src (
|
|
.clk_i (src_clk_i),
|
|
.rst_n_i(src_rst_n_i),
|
|
.dat_i (s_src_d),
|
|
.dat_o (s_src_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH($bits(dst_t))
|
|
) u_dst (
|
|
.clk_i (dst_clk_i),
|
|
.rst_n_i(s_link_dst_rst_n),
|
|
.dat_i (s_dst_d),
|
|
.dat_o (s_dst_q)
|
|
);
|
|
endmodule
|