1use std::fmt::Write as _;
27
28pub const MLAB_WORDS: usize = 32;
35
36pub const RAMSTYLE: &str = "`ifndef LOGOS_RAMSTYLE
45// The block RAM a memory of `words` words read into a register asks for: MLABs to thirty-two words (an MLAB's depth),
46// M20K blocks beyond.
47`define LOGOS_RAMSTYLE(words) (* ramstyle = (words) <= 32 ? \"MLAB\" : \"M20K\" *)
48// The same for a memory read without a register after it, its MLABs checking no read-during-write: the build times an
49// MLAB's read in the clock after its word's write.
50`define LOGOS_RAMSTYLE_ASYNC(words) (* ramstyle = (words) <= 32 ? \"MLAB, no_rw_check\" : \"M20K\" *)
51`endif
52";
53
54pub fn ram_name(w: usize) -> String {
56 format!("logos_ram_w{w}")
57}
58
59pub fn bram_name(w: usize) -> String {
61 format!("logos_bram_w{w}")
62}
63
64pub fn ram_verilog(widths: &[usize]) -> String {
66 memories(widths, ram_name, "the read address latched on the falling edge", Read::Falling)
67}
68
69pub fn bram_verilog(widths: &[usize]) -> String {
71 memories(widths, bram_name, "the read registered on the rising edge", Read::Rising)
72}
73
74pub fn bram_re_name(w: usize) -> String {
76 format!("logos_bram_re_w{w}")
77}
78
79pub fn bram_re_verilog(widths: &[usize]) -> String {
82 memories(widths, bram_re_name, "the read registered on the rising edges re is high on and held between them", Read::Held)
83}
84
85pub fn ram_ce_name(w: usize) -> String {
87 format!("logos_ram_ce_w{w}")
88}
89
90pub fn ram_ce_verilog(widths: &[usize]) -> String {
95 memories(widths, ram_ce_name, "the read registered on the rising edges cr is high on and held between them", Read::Enabled)
96}
97
98pub const STORE_READ_LATENCY: usize = 4;
101
102pub const STORE_GROUP: usize = 8;
104
105pub fn store_verilog() -> String {
119 format!("{}\n{}\n{STORE_VERILOG}", bram_re_verilog(&[512]), bram_verilog(&[512]))
120}
121
122const STORE_VERILOG: &str = r#"// The token-row store's bank in layers of 512 words, its word out four clocks after its address: the ports registered
123// (q_*), each layer's write enable decoded there; every layer read every clock, each layer's word kept only if the read
124// was of it (mine, taken with the read); eight layers' words ORed into a register (gq); the groups' ORed into the word
125// (rd_q), taken only where re was high (r2, r3: re beside the read), so held between the edges re is high on, as
126// logos_bram_re_w512. A write lands a clock after it is asked, as a read does.
127module logos_store_w512 #(parameter D = 1024, A = 10)
128 (input clk, input re, input [A-1:0] ra, output [511:0] rd,
129 input we, input [A-1:0] wa, input [511:0] wd, input [63:0] wm);
130 localparam LAYERS = (D + 511) / 512, GROUPS = (LAYERS + 7) / 8;
131 localparam LA = A > 9 ? A - 9 : 1;
132 // Each address's row within its layer and its layer.
133 wire [8:0] r_row, w_row;
134 wire [LA-1:0] r_layer, w_layer;
135 generate
136 if (A > 9) begin : deep
137 assign r_row = ra[8:0];
138 assign w_row = wa[8:0];
139 assign r_layer = ra[A-1:9];
140 assign w_layer = wa[A-1:9];
141 end else if (A == 9) begin : one
142 assign r_row = ra;
143 assign w_row = wa;
144 assign r_layer = 1'b0;
145 assign w_layer = 1'b0;
146 end else begin : shallow
147 assign r_row = {{(9-A){1'b0}}, ra};
148 assign w_row = {{(9-A){1'b0}}, wa};
149 assign r_layer = 1'b0;
150 assign w_layer = 1'b0;
151 end
152 endgenerate
153 (* maxfan = 64 *) reg q_re;
154 (* maxfan = 64 *) reg [8:0] q_ra, q_wa;
155 (* maxfan = 64 *) reg [63:0] q_wm;
156 (* maxfan = 64 *) reg [511:0] q_wd;
157 reg [LA-1:0] q_rl;
158 reg [LAYERS-1:0] q_we;
159 integer k;
160 always @(posedge clk) begin
161 q_re <= re;
162 q_ra <= r_row;
163 q_rl <= r_layer;
164 q_wa <= w_row;
165 q_wd <= wd;
166 q_wm <= wm;
167 for (k = 0; k < LAYERS; k = k + 1) q_we[k] <= we && w_layer == k[LA-1:0];
168 end
169 wire [512*LAYERS-1:0] lq;
170 (* maxfan = 64 *) reg [LAYERS-1:0] mine;
171 genvar l;
172 generate
173 for (l = 0; l < LAYERS; l = l + 1) begin : layer
174 localparam [LA-1:0] AT = l;
175 logos_bram_w512_part #(.D(512), .A(9)) part(.clk(clk), .ra(q_ra), .rd(lq[512*l +: 512]),
176 .we(q_we[l]), .wa(q_wa), .wd(q_wd), .wm(q_wm));
177 always @(posedge clk) mine[l] <= q_rl == AT;
178 end
179 endgenerate
180 // Each group's word: its layers' words where the read was of them, ORed.
181 reg [512*GROUPS-1:0] gsum, gq;
182 integer g, j;
183 always @* begin
184 gsum = {(512*GROUPS){1'b0}};
185 for (g = 0; g < GROUPS; g = g + 1)
186 for (j = 0; j < 8; j = j + 1)
187 if (8 * g + j < LAYERS) gsum[512*g +: 512] = gsum[512*g +: 512] | (mine[8*g + j] ? lq[512*(8*g + j) +: 512] : 512'd0);
188 end
189 always @(posedge clk) gq <= gsum;
190 reg [511:0] word, rd_q;
191 integer h;
192 always @* begin
193 word = 512'd0;
194 for (h = 0; h < GROUPS; h = h + 1) word = word | gq[512*h +: 512];
195 end
196 reg r2, r3;
197 always @(posedge clk) begin
198 r2 <= q_re;
199 r3 <= r2;
200 if (r3) rd_q <= word;
201 end
202 assign rd = rd_q;
203endmodule
204"#;
205
206pub const DELAY_RING_LEAST: usize = 3;
209
210pub const DELAY_M20K_LEAST: usize = 40;
213
214pub fn delay_verilog() -> &'static str {
221 DELAY_VERILOG
222}
223
224const DELAY_VERILOG: &str = r#"
225// A read's marks carried D clocks to the clock its word lands: the valid in_v and the marks in_d themselves at depth 0,
226// else through D registers, the valid in registers the reset clears; flight is high while a valid is in any of them.
227module logos_landing #(parameter W = 1, D = 0)
228 (input clk, input rst, input in_v, input [W-1:0] in_d, output out_v, output [W-1:0] out_d, output flight);
229 generate
230 if (D == 0) begin : now
231 assign out_v = in_v;
232 assign out_d = in_d;
233 assign flight = 1'b0;
234 end else begin : later
235 (* maxfan = 64 *) reg [D-1:0] v;
236 reg [W*D-1:0] m;
237 integer k;
238 always @(posedge clk) begin
239 v[0] <= !rst && in_v;
240 m[W-1:0] <= in_d;
241 for (k = 1; k < D; k = k + 1) begin
242 v[k] <= !rst && v[k-1];
243 m[W*k +: W] <= m[W*(k-1) +: W];
244 end
245 end
246 assign out_v = v[D-1];
247 assign out_d = m[W*(D-1) +: W];
248 assign flight = |v;
249 end
250 endgenerate
251endmodule
252
253// d delayed D clocks: the word itself at depth 0, a chain of registers at one or two, a ring of block RAM from three, in
254// MLABs to 32 words (an MLAB's depth), and past that in M20K blocks from 40 bits wide and in MLABs below.
255module logos_delay #(parameter W = 32, D = 1) (input clk, input [W-1:0] d, output [W-1:0] q);
256 generate
257 if (D == 0) begin : now
258 assign q = d;
259 end else if (D < 3) begin : chain
260 reg [W-1:0] r1, r2;
261 always @(posedge clk) begin
262 r1 <= d;
263 r2 <= r1;
264 end
265 assign q = D == 1 ? r1 : r2;
266 end else if (W >= 40 && (1 << $clog2(D)) > 32) begin : wide
267 logos_delay_m20k #(.W(W), .D(D)) r(.clk(clk), .d(d), .q(q));
268 end else begin : narrow
269 logos_delay_mlab #(.W(W), .D(D)) r(.clk(clk), .d(d), .q(q));
270 end
271 endgenerate
272endmodule
273
274// d delayed D clocks (D of 2 or more) through a memory of 2^A >= D words in M20K blocks: each clock's word written at the write
275// address, and the word written D - 1 clocks before read into q. The read is never of the address being written.
276module logos_delay_m20k #(parameter W = 32, D = 3) (input clk, input [W-1:0] d, output reg [W-1:0] q);
277 localparam A = $clog2(D);
278 (* ramstyle = "M20K" *) reg [W-1:0] mem [0:(1 << A) - 1];
279 localparam [A-1:0] BACK = D - 1;
280 reg [A-1:0] wa = {A{1'b0}};
281 wire [A-1:0] ra = wa - BACK;
282 always @(posedge clk) begin
283 mem[wa] <= d;
284 q <= mem[ra];
285 wa <= wa + 1'b1;
286 end
287endmodule
288
289// d delayed D clocks (D of 2 or more) through a memory of 2^A >= D words in MLABs: each clock's word written at the write
290// address, and the word written D - 1 clocks before read into q. The read is never of the address being written.
291module logos_delay_mlab #(parameter W = 32, D = 3) (input clk, input [W-1:0] d, output reg [W-1:0] q);
292 localparam A = $clog2(D);
293 (* ramstyle = "MLAB" *) reg [W-1:0] mem [0:(1 << A) - 1];
294 localparam [A-1:0] BACK = D - 1;
295 reg [A-1:0] wa = {A{1'b0}};
296 wire [A-1:0] ra = wa - BACK;
297 always @(posedge clk) begin
298 mem[wa] <= d;
299 q <= mem[ra];
300 wa <= wa + 1'b1;
301 end
302endmodule
303"#;
304
305#[derive(Clone, Copy, PartialEq, Eq)]
307enum Read {
308 Falling,
309 Rising,
310 Held,
311 Enabled,
312}
313
314fn memories(widths: &[usize], name: fn(usize) -> String, what: &str, read: Read) -> String {
322 let mut v = String::from(RAMSTYLE);
323 for &w in widths {
324 assert!(w > 0 && w % 8 == 0, "a memory {w} bits wide is not whole bytes");
325 let name = name(w);
326 let (ports, connect, edge, write) = match read {
327 Read::Falling => ("", "", "always @(negedge clk)", "we"),
328 Read::Rising => ("", "", "always @(posedge clk)", "we"),
329 Read::Held => ("input re, ", ".re(re), ", "always @(posedge clk) if (re)", "we"),
330 Read::Enabled => ("input ce, input cr, ", ".ce(ce), .cr(cr), ", "always @(posedge clk) if (cr)", "ce && we"),
331 };
332 let bytes = |mem: &str, at: &str| -> String {
333 (0..w / 8).map(|b| format!(" if (wm[{b}]) {mem}[{at}][{}:{}] <= wd[{}:{}];\n", 8 * b + 7, 8 * b, 8 * b + 7, 8 * b)).collect()
334 };
335 let edges = if read == Read::Enabled { "s ce is high on" } else { "" };
336 let stacked = matches!(read, Read::Rising | Read::Held);
337 writeln!(v, "// A block-RAM memory of {w}-bit words: {what}, writes on the rising").unwrap();
338 writeln!(v, "// edge{edges} under a byte mask.").unwrap();
339 writeln!(v, "module {name}{} #(parameter D = 1024, A = 10)", if stacked { "_part" } else { "" }).unwrap();
340 writeln!(v, " (input clk,").unwrap();
341 writeln!(v, " {ports}input [A-1:0] ra, output reg [{}:0] rd,", w - 1).unwrap();
342 writeln!(v, " input we, input [A-1:0] wa, input [{}:0] wd, input [{}:0] wm);", w - 1, w / 8 - 1).unwrap();
343 writeln!(v, " `LOGOS_RAMSTYLE(D) reg [{}:0] mem [0:D-1];", w - 1).unwrap();
344 writeln!(v, " {edge} rd <= mem[ra];").unwrap();
345 writeln!(v, " always @(posedge clk)").unwrap();
346 writeln!(v, " if ({write}) begin").unwrap();
347 v.push_str(&bytes("mem", "wa"));
348 writeln!(v, " end").unwrap();
349 writeln!(v, "endmodule").unwrap();
350 if !stacked {
351 continue;
352 }
353 writeln!(v).unwrap();
354 if w >= 40 {
355 writeln!(v, "// {name}_part, but a memory more than one layer of 512 words deep in a number of layers no power of two").unwrap();
356 writeln!(v, "// as stacks of powers of two layers: the deepest its own from address 0, each other a part.").unwrap();
357 } else {
358 writeln!(v, "// {name}_part, the memory callers name.").unwrap();
359 }
360 writeln!(v, "module {name} #(parameter D = 1024, A = 10)").unwrap();
361 writeln!(v, " (input clk,").unwrap();
362 writeln!(v, " {ports}input [A-1:0] ra, output [{}:0] rd,", w - 1).unwrap();
363 writeln!(v, " input we, input [A-1:0] wa, input [{}:0] wd, input [{}:0] wm);", w - 1, w / 8 - 1).unwrap();
364 writeln!(v, " localparam [31:0] LAYERS = (D + 511) / 512;").unwrap();
365 writeln!(v, " localparam SPLIT = {} && (LAYERS & (LAYERS - 1)) != 0;", if w >= 40 { "1" } else { "0" }).unwrap();
366 writeln!(v, " localparam TOP = $clog2(LAYERS + 1) - 1;").unwrap();
367 writeln!(v, " localparam P0 = SPLIT ? 512 << TOP : D, A0 = SPLIT ? 9 + TOP : A;").unwrap();
368 writeln!(v, " localparam [A:0] OWN = P0;").unwrap();
369 writeln!(v, " `LOGOS_RAMSTYLE(P0) reg [{}:0] mem [0:P0-1];", w - 1).unwrap();
370 writeln!(v, " reg [{}:0] rd0;", w - 1).unwrap();
371 writeln!(v, " {edge} rd0 <= mem[ra[A0-1:0]];").unwrap();
372 writeln!(v, " always @(posedge clk)").unwrap();
373 writeln!(v, " if ({write} && (!SPLIT || {{1'b0, wa}} < OWN)) begin").unwrap();
374 v.push_str(&bytes("mem", "wa[A0-1:0]"));
375 writeln!(v, " end").unwrap();
376 writeln!(v, " generate").unwrap();
377 writeln!(v, " if (SPLIT) begin : stacks").unwrap();
378 writeln!(v, " // Each stack's word, zero unless the read was of it.").unwrap();
379 writeln!(v, " wire [{}:0] got [0:23];", w - 1).unwrap();
380 writeln!(v, " reg own;").unwrap();
381 writeln!(v, " {edge} own <= {{1'b0, ra}} < OWN;").unwrap();
382 writeln!(v, " genvar j;").unwrap();
383 writeln!(v, " for (j = 0; j < 24; j = j + 1) begin : layer").unwrap();
384 writeln!(v, " if (j < TOP && LAYERS[j]) begin : stack").unwrap();
385 writeln!(v, " localparam [31:0] FROM = 512 * (LAYERS & ~((32'd2 << j) - 32'd1)), TO = FROM + (512 << j);").unwrap();
386 writeln!(v, " localparam [A:0] BASE = FROM[A:0], LIMIT = TO[A:0];").unwrap();
387 writeln!(v, " wire [{}:0] q;", w - 1).unwrap();
388 writeln!(v, " reg mine;").unwrap();
389 writeln!(v, " {edge} mine <= {{1'b0, ra}} >= BASE && {{1'b0, ra}} < LIMIT;").unwrap();
390 writeln!(v, " {name}_part #(.D(512 << j), .A(9 + j)) part(.clk(clk), {connect}.ra(ra[8+j:0]), .rd(q),").unwrap();
391 writeln!(v, " .we(we && {{1'b0, wa}} >= BASE && {{1'b0, wa}} < LIMIT), .wa(wa[8+j:0]), .wd(wd), .wm(wm));").unwrap();
392 writeln!(v, " assign got[j] = mine ? q : {{{w}{{1'b0}}}};").unwrap();
393 writeln!(v, " end else begin : none").unwrap();
394 writeln!(v, " assign got[j] = {{{w}{{1'b0}}}};").unwrap();
395 writeln!(v, " end").unwrap();
396 writeln!(v, " end").unwrap();
397 writeln!(v, " reg [{}:0] word;", w - 1).unwrap();
398 writeln!(v, " integer k;").unwrap();
399 writeln!(v, " always @* begin").unwrap();
400 writeln!(v, " word = own ? rd0 : {{{w}{{1'b0}}}};").unwrap();
401 writeln!(v, " for (k = 0; k < 24; k = k + 1) word = word | got[k];").unwrap();
402 writeln!(v, " end").unwrap();
403 writeln!(v, " assign rd = word;").unwrap();
404 writeln!(v, " end else begin : whole").unwrap();
405 writeln!(v, " assign rd = rd0;").unwrap();
406 writeln!(v, " end").unwrap();
407 writeln!(v, " endgenerate").unwrap();
408 writeln!(v, "endmodule").unwrap();
409 }
410 v
411}