1use std::ops::Range;
26
27use crate::codegen_sva::dram::StreamDesc;
28use crate::spatial::d5005::{Beat, EnginePlan};
29
30use super::image::{image_of, streamed_image_of, weight_image, Image, ImageError, ImageLayout, TensorTable};
31use super::machine::{BlockState, Layout as MachineLayout, LINE};
32use super::model::{CacheFormat, StateFormat};
33use super::rtl::streaming::{StreamEntry, STREAM_ENTRY_LINES};
34
35const SCALE_BYTES: u64 = 2;
37
38#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub struct Run {
42 pub channel: u32,
43 pub at: u64,
44 pub bytes: u64,
45}
46
47impl Run {
48 pub fn lines(&self, line_bytes: u64) -> Range<u64> {
50 self.at / line_bytes..(self.at + self.bytes).div_ceil(line_bytes)
51 }
52
53 pub fn stream(&self, write: bool) -> StreamDesc {
55 StreamDesc { base: self.at, elem_bytes: self.bytes, stride_bytes: self.bytes, count: 1, write }
56 }
57}
58
59#[derive(Clone, Debug, PartialEq, Eq)]
64pub struct Compaction {
65 pub runs: Vec<(u64, u64)>,
67 pub entries: u64,
69}
70
71impl Compaction {
72 pub fn of(machine: &MachineLayout) -> Compaction {
74 let runs: Vec<(u64, u64)> = machine.tensors.iter().filter(|t| t.streamed).map(|t| (t.at, (t.at + t.bytes).div_ceil(LINE) * LINE)).collect();
75 let removed: u64 = runs.iter().map(|(s, e)| e - s).sum();
76 Compaction { entries: (machine.bytes - removed).div_ceil(LINE) * LINE, runs }
77 }
78
79 pub fn space_bytes(&self) -> u64 {
81 self.entries + self.runs.len() as u64 * STREAM_ENTRY_LINES * LINE
82 }
83
84 pub fn find(&self, at: u64) -> Option<u64> {
87 self.place(at).or_else(|| self.runs.iter().position(|&(s, _)| s == at).map(|k| self.entries + k as u64 * STREAM_ENTRY_LINES * LINE))
88 }
89
90 pub fn space(&self, memory: &[u8], entries: &[StreamEntry]) -> Vec<u8> {
93 assert_eq!(entries.len(), self.runs.len(), "an entry a streamed tensor");
94 let mut out = self.bytes(memory);
95 out.resize(self.entries as usize, 0);
96 for e in entries {
97 out.extend(e.lines().concat());
98 }
99 out
100 }
101
102 pub fn removed(&self) -> u64 {
104 self.runs.iter().map(|(s, e)| e - s).sum()
105 }
106
107 pub fn place(&self, at: u64) -> Option<u64> {
109 if self.runs.iter().any(|&(s, e)| (s..e).contains(&at)) {
110 return None;
111 }
112 Some(at - self.runs.iter().filter(|&&(_, e)| e <= at).map(|(s, e)| e - s).sum::<u64>())
113 }
114
115 pub fn bytes(&self, memory: &[u8]) -> Vec<u8> {
117 let mut out = Vec::with_capacity(memory.len() - self.removed() as usize);
118 let mut from = 0usize;
119 for &(s, e) in &self.runs {
120 out.extend_from_slice(&memory[from..s as usize]);
121 from = e as usize;
122 }
123 out.extend_from_slice(&memory[from..]);
124 out
125 }
126}
127
128#[derive(Clone, Debug, PartialEq, Eq)]
130pub struct PhysicalMap {
131 pub image: Image,
132 pub tensors_end: u64,
134 pub lines: u64,
136 pub granule: u64,
137 pub base: u64,
139 pub channel_lines: Vec<u64>,
141}
142
143impl PhysicalMap {
144 pub fn d5005(machine: &MachineLayout, beat: Beat) -> Result<PhysicalMap, ImageError> {
146 PhysicalMap::of(machine, &ImageLayout::d5005(beat), EnginePlan::standard().state_chunk_lines)
147 }
148
149 pub fn of(machine: &MachineLayout, layout: &ImageLayout, granule: u64) -> Result<PhysicalMap, ImageError> {
151 let image = image_of(machine, layout)?;
152 let tensors_end = machine.rotary.first().map_or(machine.regions, |&(_, at)| at);
153 PhysicalMap::over(image, tensors_end, machine.bytes, layout, granule)
154 }
155
156 pub fn flat(machine: &MachineLayout, layout: &ImageLayout, granule: u64) -> Result<PhysicalMap, ImageError> {
160 let image = weight_image(&TensorTable { blocks: 0, tensors: Vec::new() }, &[], layout)?;
161 PhysicalMap::over(image, 0, machine.bytes, layout, granule)
162 }
163
164 pub fn hybrid(machine: &MachineLayout, layout: &ImageLayout, granule: u64) -> Result<(PhysicalMap, Vec<usize>, Compaction), ImageError> {
169 let (image, streamed) = streamed_image_of(machine, layout)?;
170 let compaction = Compaction::of(machine);
171 let map = PhysicalMap::over(image, 0, compaction.space_bytes(), layout, granule)?;
172 Ok((map, streamed, compaction))
173 }
174
175 fn over(image: Image, tensors_end: u64, bytes: u64, layout: &ImageLayout, granule: u64) -> Result<PhysicalMap, ImageError> {
177 let lines = (bytes - tensors_end).div_ceil(LINE);
178 let base = image.channel_lines.iter().copied().max().unwrap_or(0);
179 let mut map = PhysicalMap { image, tensors_end, lines, granule: granule.max(1), base, channel_lines: Vec::new() };
180 let channels = layout.channels as u64;
181 let granules = lines.div_ceil(map.granule);
182 map.channel_lines = (0..channels)
183 .map(|c| {
184 let local = (granules + channels - 1 - c) / channels * map.granule;
185 base + (local * LINE).div_ceil(layout.line_bytes)
186 })
187 .collect();
188 for (c, &l) in map.channel_lines.iter().enumerate() {
189 if l > layout.capacity_lines {
190 return Err(ImageError::TooBig { channel: c as u32, lines: l, capacity: layout.capacity_lines });
191 }
192 }
193 Ok(map)
194 }
195
196 fn channels(&self) -> u64 {
197 self.image.layout.channels as u64
198 }
199
200 pub fn locate(&self, at: u64) -> (u32, u64) {
202 let rel = at - self.tensors_end;
203 let (x, within) = (rel / LINE, rel % LINE);
204 let g = x / self.granule;
205 let local = g / self.channels() * self.granule + x % self.granule;
206 let lb = self.image.layout.line_bytes;
207 ((g % self.channels()) as u32, self.base * lb + local * LINE + within)
208 }
209
210 pub fn runs(&self, at: u64, bytes: u64) -> Vec<Run> {
213 let mut runs: Vec<Run> = Vec::new();
214 let (end, granule_bytes) = (at + bytes, self.granule * LINE);
215 let mut from = at;
216 while from < end {
217 let to = end.min(self.tensors_end + ((from - self.tensors_end) / granule_bytes + 1) * granule_bytes);
218 let (channel, byte) = self.locate(from);
219 match runs.iter_mut().find(|r| r.channel == channel) {
220 Some(r) if r.at + r.bytes == byte => r.bytes += to - from,
221 Some(_) => unreachable!("a channel's granules within one run are consecutive local lines"),
222 None => runs.push(Run { channel, at: byte, bytes: to - from }),
223 }
224 from = to;
225 }
226 runs.sort_by_key(|r| r.channel);
227 runs
228 }
229
230 fn block(machine: &MachineLayout, layer: usize) -> &BlockState {
231 &machine.blocks[layer]
232 }
233
234 pub fn ring_row(&self, machine: &MachineLayout, s: usize, layer: usize, p: usize) -> Vec<Run> {
236 let BlockState::Delta { ring, channels, taps, .. } = *Self::block(machine, layer) else {
237 panic!("layer {layer} is not a Gated-DeltaNet block")
238 };
239 let row = channels as u64 * state_element(machine);
240 let rows = taps.saturating_sub(1).max(1) as u64;
241 self.runs(machine.region_of(s) + ring + (p as u64 % rows) * row, row)
242 }
243
244 pub fn memory(&self, machine: &MachineLayout, s: usize, layer: usize, head: usize) -> Vec<Run> {
246 let BlockState::Delta { memory, width, .. } = *Self::block(machine, layer) else {
247 panic!("layer {layer} is not a Gated-DeltaNet block")
248 };
249 let bytes = (width * width) as u64 * state_element(machine);
250 self.runs(machine.region_of(s) + memory + head as u64 * bytes, bytes)
251 }
252
253 pub fn log(&self, machine: &MachineLayout, s: usize, layer: usize, entries: Range<usize>) -> Vec<Run> {
255 let BlockState::Delta { log: Some(log), entry, .. } = *Self::block(machine, layer) else {
256 panic!("layer {layer} keeps no log")
257 };
258 let at = machine.region_of(s) + log;
259 let mut runs = self.runs(at, LINE);
260 if !entries.is_empty() {
261 let more = self.runs(at + LINE + entries.start as u64 * entry, entries.len() as u64 * entry);
262 runs.extend(more);
263 }
264 runs
265 }
266
267 pub fn cache(&self, machine: &MachineLayout, s: usize, layer: usize, p: usize) -> Vec<Run> {
270 let BlockState::Attention { keys, values, key_scales, value_scales, kv_heads, width, value_width } = *Self::block(machine, layer) else {
271 panic!("layer {layer} is not an attention block")
272 };
273 let element = match machine.numerics.cache {
274 CacheFormat::F16 => 2,
275 CacheFormat::Int8 | CacheFormat::Int8Hadamard => 1,
276 };
277 let (key_row, value_row) = (width as u64 * element, value_width as u64 * element);
278 let region = machine.region_of(s);
279 let positions = (p + 1) as u64 * kv_heads as u64;
280 let mut runs = self.runs(region + keys, positions * key_row);
281 runs.extend(self.runs(region + values, positions * value_row));
282 for scales in [key_scales, value_scales].into_iter().flatten() {
283 runs.extend(self.runs(region + scales, positions * SCALE_BYTES));
284 }
285 runs
286 }
287
288 pub fn channel_bytes(&self, memory: &[u8], weights: &[Vec<u8>]) -> Vec<Vec<u8>> {
291 let lb = self.image.layout.line_bytes;
292 let mut out: Vec<Vec<u8>> = self.channel_lines.iter().map(|&l| vec![0u8; (l * lb) as usize]).collect();
293 for (c, w) in weights.iter().enumerate() {
294 out[c][..w.len()].copy_from_slice(w);
295 }
296 let mut from = self.tensors_end;
297 let granule_bytes = self.granule * LINE;
298 while from < memory.len() as u64 {
299 let to = (memory.len() as u64).min(self.tensors_end + ((from - self.tensors_end) / granule_bytes + 1) * granule_bytes);
300 let (c, byte) = self.locate(from);
301 out[c as usize][byte as usize..(byte + to - from) as usize].copy_from_slice(&memory[from as usize..to as usize]);
302 from = to;
303 }
304 out
305 }
306}
307
308fn state_element(machine: &MachineLayout) -> u64 {
310 match machine.numerics.state {
311 StateFormat::F32 => 4,
312 StateFormat::F16 => 2,
313 }
314}
315
316pub fn card_port_verilog() -> String {
318 CARD_PORT.to_string()
319}
320
321pub fn fast_port_verilog() -> String {
324 FAST_PORT.to_string()
325}
326
327pub fn channel_mux_verilog() -> String {
331 format!("{}{CHANNEL_MUX}", super::rtl::ram::RAMSTYLE)
332}
333
334pub fn read_credit_verilog() -> String {
339 READ_CREDIT.to_string()
340}
341
342pub fn pipe_verilog() -> &'static str {
345 PIPE
346}
347
348const PIPE: &str = r##"// A stream through one register, for a path a floorplan stretches across the device: each word taken from `in` leaves
349// `out` a clock or more later, in order, a word a clock while `out` takes them. `in_ready` is a register: a second
350// place holds the word taken the clock `out` stops taking, and `in` waits until it has left. The second place takes
351// `in`'s word every clock it is empty, so its load waits on no valid from either side.
352module logos_pipe #(parameter W = 32) (input clk, input rst_n, input in_valid, output reg in_ready, input [W-1:0] in_data,
353 output reg out_valid, input out_ready, output reg [W-1:0] out_data);
354 reg skid_v;
355 reg [W-1:0] skid;
356 wire take = in_valid && in_ready;
357 wire moves = !out_valid || out_ready;
358 always @(posedge clk)
359 if (!rst_n) begin
360 out_valid <= 1'b0;
361 skid_v <= 1'b0;
362 in_ready <= 1'b0;
363 end else begin
364 if (moves) out_valid <= skid_v || take;
365 skid_v <= !moves && (skid_v || take);
366 in_ready <= moves || !(skid_v || take);
367 end
368 always @(posedge clk) begin
369 if (moves) out_data <= skid_v ? skid : in_data;
370 if (!skid_v) skid <= in_data;
371 end
372endmodule
373
374// K pipes in a row, for a path across the die a pipe each part of the way: each word leaves K clocks or more after it is
375// taken, in order, a word a clock while `out` takes them; every ready a register.
376module logos_pipe_chain #(parameter W = 32, parameter K = 1) (input clk, input rst_n, input in_valid, output in_ready,
377 input [W-1:0] in_data, output out_valid, input out_ready, output [W-1:0] out_data);
378 wire [K:0] v, r;
379 wire [(K+1)*W-1:0] d;
380 assign v[0] = in_valid;
381 assign in_ready = r[0];
382 assign d[W-1:0] = in_data;
383 assign out_valid = v[K];
384 assign r[K] = out_ready;
385 assign out_data = d[K*W +: W];
386 genvar k;
387 generate for (k = 0; k < K; k = k + 1) begin : stage
388 logos_pipe #(.W(W)) p(.clk(clk), .rst_n(rst_n), .in_valid(v[k]), .in_ready(r[k]), .in_data(d[k*W +: W]), .out_valid(v[k+1]),
389 .out_ready(r[k+1]), .out_data(d[(k+1)*W +: W]));
390 end endgenerate
391endmodule
392"##;
393
394const CARD_PORT: &str = r##"// The engine's memory port on the card's channels: each request one logical line of the machine's memory, its place
395// the physical map's — logical line x of the granule space, which starts at logical line `space`, lies in granule
396// g = (x - space) / 2^granule_log2, on channel g mod NB, at channel line base + (g / NB) * 2^granule_log2 + (x - space)
397// mod 2^granule_log2. Requests are served one at a time, in the order they come: a read sends its line's descriptor and
398// returns the line; a write under a mask of every byte writes its line, under any other mask reads the line first and
399// writes it merged, and is ready to the engine only once its channel has written it, so a read after a write reads
400// what it wrote. A read pulse taken while a write is served waits its turn. A line below the space, or one past a
401// channel's 2^AW lines, faults: its read returns zeros, its write is dropped. A line read with a word its channel's
402// SECDED code finds uncorrectable faults too, its read returning the line as read and a merged write dropped, so a bad
403// word is never coded afresh. `fault` holds until `clear`. Every register steps at `ce`, the shell's compute step.
404module logos_card_port #(
405 parameter NB = 4, // channels, a power of two
406 parameter AW = 27, // channel line-address bits
407 parameter LW = 32, // a descriptor's line-count bits
408 parameter DL = 512 // a line's bits
409) (
410 input clk,
411 input ce,
412 input rst_n,
413 input [31:0] space,
414 input [AW-1:0] base,
415 input [4:0] granule_log2,
416 input clear,
417 output reg fault,
418 input mem_rd,
419 input [31:0] mem_rd_line,
420 output reg mem_rd_valid,
421 output reg [DL-1:0] mem_rd_data,
422 input mem_wr,
423 input [31:0] mem_wr_line,
424 input [DL-1:0] mem_wr_data,
425 input [DL/8-1:0] mem_wr_mask,
426 output reg mem_wr_ready,
427 output [NB-1:0] md_valid,
428 input [NB-1:0] md_ready,
429 output [NB*(AW+LW+1)-1:0] md_data,
430 input [NB-1:0] mr_valid,
431 output [NB-1:0] mr_ready,
432 input [NB*DL-1:0] mr_data,
433 input [NB*16-1:0] mr_ecc, // each line read's words' verdicts {uncorrectable, corrected}
434 output [NB-1:0] mw_valid,
435 input [NB-1:0] mw_ready,
436 output [NB*DL-1:0] mw_data,
437 input [NB-1:0] mdone_valid,
438 output [NB-1:0] mdone_ready
439);
440 localparam CB = NB > 1 ? $clog2(NB) : 1;
441 localparam [2:0] IDLE = 3'd0, RD_DESC = 3'd1, RD_WAIT = 3'd2, WR_SEND = 3'd3, WR_DONE = 3'd4, ACK = 3'd5;
442 reg [2:0] state;
443 reg rd_pending, merge, desc_sent, data_sent;
444 reg [31:0] rd_line;
445 reg [CB-1:0] ch;
446 reg [AW-1:0] at;
447 reg [DL-1:0] line;
448 reg [DL/8-1:0] mask;
449
450 // Where logical line `l` lives: its channel, its channel line, and whether it lies outside the space.
451 reg [31:0] x, g, in_granule, local_line;
452 reg [32:0] to;
453 reg [CB-1:0] l_ch;
454 reg [AW-1:0] l_at;
455 reg l_bad;
456 wire [31:0] l = mem_wr ? mem_wr_line : rd_line;
457 always @(*) begin
458 x = l - space;
459 g = x >> granule_log2;
460 in_granule = x & ((32'd1 << granule_log2) - 32'd1);
461 local_line = ((g >> CB) << granule_log2) | in_granule;
462 to = {1'b0, {(32-AW){1'b0}}, base} + {1'b0, local_line};
463 l_ch = g[CB-1:0];
464 l_at = to[AW-1:0];
465 l_bad = l < space || to[32:AW] != 0;
466 end
467
468 function [DL-1:0] merged(input [DL-1:0] old, input [DL-1:0] new_, input [DL/8-1:0] m);
469 integer i;
470 for (i = 0; i < DL/8; i = i + 1) merged[8*i +: 8] = m[i] ? new_[8*i +: 8] : old[8*i +: 8];
471 endfunction
472
473 wire writing = state == WR_SEND;
474 genvar b;
475 generate for (b = 0; b < NB; b = b + 1) begin : channel
476 wire mine = ch == b;
477 assign md_valid[b] = mine && (state == RD_DESC || (writing && !desc_sent));
478 assign md_data[b*(AW+LW+1) +: AW+LW+1] = {writing, at, {{(LW-1){1'b0}}, 1'b1}};
479 assign mr_ready[b] = mine && state == RD_WAIT;
480 assign mw_valid[b] = mine && writing && !data_sent;
481 assign mw_data[b*DL +: DL] = line;
482 assign mdone_ready[b] = mine && state == WR_DONE;
483 end endgenerate
484
485 always @(posedge clk or negedge rst_n) begin
486 if (!rst_n) begin
487 state <= IDLE; rd_pending <= 1'b0; merge <= 1'b0; desc_sent <= 1'b0; data_sent <= 1'b0; rd_line <= 0; ch <= 0;
488 at <= 0; line <= 0; mask <= 0; fault <= 1'b0; mem_rd_valid <= 1'b0; mem_rd_data <= 0; mem_wr_ready <= 1'b0;
489 end else if (ce) begin
490 mem_rd_valid <= 1'b0;
491 mem_wr_ready <= 1'b0;
492 if (clear) fault <= 1'b0;
493 if (mem_rd) begin
494 rd_pending <= 1'b1;
495 rd_line <= mem_rd_line;
496 end
497 case (state)
498 IDLE: if (mem_wr) begin
499 ch <= l_ch; at <= l_at; line <= mem_wr_data; mask <= mem_wr_mask; desc_sent <= 1'b0; data_sent <= 1'b0;
500 if (l_bad) begin
501 fault <= 1'b1; mem_wr_ready <= 1'b1; state <= ACK;
502 end else if (&mem_wr_mask) begin
503 merge <= 1'b0; state <= WR_SEND;
504 end else begin
505 merge <= 1'b1; state <= RD_DESC;
506 end
507 end else if (rd_pending) begin
508 rd_pending <= 1'b0;
509 ch <= l_ch; at <= l_at; merge <= 1'b0;
510 if (l_bad) begin
511 fault <= 1'b1; mem_rd_data <= {DL{1'b0}}; mem_rd_valid <= 1'b1;
512 end else state <= RD_DESC;
513 end
514 RD_DESC: if (md_ready[ch]) state <= RD_WAIT;
515 RD_WAIT: if (mr_valid[ch]) begin
516 if (|mr_ecc[ch*16 + 8 +: 8]) fault <= 1'b1;
517 if (merge && |mr_ecc[ch*16 + 8 +: 8]) begin
518 mem_wr_ready <= 1'b1; state <= ACK;
519 end else if (merge) begin
520 line <= merged(mr_data[ch*DL +: DL], line, mask); state <= WR_SEND;
521 end else begin
522 mem_rd_data <= mr_data[ch*DL +: DL]; mem_rd_valid <= 1'b1; state <= IDLE;
523 end
524 end
525 WR_SEND: begin
526 if (md_valid[ch] && md_ready[ch]) desc_sent <= 1'b1;
527 if (mw_valid[ch] && mw_ready[ch]) data_sent <= 1'b1;
528 if ((desc_sent || md_ready[ch]) && (data_sent || mw_ready[ch])) state <= WR_DONE;
529 end
530 WR_DONE: if (mdone_valid[ch]) begin mem_wr_ready <= 1'b1; state <= ACK; end
531 default: state <= IDLE;
532 endcase
533 end
534 end
535endmodule
536"##;
537
538const FAST_PORT: &str = r##"// The fast side's memory port on the channels' fast ports: whole logical lines of the granule space read and written,
539// each where the physical map puts it — logical line x of the space that starts at logical line `space` lies in granule
540// g = (x - space) / 2^granule_log2, on channel g mod NB, at channel line base + (g / NB) * 2^granule_log2 + (x - space)
541// mod 2^granule_log2, as logos_card_port places it. Reads of consecutive lines of one channel go out as one descriptor
542// of up to MAXL lines, and so do writes, a run closed when the next line asked is not its next or none is asked; reads
543// return in the order they were asked, whichever channels hold them, each taken the cycle its channel has it and given
544// from a register the cycle after (rs_valid takes no wait: the side asks for no more lines than it has room for), so the
545// line crosses to the side that asked from a register beside it; a write's line goes to its channel as it is taken,
546// ahead of its run's descriptor, a run closed early when its channel holds all the lines it has room for, and the
547// writes are counted out until their channels report them written (`writing`). A read asked before a write of its line
548// reads the line before the write, a read's descriptor never leaving behind a later write's; a line read while a write
549// of it is still out may read it before the write. A line outside the space, or past a channel's 2^AW lines, faults:
550// its read returns zeros, its write is dropped. `fault` holds until `clear`. A request is taken into two registers of
551// its kind, a clock before the port serves it, and its line's place comes from a register: the line after the one last
552// served is placed a clock ahead, and any other waits a clock to be placed, its run held open.
553module logos_fast_port #(parameter NB = 4, AW = 27, LW = 32, DL = 512, MAXL = 32, ORDER = 64)
554 (input clk, input rst,
555 input [31:0] space, input [AW-1:0] base, input [4:0] granule_log2, input clear, output reg fault,
556 input rq_valid, output rq_ready, input [31:0] rq_line, output rs_valid, output [DL-1:0] rs_data,
557 input wq_valid, output wq_ready, input [31:0] wq_line, input [DL-1:0] wq_data, output writing,
558 output [NB-1:0] md_valid, input [NB-1:0] md_ready, output [NB*(AW+LW+1)-1:0] md_data,
559 input [NB-1:0] mr_valid, output [NB-1:0] mr_ready, input [NB*DL-1:0] mr_data,
560 output [NB-1:0] mw_valid, input [NB-1:0] mw_ready, output [NB*DL-1:0] mw_data,
561 input [NB-1:0] mdone_valid, output [NB-1:0] mdone_ready);
562 localparam CB = NB > 1 ? $clog2(NB) : 1, OB = $clog2(ORDER), NW = $clog2(MAXL + 1);
563 localparam [NW-1:0] FULL = MAXL;
564
565 // Each kind's requests through two registers, taken while the second is empty, so neither the asking unit's address
566 // arithmetic nor the port's placement waits on the other within a clock: the port serves the oldest it holds (rqh_*,
567 // wqh_*), a request from the clock after it is taken. A write's word goes into one of two places as it is taken in
568 // (wq_d0, wq_d1, the next taken in going to wq_put's), and the head's word is the place wq_get names: the words move
569 // only on their own taking in, never on the port's serving a write, which only turns wq_get.
570 reg rqh_valid, rqt_valid, wqh_valid, wqt_valid;
571 reg [31:0] rqh_line, rqt_line, wqh_line, wqt_line;
572 reg [DL-1:0] wq_d0, wq_d1;
573 (* maxfan = 64 *) reg wq_get;
574 reg wq_put;
575 wire [DL-1:0] wqh_data = wq_get ? wq_d1 : wq_d0;
576 assign rq_ready = !rqt_valid;
577 assign wq_ready = !wqt_valid;
578 wire r_in = rq_valid && !rqt_valid, w_in = wq_valid && !wqt_valid;
579
580 // Where line `l` lives: whether it lies outside the space, its channel, its channel line. Its granule g = x >> gl
581 // (x its place in the space) lies on channel g mod NB as the channel's granule g >> CB, so its channel line's bits
582 // below gl are x's and those above are x's CB places higher: no shift by the granule but for the channel's bits.
583 function [CB+AW:0] placed(input [31:0] l, input [31:0] sp, input [AW-1:0] b, input [4:0] gl, input [31:0] mask);
584 reg [31:0] x, g, local_line;
585 reg [32:0] to;
586 begin
587 x = l - sp;
588 g = x >> gl;
589 local_line = (x & mask) | ((x >> CB) & ~mask);
590 to = {1'b0, {(32-AW){1'b0}}, b} + {1'b0, local_line};
591 placed = {l < sp || to[32:AW] != 0, g[CB-1:0], to[AW-1:0]};
592 end
593 endfunction
594 // The map, registered where it enters: it holds for the step, and every place is reckoned from the port's own copy,
595 // a granule's offset mask beside it.
596 reg [31:0] m_space, m_mask;
597 reg [AW-1:0] m_base;
598 reg [4:0] m_gl;
599 always @(posedge clk) begin
600 m_space <= space; m_base <= base; m_gl <= granule_log2; m_mask <= (32'd1 << granule_log2) - 32'd1;
601 end
602 // The place of each kind's next line: the line after the one last taken, or the line waiting; a line asked is placed
603 // once it is the one held (`r_hit`, `w_hit`). A map about to change drops both.
604 reg [31:0] rg_line, wg_line;
605 reg [CB+AW:0] rp, wp;
606 reg rg_ok, wg_ok;
607 wire r_hit = rg_ok && rqh_line == rg_line, w_hit = wg_ok && wqh_line == wg_line;
608 wire map_moves = space != m_space || base != m_base || granule_log2 != m_gl;
609 wire [CB+AW:0] r_here = placed(rqh_line, m_space, m_base, m_gl, m_mask), r_after = placed(rqh_line + 32'd1, m_space, m_base, m_gl, m_mask);
610 wire [CB+AW:0] w_here = placed(wqh_line, m_space, m_base, m_gl, m_mask), w_after = placed(wqh_line + 32'd1, m_space, m_base, m_gl, m_mask);
611 wire r_bad = rp[CB+AW], w_bad = wp[CB+AW];
612 wire [CB-1:0] r_ch = rp[CB+AW-1:AW], w_ch = wp[CB+AW-1:AW];
613 wire [AW-1:0] r_at = rp[AW-1:0], w_at = wp[AW-1:0];
614
615 // The descriptor on its way to its channel: {write, first line, lines}.
616 reg d_valid;
617 reg [CB-1:0] d_ch;
618 reg [AW+LW:0] d_data;
619 wire d_free = !d_valid || md_ready[d_ch];
620
621 // The reads in the order they were asked: each run's channel and lines, or a faulting line's zeros.
622 reg [CB+NW:0] order [0:ORDER-1];
623 reg [OB-1:0] o_wr, o_rd;
624 reg [OB:0] o_count;
625 reg [NW-1:0] o_given;
626 wire [CB+NW:0] o_head = order[o_rd];
627 wire o_zero = o_head[CB+NW], o_have = o_count != 0;
628 wire [CB-1:0] o_ch = o_head[CB+NW-1:NW];
629 wire [NW-1:0] o_n = o_head[NW-1:0];
630 localparam [OB:0] O_LAST = ORDER - 1;
631 wire o_room = o_count < O_LAST;
632 wire o_take = o_have && (o_zero || mr_valid[o_ch]);
633 wire o_pop = o_take && o_given + 1'b1 == o_n;
634 (* maxfan = 64 *) reg rs_q;
635 reg [DL-1:0] rs_d;
636 always @(posedge clk) begin
637 rs_q <= !rst && o_take;
638 rs_d <= o_zero ? {DL{1'b0}} : mr_data[o_ch*DL +: DL];
639 end
640 assign rs_valid = rs_q;
641 assign rs_data = rs_d;
642
643 // The run of reads being gathered, and of writes, each with the channel line it would take next.
644 reg r_open, w_open;
645 reg [CB-1:0] rr_ch, wr_ch;
646 reg [AW-1:0] rr_at, wr_at, rr_next, wr_next;
647 reg [NW-1:0] rr_n, wr_n;
648 wire r_joins = r_open && !r_bad && r_ch == rr_ch && r_at == rr_next && rr_n != FULL;
649 wire w_joins = w_open && !w_bad && w_ch == wr_ch && w_at == wr_next && wr_n != FULL;
650 // A run closes into the descriptor when the next line asked is not its next, or none is, or, for writes, when its
651 // channel has no room for the next line (the lines it holds leave only behind their descriptor): the reads' first.
652 wire r_close = r_open && !(rqh_valid && (!r_hit || r_joins)) && d_free && o_room;
653 wire w_close = w_open && !(wqh_valid && (!w_hit || (w_joins && mw_ready[wr_ch]))) && d_free && !r_close;
654 // A read is taken joining its run, opening one where none is open or the open one closes, or, faulting, with none
655 // open; a write joining its run, or opening one where none is open or the open one closes, its channel taking its
656 // line, or faulting.
657 wire rqh_ready = r_hit && (r_bad ? !r_open && o_room : r_joins || !r_open || r_close);
658 wire wqh_ready = w_hit && (w_bad || ((w_joins || !w_open || w_close) && mw_ready[w_ch]));
659 wire r_take = rqh_valid && rqh_ready, w_take = wqh_valid && wqh_ready;
660 wire r_zero = r_take && r_bad, w_line = w_take && !w_bad;
661
662 genvar b;
663 generate for (b = 0; b < NB; b = b + 1) begin : channel
664 assign md_valid[b] = d_valid && d_ch == b;
665 assign md_data[b*(AW+LW+1) +: AW+LW+1] = d_data;
666 assign mr_ready[b] = o_have && !o_zero && o_ch == b;
667 assign mw_valid[b] = wqh_valid && w_hit && !w_bad && w_ch == b && (w_joins || !w_open || w_close);
668 assign mw_data[b*DL +: DL] = wqh_data;
669 assign mdone_ready[b] = 1'b1;
670 end endgenerate
671
672 // Writes out: descriptors sent less writes reported written. The done tokens are counted by a continuous assignment,
673 // which a simulator evaluates from time 0 whether or not a token ever comes.
674 reg [OB:0] w_out;
675 function [CB:0] ones(input [NB-1:0] v);
676 integer i;
677 begin
678 ones = 0;
679 for (i = 0; i < NB; i = i + 1) ones = ones + {{CB{1'b0}}, v[i]};
680 end
681 endfunction
682 wire [CB:0] dones = ones(mdone_valid);
683 wire w_sent = d_valid && md_ready[d_ch] && d_data[AW+LW];
684 assign writing = wqh_valid || wqt_valid || w_open || (d_valid && d_data[AW+LW]) || w_out != 0;
685
686 always @(posedge clk) begin
687 if (rst) begin
688 rqh_valid <= 1'b0; rqt_valid <= 1'b0; wqh_valid <= 1'b0; wqt_valid <= 1'b0; wq_put <= 1'b0; wq_get <= 1'b0;
689 end else begin
690 case ({r_take, r_in})
691 2'b01: if (rqh_valid) begin rqt_valid <= 1'b1; rqt_line <= rq_line; end
692 else begin rqh_valid <= 1'b1; rqh_line <= rq_line; end
693 2'b10: begin rqh_valid <= rqt_valid; rqh_line <= rqt_line; rqt_valid <= 1'b0; end
694 2'b11: rqh_line <= rq_line;
695 default: ;
696 endcase
697 case ({w_take, w_in})
698 2'b01: if (wqh_valid) begin wqt_valid <= 1'b1; wqt_line <= wq_line; end
699 else begin wqh_valid <= 1'b1; wqh_line <= wq_line; end
700 2'b10: begin wqh_valid <= wqt_valid; wqh_line <= wqt_line; wqt_valid <= 1'b0; end
701 2'b11: wqh_line <= wq_line;
702 default: ;
703 endcase
704 if (w_in) wq_put <= !wq_put;
705 if (w_take) wq_get <= !wq_get;
706 end
707 if (r_take) begin rg_line <= rqh_line + 32'd1; rp <= r_after; end
708 else if (rqh_valid && !r_hit) begin rg_line <= rqh_line; rp <= r_here; end
709 if (w_in && !wq_put) wq_d0 <= wq_data;
710 if (w_in && wq_put) wq_d1 <= wq_data;
711 if (w_take) begin wg_line <= wqh_line + 32'd1; wp <= w_after; end
712 else if (wqh_valid && !w_hit) begin wg_line <= wqh_line; wp <= w_here; end
713 rg_ok <= !rst && !map_moves && (rg_ok || r_take || rqh_valid);
714 wg_ok <= !rst && !map_moves && (wg_ok || w_take || wqh_valid);
715 if (rst) begin
716 d_valid <= 1'b0; r_open <= 1'b0; w_open <= 1'b0; o_wr <= 0; o_rd <= 0; o_count <= 0; o_given <= 0; w_out <= 0;
717 fault <= 1'b0;
718 end else begin
719 if (clear) fault <= 1'b0;
720 if (r_zero || (w_take && w_bad)) fault <= 1'b1;
721 if (d_valid && md_ready[d_ch]) d_valid <= 1'b0;
722 if (r_close) begin
723 d_valid <= 1'b1; d_ch <= rr_ch; d_data <= {1'b0, rr_at, {(LW-NW){1'b0}}, rr_n};
724 order[o_wr] <= {1'b0, rr_ch, rr_n};
725 end else if (w_close) begin
726 d_valid <= 1'b1; d_ch <= wr_ch; d_data <= {1'b1, wr_at, {(LW-NW){1'b0}}, wr_n};
727 end
728 if (r_zero) order[o_wr] <= {1'b1, {CB{1'b0}}, {{(NW-1){1'b0}}, 1'b1}};
729 o_wr <= o_wr + (r_close || r_zero);
730 o_count <= o_count + (r_close || r_zero) - o_pop;
731 if (o_take) o_given <= o_pop ? 0 : o_given + 1'b1;
732 if (o_pop) o_rd <= o_rd + 1'b1;
733 if (r_take && !r_bad) begin
734 if (r_joins) begin rr_n <= rr_n + 1'b1; rr_next <= rr_next + 1'b1; end
735 else begin r_open <= 1'b1; rr_ch <= r_ch; rr_at <= r_at; rr_next <= r_at + 1'b1; rr_n <= 1; end
736 end else if (r_close) r_open <= 1'b0;
737 if (w_line) begin
738 if (w_joins) begin wr_n <= wr_n + 1'b1; wr_next <= wr_next + 1'b1; end
739 else begin w_open <= 1'b1; wr_ch <= w_ch; wr_at <= w_at; wr_next <= w_at + 1'b1; wr_n <= 1; end
740 end else if (w_close) w_open <= 1'b0;
741 w_out <= w_out + w_sent - dones;
742 end
743 end
744endmodule
745"##;
746
747const CHANNEL_MUX: &str = r##"// Several fast ports on the channels' fast ports at once: P ports' channel sides (logos_fast_port's md_*, mr_*, mw_*,
748// mdone_*; port p's of channel c at p·NB + c) onto the NB channels'. Each port's descriptors come in through two places
749// of the port's own, one descriptor at a time whatever channel each is for: the next waits behind the last until its
750// channel takes it, so the channels take a port's runs in the order the port gives them, whichever channels they are
751// of. (A port that offers several channels at once gives them one at a time.) Each port's write lines come in
752// through two places of the port's own likewise, a line leaving for its channel only after every line the port gave
753// before it has left for its own. Every ready a port sees is a register's. Each channel takes its descriptors from its
754// ports in turn, the choice a register a clock ahead: the port it names, if its descriptor is still held and the
755// channel takes one; a port just taken is not chosen the clock after, its next descriptor not yet seen. Each read's
756// lines go back to the port whose descriptor it was and each write's done token
757// likewise, in the order the channel took them (every port reads the channels' lines from mr_data itself). Reads need
758// no other rule so long as every port takes its runs' lines in the order it asked for them and waits on nothing else to
759// take one: each channel serves its descriptors in the order it took them, and descriptors are taken one at a time a
760// channel, so the earliest run taken that has lines still to give is first in both its port's order and its channel's,
761// and always moves. A reader that takes a channel's lines only as its other channels let it (the streaming unit, whose
762// channels' feeds step together) would deadlock a port waiting on a line of one channel behind the reader's run while
763// the reader waits on that port's run in another; such a reader reaches the multiplexer through logos_read_credit a
764// channel, which takes every line as it comes. A write's lines reach its channel ahead of its descriptor, so a channel
765// takes write lines from one port at a time: the port that holds it — the first whose next line is the channel's, from
766// the channel's turn, the clock after it is free — until every line it gave is in a descriptor the channel has taken and
767// its next line is not the channel's. A write's descriptor is taken only from that port. A port holds a channel only once
768// every line it gave before is in its own channel, so each of its earlier writes' descriptors is taken from a channel the
769// port still holds: no port's descriptor waits on a channel whose holder waits on that port's. Each channel's reads and
770// writes owed are at most DEPTH apiece: no descriptor is taken beyond them.
771module logos_channel_mux #(parameter P = 2, NB = 4, AW = 27, LW = 32, DL = 512, DEPTH = 32)
772 (input clk, input rst,
773 input [P*NB-1:0] pd_valid, output [P*NB-1:0] pd_ready, input [P*NB*(AW+LW+1)-1:0] pd_data,
774 output [P*NB-1:0] pr_valid, input [P*NB-1:0] pr_ready,
775 input [P*NB-1:0] pw_valid, output [P*NB-1:0] pw_ready, input [P*NB*DL-1:0] pw_data,
776 output [P*NB-1:0] pdone_valid, input [P*NB-1:0] pdone_ready,
777 output [NB-1:0] md_valid, input [NB-1:0] md_ready, output [NB*(AW+LW+1)-1:0] md_data,
778 input [NB-1:0] mr_valid, output [NB-1:0] mr_ready,
779 output [NB-1:0] mw_valid, input [NB-1:0] mw_ready, output [NB*DL-1:0] mw_data,
780 input [NB-1:0] mdone_valid, output [NB-1:0] mdone_ready);
781 localparam DLW = AW + LW + 1, PB = P > 1 ? $clog2(P) : 1, DB = $clog2(DEPTH);
782 localparam [PB-1:0] LAST = P - 1;
783 localparam [DB:0] ROOM = DEPTH - 2;
784 localparam CB = NB > 1 ? $clog2(NB) : 1;
785 // Inside: each port's held descriptor offered to its channel (h_dv, h_dd) and taken (h_dr), and each port's held
786 // write line offered to its channel (h_wv, h_wd) and taken (h_wr).
787 wire [P*NB-1:0] h_dv, h_dr, h_wv, h_wr;
788 wire [P*NB*DLW-1:0] h_dd;
789 wire [P*DL-1:0] h_wd;
790 // The line of the channel `on` names (one-hot) among each channel's `lines`.
791 function [DL-1:0] line_of(input [NB-1:0] on, input [NB*DL-1:0] lines);
792 integer b;
793 begin
794 line_of = {DL{1'b0}};
795 for (b = 0; b < NB; b = b + 1) if (on[b]) line_of = line_of | lines[b*DL +: DL];
796 end
797 endfunction
798 genvar c, q;
799 generate for (q = 0; q < P; q = q + 1) begin : held
800 // The port's descriptor: the lowest channel it offers one to and that channel's descriptor, taken while the port's
801 // ready is up; the place it leaves from, and a second place for the one taken the clock the first does not leave.
802 reg [CB-1:0] pick;
803 reg [DLW-1:0] offered;
804 integer pc;
805 always @* begin
806 pick = {CB{1'b0}};
807 offered = pd_data[q*NB*DLW +: DLW];
808 for (pc = NB - 1; pc >= 0; pc = pc - 1)
809 if (pd_valid[q*NB + pc]) begin pick = pc[CB-1:0]; offered = pd_data[(q*NB + pc)*DLW +: DLW]; end
810 end
811 reg d_rdy, d_v, d_sv;
812 reg [CB-1:0] d_ch, d_sch;
813 reg [DLW-1:0] d_d, d_sd;
814 wire d_take = |pd_valid[q*NB +: NB] && d_rdy;
815 wire d_moves = !d_v || h_dr[q*NB + d_ch];
816 always @(posedge clk) begin
817 if (d_moves) begin
818 d_v <= d_sv || d_take;
819 d_ch <= d_sv ? d_sch : pick;
820 d_d <= d_sv ? d_sd : offered;
821 end else if (d_take) begin
822 d_sch <= pick;
823 d_sd <= offered;
824 end
825 d_sv <= !d_moves && (d_sv || d_take);
826 d_rdy <= d_moves || !(d_sv || d_take);
827 if (rst) begin d_v <= 1'b0; d_sv <= 1'b0; d_rdy <= 1'b0; end
828 end
829 // The port's write lines, alike, through two places of their own in the order the port gives them, each line with
830 // its channel one-hot (none while the place is empty): the lowest channel the port offers a line to — a port offers
831 // a line to one channel at a time, so its ready is the places' whatever the channel. A line leaves for its channel
832 // only once every line the port gave before it has left for its own, so a port holds a channel's write lines only
833 // after its earlier runs' lines are in theirs, and never waits for a channel's lines behind a later run's. The spare
834 // place takes the port's line every clock it is empty, so a clock that fills it has given it the line taken.
835 wire [NB-1:0] w_offers = pw_valid[q*NB +: NB];
836 wire [NB-1:0] w_first = w_offers & ~(w_offers - {{(NB-1){1'b0}}, 1'b1});
837 wire [DL-1:0] w_line = line_of(w_first, pw_data[q*NB*DL +: NB*DL]);
838 reg w_rdy;
839 (* maxfan = 256 *) reg w_sv;
840 reg [NB-1:0] w_on, w_son;
841 reg [DL-1:0] w_d, w_sd;
842 wire w_take = |w_offers && w_rdy;
843 wire w_moves = (w_on & ~h_wr[q*NB +: NB]) == {NB{1'b0}};
844 always @(posedge clk) begin
845 if (w_moves) begin
846 w_on <= w_sv ? w_son : w_take ? w_first : {NB{1'b0}};
847 w_d <= w_sv ? w_sd : w_line;
848 end
849 if (!w_sv) begin w_son <= w_first; w_sd <= w_line; end
850 w_sv <= !w_moves && (w_sv || w_take);
851 w_rdy <= w_moves || !(w_sv || w_take);
852 if (rst) begin w_on <= {NB{1'b0}}; w_sv <= 1'b0; w_rdy <= 1'b0; end
853 end
854 assign h_wv[q*NB +: NB] = w_on;
855 assign h_wd[q*DL +: DL] = w_d;
856 for (c = 0; c < NB; c = c + 1) begin : lane
857 assign pd_ready[q*NB + c] = d_rdy && pick == c;
858 assign h_dv[q*NB + c] = d_v && d_ch == c;
859 assign h_dd[(q*NB + c)*DLW +: DLW] = d_d;
860 assign pw_ready[q*NB + c] = w_rdy;
861 end
862 end endgenerate
863 generate for (c = 0; c < NB; c = c + 1) begin : channel
864 // Every port's descriptor of this channel, and whether it is a write.
865 wire [P-1:0] offers, writes, lines_offered;
866 wire [DLW*P-1:0] descs;
867 for (q = 0; q < P; q = q + 1) begin : port
868 assign offers[q] = h_dv[q*NB + c];
869 assign descs[DLW*q +: DLW] = h_dd[(q*NB + c)*DLW +: DLW];
870 assign writes[q] = descs[DLW*q + DLW - 1];
871 assign lines_offered[q] = h_wv[q*NB + c];
872 end
873 // The reads owed (each its port and lines) and the writes (each its port), in the order taken.
874 `LOGOS_RAMSTYLE_ASYNC(DEPTH) reg [PB-1:0] r_port [0:DEPTH-1];
875 `LOGOS_RAMSTYLE_ASYNC(DEPTH) reg [LW-1:0] r_lines [0:DEPTH-1];
876 `LOGOS_RAMSTYLE_ASYNC(DEPTH) reg [PB-1:0] w_port [0:DEPTH-1];
877 reg [DB:0] r_n, w_n;
878 reg [DB-1:0] r_wr, r_rd, w_wr, w_rd;
879 reg [LW-1:0] r_given;
880 wire r_have = r_n != 0, w_have = w_n != 0;
881 wire [PB-1:0] r_head = r_port[r_rd], w_head = w_port[w_rd];
882 // The write lines' holder, and its lines given and not yet in a descriptor taken.
883 reg lock;
884 (* maxfan = 256 *) reg [PB-1:0] holder;
885 reg [LW:0] owed_lines;
886 // The port chosen for the next clock's descriptor.
887 reg chosen;
888 reg [PB-1:0] choice, turn;
889 wire [DLW-1:0] desc = descs[DLW*choice +: DLW];
890 wire takes = chosen && offers[choice];
891 assign md_valid[c] = takes;
892 assign md_data[c*DLW +: DLW] = desc;
893 wire taken = takes && md_ready[c];
894 wire w_taken = taken && desc[DLW-1];
895 for (q = 0; q < P; q = q + 1) begin : grant
896 assign h_dr[q*NB + c] = chosen && choice == q && md_ready[c];
897 assign pr_valid[q*NB + c] = mr_valid[c] && r_have && r_head == q;
898 assign pdone_valid[q*NB + c] = mdone_valid[c] && w_have && w_head == q;
899 assign h_wr[q*NB + c] = mw_ready[c] && lock && holder == q;
900 end
901 assign mr_ready[c] = r_have && pr_ready[r_head*NB + c];
902 assign mdone_ready[c] = w_have && pdone_ready[w_head*NB + c];
903 assign mw_valid[c] = lock && h_wv[holder*NB + c];
904 assign mw_data[c*DL +: DL] = h_wd[holder*DL +: DL];
905 wire given = mw_valid[c] && mw_ready[c];
906 wire r_back = mr_valid[c] && mr_ready[c];
907 wire r_done = r_back && r_given + 1'b1 == r_lines[r_rd];
908 wire w_back = mdone_valid[c] && mdone_ready[c];
909 wire [LW:0] owed_next = owed_lines + {{LW{1'b0}}, given} - (w_taken ? {1'b0, desc[LW-1:0]} : {(LW+1){1'b0}});
910 // The next clock's choice: from the turn, the first port offering a descriptor the channel may take — a read with a
911 // read owed's room after this clock's, a write from the holder with a write's room — other than the port taken now.
912 reg next_chosen;
913 reg [PB-1:0] next_choice, lock_pick;
914 reg lock_any;
915 integer k, j;
916 always @* begin
917 next_chosen = 1'b0;
918 next_choice = {PB{1'b0}};
919 lock_any = 1'b0;
920 lock_pick = {PB{1'b0}};
921 for (k = 0; k < P; k = k + 1) begin
922 j = k + turn;
923 if (j >= P) j = j - P;
924 if (!next_chosen && offers[j] && !(taken && choice == j[PB-1:0])
925 && (writes[j] ? lock && holder == j[PB-1:0] && w_n <= ROOM : r_n <= ROOM)) begin
926 next_chosen = 1'b1;
927 next_choice = j[PB-1:0];
928 end
929 if (!lock_any && lines_offered[j]) begin lock_any = 1'b1; lock_pick = j[PB-1:0]; end
930 end
931 end
932 always @(posedge clk) begin
933 chosen <= !rst && next_chosen;
934 choice <= next_choice;
935 if (taken) turn <= (choice == LAST) ? {PB{1'b0}} : choice + 1'b1;
936 if (taken && !desc[DLW-1]) begin r_port[r_wr] <= choice; r_lines[r_wr] <= desc[LW-1:0]; r_wr <= r_wr + 1'b1; end
937 if (w_taken) begin w_port[w_wr] <= choice; w_wr <= w_wr + 1'b1; end
938 if (r_back) r_given <= r_done ? {LW{1'b0}} : r_given + 1'b1;
939 if (r_done) r_rd <= r_rd + 1'b1;
940 if (w_back) w_rd <= w_rd + 1'b1;
941 r_n <= r_n + {{DB{1'b0}}, taken && !desc[DLW-1]} - {{DB{1'b0}}, r_done};
942 w_n <= w_n + {{DB{1'b0}}, w_taken} - {{DB{1'b0}}, w_back};
943 owed_lines <= owed_next;
944 if (!lock) begin lock <= lock_any; holder <= lock_pick; end
945 else if (owed_next == 0 && !h_wv[holder*NB + c]) lock <= 1'b0;
946 if (rst) begin
947 turn <= {PB{1'b0}}; r_wr <= 0; r_rd <= 0; w_wr <= 0; w_rd <= 0; r_n <= 0; w_n <= 0; r_given <= 0; owed_lines <= 0;
948 lock <= 1'b0;
949 end
950 end
951 end endgenerate
952endmodule
953"##;
954
955const READ_CREDIT: &str = r##"// One reader's lines of one channel through the channel multiplexer, asked only as they can be held. The reader's read
956// descriptors (a first line and a count of any size, its write bit clear) are asked of the channel in runs of at most
957// MAXL lines, a run only while the lines asked and not yet taken by the reader, the run's own included, are at most
958// 2^LA; every line back is held in a buffer of 2^LA lines the reader takes from in order, its next line in a register.
959// So the multiplexer never waits on the reader for a line (mr_ready is high), however the reader takes them: a reader
960// that takes a channel's lines only as its other channels let it — the streaming unit, whose channels' feeds step
961// together — needs this to share the channels with ports that take their lines in the order they asked for them.
962module logos_read_credit #(parameter AW = 27, LW = 32, DL = 512, LA = 9, MAXL = 32)
963 (input clk, input rst,
964 input in_valid, output in_ready, input [AW+LW:0] in_data,
965 output out_valid, input out_ready, output [DL-1:0] out_data,
966 output reg md_valid, input md_ready, output [AW+LW:0] md_data,
967 input mr_valid, output mr_ready, input [DL-1:0] mr_data);
968 localparam NW = $clog2(MAXL + 1);
969 localparam [LW-1:0] RUN = MAXL, TWO_RUNS = 2 * MAXL;
970 localparam [NW-1:0] RUN_N = MAXL;
971 localparam [LA:0] ALL = 1 << LA;
972 // The descriptor being asked: its next line, whether it has lines left, and whether more than a run's.
973 reg [AW-1:0] at;
974 reg [LW-1:0] left;
975 reg none, more;
976 wire [NW-1:0] n = more ? RUN_N : left[NW-1:0];
977 // The lines the buffer can still promise: all of it less the lines asked and not yet taken by the reader.
978 reg [LA:0] credit;
979 wire pop = out_valid && out_ready;
980 wire ask = !none && (!md_valid || md_ready) && credit >= {{(LA+1-NW){1'b0}}, n};
981 assign in_ready = none;
982 reg [AW-1:0] md_line;
983 reg [NW-1:0] md_n;
984 assign md_data = {1'b0, md_line, {(LW-NW){1'b0}}, md_n};
985 always @(posedge clk) begin
986 if (md_valid && md_ready) md_valid <= 1'b0;
987 if (ask) begin
988 md_valid <= 1'b1; md_line <= at; md_n <= n;
989 at <= at + {{(AW-NW){1'b0}}, n}; left <= left - {{(LW-NW){1'b0}}, n};
990 none <= !more; more <= more && left > TWO_RUNS;
991 end else if (in_valid && none) begin
992 at <= in_data[LW +: AW]; left <= in_data[LW-1:0];
993 none <= in_data[LW-1:0] == {LW{1'b0}}; more <= in_data[LW-1:0] > RUN;
994 end
995 credit <= credit - (ask ? {{(LA+1-NW){1'b0}}, n} : {(LA+1){1'b0}}) + {{LA{1'b0}}, pop};
996 if (rst) begin md_valid <= 1'b0; none <= 1'b1; more <= 1'b0; credit <= ALL; end
997 end
998 // The lines back, in the order they come: written into the buffer, read a clock ahead of the reader into one of two
999 // places beside it (o0 its next), so the reader's line and its valid are registers.
1000 assign mr_ready = 1'b1;
1001 (* ramstyle = "M20K" *) reg [DL-1:0] held [0:(1 << LA) - 1];
1002 reg [LA-1:0] wp, rp;
1003 reg [LA:0] stored;
1004 reg [DL-1:0] got, o0, o1;
1005 reg fetched;
1006 reg [1:0] on;
1007 wire [1:0] kept = on - {1'b0, pop};
1008 wire fetch = stored != {(LA+1){1'b0}} && kept + {1'b0, fetched} <= 2'd1;
1009 assign out_valid = on != 2'd0;
1010 assign out_data = o0;
1011 always @(posedge clk) begin
1012 if (mr_valid) held[wp] <= mr_data;
1013 if (fetch) got <= held[rp];
1014 if (pop) o0 <= o1;
1015 if (fetched) begin
1016 if (kept == 2'd0) o0 <= got;
1017 else o1 <= got;
1018 end
1019 wp <= wp + {{(LA-1){1'b0}}, mr_valid};
1020 rp <= rp + {{(LA-1){1'b0}}, fetch};
1021 stored <= stored + {{LA{1'b0}}, mr_valid} - {{LA{1'b0}}, fetch};
1022 fetched <= fetch;
1023 on <= kept + {1'b0, fetched};
1024 if (rst) begin wp <= {LA{1'b0}}; rp <= {LA{1'b0}}; stored <= {(LA+1){1'b0}}; fetched <= 1'b0; on <= 2'd0; end
1025 end
1026endmodule
1027"##;