1use crate::axi4::{RESP_OKAY, ReadRequest, ReadResponse, WriteRequest, WriteResponse};
18use crate::verify::RECORD_BEATS;
19use crate::verify::certs::{CertRequest, CertResult};
20use crate::verify::datapath::{Beat, CertWarm, CoreInput, Verdict, VerifyCore};
21use rhdl::prelude::*;
22use rhdl_fpga::core::{
23 dff::DFF,
24 ram::synchronous::{In as RamIn, SyncBRAM, Write as RamWrite},
25};
26
27pub const BURST_BEATS: usize = 16;
29pub const FIFO_BEATS: usize = 64;
31
32const _: () = assert!(
33 RECORD_BEATS == 26 || RECORD_BEATS == 27,
34 "the beat count below is count·16 + count·8 + count·2 (+ count for 27-beat records)"
35);
36const EXTRA_BEAT: bool = RECORD_BEATS == 27;
38
39#[derive(Clone, Copy, Debug, PartialEq, Digital)]
43pub struct ReaderInput {
44 pub base: b64,
46 pub beats: b32,
48 pub run: bool,
50 pub memory: ReadResponse,
52 pub take: bool,
54}
55
56#[derive(Clone, Copy, Debug, PartialEq, Digital)]
58pub struct ReaderOutput {
59 pub memory: ReadRequest,
61 pub beat: Beat,
63 pub error: bool,
65}
66
67#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
69#[doc(hidden)]
70pub struct ReaderControl {
71 beats: b32,
74 run: bool,
75 ar_valid: bool,
78 ar_addr: b64,
79 ar_len: b8,
80 requested: b32,
82 received: b32,
84 drained: b32,
86 reading: bool,
88 count: b2,
90 head: bool,
92 error: bool,
94}
95
96#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
98pub struct RecordReader {
99 fifo: SyncBRAM<[b8; 64], 6>,
101 control: DFF<ReaderControl>,
103 out: DFF<[[b8; 64]; 2]>,
105}
106
107impl Default for RecordReader {
108 fn default() -> Self {
110 Self {
111 fifo: SyncBRAM::default(),
112 control: DFF::new(ReaderControl::default()),
113 out: DFF::resetless(),
114 }
115 }
116}
117
118impl SynchronousIO for RecordReader {
119 type I = ReaderInput;
120 type O = ReaderOutput;
121 type Kernel = record_reader_kernel;
122}
123
124#[kernel]
126pub fn record_reader_kernel(clock_reset: ClockReset, input: ReaderInput, q: RecordReaderQ) -> (ReaderOutput, RecordReaderD) {
127 let resetting = clock_reset.reset.any();
131 let _ = resetting;
132 let mut c = q.control;
133 let mut out = q.out;
134 c.beats = input.beats;
135 c.run = input.run;
136 if q.control.ar_valid && input.memory.arready {
140 c.ar_valid = false;
141 }
142 let in_fifo = q.control.requested - q.control.drained;
143 let room = in_fifo <= b32((FIFO_BEATS - BURST_BEATS) as u128);
144 let remaining = q.control.beats - q.control.requested;
145 let burst: b32 = if remaining < b32(BURST_BEATS as u128) { remaining } else { b32(BURST_BEATS as u128) };
146 let load = !q.control.ar_valid && q.control.run && room && q.control.requested < q.control.beats;
147 if load {
148 c.ar_valid = true;
149 c.ar_addr = input.base + (q.control.requested.resize::<64>() << 6);
150 c.ar_len = (burst - b32(1)).resize();
151 c.requested = q.control.requested + burst;
152 }
153 let write = input.memory.rvalid;
155 if write {
156 c.received = q.control.received + b32(1);
157 if input.memory.rresp != RESP_OKAY {
158 c.error = true;
159 }
160 }
161 let mut count = q.control.count;
163 if q.control.reading {
164 let slot = q.control.head ^ (count == b2(1));
165 if slot {
166 out[1] = q.fifo;
167 } else {
168 out[0] = q.fifo;
169 }
170 count = count + b2(1);
171 }
172 let have = q.control.count != b2(0);
174 if input.take && have {
175 c.head = !q.control.head;
176 count = count - b2(1);
177 }
178 c.count = count;
179 let pending = q.control.count.resize::<3>() + if q.control.reading { b3(1) } else { b3(0) }
182 - if input.take && have { b3(1) } else { b3(0) };
183 let read = q.control.received != q.control.drained && pending < b3(2);
184 c.reading = read;
185 if read {
186 c.drained = q.control.drained + b32(1);
187 }
188 let head = if q.control.head { q.out[1] } else { q.out[0] };
189 let d = RecordReaderD {
190 fifo: RamIn::<[b8; 64], 6> {
191 read_addr: q.control.drained.resize(),
192 write: RamWrite::<[b8; 64], 6> {
193 addr: q.control.received.resize(),
194 value: input.memory.rdata,
195 enable: write,
196 },
197 },
198 control: c,
199 out,
200 };
201 (
202 ReaderOutput {
203 memory: ReadRequest {
204 arvalid: q.control.ar_valid,
205 araddr: q.control.ar_addr,
206 arlen: q.control.ar_len,
207 rready: true,
208 },
209 beat: Beat {
210 valid: have,
211 data: head,
212 },
213 error: q.control.error,
214 },
215 d,
216 )
217}
218
219#[derive(Clone, Copy, Debug, PartialEq, Digital)]
223pub struct WriterInput {
224 pub base: b64,
226 pub count: b32,
228 pub verdict: Verdict,
230 pub memory: WriteResponse,
232}
233
234#[derive(Clone, Copy, Debug, PartialEq, Digital)]
236pub struct WriterOutput {
237 pub memory: WriteRequest,
239 pub ready: bool,
241 pub done: bool,
243 pub error: bool,
245}
246
247#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
249#[doc(hidden)]
250pub struct WriterControl {
251 fill: b7,
253 taken: b32,
255 sending: bool,
257 aw_done: bool,
259 w_done: bool,
261 sent: b32,
263 acked: b32,
265 error: bool,
267}
268
269#[derive(Clone, Copy, Debug, PartialEq, Digital)]
271#[doc(hidden)]
272pub struct WriterData {
273 acc: [b8; 64],
275 beat: [b8; 64],
277}
278
279#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
281pub struct VerdictWriter {
282 control: DFF<WriterControl>,
284 data: DFF<WriterData>,
286}
287
288impl Default for VerdictWriter {
289 fn default() -> Self {
291 Self {
292 control: DFF::new(WriterControl::default()),
293 data: DFF::new(WriterData {
294 acc: [b8(0); 64],
295 beat: [b8(0); 64],
296 }),
297 }
298 }
299}
300
301impl SynchronousIO for VerdictWriter {
302 type I = WriterInput;
303 type O = WriterOutput;
304 type Kernel = verdict_writer_kernel;
305}
306
307#[allow(clippy::needless_range_loop)]
309#[kernel]
310pub fn verdict_writer_kernel(clock_reset: ClockReset, input: WriterInput, q: VerdictWriterQ) -> (WriterOutput, VerdictWriterD) {
311 let resetting = clock_reset.reset.any();
315 let _ = resetting;
316 let mut c = q.control;
317 let mut data = q.data;
318 let awvalid = q.control.sending && !q.control.aw_done;
320 let wvalid = q.control.sending && !q.control.w_done;
321 let aw_now = q.control.aw_done || (awvalid && input.memory.awready);
322 let w_now = q.control.w_done || (wvalid && input.memory.wready);
323 if q.control.sending {
324 if aw_now && w_now {
325 c.sending = false;
326 c.aw_done = false;
327 c.w_done = false;
328 c.sent = q.control.sent + b32(1);
329 } else {
330 c.aw_done = aw_now;
331 c.w_done = w_now;
332 }
333 }
334 if input.memory.bvalid {
336 c.acked = q.control.acked + b32(1);
337 if input.memory.bresp != RESP_OKAY {
338 c.error = true;
339 }
340 }
341 let ready = !q.control.sending;
343 if ready && input.verdict.valid {
344 let mut acc = q.data.acc;
345 for i in 0..64 {
346 if q.control.fill == b7(i as u128) {
347 acc[i] = input.verdict.byte;
348 }
349 }
350 let taken = q.control.taken + b32(1);
351 c.taken = taken;
352 if q.control.fill == b7(63) || taken == input.count {
353 data.beat = acc;
354 data.acc = [b8(0); 64];
355 c.fill = b7(0);
356 c.sending = true;
357 } else {
358 data.acc = acc;
359 c.fill = q.control.fill + b7(1);
360 }
361 }
362 let done = q.control.taken == input.count && !q.control.sending && q.control.acked == q.control.sent;
363 let d = VerdictWriterD { control: c, data };
364 (
365 WriterOutput {
366 memory: WriteRequest {
367 awvalid,
368 awaddr: input.base + (q.control.sent.resize::<64>() << 6),
369 awlen: b8(0),
370 wvalid,
371 wdata: q.data.beat,
372 wlast: true,
373 bready: true,
374 },
375 ready,
376 done,
377 error: q.control.error,
378 },
379 d,
380 )
381}
382
383#[derive(Clone, Copy, Debug, PartialEq, Digital)]
387pub struct UnitInput {
388 pub run: bool,
390 pub records_base: b64,
392 pub verdicts_base: b64,
394 pub count: b32,
396 pub records: ReadResponse,
398 pub verdicts: WriteResponse,
400 pub cert_taken: bool,
402 pub cert_result: CertResult,
404}
405
406#[derive(Clone, Copy, Debug, PartialEq, Digital)]
408pub struct UnitOutput {
409 pub records: ReadRequest,
411 pub verdicts: WriteRequest,
413 pub cert_request: CertRequest,
415 pub retired: b32,
417 pub valid: b32,
419 pub cert_checks: b32,
421 pub cert_fails: b32,
423 pub done: bool,
425 pub error: b2,
427}
428
429#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
431pub struct VerifyUnit<const TABLE_MASK: usize> {
432 reader: RecordReader,
434 core: VerifyCore<TABLE_MASK>,
436 writer: VerdictWriter,
438}
439
440impl<const TABLE_MASK: usize> Default for VerifyUnit<TABLE_MASK> {
441 fn default() -> Self {
443 Self {
444 reader: RecordReader::default(),
445 core: VerifyCore::default(),
446 writer: VerdictWriter::default(),
447 }
448 }
449}
450
451impl<const TABLE_MASK: usize> SynchronousIO for VerifyUnit<TABLE_MASK> {
452 type I = UnitInput;
453 type O = UnitOutput;
454 type Kernel = verify_unit_kernel<TABLE_MASK>;
455}
456
457#[kernel]
459pub fn verify_unit_kernel<const TABLE_MASK: usize>(
460 _clock_reset: ClockReset,
461 input: UnitInput,
462 q: VerifyUnitQ<TABLE_MASK>,
463) -> (UnitOutput, VerifyUnitD<TABLE_MASK>) {
464 let beats = (input.count << 4) + (input.count << 3) + (input.count << 1) + (if EXTRA_BEAT { input.count } else { b32(0) });
466 let take = q.reader.beat.valid && q.core.beat_ready;
467 let d = VerifyUnitD::<TABLE_MASK> {
468 reader: ReaderInput {
469 base: input.records_base,
470 beats,
471 run: input.run,
472 memory: input.records,
473 take,
474 },
475 core: CoreInput {
476 beat: q.reader.beat,
477 slot: b4(0),
478 check_epoch: false,
479 expected_epoch: b64(0),
480 warm: CertWarm {
481 valid: false,
482 slot: b4(0),
483 commitment: [b8(0); 64],
484 signature: [b8(0); 64],
485 },
486 verdict_ready: q.writer.ready,
487 cert_taken: input.cert_taken,
488 cert_result: input.cert_result,
489 },
490 writer: WriterInput {
491 base: input.verdicts_base,
492 count: input.count,
493 verdict: q.core.verdict,
494 memory: input.verdicts,
495 },
496 };
497 (
498 UnitOutput {
499 records: q.reader.memory,
500 verdicts: q.writer.memory,
501 cert_request: q.core.cert_request,
502 retired: q.core.retired,
503 valid: q.core.valid,
504 cert_checks: q.core.cert_checks,
505 cert_fails: q.core.cert_fails,
506 done: q.writer.done,
507 error: (if q.reader.error { b2(1) } else { b2(0) }) | (if q.writer.error { b2(2) } else { b2(0) }),
508 },
509 d,
510 )
511}