1use rhdl::{core::ReplicatedSynchronous, prelude::*};
40use rhdl_fpga::core::dff::DFF;
41use rhdl_primitives::NoResetDff;
42use sha256_rhdl::lane::{
43 CompressionInput, CompressionOutput, ExternalFullCompressionLane, InlineCompressionLane,
44};
45
46use crate::{
47 blocks::HashBytes,
48 digits::MessageBytes,
49 engine::{ContextEngineInput, SingleContextWorkEngine},
50 tag::{decode_tag_kernel, tag_is_well_formed_kernel},
51};
52
53pub const LANE_LOCAL_CONTEXTS: usize = 4;
55pub const LANE_LOCAL_OUTPUT_BEATS: usize = 35;
57pub const LANE_LOCAL_BEAT_BUFFER_DEPTH: usize = 2;
59
60const _: () = {
61 assert!(LANE_LOCAL_CONTEXTS == 4);
62 assert!(LANE_LOCAL_OUTPUT_BEATS == 35);
63 assert!(LANE_LOCAL_BEAT_BUFFER_DEPTH == 2);
64};
65
66pub trait CompressionLaneComponent:
75 Synchronous<I = CompressionInput, O = CompressionOutput>
76 + Clone
77 + Copy
78 + PartialEq
79 + core::fmt::Debug
80{
81}
82
83impl<T> CompressionLaneComponent for T where
84 T: Synchronous<I = CompressionInput, O = CompressionOutput>
85 + Clone
86 + Copy
87 + PartialEq
88 + core::fmt::Debug
89{
90}
91
92#[derive(Clone, Copy, Debug, Digital, Eq, PartialEq)]
98pub struct LaneLocalBeat {
99 pub data: [b8; 64],
101 pub keep: b64,
103 pub last: bool,
105 pub job_id: b32,
107}
108
109impl Default for LaneLocalBeat {
110 fn default() -> Self {
111 Self {
112 data: [b8(0); 64],
113 keep: b64(0),
114 last: false,
115 job_id: b32(0),
116 }
117 }
118}
119
120#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
122pub struct AssembleLaneLocalBeatInput {
123 pub beat: b6,
125 pub signature_first: HashBytes,
127 pub signature_second: HashBytes,
129 pub public_seed: HashBytes,
131 pub public_key_hash: HashBytes,
133 pub job_id: b32,
135}
136
137#[kernel]
143pub fn assemble_lane_local_beat_kernel(input: AssembleLaneLocalBeatInput) -> LaneLocalBeat {
144 let mut output = LaneLocalBeat::default();
145 if input.beat < b6(33) {
146 for lane in 0..32 {
147 output.data[lane] = input.signature_first[lane];
148 output.data[lane + 32] = input.signature_second[lane];
149 }
150 output.keep = b64(0xffff_ffff_ffff_ffff);
151 output.job_id = input.job_id;
152 } else if input.beat == b6(33) {
153 for lane in 0..32 {
154 output.data[lane] = input.signature_first[lane];
155 output.data[lane + 32] = input.public_seed[lane];
156 }
157 output.keep = b64(0xffff_ffff_ffff_ffff);
158 output.job_id = input.job_id;
159 } else if input.beat == b6(34) {
160 for lane in 0..32 {
161 output.data[lane] = input.public_key_hash[lane];
162 }
163 output.keep = b64(0xffff_ffff);
164 output.last = true;
165 output.job_id = input.job_id;
166 }
167 output
168}
169
170#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
172pub struct LaneIssueControlInput {
173 pub valid: [bool; LANE_LOCAL_CONTEXTS],
175 pub tags: [b32; LANE_LOCAL_CONTEXTS],
177}
178
179#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
181pub struct LaneIssueControlOutput {
182 pub valid: bool,
184 pub context: b2,
186 pub accepted: [bool; LANE_LOCAL_CONTEXTS],
188 pub rejected: [bool; LANE_LOCAL_CONTEXTS],
190 pub next_cursor: b2,
192}
193
194#[kernel]
196#[allow(clippy::assign_op_pattern)] pub fn lane_issue_control_kernel(
198 input: LaneIssueControlInput,
199 cursor: b2,
200) -> LaneIssueControlOutput {
201 let fields = [
202 decode_tag_kernel(input.tags[0]),
203 decode_tag_kernel(input.tags[1]),
204 decode_tag_kernel(input.tags[2]),
205 decode_tag_kernel(input.tags[3]),
206 ];
207 let canonical = [
208 tag_is_well_formed_kernel(input.tags[0]) && fields[0].context == b4(0),
209 tag_is_well_formed_kernel(input.tags[1]) && fields[1].context == b4(1),
210 tag_is_well_formed_kernel(input.tags[2]) && fields[2].context == b4(2),
211 tag_is_well_formed_kernel(input.tags[3]) && fields[3].context == b4(3),
212 ];
213 let rejected = [
214 input.valid[0] && !canonical[0],
215 input.valid[1] && !canonical[1],
216 input.valid[2] && !canonical[2],
217 input.valid[3] && !canonical[3],
218 ];
219 let eligible = [
220 input.valid[0] && canonical[0],
221 input.valid[1] && canonical[1],
222 input.valid[2] && canonical[2],
223 input.valid[3] && canonical[3],
224 ];
225 let mut output = LaneIssueControlOutput {
226 rejected,
227 next_cursor: cursor,
228 ..LaneIssueControlOutput::default()
229 };
230 let mut scan = cursor;
231 for _offset in 0..LANE_LOCAL_CONTEXTS {
232 if !output.valid && eligible[scan] {
233 output.valid = true;
234 output.context = scan;
235 output.accepted[scan] = true;
236 output.next_cursor = scan + b2(1);
237 }
238 scan = scan + b2(1);
239 }
240 output
241}
242
243#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
245pub struct LaneIssueInput {
246 pub requests: [CompressionInput; LANE_LOCAL_CONTEXTS],
248}
249
250#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
252pub struct LaneIssueOutput {
253 pub lane: CompressionInput,
255 pub accepted: [bool; LANE_LOCAL_CONTEXTS],
257 pub rejected: [bool; LANE_LOCAL_CONTEXTS],
259 pub next_cursor: b2,
261}
262
263#[kernel]
265pub fn issue_lane_request_kernel(input: LaneIssueInput, cursor: b2) -> LaneIssueOutput {
266 let control = lane_issue_control_kernel(
267 LaneIssueControlInput {
268 valid: [
269 input.requests[0].valid,
270 input.requests[1].valid,
271 input.requests[2].valid,
272 input.requests[3].valid,
273 ],
274 tags: [
275 input.requests[0].tag,
276 input.requests[1].tag,
277 input.requests[2].tag,
278 input.requests[3].tag,
279 ],
280 },
281 cursor,
282 );
283 let mut lane = CompressionInput::default();
284 if control.valid {
285 lane = match control.context {
286 Bits::<2>(0) => input.requests[0],
287 Bits::<2>(1) => input.requests[1],
288 Bits::<2>(2) => input.requests[2],
289 _ => input.requests[3],
290 };
291 }
292 LaneIssueOutput {
293 lane,
294 accepted: control.accepted,
295 rejected: control.rejected,
296 next_cursor: control.next_cursor,
297 }
298}
299
300#[allow(clippy::struct_excessive_bools)] #[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
303pub struct LaneResponseRouteOutput {
304 pub contexts: [CompressionOutput; LANE_LOCAL_CONTEXTS],
306 pub accepted: bool,
308 pub context: b2,
310 pub malformed: bool,
312 pub out_of_range: bool,
314 pub rejected: bool,
316}
317
318#[kernel]
320pub fn route_lane_response_kernel(response: CompressionOutput) -> LaneResponseRouteOutput {
321 let fields = decode_tag_kernel(response.tag);
322 let canonical = tag_is_well_formed_kernel(response.tag);
323 let in_range = fields.context < b4(4);
324 let accepted = response.valid && canonical && in_range;
325 let malformed = response.valid && !canonical;
326 let out_of_range = response.valid && canonical && !in_range;
327 let mut contexts = [CompressionOutput::default(); LANE_LOCAL_CONTEXTS];
328 if accepted {
329 let local_context: b2 = fields.context.resize();
330 contexts[local_context] = response;
331 }
332 LaneResponseRouteOutput {
333 contexts,
334 accepted,
335 context: fields.context.resize(),
336 malformed,
337 out_of_range,
338 rejected: malformed || out_of_range,
339 }
340}
341
342#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
344pub struct LaneFramerContextInput {
345 pub result_valid: bool,
347 pub fault: bool,
349 pub result_read_valid: bool,
351 pub result_read_pair: b6,
353 pub signature_first: HashBytes,
355 pub signature_second: HashBytes,
357 pub public_seed: HashBytes,
359 pub public_key_hash: HashBytes,
361 pub job_id: b32,
363}
364
365#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
367pub struct LaneLocalResultFramerInput {
368 pub contexts: [LaneFramerContextInput; LANE_LOCAL_CONTEXTS],
370 pub output_ready: bool,
372 pub error_ready: bool,
374}
375
376#[allow(clippy::struct_excessive_bools)] #[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
379pub struct LaneLocalResultFramerOutput {
380 pub result_read_enable: [bool; LANE_LOCAL_CONTEXTS],
382 pub result_read_pair: [b6; LANE_LOCAL_CONTEXTS],
384 pub result_taken: [bool; LANE_LOCAL_CONTEXTS],
386 pub discarded: [bool; LANE_LOCAL_CONTEXTS],
388 pub error_valid: bool,
390 pub error_job_id: b32,
392 pub error_context: b2,
394 pub error_credit_release: bool,
396 pub output_valid: bool,
398 pub output: LaneLocalBeat,
400 pub frame_active: bool,
402 pub frame_context: b2,
404 pub output_credit_release: bool,
406 pub fault: bool,
408}
409
410#[allow(clippy::struct_excessive_bools)] #[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
413pub struct LaneLocalFramerControl {
414 pub cursor: b2,
416 pub frame_active: bool,
418 pub frame_context: b2,
420 pub next_fetch: b6,
422 pub read_inflight: bool,
424 pub read_pair: b6,
426 pub fifo_count: b2,
428 pub fault: bool,
430 pub error_valid: bool,
432 pub error_context: b2,
434 pub error_cursor: b2,
436 pub release_pending: bool,
438 pub release_context: b2,
440}
441
442#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
444pub struct LaneResultSelection {
445 pub valid: bool,
447 pub context: b2,
449 pub next_cursor: b2,
451}
452
453#[kernel]
455#[allow(clippy::assign_op_pattern)] pub fn select_lane_result_kernel(
457 eligible: [bool; LANE_LOCAL_CONTEXTS],
458 cursor: b2,
459) -> LaneResultSelection {
460 let mut selection = LaneResultSelection {
461 next_cursor: cursor,
462 ..LaneResultSelection::default()
463 };
464 let mut scan = cursor;
465 for _offset in 0..LANE_LOCAL_CONTEXTS {
466 if !selection.valid && eligible[scan] {
467 selection.valid = true;
468 selection.context = scan;
469 selection.next_cursor = scan + b2(1);
470 }
471 scan = scan + b2(1);
472 }
473 selection
474}
475
476#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
478pub struct LaneLocalResultFramer {
479 control: DFF<LaneLocalFramerControl>,
480 head: NoResetDff<LaneLocalBeat>,
481 tail: NoResetDff<LaneLocalBeat>,
482 error_job_id: NoResetDff<b32>,
483}
484
485impl Default for LaneLocalResultFramer {
486 fn default() -> Self {
487 Self {
488 control: DFF::new(LaneLocalFramerControl::default()),
489 head: NoResetDff::new(),
490 tail: NoResetDff::new(),
491 error_job_id: NoResetDff::new(),
492 }
493 }
494}
495
496impl SynchronousIO for LaneLocalResultFramer {
497 type I = LaneLocalResultFramerInput;
498 type O = LaneLocalResultFramerOutput;
499 type Kernel = lane_local_result_framer_kernel;
500}
501
502#[kernel]
504#[allow(clippy::assign_op_pattern)] pub fn lane_local_result_framer_kernel(
506 clock_reset: ClockReset,
507 input: LaneLocalResultFramerInput,
508 q: LaneLocalResultFramerQ,
509) -> (LaneLocalResultFramerOutput, LaneLocalResultFramerD) {
510 let resetting = clock_reset.reset.any();
511 let mut control = q.control;
512 let mut head = q.head;
513 let mut tail = q.tail;
514 let mut error_job_id = q.error_job_id;
515 let mut result_read_enable = [false; LANE_LOCAL_CONTEXTS];
516 let mut result_read_pair = [b6(0); LANE_LOCAL_CONTEXTS];
517 let mut result_taken = [false; LANE_LOCAL_CONTEXTS];
518 let mut discarded = [false; LANE_LOCAL_CONTEXTS];
519 let mut output_credit_release = false;
520 let mut error_credit_release = false;
521
522 if !resetting
523 && q.control.release_pending
524 && !input.contexts[q.control.release_context].result_valid
525 {
526 control.release_pending = false;
527 }
528
529 if !resetting && q.control.error_valid && input.error_ready {
533 result_taken[q.control.error_context] = true;
534 discarded[q.control.error_context] = true;
535 error_credit_release = true;
536 control.error_valid = false;
537 control.release_pending = true;
538 control.release_context = q.control.error_context;
539 }
540
541 let mut selected_frame_invalid = false;
542 let mut expected_return = false;
543 if !resetting && q.control.frame_active {
544 let selected = input.contexts[q.control.frame_context];
545 selected_frame_invalid = !selected.result_valid || selected.fault;
546 expected_return = q.control.read_inflight
547 && selected.result_read_valid
548 && selected.result_read_pair == q.control.read_pair;
549 }
550 let mut unexpected_return = false;
551 if !resetting {
552 for context in 0..LANE_LOCAL_CONTEXTS {
553 if input.contexts[context].result_read_valid
554 && (!q.control.frame_active
555 || q.control.frame_context != b2(context as u128)
556 || !q.control.read_inflight
557 || input.contexts[context].result_read_pair != q.control.read_pair)
558 {
559 unexpected_return = true;
560 }
561 }
562 }
563 let corrupt_fifo = q.control.fifo_count == b2(3);
564 let orphan_final = q.control.fifo_count != b2(0) && q.head.last && !q.control.frame_active;
565 let suppress_output =
566 selected_frame_invalid || unexpected_return || corrupt_fifo || orphan_final;
567 let output_valid = !resetting && q.control.fifo_count != b2(0) && !suppress_output;
568 let output_handshake = output_valid && input.output_ready;
569 if output_handshake {
570 if q.head.last {
571 if q.control.frame_active {
572 result_taken[q.control.frame_context] = true;
573 output_credit_release = true;
574 control.release_pending = true;
575 control.release_context = q.control.frame_context;
576 } else {
577 control.fault = true;
578 }
579 control.frame_active = false;
580 control.read_inflight = false;
581 control.next_fetch = b6(0);
582 control.fifo_count = b2(0);
583 } else if q.control.fifo_count == b2(2) {
584 head = q.tail;
585 control.fifo_count = b2(1);
586 } else if q.control.fifo_count == b2(1) {
587 control.fifo_count = b2(0);
588 } else {
589 control.fault = true;
590 control.fifo_count = b2(0);
591 }
592 } else if corrupt_fifo || orphan_final {
593 control.fault = true;
594 control.fifo_count = b2(0);
595 }
596
597 if !resetting && control.frame_active && selected_frame_invalid {
598 let selected = input.contexts[control.frame_context];
599 if selected.result_valid && !control.error_valid {
600 control.error_valid = true;
601 control.error_context = control.frame_context;
602 control.error_cursor = control.frame_context + b2(1);
603 error_job_id = selected.job_id;
604 }
605 control.frame_active = false;
606 control.read_inflight = false;
607 control.next_fetch = b6(0);
608 control.fifo_count = b2(0);
609 control.fault = true;
610 }
611
612 if !resetting && unexpected_return {
615 for context in 0..LANE_LOCAL_CONTEXTS {
616 if input.contexts[context].result_read_valid
617 && (!q.control.frame_active
618 || q.control.frame_context != b2(context as u128)
619 || !q.control.read_inflight
620 || input.contexts[context].result_read_pair != q.control.read_pair)
621 {
622 control.fault = true;
623 control.frame_active = false;
624 control.read_inflight = false;
625 control.next_fetch = b6(0);
626 control.fifo_count = b2(0);
627 if input.contexts[context].result_valid && !control.error_valid {
628 control.error_valid = true;
629 control.error_context = b2(context as u128);
630 control.error_cursor = b2(context as u128) + b2(1);
631 error_job_id = input.contexts[context].job_id;
632 }
633 }
634 }
635 }
636
637 if expected_return && control.frame_active {
638 let selected = input.contexts[control.frame_context];
639 let incoming = assemble_lane_local_beat_kernel(AssembleLaneLocalBeatInput {
640 beat: selected.result_read_pair,
641 signature_first: selected.signature_first,
642 signature_second: selected.signature_second,
643 public_seed: selected.public_seed,
644 public_key_hash: selected.public_key_hash,
645 job_id: selected.job_id,
646 });
647 control.read_inflight = false;
648 if control.fifo_count == b2(0) {
649 head = incoming;
650 control.fifo_count = b2(1);
651 } else if control.fifo_count == b2(1) {
652 tail = incoming;
653 control.fifo_count = b2(2);
654 } else {
655 control.fault = true;
656 control.frame_active = false;
657 control.fifo_count = b2(0);
658 if !control.error_valid {
659 control.error_valid = true;
660 control.error_context = control.frame_context;
661 control.error_cursor = control.frame_context + b2(1);
662 error_job_id = selected.job_id;
663 }
664 }
665 }
666
667 if !resetting
670 && control.frame_active
671 && !control.read_inflight
672 && control.next_fetch == b6(34)
673 && control.fifo_count < b2(2)
674 {
675 let selected = input.contexts[control.frame_context];
676 let incoming = assemble_lane_local_beat_kernel(AssembleLaneLocalBeatInput {
677 beat: b6(34),
678 signature_first: [b8(0); 32],
679 signature_second: [b8(0); 32],
680 public_seed: selected.public_seed,
681 public_key_hash: selected.public_key_hash,
682 job_id: selected.job_id,
683 });
684 if control.fifo_count == b2(0) {
685 head = incoming;
686 control.fifo_count = b2(1);
687 } else {
688 tail = incoming;
689 control.fifo_count = b2(2);
690 }
691 control.next_fetch = b6(35);
692 }
693
694 if !resetting
698 && control.frame_active
699 && !control.read_inflight
700 && control.next_fetch < b6(34)
701 && control.fifo_count < b2(2)
702 {
703 result_read_enable[control.frame_context] = true;
704 result_read_pair[control.frame_context] = control.next_fetch;
705 control.read_inflight = true;
706 control.read_pair = control.next_fetch;
707 control.next_fetch = control.next_fetch + b6(1);
708 }
709
710 if !resetting
714 && !control.frame_active
715 && control.fifo_count == b2(0)
716 && !control.error_valid
717 && !control.release_pending
718 && !control.fault
719 {
720 let faulted = [
721 input.contexts[0].result_valid && input.contexts[0].fault,
722 input.contexts[1].result_valid && input.contexts[1].fault,
723 input.contexts[2].result_valid && input.contexts[2].fault,
724 input.contexts[3].result_valid && input.contexts[3].fault,
725 ];
726 let error_selection = select_lane_result_kernel(faulted, control.error_cursor);
727 if error_selection.valid {
728 control.error_valid = true;
729 control.error_context = error_selection.context;
730 control.error_cursor = error_selection.next_cursor;
731 error_job_id = input.contexts[error_selection.context].job_id;
732 }
733 }
734
735 if !resetting
736 && !control.frame_active
737 && control.fifo_count == b2(0)
738 && !control.error_valid
739 && !control.release_pending
740 && !control.fault
741 {
742 let eligible = [
743 input.contexts[0].result_valid && !input.contexts[0].fault,
744 input.contexts[1].result_valid && !input.contexts[1].fault,
745 input.contexts[2].result_valid && !input.contexts[2].fault,
746 input.contexts[3].result_valid && !input.contexts[3].fault,
747 ];
748 let selection = select_lane_result_kernel(eligible, control.cursor);
749 if selection.valid {
750 control.frame_active = true;
751 control.frame_context = selection.context;
752 control.cursor = selection.next_cursor;
753 control.next_fetch = b6(1);
754 control.read_inflight = true;
755 control.read_pair = b6(0);
756 result_read_enable[selection.context] = true;
757 result_read_pair[selection.context] = b6(0);
758 }
759 }
760
761 if resetting {
762 control = LaneLocalFramerControl::default();
763 result_read_enable = [false; LANE_LOCAL_CONTEXTS];
764 result_read_pair = [b6(0); LANE_LOCAL_CONTEXTS];
765 result_taken = [false; LANE_LOCAL_CONTEXTS];
766 discarded = [false; LANE_LOCAL_CONTEXTS];
767 output_credit_release = false;
768 error_credit_release = false;
769 }
770
771 let output = LaneLocalResultFramerOutput {
772 result_read_enable,
773 result_read_pair,
774 result_taken,
775 discarded,
776 error_valid: !resetting && q.control.error_valid,
777 error_job_id: q.error_job_id,
778 error_context: q.control.error_context,
779 error_credit_release,
780 output_valid,
781 output: q.head,
782 frame_active: !resetting && q.control.frame_active,
783 frame_context: q.control.frame_context,
784 output_credit_release,
785 fault: !resetting && q.control.fault,
786 };
787 let d = LaneLocalResultFramerD {
788 control,
789 head,
790 tail,
791 error_job_id,
792 };
793 (output, d)
794}
795
796#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
798pub struct LaneLocalClusterInput {
799 pub start_valid: bool,
801 pub private_seed: HashBytes,
803 pub message: MessageBytes,
805 pub job_id: b32,
807 pub output_ready: bool,
809 pub error_ready: bool,
811}
812
813#[allow(clippy::struct_excessive_bools)] #[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
816pub struct LaneLocalClusterOutput {
817 pub start_ready: bool,
819 pub start_accepted: bool,
821 pub start_context: b2,
823 pub start_generation: b8,
825 pub context_active: [bool; LANE_LOCAL_CONTEXTS],
827 pub context_credit_reserved: [bool; LANE_LOCAL_CONTEXTS],
829 pub context_result_valid: [bool; LANE_LOCAL_CONTEXTS],
831 pub context_fault: [bool; LANE_LOCAL_CONTEXTS],
833 pub request_accepted: [bool; LANE_LOCAL_CONTEXTS],
835 pub lane_response_rejected: bool,
837 pub discarded: [bool; LANE_LOCAL_CONTEXTS],
839 pub error_valid: bool,
841 pub error_job_id: b32,
843 pub error_context: b2,
845 pub error_credit_release: bool,
847 pub output_valid: bool,
849 pub output: LaneLocalBeat,
851 pub output_credit_release: bool,
853 pub fault: bool,
855}
856
857#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
859pub struct LaneLocalClusterControl {
860 pub allocation_cursor: b2,
862 pub issue_cursor: b2,
864 pub fault: bool,
866}
867
868#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
870pub struct LaneStartSelection {
871 pub valid: bool,
873 pub context: b2,
875 pub next_cursor: b2,
877}
878
879#[kernel]
881#[allow(clippy::assign_op_pattern)] pub fn select_lane_start_kernel(
883 ready: [bool; LANE_LOCAL_CONTEXTS],
884 cursor: b2,
885) -> LaneStartSelection {
886 let mut selection = LaneStartSelection {
887 next_cursor: cursor,
888 ..LaneStartSelection::default()
889 };
890 let mut scan = cursor;
891 for _offset in 0..LANE_LOCAL_CONTEXTS {
892 if !selection.valid && ready[scan] {
893 selection.valid = true;
894 selection.context = scan;
895 selection.next_cursor = scan + b2(1);
896 }
897 scan = scan + b2(1);
898 }
899 selection
900}
901
902#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
911pub struct LaneLocalCluster<
912 const ROUNDS: usize,
913 const ADDRESS_BITS: usize,
914 L = InlineCompressionLane<ROUNDS, ADDRESS_BITS>,
915> where
916 rhdl::bits::W<ROUNDS>: BitWidth,
917 rhdl::bits::W<ADDRESS_BITS>: BitWidth,
918 L: CompressionLaneComponent,
919{
920 contexts: ReplicatedSynchronous<SingleContextWorkEngine, LANE_LOCAL_CONTEXTS>,
921 lane: L,
922 framer: LaneLocalResultFramer,
923 control: DFF<LaneLocalClusterControl>,
924 generations: DFF<[b8; LANE_LOCAL_CONTEXTS]>,
925 job_ids: NoResetDff<[b32; LANE_LOCAL_CONTEXTS]>,
926}
927
928impl<const ROUNDS: usize, const ADDRESS_BITS: usize, L> Default
929 for LaneLocalCluster<ROUNDS, ADDRESS_BITS, L>
930where
931 rhdl::bits::W<ROUNDS>: BitWidth,
932 rhdl::bits::W<ADDRESS_BITS>: BitWidth,
933 L: CompressionLaneComponent + Default,
934{
935 fn default() -> Self {
936 Self {
937 contexts: ReplicatedSynchronous::new(SingleContextWorkEngine::default()),
938 lane: L::default(),
939 framer: LaneLocalResultFramer::default(),
940 control: DFF::new(LaneLocalClusterControl::default()),
941 generations: DFF::new([b8(0); LANE_LOCAL_CONTEXTS]),
942 job_ids: NoResetDff::new(),
943 }
944 }
945}
946
947impl<const ROUNDS: usize, const ADDRESS_BITS: usize, L> SynchronousIO
948 for LaneLocalCluster<ROUNDS, ADDRESS_BITS, L>
949where
950 rhdl::bits::W<ROUNDS>: BitWidth,
951 rhdl::bits::W<ADDRESS_BITS>: BitWidth,
952 L: CompressionLaneComponent,
953{
954 type I = LaneLocalClusterInput;
955 type O = LaneLocalClusterOutput;
956 type Kernel = lane_local_cluster_with_lane_kernel<ROUNDS, ADDRESS_BITS, L>;
957}
958
959#[kernel]
961#[allow(clippy::needless_range_loop)] pub fn lane_local_cluster_with_lane_kernel<
963 const ROUNDS: usize,
964 const ADDRESS_BITS: usize,
965 L: CompressionLaneComponent,
966>(
967 clock_reset: ClockReset,
968 input: LaneLocalClusterInput,
969 q: LaneLocalClusterQ<ROUNDS, ADDRESS_BITS, L>,
970) -> (
971 LaneLocalClusterOutput,
972 LaneLocalClusterD<ROUNDS, ADDRESS_BITS, L>,
973)
974where
975 rhdl::bits::W<ROUNDS>: BitWidth,
976 rhdl::bits::W<ADDRESS_BITS>: BitWidth,
977{
978 let resetting = clock_reset.reset.any();
979 let ready = [
983 !q.contexts[0].active
984 && !q.contexts[0].result_valid
985 && !q.contexts[0].output_credit_reserved,
986 !q.contexts[1].active
987 && !q.contexts[1].result_valid
988 && !q.contexts[1].output_credit_reserved,
989 !q.contexts[2].active
990 && !q.contexts[2].result_valid
991 && !q.contexts[2].output_credit_reserved,
992 !q.contexts[3].active
993 && !q.contexts[3].result_valid
994 && !q.contexts[3].output_credit_reserved,
995 ];
996 let start_selection = select_lane_start_kernel(ready, q.control.allocation_cursor);
997 let start_ready = !resetting && start_selection.valid;
998 let start_accepted = start_ready && input.start_valid;
999
1000 let issue = issue_lane_request_kernel(
1001 LaneIssueInput {
1002 requests: [
1003 q.contexts[0].request,
1004 q.contexts[1].request,
1005 q.contexts[2].request,
1006 q.contexts[3].request,
1007 ],
1008 },
1009 q.control.issue_cursor,
1010 );
1011 let returns = route_lane_response_kernel(q.lane);
1012 let mut lane_input = issue.lane;
1013 if resetting {
1014 lane_input.valid = false;
1015 }
1016
1017 let mut control = q.control;
1018 let mut generations = q.generations;
1019 let mut job_ids = q.job_ids;
1020 if start_accepted {
1021 control.allocation_cursor = start_selection.next_cursor;
1022 generations[start_selection.context] = q.generations[start_selection.context] + b8(1);
1023 job_ids[start_selection.context] = input.job_id;
1024 }
1025 if issue.lane.valid {
1026 control.issue_cursor = issue.next_cursor;
1027 }
1028
1029 let request_rejected =
1030 issue.rejected[0] || issue.rejected[1] || issue.rejected[2] || issue.rejected[3];
1031 let context_fault =
1032 q.contexts[0].fault || q.contexts[1].fault || q.contexts[2].fault || q.contexts[3].fault;
1033 let response_rejected = q.contexts[0].response_rejected
1034 || q.contexts[1].response_rejected
1035 || q.contexts[2].response_rejected
1036 || q.contexts[3].response_rejected;
1037 if !resetting
1038 && (request_rejected
1039 || returns.rejected
1040 || context_fault
1041 || response_rejected
1042 || q.framer.fault)
1043 {
1044 control.fault = true;
1045 }
1046 if resetting {
1047 control = LaneLocalClusterControl::default();
1048 generations = [b8(0); LANE_LOCAL_CONTEXTS];
1049 }
1050
1051 let mut context_inputs = [ContextEngineInput::default(); LANE_LOCAL_CONTEXTS];
1052 for context in 0..LANE_LOCAL_CONTEXTS {
1053 context_inputs[context].context = b4(context as u128);
1054 context_inputs[context].context_enabled = true;
1055 context_inputs[context].generation = q.generations[context];
1056 context_inputs[context].output_credit_available = true;
1057 context_inputs[context].request_ready = !resetting && issue.accepted[context];
1058 context_inputs[context].response = if resetting {
1059 CompressionOutput::default()
1060 } else {
1061 returns.contexts[context]
1062 };
1063 context_inputs[context].result_taken = !resetting && q.framer.result_taken[context];
1064 context_inputs[context].result_read_enable =
1065 !resetting && q.framer.result_read_enable[context];
1066 context_inputs[context].result_read_pair = q.framer.result_read_pair[context];
1067 }
1068 if start_accepted {
1069 context_inputs[start_selection.context].start_valid = true;
1070 context_inputs[start_selection.context].private_seed = input.private_seed;
1071 context_inputs[start_selection.context].message = input.message;
1072 context_inputs[start_selection.context].generation = q.generations[start_selection.context];
1073 }
1074
1075 let mut framer_contexts = [LaneFramerContextInput::default(); LANE_LOCAL_CONTEXTS];
1076 for context in 0..LANE_LOCAL_CONTEXTS {
1077 framer_contexts[context] = LaneFramerContextInput {
1078 result_valid: q.contexts[context].result_valid,
1079 fault: q.contexts[context].fault,
1080 result_read_valid: q.contexts[context].result_read_valid,
1081 result_read_pair: q.contexts[context].result_read_pair,
1082 signature_first: q.contexts[context].signature_first,
1083 signature_second: q.contexts[context].signature_second,
1084 public_seed: q.contexts[context].public_seed,
1085 public_key_hash: q.contexts[context].public_key_hash,
1086 job_id: q.job_ids[context],
1087 };
1088 }
1089
1090 let output = LaneLocalClusterOutput {
1091 start_ready,
1092 start_accepted,
1093 start_context: start_selection.context,
1094 start_generation: q.generations[start_selection.context],
1095 context_active: [
1096 !resetting && q.contexts[0].active,
1097 !resetting && q.contexts[1].active,
1098 !resetting && q.contexts[2].active,
1099 !resetting && q.contexts[3].active,
1100 ],
1101 context_credit_reserved: [
1102 !resetting && q.contexts[0].output_credit_reserved,
1103 !resetting && q.contexts[1].output_credit_reserved,
1104 !resetting && q.contexts[2].output_credit_reserved,
1105 !resetting && q.contexts[3].output_credit_reserved,
1106 ],
1107 context_result_valid: [
1108 !resetting && q.contexts[0].result_valid,
1109 !resetting && q.contexts[1].result_valid,
1110 !resetting && q.contexts[2].result_valid,
1111 !resetting && q.contexts[3].result_valid,
1112 ],
1113 context_fault: [
1114 !resetting && q.contexts[0].fault,
1115 !resetting && q.contexts[1].fault,
1116 !resetting && q.contexts[2].fault,
1117 !resetting && q.contexts[3].fault,
1118 ],
1119 request_accepted: if resetting {
1120 [false; LANE_LOCAL_CONTEXTS]
1121 } else {
1122 issue.accepted
1123 },
1124 lane_response_rejected: !resetting && returns.rejected,
1125 discarded: q.framer.discarded,
1126 error_valid: !resetting && q.framer.error_valid,
1127 error_job_id: q.framer.error_job_id,
1128 error_context: q.framer.error_context,
1129 error_credit_release: !resetting && q.framer.error_credit_release,
1130 output_valid: !resetting && q.framer.output_valid,
1131 output: q.framer.output,
1132 output_credit_release: !resetting && q.framer.output_credit_release,
1133 fault: !resetting && q.control.fault,
1134 };
1135 let d = LaneLocalClusterD::<ROUNDS, ADDRESS_BITS, L> {
1136 contexts: context_inputs,
1137 lane: lane_input,
1138 framer: LaneLocalResultFramerInput {
1139 contexts: framer_contexts,
1140 output_ready: input.output_ready,
1141 error_ready: input.error_ready,
1142 },
1143 control,
1144 generations,
1145 job_ids,
1146 };
1147 (output, d)
1148}
1149
1150#[kernel]
1156pub fn lane_local_cluster_kernel<const ROUNDS: usize, const ADDRESS_BITS: usize>(
1157 clock_reset: ClockReset,
1158 input: LaneLocalClusterInput,
1159 q: LaneLocalClusterQ<ROUNDS, ADDRESS_BITS>,
1160) -> (
1161 LaneLocalClusterOutput,
1162 LaneLocalClusterD<ROUNDS, ADDRESS_BITS>,
1163)
1164where
1165 rhdl::bits::W<ROUNDS>: BitWidth,
1166 rhdl::bits::W<ADDRESS_BITS>: BitWidth,
1167{
1168 lane_local_cluster_with_lane_kernel::<
1169 ROUNDS,
1170 ADDRESS_BITS,
1171 InlineCompressionLane<ROUNDS, ADDRESS_BITS>,
1172 >(clock_reset, input, q)
1173}
1174
1175pub type Sha256LaneLocalCluster = LaneLocalCluster<64, 6>;
1177
1178pub type ModularSha256LaneLocalCluster = LaneLocalCluster<64, 6, ExternalFullCompressionLane>;