1use crate::chain_lane::{ChainLane, LANE_LATENCY};
76use crate::chain_step::{ChainStepInput, ChainStepOutput};
77use crate::slice_chain::salt_kernel;
78use crate::{CHAINS, ELEMENT_BYTES};
79use rhdl::prelude::*;
80use rhdl_fpga::core::{
81 dff::DFF,
82 ram::synchronous::{In as RamIn, SyncBRAM, Write as RamWrite},
83};
84
85pub const CONTEXTS: usize = 64;
87pub const LANES: usize = 3;
89pub const LANE_CHAINS: usize = CHAINS.div_ceil(LANES);
91pub const ROWS: usize = (16 + CHAINS * ELEMENT_BYTES).div_ceil(64);
93pub const END_BANKS: usize = 5;
95pub const NODE_BANKS: usize = 2;
97pub const ROW_LATENCY: usize = 2;
99const LAST_LANE_CHAIN: usize = LANE_CHAINS - 1;
101const CHAIN_66: usize = CHAINS - 2;
103const CHAIN_67: usize = CHAINS - 1;
105const LANE_LAST: usize = LANE_LATENCY - 1;
107const SKID: usize = 4;
109
110const _: () = assert!(LANES == 3, "the bank maps below assume three lanes");
111const _: () = assert!(ROWS == 20);
112const _: () = assert!(CONTEXTS == 64, "contexts are 6-bit numbers");
113
114#[derive(Clone, Copy, Debug, PartialEq, Digital)]
116pub struct NodeWrite {
117 pub enable: bool,
119 pub addr: b10,
121 pub value: [b8; ELEMENT_BYTES],
123}
124
125impl Default for NodeWrite {
126 fn default() -> Self {
128 Self {
129 enable: false,
130 addr: b10(0),
131 value: [b8(0); ELEMENT_BYTES],
132 }
133 }
134}
135
136#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
138pub struct NonceWrite {
139 pub valid: bool,
141 pub ctx: b6,
143 pub value: [b8; 16],
145}
146
147#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
149pub struct Retire {
150 pub valid: bool,
152 pub ctx: b6,
154}
155
156#[derive(Clone, Copy, Debug, PartialEq, Digital)]
158pub struct LaneDigits {
159 pub valid: bool,
161 pub ctx: b6,
163 pub digits: [b2; LANE_CHAINS],
165}
166
167impl Default for LaneDigits {
168 fn default() -> Self {
170 Self {
171 valid: false,
172 ctx: b6(0),
173 digits: [b2(0); LANE_CHAINS],
174 }
175 }
176}
177
178#[derive(Clone, Copy, Debug, PartialEq, Digital)]
180pub struct DigitsEvent {
181 pub valid: bool,
183 pub ctx: b6,
185 pub digits: [b2; CHAINS],
187}
188
189impl Default for DigitsEvent {
190 fn default() -> Self {
192 Self {
193 valid: false,
194 ctx: b6(0),
195 digits: [b2(0); CHAINS],
196 }
197 }
198}
199
200#[derive(Clone, Copy, Debug, PartialEq, Digital)]
204pub struct RingInput {
205 pub lane: b2,
208 pub nodes: [NodeWrite; NODE_BANKS],
210 pub digits: LaneDigits,
212 pub nonce: NonceWrite,
214 pub retire: Retire,
216 pub end_read: [b9; END_BANKS],
218}
219
220#[derive(Clone, Copy, Debug, PartialEq, Digital)]
222pub struct RingOutput {
223 pub ends: [[b8; ELEMENT_BYTES]; END_BANKS],
225 pub done: [bool; CONTEXTS],
227 pub ready: bool,
229 pub stepped: bool,
231}
232
233#[derive(Clone, Copy, Debug, PartialEq, Digital)]
235#[doc(hidden)]
236pub struct RingItem {
237 node: [b8; ELEMENT_BYTES],
239 nonce: [b8; 16],
241 salted: [b8; 16],
243 tag: b32,
245}
246
247#[derive(Clone, Copy, Debug, PartialEq, Digital)]
249#[doc(hidden)]
250pub struct RingControl {
251 queue_tail: b7,
253 queue_head: b7,
255 phase: b2,
257 active: bool,
259 ctx: b6,
261 k: b5,
263 k_mod5: b3,
265 k_div5: b3,
267 fetching: bool,
269 count: b3,
271 head: b2,
273 ends_count: b3,
275 ends_head: b2,
277 recirculate: bool,
279 ends: [b5; CONTEXTS],
281}
282
283impl Default for RingControl {
284 fn default() -> Self {
286 Self {
287 queue_tail: b7(0),
288 queue_head: b7(0),
289 phase: b2(0),
290 active: false,
291 ctx: b6(0),
292 k: b5(0),
293 k_mod5: b3(0),
294 k_div5: b3(0),
295 fetching: false,
296 count: b3(0),
297 head: b2(0),
298 ends_count: b3(0),
299 ends_head: b2(0),
300 recirculate: false,
301 ends: [b5(0); CONTEXTS],
302 }
303 }
304}
305
306#[derive(Clone, Copy, Debug, PartialEq, Digital)]
308#[doc(hidden)]
309pub struct RingData {
310 digits: [b2; LANE_CHAINS],
312 nonce: [b8; 16],
314 fetch: RingItem,
316 fetch_bank: bool,
318 skid: [RingItem; SKID],
320 end_queue: [RingItem; SKID],
322 recirculate: RingItem,
324}
325
326#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
328pub struct VerifyRing<const TABLE_MASK: usize> {
329 lane: ChainLane<TABLE_MASK>,
331 nonces_in_lane: [DFF<[b8; 16]>; LANE_LATENCY],
333 nodes: [SyncBRAM<[b8; ELEMENT_BYTES], 10>; NODE_BANKS],
335 ends: [SyncBRAM<[b8; ELEMENT_BYTES], 9>; END_BANKS],
337 queue: SyncBRAM<b6, 6>,
339 digits: SyncBRAM<[b2; LANE_CHAINS], 6>,
341 nonce_ram: SyncBRAM<[b8; 16], 6>,
343 control: DFF<RingControl>,
345 data: DFF<RingData>,
347}
348
349impl<const TABLE_MASK: usize> Default for VerifyRing<TABLE_MASK> {
350 fn default() -> Self {
352 Self {
353 lane: ChainLane::default(),
354 nonces_in_lane: core::array::from_fn(|_| DFF::resetless()),
355 nodes: core::array::from_fn(|_| SyncBRAM::default()),
356 ends: core::array::from_fn(|_| SyncBRAM::default()),
357 queue: SyncBRAM::default(),
358 digits: SyncBRAM::default(),
359 nonce_ram: SyncBRAM::default(),
360 control: DFF::new(RingControl::default()),
361 data: DFF::resetless(),
362 }
363 }
364}
365
366impl<const TABLE_MASK: usize> SynchronousIO for VerifyRing<TABLE_MASK> {
367 type I = RingInput;
368 type O = RingOutput;
369 type Kernel = verify_ring_kernel<TABLE_MASK>;
370}
371
372#[kernel]
374pub fn ring_tag_kernel(ctx: b6, chain: b7, depth: b2, k_mod5: b3, k_div5: b3) -> b32 {
375 ctx.resize::<32>()
376 | (chain.resize::<32>() << 6)
377 | (depth.resize::<32>() << 13)
378 | (k_mod5.resize::<32>() << 15)
379 | (k_div5.resize::<32>() << 18)
380}
381
382const DEPTH_CLEAR: b32 = b32(0xffff_ffff ^ (0b11 << 13));
384const ZERO_NODE: [b8; ELEMENT_BYTES] = [b8(0); ELEMENT_BYTES];
386
387#[allow(clippy::needless_range_loop, clippy::too_many_lines)]
393#[kernel]
394pub fn verify_ring_kernel<const TABLE_MASK: usize>(
395 clock_reset: ClockReset,
396 input: RingInput,
397 q: VerifyRingQ<TABLE_MASK>,
398) -> (RingOutput, VerifyRingD<TABLE_MASK>) {
399 let resetting = clock_reset.reset.any();
403 let _ = resetting;
404 let mut c = q.control;
405 let mut data = q.data;
406 let chains: b5 = if input.lane == b2(2) { b5(22) } else { b5(23) };
408
409 if input.digits.valid {
412 c.queue_tail = q.control.queue_tail + b7(1);
413 }
414 let queue_write = RamWrite::<b6, 6> {
415 addr: q.control.queue_tail.resize(),
416 value: input.digits.ctx,
417 enable: input.digits.valid,
418 };
419 let digits_write = RamWrite::<[b2; LANE_CHAINS], 6> {
420 addr: input.digits.ctx,
421 value: input.digits.digits,
422 enable: input.digits.valid,
423 };
424 let nonce_write = RamWrite::<[b8; 16], 6> {
425 addr: input.nonce.ctx,
426 value: input.nonce.value,
427 enable: input.nonce.valid,
428 };
429
430 let mut queue_read: b6 = q.control.queue_head.resize();
433 let mut context_read: b6 = q.control.ctx;
434 if q.control.phase == b2(1) {
435 context_read = q.queue;
436 c.ctx = q.queue;
437 c.queue_head = q.control.queue_head + b7(1);
438 c.phase = b2(2);
439 } else if q.control.phase == b2(2) {
440 data.digits = q.digits;
441 data.nonce = q.nonce_ram;
442 c.active = true;
443 c.k = b5(0);
444 c.k_mod5 = b3(0);
445 c.k_div5 = b3(0);
446 c.phase = b2(0);
447 } else if !q.control.active && q.control.queue_head != q.control.queue_tail {
448 queue_read = q.control.queue_head.resize();
449 c.phase = b2(1);
450 }
451
452 let mut count = q.control.count;
457 let mut skid = q.data.skid;
458 let mut ends_count = q.control.ends_count;
459 let mut end_queue = q.data.end_queue;
460 if q.control.fetching {
461 let mut item = q.data.fetch;
462 item.node = if q.data.fetch_bank { q.nodes[1] } else { q.nodes[0] };
463 let landed_depth: b2 = (item.tag >> 13).resize();
464 if landed_depth == b2(3) {
465 let slot: b2 = q.control.ends_head + ends_count.resize::<2>();
466 for s in 0..SKID {
467 if slot == b2(s as u128) {
468 end_queue[s] = item;
469 }
470 }
471 ends_count = ends_count + b3(1);
472 } else {
473 let slot: b2 = q.control.head + count.resize::<2>();
474 for s in 0..SKID {
475 if slot == b2(s as u128) {
476 skid[s] = item;
477 }
478 }
479 count = count + b3(1);
480 }
481 }
482 let in_flight = if q.control.fetching { b4(1) } else { b4(0) };
484 let fetch = q.control.active
485 && q.control.phase == b2(0)
486 && q.control.k < chains
487 && (q.control.count.resize::<4>() + in_flight) < b4(SKID as u128)
488 && (q.control.ends_count.resize::<4>() + in_flight) < b4(SKID as u128);
489 c.fetching = fetch;
490 let node_read: b10 = (q.control.ctx.resize::<10>() << 4) | (q.control.k >> 1).resize::<10>();
491 if fetch {
492 let mut digit = b2(0);
494 for k in 0..LANE_CHAINS {
495 if q.control.k == b5(k as u128) {
496 digit = q.data.digits[k];
497 }
498 }
499 let k7: b7 = q.control.k.resize();
500 let chain: b7 = input.lane.resize::<7>() + (k7 << 1) + k7;
501 data.fetch = RingItem {
502 node: ZERO_NODE,
503 nonce: q.data.nonce,
504 salted: salt_kernel(q.data.nonce, chain, digit),
505 tag: ring_tag_kernel(q.control.ctx, chain, digit, q.control.k_mod5, q.control.k_div5),
506 };
507 data.fetch_bank = (q.control.k & b5(1)) == b5(1);
508 c.k = q.control.k + b5(1);
509 if q.control.k_mod5 == b3(4) {
510 c.k_mod5 = b3(0);
511 c.k_div5 = q.control.k_div5 + b3(1);
512 } else {
513 c.k_mod5 = q.control.k_mod5 + b3(1);
514 }
515 if q.control.k + b5(1) == chains {
517 c.active = false;
518 }
519 }
520
521 let out: ChainStepOutput = q.lane;
523 let out_depth: b2 = (out.tag >> 13).resize();
524 let out_chain: b7 = (out.tag >> 6).resize();
525 let completes = out.valid && out_depth == b2(2);
527 c.recirculate = out.valid && out_depth != b2(2);
529 let next_depth = out_depth + b2(1);
530 let lane_nonce = q.nonces_in_lane[LANE_LAST];
531 data.recirculate = RingItem {
532 node: out.node,
533 nonce: lane_nonce,
534 salted: salt_kernel(lane_nonce, out_chain, next_depth),
535 tag: (out.tag & DEPTH_CLEAR) | (next_depth.resize::<32>() << 13),
536 };
537
538 let mut head_item = q.data.skid[0];
540 for s in 0..SKID {
541 if q.control.head == b2(s as u128) {
542 head_item = q.data.skid[s];
543 }
544 }
545 let have_head = q.control.count != b3(0);
546 let mut lane_in = ChainStepInput {
547 salted_nonce: q.data.recirculate.salted,
548 secret: q.data.recirculate.node,
549 tag: q.data.recirculate.tag,
550 valid: q.control.recirculate,
551 };
552 let mut lane_nonce_in = q.data.recirculate.nonce;
553 if !q.control.recirculate && have_head {
555 lane_in = ChainStepInput {
556 salted_nonce: head_item.salted,
557 secret: head_item.node,
558 tag: head_item.tag,
559 valid: true,
560 };
561 lane_nonce_in = head_item.nonce;
562 c.head = q.control.head + b2(1);
563 count = count - b3(1);
564 }
565 c.count = count;
566 data.skid = skid;
567 let mut end_item = q.data.end_queue[0];
570 for s in 0..SKID {
571 if q.control.ends_head == b2(s as u128) {
572 end_item = q.data.end_queue[s];
573 }
574 }
575 let skip = q.control.ends_count != b3(0) && !completes;
576 if skip {
577 c.ends_head = q.control.ends_head + b2(1);
578 ends_count = ends_count - b3(1);
579 }
580 c.ends_count = ends_count;
581 data.end_queue = end_queue;
582
583 let write_tag = if completes { out.tag } else { end_item.tag };
585 let write_node = if completes { out.node } else { end_item.node };
586 let write = completes || skip;
587 let write_ctx: b6 = write_tag.resize();
588 let write_bank: b3 = (write_tag >> 15).resize();
589 let write_addr: b9 = (write_ctx.resize::<9>() << 3) | (write_tag >> 18).resize::<9>();
590 let mut end_ports = [RamIn::<[b8; ELEMENT_BYTES], 9> {
591 read_addr: b9(0),
592 write: RamWrite::<[b8; ELEMENT_BYTES], 9> {
593 addr: write_addr,
594 value: write_node,
595 enable: false,
596 },
597 }; END_BANKS];
598 for b in 0..END_BANKS {
599 end_ports[b].read_addr = input.end_read[b];
600 end_ports[b].write.enable = write && write_bank == b3(b as u128);
601 }
602
603 let mut ends = q.control.ends;
605 let mut done = [false; CONTEXTS];
606 for x in 0..CONTEXTS {
607 if input.retire.valid && input.retire.ctx == b6(x as u128) {
608 ends[x] = b5(0);
609 } else if write && write_ctx == b6(x as u128) {
610 ends[x] = q.control.ends[x] + b5(1);
611 }
612 done[x] = q.control.ends[x] == chains;
613 }
614 c.ends = ends;
615
616 let mut nonces_in_lane = q.nonces_in_lane;
618 nonces_in_lane[0] = lane_nonce_in;
619 for s in 1..LANE_LATENCY {
620 nonces_in_lane[s] = q.nonces_in_lane[s - 1];
621 }
622 let d = VerifyRingD::<TABLE_MASK> {
623 lane: lane_in,
624 nonces_in_lane,
625 nodes: [
626 RamIn::<[b8; ELEMENT_BYTES], 10> {
627 read_addr: node_read,
628 write: RamWrite::<[b8; ELEMENT_BYTES], 10> {
629 addr: input.nodes[0].addr,
630 value: input.nodes[0].value,
631 enable: input.nodes[0].enable,
632 },
633 },
634 RamIn::<[b8; ELEMENT_BYTES], 10> {
635 read_addr: node_read,
636 write: RamWrite::<[b8; ELEMENT_BYTES], 10> {
637 addr: input.nodes[1].addr,
638 value: input.nodes[1].value,
639 enable: input.nodes[1].enable,
640 },
641 },
642 ],
643 ends: end_ports,
644 queue: RamIn::<b6, 6> {
645 read_addr: queue_read,
646 write: queue_write,
647 },
648 digits: RamIn::<[b2; LANE_CHAINS], 6> {
649 read_addr: context_read,
650 write: digits_write,
651 },
652 nonce_ram: RamIn::<[b8; 16], 6> {
653 read_addr: context_read,
654 write: nonce_write,
655 },
656 control: c,
657 data,
658 };
659 (
660 RingOutput {
661 ends: q.ends,
662 done,
663 ready: out.ready,
664 stepped: lane_in.valid,
665 },
666 d,
667 )
668}
669
670#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
674pub struct RowRequest {
675 pub valid: bool,
677 pub ctx: b6,
679 pub row: b5,
681}
682
683#[derive(Clone, Copy, Debug, PartialEq, Digital)]
685pub struct ChainsInput {
686 pub nodes: [NodeWrite; 2 * LANES],
688 pub digits: DigitsEvent,
690 pub nonce: NonceWrite,
692 pub retire: Retire,
694 pub row: RowRequest,
696}
697
698#[derive(Clone, Copy, Debug, PartialEq, Digital)]
700pub struct ChainsOutput {
701 pub row: [b8; 64],
703 pub row_valid: bool,
705 pub done: [bool; CONTEXTS],
707 pub ready: bool,
709 pub stepped: [bool; LANES],
711}
712
713#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
715#[doc(hidden)]
716pub struct RowStage {
717 pending: bool,
719 row: b5,
721 out_valid: bool,
723}
724
725#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
727pub struct VerifyChains<const TABLE_MASK: usize> {
728 rings: rhdl::core::ReplicatedSynchronous<VerifyRing<TABLE_MASK>, LANES>,
730 nonces: SyncBRAM<[b8; 16], 6>,
732 stage: DFF<RowStage>,
734 out: DFF<[b8; 64]>,
736}
737
738impl<const TABLE_MASK: usize> Default for VerifyChains<TABLE_MASK> {
739 fn default() -> Self {
741 Self {
742 rings: rhdl::core::ReplicatedSynchronous::new(VerifyRing::default()),
743 nonces: SyncBRAM::default(),
744 stage: DFF::new(RowStage::default()),
745 out: DFF::resetless(),
746 }
747 }
748}
749
750impl<const TABLE_MASK: usize> SynchronousIO for VerifyChains<TABLE_MASK> {
751 type I = ChainsInput;
752 type O = ChainsOutput;
753 type Kernel = verify_chains_kernel<TABLE_MASK>;
754}
755
756const SOURCES: usize = LANES * END_BANKS + 2;
759pub const NONCE_SOURCE: usize = LANES * END_BANKS;
761pub const ZERO_SOURCE: usize = LANES * END_BANKS + 1;
763
764pub const ROW_SOURCE: [[(usize, usize); 64]; ROWS] = row_sources();
770pub const ROW_SOURCE_BANK: [[usize; 64]; ROWS] = split_sources().0;
772pub const ROW_SOURCE_BYTE: [[usize; 64]; ROWS] = split_sources().1;
774pub const ROW_SLOT: [[b3; LANES * END_BANKS]; ROWS] = row_slots();
777
778const fn row_sources() -> [[(usize, usize); 64]; ROWS] {
780 let mut table = [[(ZERO_SOURCE, 0); 64]; ROWS];
781 let mut j = 0;
782 while j < ROWS {
783 let mut p = 0;
784 while p < 64 {
785 let m = 64 * j + p;
786 table[j][p] = if m < 16 {
787 (NONCE_SOURCE, m)
788 } else if m < 16 + CHAINS * ELEMENT_BYTES {
789 let chain = (m - 16) / ELEMENT_BYTES;
790 (END_BANKS * (chain % LANES) + (chain / LANES) % END_BANKS, (m - 16) % ELEMENT_BYTES)
791 } else {
792 (ZERO_SOURCE, 0)
793 };
794 p += 1;
795 }
796 j += 1;
797 }
798 table
799}
800
801const fn split_sources() -> ([[usize; 64]; ROWS], [[usize; 64]; ROWS]) {
803 let mut banks = [[0; 64]; ROWS];
804 let mut bytes = [[0; 64]; ROWS];
805 let mut j = 0;
806 while j < ROWS {
807 let mut p = 0;
808 while p < 64 {
809 banks[j][p] = ROW_SOURCE[j][p].0;
810 bytes[j][p] = ROW_SOURCE[j][p].1;
811 p += 1;
812 }
813 j += 1;
814 }
815 (banks, bytes)
816}
817
818const fn row_slots() -> [[b3; LANES * END_BANKS]; ROWS] {
820 let mut table = [[b3(0); LANES * END_BANKS]; ROWS];
821 let mut used = [[false; LANES * END_BANKS]; ROWS];
822 let mut chain = 0;
823 while chain < CHAINS {
824 let first = (16 + chain * ELEMENT_BYTES) / 64;
825 let last = (16 + chain * ELEMENT_BYTES + ELEMENT_BYTES - 1) / 64;
826 let bank = END_BANKS * (chain % LANES) + (chain / LANES) % END_BANKS;
827 let mut j = first;
828 while j <= last {
829 assert!(!used[j][bank], "two chains of one block share an end bank");
830 used[j][bank] = true;
831 table[j][bank] = b3(((chain / LANES) / END_BANKS) as u128);
832 j += 1;
833 }
834 chain += 1;
835 }
836 table
837}
838
839#[allow(clippy::needless_range_loop)]
841#[kernel]
842pub fn verify_chains_kernel<const TABLE_MASK: usize>(
843 clock_reset: ClockReset,
844 input: ChainsInput,
845 q: VerifyChainsQ<TABLE_MASK>,
846) -> (ChainsOutput, VerifyChainsD<TABLE_MASK>) {
847 let resetting = clock_reset.reset.any();
851 let _ = resetting;
852 let mut onehot = [false; ROWS];
854 for j in 0..ROWS {
855 onehot[j] = input.row.row == b5(j as u128);
856 }
857 let mut end_read = [[b9(0); END_BANKS]; LANES];
859 for l in 0..LANES {
860 for b in 0..END_BANKS {
861 let mut slot = b3(0);
862 for j in 0..ROWS {
863 if onehot[j] {
864 slot = slot | ROW_SLOT[j][END_BANKS * l + b];
865 }
866 }
867 end_read[l][b] = (input.row.ctx.resize::<9>() << 3) | slot.resize::<9>();
868 }
869 }
870 let mut lane_digits = [[b2(0); LANE_CHAINS]; LANES];
872 for l in 0..LANES {
873 for k in 0..LAST_LANE_CHAIN {
874 lane_digits[l][k] = input.digits.digits[l + LANES * k];
875 }
876 }
877 lane_digits[0][LAST_LANE_CHAIN] = input.digits.digits[CHAIN_66];
878 lane_digits[1][LAST_LANE_CHAIN] = input.digits.digits[CHAIN_67];
879 let mut rings = [RingInput {
881 lane: b2(0),
882 nodes: [NodeWrite::default(); NODE_BANKS],
883 digits: LaneDigits::default(),
884 nonce: input.nonce,
885 retire: input.retire,
886 end_read: [b9(0); END_BANKS],
887 }; LANES];
888 for l in 0..LANES {
889 rings[l].lane = b2(l as u128);
890 rings[l].nodes = [input.nodes[l], input.nodes[l + LANES]];
891 rings[l].digits = LaneDigits {
892 valid: input.digits.valid,
893 ctx: input.digits.ctx,
894 digits: lane_digits[l],
895 };
896 rings[l].end_read = end_read[l];
897 }
898
899 let r0 = q.rings[0].ends;
901 let r1 = q.rings[1].ends;
902 let r2 = q.rings[2].ends;
903 let mut nonce18 = [b8(0); ELEMENT_BYTES];
904 for b in 0..16 {
905 nonce18[b] = q.nonces[b];
906 }
907 let sources: [[b8; ELEMENT_BYTES]; SOURCES] = [
908 r0[0], r0[1], r0[2], r0[3], r0[4], r1[0], r1[1], r1[2], r1[3], r1[4], r2[0], r2[1], r2[2], r2[3], r2[4],
909 nonce18, ZERO_NODE,
910 ];
911 let mut block_onehot = [false; ROWS];
912 for j in 0..ROWS {
913 block_onehot[j] = q.stage.row == b5(j as u128);
914 }
915 let mut row = [b8(0); 64];
916 for p in 0..64 {
917 let mut value = b8(0);
918 for j in 0..ROWS {
919 if block_onehot[j] {
920 value = value | sources[ROW_SOURCE_BANK[j][p]][ROW_SOURCE_BYTE[j][p]];
921 }
922 }
923 row[p] = value;
924 }
925 let mut done = [false; CONTEXTS];
926 for x in 0..CONTEXTS {
927 done[x] = q.rings[0].done[x] && q.rings[1].done[x] && q.rings[2].done[x];
928 }
929 let d = VerifyChainsD::<TABLE_MASK> {
930 rings,
931 nonces: RamIn::<[b8; 16], 6> {
932 read_addr: input.row.ctx,
933 write: RamWrite::<[b8; 16], 6> {
934 addr: input.nonce.ctx,
935 value: input.nonce.value,
936 enable: input.nonce.valid,
937 },
938 },
939 stage: RowStage {
940 pending: input.row.valid,
941 row: input.row.row,
942 out_valid: q.stage.pending,
943 },
944 out: row,
945 };
946 (
947 ChainsOutput {
948 row: q.out,
949 row_valid: q.stage.out_valid,
950 done,
951 ready: q.rings[0].ready && q.rings[1].ready && q.rings[2].ready,
952 stepped: [q.rings[0].stepped, q.rings[1].stepped, q.rings[2].stepped],
953 },
954 d,
955 )
956}
957
958#[cfg(test)]
960mod tests {
961 use super::*;
962
963 #[test]
965 fn row_map_covers_the_commitment_message() {
966 let mut seen = 0;
967 for j in 0..ROWS {
968 for p in 0..64 {
969 let m = 64 * j + p;
970 let (source, byte) = ROW_SOURCE[j][p];
971 if m < 16 {
972 assert_eq!((source, byte), (NONCE_SOURCE, m));
973 seen += 1;
974 } else if m < 1240 {
975 let chain = (m - 16) / 18;
976 assert_eq!(source, 5 * (chain % 3) + (chain / 3) % 5);
977 assert_eq!(byte, (m - 16) % 18);
978 assert_eq!(ROW_SLOT[j][source], b3(((chain / 3) / 5) as u128));
979 seen += 1;
980 } else {
981 assert_eq!(source, ZERO_SOURCE);
982 }
983 }
984 }
985 assert_eq!(seen, 1240);
986 }
987}