1use rhdl::{core::ReplicatedSynchronous, prelude::*};
52use rhdl_fpga::core::dff::DFF;
53use rhdl_primitives::NoResetDff;
54use sha256_rhdl::lane::{
55 CompressionInput, CompressionOutput, ExternalFullCompressionLane, InlineCompressionLane,
56};
57
58use crate::{
59 cluster::CompressionLaneComponent,
60 verify::{
61 framing::VerifyStreamBeat,
62 sha::{
63 VERIFY_ERROR_NONE, VERIFY_ERROR_TRANSPORT, VerifyContextInput, VerifySingleContextSha,
64 },
65 transport::{
66 VERIFY_CLUSTER_CONTEXTS, VerifyTransport, VerifyTransportInput, VerifyTransportOutput,
67 },
68 },
69};
70
71pub const VERIFY_DIAGNOSTIC_CHILD_RESPONSE_REJECT: u128 = 1 << 5;
73pub const VERIFY_DIAGNOSTIC_CHILD_FAULT: u128 = 1 << 6;
75pub const VERIFY_DIAGNOSTIC_OWNERSHIP: u128 = 1 << 7;
77pub const VERIFY_DIAGNOSTIC_EXTERNAL_ABORT: u128 = 1 << 8;
79
80const _: () = {
81 assert!(VERIFY_CLUSTER_CONTEXTS == 4);
82};
83
84#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
86pub struct VerifyResult {
87 pub job_id: b32,
89 pub verified: bool,
91 pub error: b3,
93}
94
95#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
97pub struct VerifyClusterInput {
98 pub stream_valid: bool,
100 pub stream: VerifyStreamBeat,
102 pub result_ready: bool,
104 pub external_abort: bool,
113}
114
115#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
117pub struct VerifyClusterCoreInput {
118 pub stream_valid: bool,
120 pub stream: VerifyStreamBeat,
122 pub result_ready: bool,
124 pub lane_response: CompressionOutput,
126 pub external_abort: bool,
129}
130
131#[allow(clippy::struct_excessive_bools)] #[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
134pub struct VerifyClusterOutput {
135 pub stream_ready: bool,
137 pub stream_accepted: bool,
139 pub stream_context: b2,
141 pub stream_generation: b8,
143 pub frame_active: bool,
145 pub frame_beat: b6,
147 pub frame_job_id: b32,
149 pub lane_request: CompressionInput,
151 pub request_accepted: [bool; VERIFY_CLUSTER_CONTEXTS],
153 pub request_rejected: [bool; VERIFY_CLUSTER_CONTEXTS],
155 pub response_routed: [bool; VERIFY_CLUSTER_CONTEXTS],
161 pub context_response_ready: [bool; VERIFY_CLUSTER_CONTEXTS],
163 pub context_response_rejected: [bool; VERIFY_CLUSTER_CONTEXTS],
165 pub context_loading: [bool; VERIFY_CLUSTER_CONTEXTS],
167 pub context_active: [bool; VERIFY_CLUSTER_CONTEXTS],
169 pub context_result_valid: [bool; VERIFY_CLUSTER_CONTEXTS],
171 pub context_fault: [bool; VERIFY_CLUSTER_CONTEXTS],
173 pub context_total_issued: [b11; VERIFY_CLUSTER_CONTEXTS],
175 pub context_total_retired: [b11; VERIFY_CLUSTER_CONTEXTS],
177 pub result_valid: bool,
179 pub result: VerifyResult,
182 pub result_transfer: bool,
185 pub fault: bool,
191 pub diagnostics: b16,
193}
194
195#[allow(clippy::struct_excessive_bools)] #[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
198pub struct VerifyClusterControl {
199 pub allocation_cursor: b2,
201 pub frame_active: bool,
203 pub frame_owner: b2,
205 pub frame_beat: b6,
207 pub result_cursor: b2,
209 pub result_valid: bool,
211 pub poison: bool,
213 pub diagnostics: b16,
215}
216
217#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
219pub struct VerifyClusterSelection {
220 pub valid: bool,
222 pub context: b2,
224 pub next_cursor: b2,
226}
227
228#[allow(clippy::struct_excessive_bools)] #[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
231pub struct VerifyClusterTransportGateInput {
232 pub transport: VerifyTransportOutput,
234 pub resetting: bool,
236 pub poisoned: bool,
238 pub independent_fatal: bool,
240}
241
242#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
244pub struct VerifyClusterTransportGateOutput {
245 pub lane_request: CompressionInput,
247 pub request_accepted: [bool; VERIFY_CLUSTER_CONTEXTS],
249 pub responses: [CompressionOutput; VERIFY_CLUSTER_CONTEXTS],
251 pub response_routed: [bool; VERIFY_CLUSTER_CONTEXTS],
253}
254
255#[kernel]
261pub fn verify_cluster_transport_gate_kernel(
262 input: VerifyClusterTransportGateInput,
263) -> VerifyClusterTransportGateOutput {
264 let response_delivery_enabled = !input.resetting && !input.poisoned && !input.independent_fatal;
265 VerifyClusterTransportGateOutput {
266 lane_request: if response_delivery_enabled {
267 input.transport.lane_request
268 } else {
269 CompressionInput::default()
270 },
271 request_accepted: if response_delivery_enabled {
272 input.transport.request_accepted
273 } else {
274 [false; VERIFY_CLUSTER_CONTEXTS]
275 },
276 responses: if response_delivery_enabled {
277 input.transport.responses
278 } else {
279 [CompressionOutput::default(); VERIFY_CLUSTER_CONTEXTS]
280 },
281 response_routed: if response_delivery_enabled {
282 input.transport.response_routed
283 } else {
284 [false; VERIFY_CLUSTER_CONTEXTS]
285 },
286 }
287}
288
289#[kernel]
291#[allow(clippy::assign_op_pattern)] pub fn select_verify_cluster_context_kernel(
293 eligible: [bool; VERIFY_CLUSTER_CONTEXTS],
294 cursor: b2,
295) -> VerifyClusterSelection {
296 let mut selection = VerifyClusterSelection {
297 next_cursor: cursor,
298 ..VerifyClusterSelection::default()
299 };
300 let mut scan = cursor;
301 for _offset in 0..VERIFY_CLUSTER_CONTEXTS {
302 if !selection.valid && eligible[scan] {
303 selection.valid = true;
304 selection.context = scan;
305 selection.next_cursor = scan + b2(1);
306 }
307 scan = scan + b2(1);
308 }
309 selection
310}
311
312#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
314pub struct VerifyClusterCore {
315 contexts: ReplicatedSynchronous<VerifySingleContextSha, VERIFY_CLUSTER_CONTEXTS>,
316 transport: VerifyTransport,
317 control: DFF<VerifyClusterControl>,
318 assigned_generations: DFF<[b8; VERIFY_CLUSTER_CONTEXTS]>,
319 next_generations: DFF<[b8; VERIFY_CLUSTER_CONTEXTS]>,
320 frame_job_id: NoResetDff<b32>,
321 result: NoResetDff<VerifyResult>,
322}
323
324impl Default for VerifyClusterCore {
325 fn default() -> Self {
326 Self {
327 contexts: ReplicatedSynchronous::new(VerifySingleContextSha::default()),
328 transport: VerifyTransport::default(),
329 control: DFF::new(VerifyClusterControl::default()),
330 assigned_generations: DFF::new([b8(0); VERIFY_CLUSTER_CONTEXTS]),
331 next_generations: DFF::new([b8(0); VERIFY_CLUSTER_CONTEXTS]),
332 frame_job_id: NoResetDff::new(),
333 result: NoResetDff::new(),
334 }
335 }
336}
337
338impl SynchronousIO for VerifyClusterCore {
339 type I = VerifyClusterCoreInput;
340 type O = VerifyClusterOutput;
341 type Kernel = verify_cluster_core_kernel;
342}
343
344#[kernel]
346#[allow(clippy::assign_op_pattern, clippy::needless_range_loop)] pub fn verify_cluster_core_kernel(
348 clock_reset: ClockReset,
349 input: VerifyClusterCoreInput,
350 q: VerifyClusterCoreQ,
351) -> (VerifyClusterOutput, VerifyClusterCoreD) {
352 let resetting = clock_reset.reset.any();
353 let loading = [
354 q.contexts[0].loading,
355 q.contexts[1].loading,
356 q.contexts[2].loading,
357 q.contexts[3].loading,
358 ];
359 let active = [
360 q.contexts[0].active,
361 q.contexts[1].active,
362 q.contexts[2].active,
363 q.contexts[3].active,
364 ];
365 let child_results = [
366 q.contexts[0].result_valid,
367 q.contexts[1].result_valid,
368 q.contexts[2].result_valid,
369 q.contexts[3].result_valid,
370 ];
371 let child_faults = [
372 q.contexts[0].fault,
373 q.contexts[1].fault,
374 q.contexts[2].fault,
375 q.contexts[3].fault,
376 ];
377 let child_latched_faults = [
378 q.contexts[0].fault_latched,
379 q.contexts[1].fault_latched,
380 q.contexts[2].fault_latched,
381 q.contexts[3].fault_latched,
382 ];
383 let child_response_rejected = q.contexts[0].response_rejected
384 || q.contexts[1].response_rejected
385 || q.contexts[2].response_rejected
386 || q.contexts[3].response_rejected;
387 let child_fault = child_latched_faults[0]
388 || child_latched_faults[1]
389 || child_latched_faults[2]
390 || child_latched_faults[3];
391
392 let owner_loading = match q.control.frame_owner {
393 Bits::<2>(0) => loading[0] && !loading[1] && !loading[2] && !loading[3],
394 Bits::<2>(1) => !loading[0] && loading[1] && !loading[2] && !loading[3],
395 Bits::<2>(2) => !loading[0] && !loading[1] && loading[2] && !loading[3],
396 _ => !loading[0] && !loading[1] && !loading[2] && loading[3],
397 };
398 let any_loading = loading[0] || loading[1] || loading[2] || loading[3];
399 let ownership_fault = !q.control.poison
400 && ((q.control.frame_active && !owner_loading) || (!q.control.frame_active && any_loading));
401
402 let mut local_diagnostic_now = q.transport.diagnostics;
403 if child_response_rejected {
404 local_diagnostic_now = local_diagnostic_now | b16(VERIFY_DIAGNOSTIC_CHILD_RESPONSE_REJECT);
405 }
406 if child_fault {
407 local_diagnostic_now = local_diagnostic_now | b16(VERIFY_DIAGNOSTIC_CHILD_FAULT);
408 }
409 if ownership_fault {
410 local_diagnostic_now = local_diagnostic_now | b16(VERIFY_DIAGNOSTIC_OWNERSHIP);
411 }
412 let external_abort_transition = !resetting && !q.control.poison && input.external_abort;
413 let mut transition_diagnostic_now = local_diagnostic_now;
414 if external_abort_transition {
415 transition_diagnostic_now =
416 transition_diagnostic_now | b16(VERIFY_DIAGNOSTIC_EXTERNAL_ABORT);
417 }
418 let local_independent_fatal_now =
419 !resetting && !q.control.poison && (q.transport.fatal || child_fault || ownership_fault);
420 let local_fatal_now = local_independent_fatal_now || (!resetting && child_response_rejected);
421 let poison_transition_now = local_fatal_now || external_abort_transition;
422 let transport_gate = verify_cluster_transport_gate_kernel(VerifyClusterTransportGateInput {
430 transport: q.transport,
431 resetting,
432 poisoned: q.control.poison,
433 independent_fatal: local_independent_fatal_now,
434 });
435
436 let idle = [
443 !loading[0] && !active[0] && !child_results[0] && !child_latched_faults[0],
444 !loading[1] && !active[1] && !child_results[1] && !child_latched_faults[1],
445 !loading[2] && !active[2] && !child_results[2] && !child_latched_faults[2],
446 !loading[3] && !active[3] && !child_results[3] && !child_latched_faults[3],
447 ];
448 let allocation = select_verify_cluster_context_kernel(idle, q.control.allocation_cursor);
449 let selected_context = if q.control.frame_active {
450 q.control.frame_owner
451 } else {
452 allocation.context
453 };
454 let selected_generation = if q.control.frame_active {
455 match q.control.frame_owner {
456 Bits::<2>(0) => q.assigned_generations[0],
457 Bits::<2>(1) => q.assigned_generations[1],
458 Bits::<2>(2) => q.assigned_generations[2],
459 _ => q.assigned_generations[3],
460 }
461 } else {
462 match allocation.context {
463 Bits::<2>(0) => q.next_generations[0],
464 Bits::<2>(1) => q.next_generations[1],
465 Bits::<2>(2) => q.next_generations[2],
466 _ => q.next_generations[3],
467 }
468 };
469 let frame_ready = if q.control.frame_active {
470 owner_loading
471 } else {
472 allocation.valid
473 };
474 let stream_ready =
479 !resetting && !q.control.poison && !local_independent_fatal_now && frame_ready;
480 let stream_accepted = stream_ready && input.stream_valid;
481
482 let result_transfer =
483 !resetting && !local_fatal_now && q.control.result_valid && input.result_ready;
484 let result_slot_available = !q.control.result_valid || result_transfer;
485 let child_errors = [
486 q.contexts[0].error,
487 q.contexts[1].error,
488 q.contexts[2].error,
489 q.contexts[3].error,
490 ];
491 let child_result_committed = [
497 child_errors[0] != b3(VERIFY_ERROR_TRANSPORT) || child_latched_faults[0],
498 child_errors[1] != b3(VERIFY_ERROR_TRANSPORT) || child_latched_faults[1],
499 child_errors[2] != b3(VERIFY_ERROR_TRANSPORT) || child_latched_faults[2],
500 child_errors[3] != b3(VERIFY_ERROR_TRANSPORT) || child_latched_faults[3],
501 ];
502 let result_eligible = [
503 child_results[0]
504 && (!q.control.poison || child_errors[0] != b3(VERIFY_ERROR_NONE))
505 && child_result_committed[0],
506 child_results[1]
507 && (!q.control.poison || child_errors[1] != b3(VERIFY_ERROR_NONE))
508 && child_result_committed[1],
509 child_results[2]
510 && (!q.control.poison || child_errors[2] != b3(VERIFY_ERROR_NONE))
511 && child_result_committed[2],
512 child_results[3]
513 && (!q.control.poison || child_errors[3] != b3(VERIFY_ERROR_NONE))
514 && child_result_committed[3],
515 ];
516 let result_selection =
517 select_verify_cluster_context_kernel(result_eligible, q.control.result_cursor);
518 let capture_result =
519 !resetting && !local_fatal_now && result_slot_available && result_selection.valid;
520 let selected_result_job_id = match result_selection.context {
521 Bits::<2>(0) => q.contexts[0].result_job_id,
522 Bits::<2>(1) => q.contexts[1].result_job_id,
523 Bits::<2>(2) => q.contexts[2].result_job_id,
524 _ => q.contexts[3].result_job_id,
525 };
526 let selected_result_verified = match result_selection.context {
527 Bits::<2>(0) => q.contexts[0].verified,
528 Bits::<2>(1) => q.contexts[1].verified,
529 Bits::<2>(2) => q.contexts[2].verified,
530 _ => q.contexts[3].verified,
531 };
532 let selected_result_error = match result_selection.context {
533 Bits::<2>(0) => q.contexts[0].error,
534 Bits::<2>(1) => q.contexts[1].error,
535 Bits::<2>(2) => q.contexts[2].error,
536 _ => q.contexts[3].error,
537 };
538 let context_0_result_ready = capture_result && result_selection.context == b2(0);
539 let context_1_result_ready = capture_result && result_selection.context == b2(1);
540 let context_2_result_ready = capture_result && result_selection.context == b2(2);
541 let context_3_result_ready = capture_result && result_selection.context == b2(3);
542
543 let mut control = q.control;
544 let mut assigned_generations = q.assigned_generations;
545 let mut next_generations = q.next_generations;
546 let mut frame_job_id = q.frame_job_id;
547 let mut result = q.result;
548 let abort_clean_result = !resetting
549 && (local_fatal_now || q.control.poison)
550 && q.control.result_valid
551 && q.result.error == b3(VERIFY_ERROR_NONE);
552 let mut visible_result = q.result;
553
554 if abort_clean_result {
555 visible_result = VerifyResult {
556 job_id: q.result.job_id,
557 verified: false,
558 error: b3(VERIFY_ERROR_TRANSPORT),
559 };
560 result = visible_result;
561 }
562
563 if result_transfer {
564 control.result_valid = false;
565 }
566 if capture_result {
567 result = VerifyResult {
568 job_id: selected_result_job_id,
569 verified: selected_result_error == b3(VERIFY_ERROR_NONE) && selected_result_verified,
570 error: selected_result_error,
571 };
572 control.result_valid = true;
573 control.result_cursor = result_selection.next_cursor;
574 }
575
576 if stream_accepted {
577 if q.control.frame_active {
578 if q.control.frame_beat == b6(34) {
579 control.frame_active = false;
580 control.frame_beat = b6(0);
581 } else {
582 control.frame_beat = q.control.frame_beat + b6(1);
583 }
584 } else {
585 control.frame_active = true;
586 control.frame_owner = allocation.context;
587 control.frame_beat = b6(1);
588 control.allocation_cursor = allocation.next_cursor;
589 assigned_generations[allocation.context] = q.next_generations[allocation.context];
590 next_generations[allocation.context] = q.next_generations[allocation.context] + b8(1);
591 frame_job_id = input.stream.job_id;
592 }
593 }
594
595 if poison_transition_now {
596 control.poison = true;
597 control.frame_active = false;
598 control.frame_beat = b6(0);
599 control.diagnostics = q.control.diagnostics | transition_diagnostic_now;
600 }
601 if resetting {
602 control = VerifyClusterControl::default();
603 assigned_generations = [b8(0); VERIFY_CLUSTER_CONTEXTS];
604 next_generations = [b8(0); VERIFY_CLUSTER_CONTEXTS];
605 }
606
607 let context_0_stream_valid = stream_accepted && selected_context == b2(0);
612 let context_1_stream_valid = stream_accepted && selected_context == b2(1);
613 let context_2_stream_valid = stream_accepted && selected_context == b2(2);
614 let context_3_stream_valid = stream_accepted && selected_context == b2(3);
615 let context_0_generation = if context_0_stream_valid {
616 selected_generation
617 } else {
618 q.assigned_generations[0]
619 };
620 let context_1_generation = if context_1_stream_valid {
621 selected_generation
622 } else {
623 q.assigned_generations[1]
624 };
625 let context_2_generation = if context_2_stream_valid {
626 selected_generation
627 } else {
628 q.assigned_generations[2]
629 };
630 let context_3_generation = if context_3_stream_valid {
631 selected_generation
632 } else {
633 q.assigned_generations[3]
634 };
635 let context_transport_abort = !resetting && q.control.poison;
640 let context_inputs = [
641 VerifyContextInput {
642 stream_valid: context_0_stream_valid,
643 stream: input.stream,
644 context: b4(0),
645 context_enabled: true,
646 generation: context_0_generation,
647 request_ready: transport_gate.request_accepted[0],
648 response: transport_gate.responses[0],
649 result_ready: context_0_result_ready,
650 transport_abort: context_transport_abort,
651 },
652 VerifyContextInput {
653 stream_valid: context_1_stream_valid,
654 stream: input.stream,
655 context: b4(1),
656 context_enabled: true,
657 generation: context_1_generation,
658 request_ready: transport_gate.request_accepted[1],
659 response: transport_gate.responses[1],
660 result_ready: context_1_result_ready,
661 transport_abort: context_transport_abort,
662 },
663 VerifyContextInput {
664 stream_valid: context_2_stream_valid,
665 stream: input.stream,
666 context: b4(2),
667 context_enabled: true,
668 generation: context_2_generation,
669 request_ready: transport_gate.request_accepted[2],
670 response: transport_gate.responses[2],
671 result_ready: context_2_result_ready,
672 transport_abort: context_transport_abort,
673 },
674 VerifyContextInput {
675 stream_valid: context_3_stream_valid,
676 stream: input.stream,
677 context: b4(3),
678 context_enabled: true,
679 generation: context_3_generation,
680 request_ready: transport_gate.request_accepted[3],
681 response: transport_gate.responses[3],
682 result_ready: context_3_result_ready,
683 transport_abort: context_transport_abort,
684 },
685 ];
686
687 let output = VerifyClusterOutput {
688 stream_ready,
689 stream_accepted,
690 stream_context: selected_context,
691 stream_generation: selected_generation,
692 frame_active: !resetting && q.control.frame_active,
693 frame_beat: q.control.frame_beat,
694 frame_job_id: q.frame_job_id,
695 lane_request: transport_gate.lane_request,
696 request_accepted: transport_gate.request_accepted,
697 request_rejected: if resetting || q.control.poison {
698 [false; VERIFY_CLUSTER_CONTEXTS]
699 } else {
700 q.transport.request_rejected
701 },
702 response_routed: transport_gate.response_routed,
703 context_response_ready: if resetting {
704 [false; VERIFY_CLUSTER_CONTEXTS]
705 } else {
706 [
707 q.contexts[0].response_ready,
708 q.contexts[1].response_ready,
709 q.contexts[2].response_ready,
710 q.contexts[3].response_ready,
711 ]
712 },
713 context_response_rejected: if resetting {
714 [false; VERIFY_CLUSTER_CONTEXTS]
715 } else {
716 [
717 q.contexts[0].response_rejected,
718 q.contexts[1].response_rejected,
719 q.contexts[2].response_rejected,
720 q.contexts[3].response_rejected,
721 ]
722 },
723 context_loading: if resetting {
724 [false; VERIFY_CLUSTER_CONTEXTS]
725 } else {
726 loading
727 },
728 context_active: if resetting {
729 [false; VERIFY_CLUSTER_CONTEXTS]
730 } else {
731 active
732 },
733 context_result_valid: if resetting {
734 [false; VERIFY_CLUSTER_CONTEXTS]
735 } else {
736 child_results
737 },
738 context_fault: if resetting {
739 [false; VERIFY_CLUSTER_CONTEXTS]
740 } else {
741 child_faults
742 },
743 context_total_issued: if resetting {
744 [b11(0); VERIFY_CLUSTER_CONTEXTS]
745 } else {
746 [
747 q.contexts[0].total_issued,
748 q.contexts[1].total_issued,
749 q.contexts[2].total_issued,
750 q.contexts[3].total_issued,
751 ]
752 },
753 context_total_retired: if resetting {
754 [b11(0); VERIFY_CLUSTER_CONTEXTS]
755 } else {
756 [
757 q.contexts[0].total_retired,
758 q.contexts[1].total_retired,
759 q.contexts[2].total_retired,
760 q.contexts[3].total_retired,
761 ]
762 },
763 result_valid: !resetting && q.control.result_valid,
764 result: visible_result,
765 result_transfer,
766 fault: !resetting && (q.control.poison || local_fatal_now),
767 diagnostics: if resetting {
768 b16(0)
769 } else {
770 q.control.diagnostics | local_diagnostic_now
771 },
772 };
773 let d = VerifyClusterCoreD {
774 contexts: context_inputs,
775 transport: VerifyTransportInput {
776 requests: [
777 q.contexts[0].transport_request,
778 q.contexts[1].transport_request,
779 q.contexts[2].transport_request,
780 q.contexts[3].transport_request,
781 ],
782 assigned_generations: q.assigned_generations,
783 lane_response: input.lane_response,
784 abort: !resetting && q.control.poison,
788 },
789 control,
790 assigned_generations,
791 next_generations,
792 frame_job_id,
793 result,
794 };
795 (output, d)
796}
797
798#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
805pub struct VerifierCluster<L = InlineCompressionLane<64, 6>>
806where
807 L: CompressionLaneComponent,
808{
809 core: VerifyClusterCore,
810 lane: L,
811}
812
813impl<L> Default for VerifierCluster<L>
814where
815 L: CompressionLaneComponent + Default,
816{
817 fn default() -> Self {
818 Self {
819 core: VerifyClusterCore::default(),
820 lane: L::default(),
821 }
822 }
823}
824
825impl<L> SynchronousIO for VerifierCluster<L>
826where
827 L: CompressionLaneComponent,
828{
829 type I = VerifyClusterInput;
830 type O = VerifyClusterOutput;
831 type Kernel = verifier_cluster_with_lane_kernel<L>;
832}
833
834#[kernel]
836pub fn verifier_cluster_with_lane_kernel<L: CompressionLaneComponent>(
837 clock_reset: ClockReset,
838 input: VerifyClusterInput,
839 q: VerifierClusterQ<L>,
840) -> (VerifyClusterOutput, VerifierClusterD<L>) {
841 let _ = clock_reset;
842 let d = VerifierClusterD::<L> {
843 core: VerifyClusterCoreInput {
844 stream_valid: input.stream_valid,
845 stream: input.stream,
846 result_ready: input.result_ready,
847 lane_response: q.lane,
848 external_abort: input.external_abort,
849 },
850 lane: q.core.lane_request,
851 };
852 (q.core, d)
853}
854
855pub type Sha256VerifierCluster = VerifierCluster<InlineCompressionLane<64, 6>>;
857
858pub type ModularSha256VerifierCluster = VerifierCluster<ExternalFullCompressionLane>;
864
865#[kernel]
867pub fn sha256_verifier_cluster_kernel(
868 clock_reset: ClockReset,
869 input: VerifyClusterInput,
870 q: VerifierClusterQ<InlineCompressionLane<64, 6>>,
871) -> (
872 VerifyClusterOutput,
873 VerifierClusterD<InlineCompressionLane<64, 6>>,
874) {
875 verifier_cluster_with_lane_kernel::<InlineCompressionLane<64, 6>>(clock_reset, input, q)
876}
877
878#[cfg(test)]
879mod tests {
880 use super::*;
881 use crate::verify::sha::VerifyContextOutput;
882
883 fn rejection_q(frame_active: bool) -> VerifyClusterCoreQ {
884 let mut contexts = [VerifyContextOutput::default(); VERIFY_CLUSTER_CONTEXTS];
885 contexts[3].response_rejected = true;
886 contexts[3].fault = true;
887 if frame_active {
888 contexts[1].loading = true;
889 }
890 VerifyClusterCoreQ {
891 contexts,
892 transport: VerifyTransportOutput {
893 lane_request: CompressionInput {
894 tag: b32(0x1234_5678),
895 valid: true,
896 ..CompressionInput::default()
897 },
898 request_accepted: [true, false, false, false],
899 response_routed: [false, false, false, true],
900 ..VerifyTransportOutput::default()
901 },
902 control: VerifyClusterControl {
903 frame_active,
904 frame_owner: b2(1),
905 frame_beat: if frame_active { b6(7) } else { b6(0) },
906 result_valid: true,
907 ..VerifyClusterControl::default()
908 },
909 assigned_generations: [b8(0), b8(5), b8(0), b8(0)],
910 next_generations: [b8(0); VERIFY_CLUSTER_CONTEXTS],
911 frame_job_id: b32(0x6000_0001),
912 result: VerifyResult {
913 job_id: b32(0x5fff_0001),
914 verified: true,
915 error: b3(VERIFY_ERROR_NONE),
916 },
917 }
918 }
919
920 #[test]
921 #[allow(clippy::too_many_lines)] fn stream_result_and_response_fields_remain_independent_on_one_edge() {
923 let stream = VerifyStreamBeat {
924 job_id: b32(0x5757_0005),
925 ..VerifyStreamBeat::default()
926 };
927 let routed_response = CompressionOutput {
928 tag: b32(0x5858_0005),
929 valid: true,
930 ..CompressionOutput::default()
931 };
932 let mut contexts = [VerifyContextOutput::default(); VERIFY_CLUSTER_CONTEXTS];
933 contexts[1].loading = true;
934 contexts[2].result_valid = true;
935 contexts[2].result_job_id = b32(0x5959_0005);
936 contexts[2].verified = true;
937 contexts[2].error = b3(VERIFY_ERROR_NONE);
938 let q = VerifyClusterCoreQ {
939 contexts,
940 transport: VerifyTransportOutput {
941 responses: [
942 CompressionOutput::default(),
943 CompressionOutput::default(),
944 CompressionOutput::default(),
945 routed_response,
946 ],
947 response_routed: [false, false, false, true],
948 ..VerifyTransportOutput::default()
949 },
950 control: VerifyClusterControl {
951 frame_active: true,
952 frame_owner: b2(1),
953 frame_beat: b6(7),
954 result_cursor: b2(2),
955 ..VerifyClusterControl::default()
956 },
957 assigned_generations: [b8(1), b8(5), b8(9), b8(11)],
958 next_generations: [b8(2), b8(6), b8(10), b8(12)],
959 frame_job_id: stream.job_id,
960 result: VerifyResult::default(),
961 };
962 let input = VerifyClusterCoreInput {
963 stream_valid: true,
964 stream,
965 result_ready: false,
966 ..VerifyClusterCoreInput::default()
967 };
968
969 let (output, d) =
970 verify_cluster_core_kernel(clock_reset(clock(false), reset(false)), input, q);
971 assert!(output.stream_accepted);
972 assert_eq!(output.stream_context, b2(1));
973 assert!(d.contexts[1].stream_valid);
974 assert_eq!(d.contexts[1].stream, stream);
975 assert_eq!(d.contexts[1].generation, b8(5));
976 assert!(!d.contexts[0].stream_valid);
977 assert!(!d.contexts[2].stream_valid);
978 assert!(!d.contexts[3].stream_valid);
979 assert!(d.contexts[2].result_ready);
980 assert!(!d.contexts[0].result_ready);
981 assert!(!d.contexts[1].result_ready);
982 assert!(!d.contexts[3].result_ready);
983 assert_eq!(d.contexts[3].response, routed_response);
984 assert!(d.control.result_valid);
985 assert_eq!(d.result.job_id, b32(0x5959_0005));
986 assert!(d.result.verified);
987 assert_eq!(d.result.error, b3(VERIFY_ERROR_NONE));
988
989 let (_, no_stream_d) = verify_cluster_core_kernel(
990 clock_reset(clock(false), reset(false)),
991 VerifyClusterCoreInput {
992 stream_valid: false,
993 ..input
994 },
995 q,
996 );
997 for context in no_stream_d.contexts {
998 assert!(!context.stream_valid);
999 }
1000 assert!(no_stream_d.contexts[2].result_ready);
1001 assert_eq!(no_stream_d.contexts[3].response, routed_response);
1002
1003 let mut rejection_q = q;
1004 rejection_q.contexts[3].response_rejected = true;
1005 rejection_q.contexts[3].fault = true;
1006 let (faulted, faulted_d) =
1007 verify_cluster_core_kernel(clock_reset(clock(false), reset(false)), input, rejection_q);
1008 assert!(faulted.fault);
1009 assert!(faulted.stream_accepted);
1010 for (faulted_context, clean_context) in faulted_d.contexts.iter().zip(d.contexts.iter()) {
1011 assert_eq!(faulted_context.stream_valid, clean_context.stream_valid);
1012 assert_eq!(faulted_context.stream, stream);
1013 assert!(!faulted_context.result_ready);
1014 }
1015 assert_eq!(faulted_d.contexts[3].response, routed_response);
1016 }
1017
1018 fn assert_containment_edge(frame_active: bool, expected_context: b2) -> VerifyClusterCoreD {
1019 let stream = VerifyStreamBeat {
1020 job_id: if frame_active {
1021 b32(0x6000_0001)
1022 } else {
1023 b32(0x6000_0002)
1024 },
1025 ..VerifyStreamBeat::default()
1026 };
1027 let (output, d) = verify_cluster_core_kernel(
1028 clock_reset(clock(false), reset(false)),
1029 VerifyClusterCoreInput {
1030 stream_valid: true,
1031 stream,
1032 result_ready: true,
1033 ..VerifyClusterCoreInput::default()
1034 },
1035 rejection_q(frame_active),
1036 );
1037
1038 assert!(output.fault);
1041 assert!(output.stream_ready);
1042 assert!(output.stream_accepted);
1043 assert_eq!(output.stream_context, expected_context);
1044 assert!(!output.result_transfer);
1045 assert!(output.result_valid);
1046 assert_eq!(output.result.job_id, b32(0x5fff_0001));
1047 assert!(!output.result.verified);
1048 assert_eq!(output.result.error, b3(VERIFY_ERROR_TRANSPORT));
1049 assert_ne!(
1050 output.diagnostics & b16(VERIFY_DIAGNOSTIC_CHILD_RESPONSE_REJECT),
1051 b16(0)
1052 );
1053
1054 assert!(d.contexts[expected_context].stream_valid);
1056 assert_eq!(d.contexts[expected_context].stream, stream);
1057 for context in 0..VERIFY_CLUSTER_CONTEXTS {
1058 if b2(context as u128) != expected_context {
1059 assert!(!d.contexts[context].stream_valid);
1060 }
1061 }
1062 assert!(d.control.poison);
1063 assert!(!d.control.frame_active);
1064 assert_eq!(d.control.frame_beat, b6(0));
1065 assert_eq!(d.frame_job_id, stream.job_id);
1066
1067 assert!(output.lane_request.valid);
1071 assert_eq!(output.request_accepted, [true, false, false, false]);
1072 d
1073 }
1074
1075 #[test]
1076 fn child_rejection_containment_edge_accepts_beat_zero_and_midframe_then_poison_blocks() {
1077 let beat_zero_d = assert_containment_edge(false, b2(0));
1078 assert_eq!(beat_zero_d.assigned_generations[0], b8(0));
1079 assert_eq!(beat_zero_d.next_generations[0], b8(1));
1080
1081 let midframe_d = assert_containment_edge(true, b2(1));
1082 assert_eq!(midframe_d.frame_job_id, b32(0x6000_0001));
1083
1084 let (poisoned, poisoned_d) = verify_cluster_core_kernel(
1085 clock_reset(clock(false), reset(false)),
1086 VerifyClusterCoreInput {
1087 stream_valid: true,
1088 stream: VerifyStreamBeat {
1089 job_id: b32(0x6000_0002),
1090 ..VerifyStreamBeat::default()
1091 },
1092 ..VerifyClusterCoreInput::default()
1093 },
1094 VerifyClusterCoreQ {
1095 contexts: [VerifyContextOutput::default(); VERIFY_CLUSTER_CONTEXTS],
1096 transport: VerifyTransportOutput {
1097 lane_request: CompressionInput {
1098 valid: true,
1099 ..CompressionInput::default()
1100 },
1101 request_accepted: [true, false, false, false],
1102 ..VerifyTransportOutput::default()
1103 },
1104 control: beat_zero_d.control,
1105 assigned_generations: beat_zero_d.assigned_generations,
1106 next_generations: beat_zero_d.next_generations,
1107 frame_job_id: beat_zero_d.frame_job_id,
1108 result: beat_zero_d.result,
1109 },
1110 );
1111 assert!(poisoned.fault);
1112 assert!(!poisoned.stream_ready);
1113 assert!(!poisoned.stream_accepted);
1114 assert!(!poisoned.lane_request.valid);
1115 assert_eq!(poisoned.request_accepted, [false; VERIFY_CLUSTER_CONTEXTS]);
1116 for context in 0..VERIFY_CLUSTER_CONTEXTS {
1117 assert!(poisoned_d.contexts[context].transport_abort);
1118 assert!(!poisoned_d.contexts[context].stream_valid);
1119 }
1120 }
1121
1122 #[test]
1123 #[allow(clippy::too_many_lines)] fn external_abort_is_transition_only_until_registered_poison_contains() {
1125 let clean = VerifyResult {
1126 job_id: b32(0x7000_0007),
1127 verified: true,
1128 error: b3(VERIFY_ERROR_NONE),
1129 };
1130 let q = VerifyClusterCoreQ {
1131 contexts: [VerifyContextOutput::default(); VERIFY_CLUSTER_CONTEXTS],
1132 transport: VerifyTransportOutput {
1133 lane_request: CompressionInput {
1134 valid: true,
1135 ..CompressionInput::default()
1136 },
1137 request_accepted: [true, false, false, false],
1138 response_routed: [false, true, false, false],
1139 responses: [
1140 CompressionOutput::default(),
1141 CompressionOutput {
1142 valid: true,
1143 ..CompressionOutput::default()
1144 },
1145 CompressionOutput::default(),
1146 CompressionOutput::default(),
1147 ],
1148 ..VerifyTransportOutput::default()
1149 },
1150 control: VerifyClusterControl {
1151 result_valid: true,
1152 ..VerifyClusterControl::default()
1153 },
1154 assigned_generations: [b8(0); VERIFY_CLUSTER_CONTEXTS],
1155 next_generations: [b8(0); VERIFY_CLUSTER_CONTEXTS],
1156 frame_job_id: b32(0),
1157 result: clean,
1158 };
1159 let parent_quiescent = VerifyClusterCoreInput {
1160 stream_valid: false,
1161 result_ready: false,
1162 ..VerifyClusterCoreInput::default()
1163 };
1164 let (baseline, _) = verify_cluster_core_kernel(
1165 clock_reset(clock(false), reset(false)),
1166 parent_quiescent,
1167 q,
1168 );
1169 let (command_edge, d) = verify_cluster_core_kernel(
1170 clock_reset(clock(false), reset(false)),
1171 VerifyClusterCoreInput {
1172 external_abort: true,
1173 ..parent_quiescent
1174 },
1175 q,
1176 );
1177
1178 assert_eq!(command_edge, baseline);
1182 assert!(!command_edge.fault);
1183 assert!(command_edge.stream_ready);
1184 assert!(!command_edge.stream_accepted);
1185 assert!(command_edge.lane_request.valid);
1186 assert_eq!(command_edge.request_accepted, [true, false, false, false]);
1187 assert!(!command_edge.result_transfer);
1188 assert!(command_edge.result_valid);
1189 assert_eq!(command_edge.result.job_id, clean.job_id);
1190 assert!(command_edge.result.verified);
1191 assert_eq!(command_edge.result.error, b3(VERIFY_ERROR_NONE));
1192 assert_eq!(
1193 command_edge.diagnostics & b16(VERIFY_DIAGNOSTIC_EXTERNAL_ABORT),
1194 b16(0)
1195 );
1196 assert!(d.control.poison);
1197 assert_ne!(
1198 d.control.diagnostics & b16(VERIFY_DIAGNOSTIC_EXTERNAL_ABORT),
1199 b16(0)
1200 );
1201 assert_eq!(d.result, clean);
1202
1203 let (registered, registered_d) = verify_cluster_core_kernel(
1204 clock_reset(clock(false), reset(false)),
1205 VerifyClusterCoreInput {
1206 stream_valid: true,
1207 result_ready: false,
1208 external_abort: true,
1209 ..VerifyClusterCoreInput::default()
1210 },
1211 VerifyClusterCoreQ {
1212 contexts: [VerifyContextOutput::default(); VERIFY_CLUSTER_CONTEXTS],
1213 transport: q.transport,
1214 control: d.control,
1215 assigned_generations: d.assigned_generations,
1216 next_generations: d.next_generations,
1217 frame_job_id: d.frame_job_id,
1218 result: d.result,
1219 },
1220 );
1221 assert!(registered.fault);
1222 assert!(!registered.stream_ready);
1223 assert!(!registered.stream_accepted);
1224 assert!(!registered.lane_request.valid);
1225 assert_eq!(
1226 registered.request_accepted,
1227 [false; VERIFY_CLUSTER_CONTEXTS]
1228 );
1229 assert_eq!(registered.response_routed, [false; VERIFY_CLUSTER_CONTEXTS]);
1230 assert!(registered.result_valid);
1231 assert!(!registered.result_transfer);
1232 assert_eq!(registered.result.job_id, clean.job_id);
1233 assert!(!registered.result.verified);
1234 assert_eq!(registered.result.error, b3(VERIFY_ERROR_TRANSPORT));
1235 assert_ne!(
1236 registered.diagnostics & b16(VERIFY_DIAGNOSTIC_EXTERNAL_ABORT),
1237 b16(0)
1238 );
1239 for context in registered_d.contexts {
1240 assert!(context.transport_abort);
1241 assert!(!context.stream_valid);
1242 assert!(!context.response.valid);
1243 }
1244 assert!(registered_d.transport.abort);
1245
1246 let mut simultaneous_q = q;
1250 simultaneous_q.transport.fatal = true;
1251 let (simultaneous, simultaneous_d) = verify_cluster_core_kernel(
1252 clock_reset(clock(false), reset(false)),
1253 VerifyClusterCoreInput {
1254 external_abort: true,
1255 ..VerifyClusterCoreInput::default()
1256 },
1257 simultaneous_q,
1258 );
1259 assert!(simultaneous.fault);
1260 assert_eq!(simultaneous.result.error, b3(VERIFY_ERROR_TRANSPORT));
1261 assert_eq!(
1262 simultaneous.diagnostics & b16(VERIFY_DIAGNOSTIC_EXTERNAL_ABORT),
1263 b16(0)
1264 );
1265 assert_ne!(
1266 simultaneous_d.control.diagnostics & b16(VERIFY_DIAGNOSTIC_EXTERNAL_ABORT),
1267 b16(0)
1268 );
1269 }
1270}