logicaffeine_compile/codegen_sva/
infinichip_rtl.rs1use super::dram::emit_dram_executor_verilog;
26use super::scratchpad_rtl::{emit_double_buffer_verilog, emit_secded_verilog};
27
28fn infinichip_top() -> &'static str {
35 r#"// infinichip — the integrated deterministic memory subsystem (Stage 2 tape-out top).
36// A STRUCTURAL composition of four independently-certified sub-blocks, registered at the
37// ECC boundaries:
38// dram_executor — the command player (in-order countdown FSM; decode + issue timing
39// certified by our SAT in codegen_sva::dram),
40// secded_enc/dec — SECDED(72,64) ECC on the data path (single-error-correct, double-error-
41// detect; exhaustively verified + Hamming-SAT-proven),
42// double_buffer — ping-pong bank select (compute never reads the DMA-filled bank;
43// read_bank != fill_bank by construction).
44// Each sub-block carries its own proof; tb_infinichip certifies the wiring composes correctly.
45module infinichip(
46 input clk,
47 input rst,
48 // command player — compile-time DRAM command stream in, issued commands out
49 input [63:0] cmd_word,
50 input cmd_valid,
51 output cmd_ack,
52 output issue,
53 output [2:0] op,
54 output [4:0] bank,
55 output [31:0] row,
56 // ECC write path — 64-bit data in, its 72-bit protected codeword out one edge later
57 input [63:0] wr_data,
58 output [71:0] wr_cw,
59 // ECC read path — 72-bit codeword in, corrected 64-bit data + status out one edge later
60 input [71:0] rd_cw,
61 output reg [63:0] rd_data,
62 output reg rd_corrected,
63 output reg rd_double_err,
64 // scratchpad double buffer
65 input swap,
66 output read_bank,
67 output fill_bank
68);
69 reg [63:0] wr_word;
70 wire [63:0] rd_word;
71 wire rd_fix, rd_bad;
72 dram_executor u_cmd (.clk(clk), .rst(rst), .cmd_word(cmd_word), .cmd_valid(cmd_valid),
73 .cmd_ack(cmd_ack), .issue(issue), .op(op), .bank(bank), .row(row));
74 secded_enc u_enc (.din(wr_word), .cw(wr_cw));
75 secded_dec u_dec (.cw_in(rd_cw), .dout(rd_word), .corrected(rd_fix),
76 .double_err(rd_bad));
77 double_buffer u_dbuf(.clk(clk), .rst(rst), .swap(swap),
78 .read_bank(read_bank), .fill_bank(fill_bank));
79 always @(posedge clk) begin
80 wr_word <= wr_data;
81 rd_data <= rd_word;
82 rd_corrected <= rd_fix;
83 rd_double_err <= rd_bad;
84 end
85endmodule
86"#
87}
88
89pub fn emit_infinichip_verilog() -> String {
92 let mut v = String::new();
93 v.push_str(&emit_dram_executor_verilog());
94 v.push('\n');
95 v.push_str(&emit_secded_verilog());
96 v.push('\n');
97 v.push_str(&emit_double_buffer_verilog());
98 v.push('\n');
99 v.push_str(infinichip_top());
100 v
101}
102
103pub fn emit_infinichip_testbench() -> String {
109 r#"// tb_infinichip — self-checking integration of the composed memory subsystem.
110`timescale 1ns/1ps
111module tb_infinichip;
112 reg clk = 0; always #5 clk = ~clk;
113 reg rst = 1;
114 reg [63:0] cmd_word = 0; reg cmd_valid = 0;
115 wire cmd_ack, issue; wire [2:0] op; wire [4:0] bank; wire [31:0] row;
116 reg [63:0] wr_data = 64'hDEAD_BEEF_1234_5678;
117 wire [71:0] wr_cw;
118 reg [71:0] rd_cw = 0;
119 wire [63:0] rd_data; wire rd_corrected, rd_double_err;
120 reg swap = 0; wire read_bank, fill_bank;
121 integer errors = 0;
122 reg saw_issue = 0;
123
124 infinichip dut(.clk(clk), .rst(rst), .cmd_word(cmd_word), .cmd_valid(cmd_valid),
125 .cmd_ack(cmd_ack), .issue(issue), .op(op), .bank(bank), .row(row),
126 .wr_data(wr_data), .wr_cw(wr_cw),
127 .rd_cw(rd_cw), .rd_data(rd_data), .rd_corrected(rd_corrected),
128 .rd_double_err(rd_double_err),
129 .swap(swap), .read_bank(read_bank), .fill_bank(fill_bank));
130
131 // A delta-0 command issues the cycle after it loads; latch and check its payload.
132 always @(posedge clk) if (issue) begin
133 saw_issue <= 1'b1;
134 if (op !== 3'd1 || bank !== 5'd3 || row !== 32'd7) errors = errors + 1;
135 end
136
137 initial begin
138 repeat (3) @(posedge clk);
139 rst = 0;
140 @(posedge clk);
141
142 // (1) ECC read path corrects a single-bit fault (position 7 = a Hamming data bit), its
143 // verdict registered one edge after the codeword arrives.
144 #1 rd_cw = wr_cw ^ (72'b1 << 7);
145 @(posedge clk); #1;
146 if (rd_data !== wr_data) begin errors = errors + 1; $display("FAIL: ECC did not recover data"); end
147 if (rd_corrected !== 1'b1) begin errors = errors + 1; $display("FAIL: ECC did not flag corrected"); end
148 if (rd_double_err !== 1'b0) begin errors = errors + 1; $display("FAIL: ECC false double-error"); end
149
150 // (2) Double-buffer disjointness holds now and across a swap.
151 if (fill_bank !== ~read_bank) begin errors = errors + 1; $display("FAIL: banks not disjoint"); end
152 swap = 1; @(posedge clk); swap = 0; @(posedge clk);
153 if (fill_bank !== ~read_bank) begin errors = errors + 1; $display("FAIL: banks not disjoint after swap"); end
154
155 // (3) Command player issues a loaded (delta-0) command with the exact payload.
156 cmd_word = {24'd0, 3'd1, 5'd3, 32'd7}; cmd_valid = 1;
157 repeat (6) @(posedge clk);
158 cmd_valid = 0;
159 repeat (2) @(posedge clk);
160 if (!saw_issue) begin errors = errors + 1; $display("FAIL: command player never issued"); end
161
162 if (errors == 0)
163 $display("PASS: infinichip integration - command player + SECDED ECC + double buffer");
164 else
165 $display("FAIL: %0d integration errors", errors);
166 $finish;
167 end
168endmodule
169"#
170 .to_string()
171}
172
173#[cfg(test)]
174#[path = "../../../logicaffeine_tests/unit/logicaffeine_compile/codegen_sva/infinichip_rtl/tests.rs"]
175mod tests;