mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
Implement SPI-P1 from docs/ip/spi.md with a PCLK master, registered APB responses, packed 8x32 TX/RX FIFOs, finite quotas and retained-session abort/recovery. Add the handwritten SVH/C register mirror and private source, credit and GPIO qualification interfaces. Add independent pin timing and data checks, malformed-waveform negatives, FIFO stall/resume and phase-safe abort cases. Provide isolated lint, simulation, compositional formal, synthesis, netlist and observational STA flows with source-bound artifacts and tool-component fingerprints. Validation on the reviewed worktree: 2085 pytest tests passed and one optional pypdf test skipped; both RTL simulators, fresh depth-192 BMC and cover, and ICS55/IHP130 block synthesis and netlist simulation passed. Formatting, style/readiness, Ruff and regression dry-runs passed. SPI remains outside product filelists with DMA capability disabled. SPI-P2/P3 integration, full-product regression closure and physical/IO qualification remain pending; no phase acceptance or policy change.
98 lines
3.2 KiB
Systemverilog
98 lines
3.2 KiB
Systemverilog
// One finite frame, exclusively clocked by PCLK. Abort never samples more bits.
|
|
module spi_phase_ctrl (
|
|
input logic clk_i,
|
|
rst_n_i,
|
|
start_i,
|
|
abort_i,
|
|
clamp_i,
|
|
input logic cpol_i,
|
|
cpha_i,
|
|
lsb_first_i,
|
|
frame16_i,
|
|
input logic [15:0] divider_i,
|
|
tx_data_i,
|
|
input logic miso_i,
|
|
output logic active_o,
|
|
done_o,
|
|
sck_o,
|
|
mosi_o,
|
|
output logic [15:0] rx_data_o
|
|
);
|
|
typedef struct packed {
|
|
logic active, sck, mosi;
|
|
logic [15:0] timer, tx_data, rx_data;
|
|
logic [5:0] edge_count;
|
|
} phase_t;
|
|
phase_t s_phase_d, s_phase_q;
|
|
logic [3:0] s_bit;
|
|
logic [3:0] s_next_bit;
|
|
logic [4:0] s_last_bit;
|
|
logic s_trailing;
|
|
assign s_last_bit = frame16_i ? 5'd15 : 5'd7;
|
|
assign s_trailing = s_phase_q.edge_count[0];
|
|
assign s_bit = lsb_first_i ? s_phase_q.edge_count[4:1] :
|
|
4'(s_last_bit - s_phase_q.edge_count[5:1]);
|
|
assign s_next_bit = lsb_first_i ? s_bit[3:0] + 4'd1 : s_bit[3:0] - 4'd1;
|
|
assign active_o = s_phase_q.active;
|
|
assign sck_o = clamp_i ? cpol_i : s_phase_q.sck;
|
|
assign mosi_o = s_phase_q.mosi;
|
|
always_comb begin
|
|
s_phase_d = s_phase_q;
|
|
done_o = 1'b0;
|
|
rx_data_o = s_phase_q.rx_data;
|
|
if (clamp_i) begin
|
|
s_phase_d.active = 1'b0;
|
|
s_phase_d.sck = cpol_i;
|
|
end else if (!s_phase_q.active) begin
|
|
s_phase_d.sck = cpol_i;
|
|
if (start_i && !abort_i) begin
|
|
s_phase_d.active = 1'b1;
|
|
s_phase_d.edge_count = '0;
|
|
s_phase_d.timer = divider_i - 16'd1;
|
|
s_phase_d.tx_data = tx_data_i;
|
|
s_phase_d.rx_data = '0;
|
|
s_phase_d.mosi = cpha_i ? 1'b0 : tx_data_i[lsb_first_i?4'd0 : s_last_bit[3:0]];
|
|
end
|
|
end else if (abort_i && (s_phase_q.sck == cpol_i)) begin
|
|
s_phase_d.active = 1'b0;
|
|
end else if (s_phase_q.timer != 16'd0) begin
|
|
s_phase_d.timer = s_phase_q.timer - 16'd1;
|
|
end else if (abort_i) begin
|
|
s_phase_d.sck = cpol_i;
|
|
s_phase_d.active = 1'b0;
|
|
end else begin
|
|
s_phase_d.timer = divider_i - 16'd1;
|
|
s_phase_d.sck = !s_phase_q.sck;
|
|
s_phase_d.edge_count = s_phase_q.edge_count + 6'd1;
|
|
if (s_trailing == cpha_i) begin
|
|
s_phase_d.rx_data[s_bit[3:0]] = miso_i;
|
|
rx_data_o[s_bit[3:0]] = miso_i;
|
|
end
|
|
if (cpha_i && !s_trailing) s_phase_d.mosi = s_phase_q.tx_data[s_bit[3:0]];
|
|
if (!cpha_i && s_trailing && (s_phase_q.edge_count != {s_last_bit, 1'b1}))
|
|
s_phase_d.mosi = s_phase_q.tx_data[s_next_bit[3:0]];
|
|
if (s_phase_q.edge_count == {s_last_bit, 1'b1}) begin
|
|
done_o = 1'b1;
|
|
s_phase_d.active = 1'b0;
|
|
end
|
|
end
|
|
end
|
|
dffr #(
|
|
.DATA_WIDTH($bits(phase_t))
|
|
) u_phase (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_phase_d),
|
|
.dat_o (s_phase_q)
|
|
);
|
|
`ifdef FORMAL
|
|
// Reachable timer/edge invariants strengthen induction without constraining the environment.
|
|
always_ff @(posedge clk_i) begin
|
|
if (rst_n_i && s_phase_q.active) begin
|
|
assert (s_phase_q.timer < divider_i);
|
|
assert (s_phase_q.edge_count <= {s_last_bit, 1'b1});
|
|
end
|
|
end
|
|
`endif
|
|
endmodule
|