Skip to main content

logicaffeine_compile/codegen_sva/
computedram_rtl.rs

1//! computedram_rtl — Phase 4: the in-DRAM compute path proven end-to-end in simulation.
2//!
3//! A behavioral device that IMPLEMENTS the ComputeDRAM effect: on the ACT→PRE→ACT cadence (an ACT, an
4//! interrupted precharge, then a second ACT with no RD/WR between — the signature `emit_computedram_stream`
5//! produces), it performs RowClone (`dst := src`). Driven by the proven `dram_executor` (which issues the
6//! delta-encoded raw sub-JEDEC command stream), it lets a `ComputeDramOp` execute as real commands and
7//! yields a result checked against the `RowStore`/VM oracle — "compute over RAM," proven in sim.
8//!
9//! This models the CHIP computing; whether the physical Micron DDR2 exhibits the effect is the Phase-3
10//! characterization (which may differ). The harness plays the role of the fabric orchestrating the op:
11//! it places the operand row, triggers the stream, and reads the result back.
12
13/// Emit `computedram_model` — the behavioral ComputeDRAM device. On the ACT→PRE→ACT compute cadence it
14/// performs RowClone (2 activations: `dst := src`); on ACT→PRE→ACT→PRE→ACT (3 activations) it performs
15/// the sense-amp triple-row MAJORITY over the ORIGINAL operand values, writing it to all three rows —
16/// so bitwise AND = MAJ(a,b,0) and OR = MAJ(a,b,1). Snapshots the originals so the tentative 2-row
17/// clone can be corrected by a 3rd activation. A RD/WR closes the cadence. Preload/read ports for the
18/// harness (which plays the fabric's orchestration role).
19pub fn emit_computedram_model_verilog() -> String {
20    r#"// computedram_model — behavioral ComputeDRAM device: RowClone (2-ACT) + triple-row MAJORITY (3-ACT)
21// + AMBIT bulk NOT (op 5, dual-contact cells): mem[bank] := ~mem[row]. NOT completes the boolean
22// basis {copy, AND, OR, NOT} so XOR / set-difference / popcount kernels run on this engine.
23module computedram_model #(parameter AW=8, DW=64) (
24  input clk, input rst,
25  input issue, input [2:0] op, input [4:0] bank, input [31:0] row,
26  input pre_we, input [AW-1:0] pre_addr, input [DW-1:0] pre_data,
27  input [AW-1:0] rd_addr, output [DW-1:0] rd_out
28);
29  (* ram_style = "registers" *) reg [DW-1:0] mem [0:(1<<AW)-1];
30  reg [AW-1:0] r0_i, r1_i;
31  reg [DW-1:0] snap0, snap1;
32  reg cad_open, pre_int; reg [1:0] ncad;
33  assign rd_out = mem[rd_addr];
34  function [DW-1:0] maj3(input [DW-1:0] a, input [DW-1:0] b, input [DW-1:0] c);
35    maj3 = (a & b) | (a & c) | (b & c);
36  endfunction
37  always @(posedge clk) begin
38    if (pre_we) mem[pre_addr] <= pre_data;   // operand placement (works during reset too)
39    if (rst) begin
40      cad_open <= 1'b0; pre_int <= 1'b0; ncad <= 2'd0; r0_i <= 0; r1_i <= 0; snap0 <= 0; snap1 <= 0;
41    end else if (issue) begin
42      case (op)
43        3'd0: begin // ACT
44          if (cad_open && pre_int && ncad == 2'd1) begin
45            // 2nd activation: tentative RowClone (dst := src), snapshot the originals
46            r1_i <= row[AW-1:0]; snap1 <= mem[row[AW-1:0]];
47            mem[row[AW-1:0]] <= snap0;
48            ncad <= 2'd2; pre_int <= 1'b0;
49          end else if (cad_open && pre_int && ncad == 2'd2) begin
50            // 3rd activation: triple-row majority over the ORIGINAL snapshots → all three rows
51            mem[r0_i] <= maj3(snap0, snap1, mem[row[AW-1:0]]);
52            mem[r1_i] <= maj3(snap0, snap1, mem[row[AW-1:0]]);
53            mem[row[AW-1:0]] <= maj3(snap0, snap1, mem[row[AW-1:0]]);
54            ncad <= 2'd3; pre_int <= 1'b0;
55          end else begin
56            // fresh activation: start a new cadence
57            r0_i <= row[AW-1:0]; snap0 <= mem[row[AW-1:0]]; ncad <= 2'd1; cad_open <= 1'b1; pre_int <= 1'b0;
58          end
59        end
60        3'd3: if (cad_open) pre_int <= 1'b1;                               // interrupted precharge
61        3'd1, 3'd2: begin cad_open <= 1'b0; pre_int <= 1'b0; ncad <= 2'd0; end // RD/WR closes the cadence
62        3'd5: mem[bank[AW-1:0]] <= ~mem[row[AW-1:0]];                      // Ambit bulk NOT: dst(bank) := ~src(row)
63        default: ;
64      endcase
65    end
66  end
67endmodule
68"#
69    .to_string()
70}
71
72/// Emit `tb_computedram` — the harness (the fabric's role): preload `src` with `pattern`, replay the
73/// delta-encoded ComputeDRAM stream (`cdram_plan.hex`) through `dram_executor` into the device, then read
74/// `dst` and assert it equals `pattern` (the RowClone oracle). `nwords` = stream length.
75pub fn emit_computedram_harness_testbench(
76    nwords: usize,
77    src: usize,
78    dst: usize,
79    pattern: u64,
80) -> String {
81    let last = nwords.saturating_sub(1);
82    let runcycles = nwords * 8 + 40;
83    format!(
84        r#"`timescale 1ns/1ps
85module tb_computedram;
86  reg clk=0; always #5 clk=~clk;
87  reg rst=1;
88  reg [63:0] rom [0:{last}];
89  integer idx=0;
90  wire [63:0] cmd_word = rom[idx];
91  wire cmd_valid = (idx <= {last});
92  wire cmd_ack, issue; wire [2:0] op; wire [4:0] bank; wire [31:0] row;
93  dram_executor exec(.clk(clk), .rst(rst), .cmd_word(cmd_word), .cmd_valid(cmd_valid),
94                     .cmd_ack(cmd_ack), .issue(issue), .op(op), .bank(bank), .row(row));
95  reg pre_we; reg [7:0] pre_addr; reg [63:0] pre_data; reg [7:0] rd_addr; wire [63:0] rd_out;
96  computedram_model #(.AW(8), .DW(64)) dut(.clk(clk), .rst(rst), .issue(issue), .op(op), .bank(bank),
97    .row(row), .pre_we(pre_we), .pre_addr(pre_addr), .pre_data(pre_data), .rd_addr(rd_addr), .rd_out(rd_out));
98  integer errors;
99  always @(posedge clk) if (!rst && cmd_ack) idx <= idx + 1;
100  initial begin
101    errors=0; pre_we=0; pre_addr=0; pre_data=0; rd_addr=0;
102    $readmemh("cdram_plan.hex", rom);
103    @(posedge clk); pre_we=1; pre_addr=8'd{src}; pre_data=64'h{pattern:016x}; @(posedge clk); pre_we=0;
104    @(posedge clk); rst=0;
105    repeat ({runcycles}) @(posedge clk);
106    rd_addr=8'd{dst}; @(posedge clk); @(posedge clk);
107    if (rd_out !== 64'h{pattern:016x}) begin
108      errors=errors+1; $display("FAIL: RowClone dst(%0d)=%h exp=%h", {dst}, rd_out, 64'h{pattern:016x});
109    end
110    if (errors==0)
111      $display("PASS: ComputeDRAM RowClone ran as raw sub-JEDEC commands through the executor; dst == src (== the RowStore/VM oracle)");
112    $finish;
113  end
114endmodule
115"#
116    )
117}
118
119/// Emit `tb_computedram_reduce` — proves in-DRAM bitwise AND and OR via triple-row activation. Preloads
120/// operands (`a=aval, b=bval, zero=0` for AND with a 0 control; `a2=aval, b2=bval, ones=~0` for OR with a
121/// 1 control), replays the concatenated `Maj` streams from `cdram_reduce.hex`, and asserts the majority
122/// result: `mem[a] == aval & bval` (AND = MAJ(a,b,0)) and `mem[a2] == aval | bval` (OR = MAJ(a,b,1)).
123#[allow(clippy::too_many_arguments)]
124pub fn emit_computedram_reduce_testbench(
125    nwords: usize,
126    a: usize,
127    b: usize,
128    zero: usize,
129    a2: usize,
130    b2: usize,
131    ones: usize,
132    aval: u64,
133    bval: u64,
134) -> String {
135    let last = nwords.saturating_sub(1);
136    let runcycles = nwords * 8 + 60;
137    let and_exp = aval & bval;
138    let or_exp = aval | bval;
139    format!(
140        r#"`timescale 1ns/1ps
141module tb_computedram_reduce;
142  reg clk=0; always #5 clk=~clk;
143  reg rst=1;
144  reg [63:0] rom [0:{last}];
145  integer idx=0;
146  wire [63:0] cmd_word = rom[idx];
147  wire cmd_valid = (idx <= {last});
148  wire cmd_ack, issue; wire [2:0] op; wire [4:0] bank; wire [31:0] row;
149  dram_executor exec(.clk(clk), .rst(rst), .cmd_word(cmd_word), .cmd_valid(cmd_valid),
150                     .cmd_ack(cmd_ack), .issue(issue), .op(op), .bank(bank), .row(row));
151  reg pre_we; reg [7:0] pre_addr; reg [63:0] pre_data; reg [7:0] rd_addr; wire [63:0] rd_out;
152  computedram_model #(.AW(8), .DW(64)) dut(.clk(clk), .rst(rst), .issue(issue), .op(op), .bank(bank),
153    .row(row), .pre_we(pre_we), .pre_addr(pre_addr), .pre_data(pre_data), .rd_addr(rd_addr), .rd_out(rd_out));
154  integer errors;
155  always @(posedge clk) if (!rst && cmd_ack) idx <= idx + 1;
156  task preload(input [7:0] addr, input [63:0] val); begin
157    @(posedge clk); pre_we=1; pre_addr=addr; pre_data=val; @(posedge clk); pre_we=0;
158  end endtask
159  initial begin
160    errors=0; pre_we=0; pre_addr=0; pre_data=0; rd_addr=0;
161    $readmemh("cdram_reduce.hex", rom);
162    preload(8'd{a},  64'h{aval:016x});   // AND operands + 0 control
163    preload(8'd{b},  64'h{bval:016x});
164    preload(8'd{zero}, 64'h0000000000000000);
165    preload(8'd{a2}, 64'h{aval:016x});   // OR operands + all-ones control
166    preload(8'd{b2}, 64'h{bval:016x});
167    preload(8'd{ones}, 64'hffffffffffffffff);
168    @(posedge clk); rst=0;
169    repeat ({runcycles}) @(posedge clk);
170    rd_addr=8'd{a}; @(posedge clk); @(posedge clk);
171    if (rd_out !== 64'h{and_exp:016x}) begin errors=errors+1; $display("FAIL: in-DRAM AND = %h exp %h", rd_out, 64'h{and_exp:016x}); end
172    rd_addr=8'd{a2}; @(posedge clk); @(posedge clk);
173    if (rd_out !== 64'h{or_exp:016x}) begin errors=errors+1; $display("FAIL: in-DRAM OR = %h exp %h", rd_out, 64'h{or_exp:016x}); end
174    if (errors==0)
175      $display("PASS: in-DRAM bitwise AND (MAJ a,b,0) and OR (MAJ a,b,1) via triple-row activation == the oracle");
176    $finish;
177  end
178endmodule
179"#
180    )
181}
182
183#[cfg(test)]
184#[path = "../../../logicaffeine_tests/unit/logicaffeine_compile/codegen_sva/computedram_rtl/tests.rs"]
185mod tests;