1use rhdl::prelude::*;
4use rhdl_fpga::core::{dff::DFF, ram::synchronous::Write as SyncBramWrite};
5use rhdl_primitives::NoResetDff;
6use wots_rhdl::{SignerOutputBeat, SignerTopInput, SignerTopOutput};
7
8use crate::{
9 abi::{
10 self, ERROR_BUFFER, ERROR_CONFIGURATION, ERROR_COUNT, ERROR_FRAME, ERROR_INPUT_AXI,
11 ERROR_OUTPUT_AXI, ERROR_SCOREBOARD, ERROR_SIGNER_COMPLETION, ERROR_SIGNER_FAULT,
12 ERROR_SOFT_ABORT, ERROR_SUMMARY_AXI, OUTPUT_AXI_LEN, TERMINAL_ABORTED_BITS,
13 TERMINAL_CONFIGURATION_BITS, TERMINAL_INPUT_AXI_BITS, TERMINAL_OUTPUT_AXI_BITS,
14 TERMINAL_PROTOCOL_BITS, TERMINAL_SIGNER_BITS, TERMINAL_SUCCESS_BITS,
15 TERMINAL_SUMMARY_AXI_BITS,
16 },
17 axi::{
18 AXI_BURST_INCR, AXI_CACHE_NORMAL, AXI_LOCK_NORMAL, AXI_RESP_DECERR, AXI_RESP_OKAY,
19 AXI_RESP_SLVERR, AXI_SIZE_64_BYTES, Axi512ReadKernelToMemory, Axi512ReadMemoryToKernel,
20 Axi512WriteKernelToMemory, Axi512WriteMemoryToKernel, AxiLiteHostToKernel,
21 AxiLiteKernelToHost, idle_read_master_kernel,
22 },
23 control::{
24 LaunchConfig, RunState, SummaryFields, SummaryWriteStatus, apply_write_strobes_kernel,
25 high_word_kernel, last_status_kernel, low_word_kernel, replace_high_word_kernel,
26 replace_low_word_kernel, summary_line_kernel, validate_config_kernel,
27 },
28 frame_store::{
29 FRAME_STORE_ADDRESS_BITS, FrameStore, FrameStoreInput, captured_beat_is_canonical_kernel,
30 frame_store_address_kernel,
31 },
32 reader::{InputJob, ReaderState, input_job_address_kernel, input_response_is_canonical_kernel},
33 scoreboard::{
34 JobEntry, ScoreboardEvents, scoreboard_occupancy_kernel, scoreboard_update_kernel,
35 },
36 writer::{
37 FrameQueue, FrameQueueOperation, MemoryCompletionTiming, WriterState,
38 frame_queue_update_kernel, frame_slot_is_reserved_kernel, output_job_address_kernel,
39 output_response_is_canonical_kernel, record_memory_completion_kernel,
40 },
41};
42
43pub const U280_HBM_SHELL_MODULE: &str = "hashsigs_u280_hbm_shell_rhdl";
45
46pub fn try_u280_hbm_shell_unchecked_verilog() -> Result<String, RHDLError> {
55 let descriptor = U280HbmShell::default().descriptor(U280_HBM_SHELL_MODULE.into())?;
56 let hdl = descriptor.hdl_unchecked()?;
57 Ok(hdl.modules.pretty())
58}
59
60#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
62pub struct HbmShellInput {
63 pub control: AxiLiteHostToKernel,
65 pub input_hbm: Axi512ReadMemoryToKernel,
67 pub output_hbm: Axi512WriteMemoryToKernel,
69 pub signer: SignerTopOutput,
71}
72
73#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
75pub struct HbmShellOutput {
76 pub control: AxiLiteKernelToHost,
78 pub input_hbm: Axi512ReadKernelToMemory,
80 pub output_hbm: Axi512WriteKernelToMemory,
82 pub signer: SignerTopInput,
84 pub signer_soft_reset: bool,
86 pub interrupt: bool,
88}
89
90#[allow(clippy::struct_excessive_bools)] #[derive(Clone, Copy, Debug, Digital, Eq, PartialEq)]
93pub struct ShellControl {
94 pub run_state: RunState,
96 pub config: LaunchConfig,
98 pub ap_done: bool,
100 pub gier: bool,
102 pub ier: b2,
104 pub isr: b2,
106 pub terminal_code: b8,
108 pub error_flags: b16,
110 pub first_error_job: b32,
112 pub first_error_detail: b32,
114 pub summary_aw_handshake: bool,
116 pub summary_w_handshake: bool,
118 pub summary_b_handshake: bool,
120 pub summary_b_canonical: bool,
122 pub axil_aw_pending: bool,
124 pub axil_awaddr: b8,
126 pub axil_aw_locked_at_accept: bool,
128 pub axil_w_pending: bool,
130 pub axil_wdata: b32,
132 pub axil_wstrb: b4,
134 pub axil_w_locked_at_accept: bool,
136 pub axil_bvalid: bool,
138 pub axil_bresp: b2,
140 pub axil_rvalid: bool,
142 pub axil_rdata: b32,
144 pub axil_rresp: b2,
146 pub axil_r_ap_ctrl: bool,
148 pub reader_state: ReaderState,
150 pub reader_job: b32,
152 pub input_job_valid: bool,
154 pub scoreboard: [JobEntry; abi::LIVE_JOB_SLOTS],
156 pub ingress_valid: bool,
158 pub capture_active: bool,
160 pub capture_slot: b1,
162 pub capture_beat: b6,
164 pub capture_job: b32,
166 pub capture_cluster: b2,
168 pub frame_queue: FrameQueue,
170 pub writer_state: WriterState,
172 pub writer_slot: b1,
174 pub writer_job: b32,
176 pub writer_beat: b6,
178 pub read_issue_beat: b6,
180 pub read_pending: bool,
182 pub prefetch_count: b2,
184 pub writer_early_b_drained: bool,
186 pub abort_cleaned: bool,
188 pub input_ar_count: b16,
190 pub input_r_count: b16,
192 pub core_admission_count: b16,
194 pub frame_capture_count: b16,
196 pub core_error_count: b16,
198 pub payload_aw_count: b16,
200 pub payload_b_count: b16,
202 pub payload_w_count: b32,
204 pub memory_completed_count: b16,
206 pub memory_completion_timing: MemoryCompletionTiming,
208 pub run_cycle: b64,
210 pub payload_cycles: b64,
212 pub output_stall_cycles: b64,
214 pub first_admission_seen: bool,
216 pub first_admission_cycle: b64,
218 pub first_capture_seen: bool,
220 pub first_capture_cycle: b64,
222 pub core_latency_cycles: b64,
224 pub capture_span_cycles: b64,
226}
227
228impl Default for ShellControl {
229 fn default() -> Self {
230 Self {
231 run_state: RunState::Idle,
232 config: LaunchConfig::default(),
233 ap_done: false,
234 gier: false,
235 ier: b2(0),
236 isr: b2(0),
237 terminal_code: TERMINAL_SUCCESS_BITS,
238 error_flags: b16(0),
239 first_error_job: b32(0xffff_ffff),
240 first_error_detail: b32(0),
241 summary_aw_handshake: false,
242 summary_w_handshake: false,
243 summary_b_handshake: false,
244 summary_b_canonical: false,
245 axil_aw_pending: false,
246 axil_awaddr: b8(0),
247 axil_aw_locked_at_accept: false,
248 axil_w_pending: false,
249 axil_wdata: b32(0),
250 axil_wstrb: b4(0),
251 axil_w_locked_at_accept: false,
252 axil_bvalid: false,
253 axil_bresp: AXI_RESP_OKAY,
254 axil_rvalid: false,
255 axil_rdata: b32(0),
256 axil_rresp: AXI_RESP_OKAY,
257 axil_r_ap_ctrl: false,
258 reader_state: ReaderState::Idle,
259 reader_job: b32(0),
260 input_job_valid: false,
261 scoreboard: [JobEntry::default(); abi::LIVE_JOB_SLOTS],
262 ingress_valid: false,
263 capture_active: false,
264 capture_slot: b1(0),
265 capture_beat: b6(0),
266 capture_job: b32(0),
267 capture_cluster: b2(0),
268 frame_queue: FrameQueue::default(),
269 writer_state: WriterState::Idle,
270 writer_slot: b1(0),
271 writer_job: b32(0),
272 writer_beat: b6(0),
273 read_issue_beat: b6(0),
274 read_pending: false,
275 prefetch_count: b2(0),
276 writer_early_b_drained: false,
277 abort_cleaned: false,
278 input_ar_count: b16(0),
279 input_r_count: b16(0),
280 core_admission_count: b16(0),
281 frame_capture_count: b16(0),
282 core_error_count: b16(0),
283 payload_aw_count: b16(0),
284 payload_b_count: b16(0),
285 payload_w_count: b32(0),
286 memory_completed_count: b16(0),
287 memory_completion_timing: MemoryCompletionTiming::default(),
288 run_cycle: b64(0),
289 payload_cycles: b64(0),
290 output_stall_cycles: b64(0),
291 first_admission_seen: false,
292 first_admission_cycle: b64(0),
293 first_capture_seen: false,
294 first_capture_cycle: b64(0),
295 core_latency_cycles: b64(0),
296 capture_span_cycles: b64(0),
297 }
298 }
299}
300
301#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
303pub struct U280HbmShell {
304 control: DFF<ShellControl>,
305 input_job: NoResetDff<InputJob>,
306 ingress: NoResetDff<SignerOutputBeat>,
307 prefetch0: NoResetDff<[b8; 64]>,
308 prefetch1: NoResetDff<[b8; 64]>,
309 frame_store: FrameStore,
310}
311
312impl Default for U280HbmShell {
313 fn default() -> Self {
314 Self {
315 control: DFF::new(ShellControl::default()),
316 input_job: NoResetDff::new(),
317 ingress: NoResetDff::new(),
318 prefetch0: NoResetDff::new(),
319 prefetch1: NoResetDff::new(),
320 frame_store: FrameStore::default(),
321 }
322 }
323}
324
325impl SynchronousIO for U280HbmShell {
326 type I = HbmShellInput;
327 type O = HbmShellOutput;
328 type Kernel = u280_hbm_shell_kernel;
329}
330
331#[kernel]
333pub fn queue_status_kernel(control: ShellControl) -> b32 {
334 let scoreboard: b32 = scoreboard_occupancy_kernel(control.scoreboard).resize();
335 let frames: b32 = control.frame_queue.count.resize();
336 let reader: b32 = match control.reader_state {
337 ReaderState::Idle => b32(0),
338 ReaderState::Address => b32(1),
339 ReaderState::Response => b32(2),
340 ReaderState::Hold => b32(3),
341 };
342 let writer: b32 = match control.writer_state {
343 WriterState::Idle => b32(0),
344 WriterState::Address => b32(1),
345 WriterState::Data => b32(2),
346 WriterState::Response => b32(3),
347 };
348 scoreboard
349 | (frames << 4)
350 | (bool_word_kernel(control.ingress_valid) << 6)
351 | (bool_word_kernel(control.capture_active) << 7)
352 | (reader << 8)
353 | (writer << 10)
354 | (control.prefetch_count.resize() << 12)
355}
356
357#[kernel]
359pub fn bool_word_kernel(value: bool) -> b32 {
360 if value { b32(1) } else { b32(0) }
361}
362
363#[kernel]
365#[allow(
366 clippy::assign_op_pattern,
367 clippy::field_reassign_with_default,
368 clippy::if_not_else,
369 clippy::match_same_arms,
370 clippy::needless_range_loop,
371 clippy::too_many_lines
372)] pub fn u280_hbm_shell_kernel(
374 clock_reset: ClockReset,
375 input: HbmShellInput,
376 q: U280HbmShellQ,
377) -> (HbmShellOutput, U280HbmShellD) {
378 let resetting = clock_reset.reset.any();
379 let mut control = q.control;
380 let mut input_job = q.input_job;
381 let mut ingress = q.ingress;
382 let mut prefetch0 = q.prefetch0;
383 let mut prefetch1 = q.prefetch1;
384 let mut frame_store = FrameStoreInput::default();
385
386 let idle_for_registers = q.control.run_state == RunState::Idle;
387 let axil_awready = !q.control.axil_aw_pending && !q.control.axil_bvalid;
388 let axil_wready = !q.control.axil_w_pending && !q.control.axil_bvalid;
389 let axil_arready = !q.control.axil_rvalid;
390 let axil_b_handshake = q.control.axil_bvalid && input.control.bready;
391 let axil_r_handshake = q.control.axil_rvalid && input.control.rready;
392 let mut start_command = false;
393 let mut abort_command = false;
394
395 if axil_b_handshake {
396 control.axil_bvalid = false;
397 }
398 if axil_r_handshake {
399 control.axil_rvalid = false;
400 if q.control.axil_r_ap_ctrl {
401 control.ap_done = false;
402 }
403 }
404 if axil_awready && input.control.awvalid {
405 control.axil_aw_pending = true;
406 control.axil_awaddr = input.control.awaddr;
407 control.axil_aw_locked_at_accept = !idle_for_registers;
408 }
409 if axil_wready && input.control.wvalid {
410 control.axil_w_pending = true;
411 control.axil_wdata = input.control.wdata;
412 control.axil_wstrb = input.control.wstrb;
413 control.axil_w_locked_at_accept = !idle_for_registers;
414 }
415
416 if q.control.axil_aw_pending && q.control.axil_w_pending && !q.control.axil_bvalid {
417 let address = q.control.axil_awaddr;
418 let aligned = address & b8(3) == b8(0);
419 let lane0_written = q.control.axil_wstrb & b4(1) != b4(0);
420 let locked = q.control.axil_aw_locked_at_accept
421 || q.control.axil_w_locked_at_accept
422 || !idle_for_registers;
423 let mut response = AXI_RESP_OKAY;
424 if !aligned {
425 response = AXI_RESP_DECERR;
426 } else {
427 match address {
428 Bits::<8>(0x00) => {
429 if lane0_written && q.control.axil_wdata & b32(1) != b32(0) {
430 if !locked {
431 start_command = true;
432 } else {
433 response = AXI_RESP_SLVERR;
434 }
435 }
436 }
437 Bits::<8>(0x04) => {
438 if lane0_written {
439 control.gier = q.control.axil_wdata & b32(1) != b32(0);
440 }
441 }
442 Bits::<8>(0x08) => {
443 if lane0_written {
444 control.ier = q.control.axil_wdata.resize();
445 }
446 }
447 Bits::<8>(0x0c) => {
448 if lane0_written {
449 let toggles: b2 = q.control.axil_wdata.resize();
450 control.isr = q.control.isr ^ toggles;
451 }
452 }
453 Bits::<8>(0x10) => {
454 if !locked {
455 control.config.input_base = replace_low_word_kernel(
456 q.control.config.input_base,
457 q.control.axil_wdata,
458 q.control.axil_wstrb,
459 );
460 } else {
461 response = AXI_RESP_SLVERR;
462 }
463 }
464 Bits::<8>(0x14) => {
465 if !locked {
466 control.config.input_base = replace_high_word_kernel(
467 q.control.config.input_base,
468 q.control.axil_wdata,
469 q.control.axil_wstrb,
470 );
471 } else {
472 response = AXI_RESP_SLVERR;
473 }
474 }
475 Bits::<8>(0x1c) => {
476 if !locked {
477 control.config.output_base = replace_low_word_kernel(
478 q.control.config.output_base,
479 q.control.axil_wdata,
480 q.control.axil_wstrb,
481 );
482 } else {
483 response = AXI_RESP_SLVERR;
484 }
485 }
486 Bits::<8>(0x20) => {
487 if !locked {
488 control.config.output_base = replace_high_word_kernel(
489 q.control.config.output_base,
490 q.control.axil_wdata,
491 q.control.axil_wstrb,
492 );
493 } else {
494 response = AXI_RESP_SLVERR;
495 }
496 }
497 Bits::<8>(0x28) => {
498 if !locked {
499 control.config.summary_base = replace_low_word_kernel(
500 q.control.config.summary_base,
501 q.control.axil_wdata,
502 q.control.axil_wstrb,
503 );
504 } else {
505 response = AXI_RESP_SLVERR;
506 }
507 }
508 Bits::<8>(0x2c) => {
509 if !locked {
510 control.config.summary_base = replace_high_word_kernel(
511 q.control.config.summary_base,
512 q.control.axil_wdata,
513 q.control.axil_wstrb,
514 );
515 } else {
516 response = AXI_RESP_SLVERR;
517 }
518 }
519 Bits::<8>(0x34) => {
520 if !locked {
521 control.config.batch = apply_write_strobes_kernel(
522 q.control.config.batch,
523 q.control.axil_wdata,
524 q.control.axil_wstrb,
525 );
526 } else {
527 response = AXI_RESP_SLVERR;
528 }
529 }
530 Bits::<8>(0x38) => {
531 if !locked {
532 control.config.abi_word = apply_write_strobes_kernel(
533 q.control.config.abi_word,
534 q.control.axil_wdata,
535 q.control.axil_wstrb,
536 );
537 } else {
538 response = AXI_RESP_SLVERR;
539 }
540 }
541 Bits::<8>(0x3c) => {
542 if !locked {
543 control.config.nonce = replace_low_word_kernel(
544 q.control.config.nonce,
545 q.control.axil_wdata,
546 q.control.axil_wstrb,
547 );
548 } else {
549 response = AXI_RESP_SLVERR;
550 }
551 }
552 Bits::<8>(0x40) => {
553 if !locked {
554 control.config.nonce = replace_high_word_kernel(
555 q.control.config.nonce,
556 q.control.axil_wdata,
557 q.control.axil_wstrb,
558 );
559 } else {
560 response = AXI_RESP_SLVERR;
561 }
562 }
563 Bits::<8>(0x18) => {}
564 Bits::<8>(0x24) => {}
565 Bits::<8>(0x30) => {}
566 Bits::<8>(0x44) => {}
567 Bits::<8>(0x48) => {
568 if lane0_written && q.control.axil_wdata & b32(1) != b32(0) {
569 let abortable_now = q.control.run_state == RunState::Validate
570 || q.control.run_state == RunState::Execute;
571 if locked && abortable_now {
572 abort_command = true;
573 } else {
574 response = AXI_RESP_SLVERR;
578 }
579 }
580 }
581 Bits::<8>(0x4c) => response = AXI_RESP_SLVERR,
582 Bits::<8>(0x50) => response = AXI_RESP_SLVERR,
583 Bits::<8>(0x54) => response = AXI_RESP_SLVERR,
584 Bits::<8>(0x58) => response = AXI_RESP_SLVERR,
585 Bits::<8>(0x5c) => response = AXI_RESP_SLVERR,
586 Bits::<8>(0x60) => response = AXI_RESP_SLVERR,
587 Bits::<8>(0x64) => response = AXI_RESP_SLVERR,
588 Bits::<8>(0x68) => response = AXI_RESP_SLVERR,
589 Bits::<8>(0x6c) => response = AXI_RESP_SLVERR,
590 Bits::<8>(0x70) => response = AXI_RESP_SLVERR,
591 Bits::<8>(0x74) => response = AXI_RESP_SLVERR,
592 Bits::<8>(0x78) => response = AXI_RESP_SLVERR,
593 Bits::<8>(0x7c) => response = AXI_RESP_SLVERR,
594 Bits::<8>(0x80) => response = AXI_RESP_SLVERR,
595 _ => {
596 response = AXI_RESP_DECERR;
597 }
598 }
599 }
600 control.axil_aw_pending = false;
601 control.axil_aw_locked_at_accept = false;
602 control.axil_w_pending = false;
603 control.axil_w_locked_at_accept = false;
604 control.axil_bvalid = true;
605 control.axil_bresp = response;
606 }
607
608 if axil_arready && input.control.arvalid {
609 let address = input.control.araddr;
610 let aligned = address & b8(3) == b8(0);
611 let idle = q.control.run_state == RunState::Idle || q.control.run_state == RunState::Done;
612 let ready = q.control.run_state == RunState::Idle;
613 let ap_ctrl: b32 = (bool_word_kernel(q.control.ap_done) << 1)
614 | (bool_word_kernel(idle) << 2)
615 | (bool_word_kernel(ready) << 3);
616 let last_status = last_status_kernel(
617 q.control.terminal_code,
618 q.control.error_flags,
619 SummaryWriteStatus {
620 aw: q.control.summary_aw_handshake,
621 w: q.control.summary_w_handshake,
622 b: q.control.summary_b_handshake,
623 b_canonical: q.control.summary_b_canonical,
624 },
625 );
626 let progress0: b32 = q.control.core_admission_count.resize()
627 | (q.control.frame_capture_count.resize() << 16);
628 let progress1: b32 =
629 q.control.memory_completed_count.resize() | (q.control.core_error_count.resize() << 16);
630 let mut response = AXI_RESP_OKAY;
631 let mut data = b32(0);
632 let mut is_ap_ctrl = false;
633 if !aligned {
634 response = AXI_RESP_DECERR;
635 } else {
636 match address {
637 Bits::<8>(0x00) => {
638 data = ap_ctrl;
639 is_ap_ctrl = true;
640 }
641 Bits::<8>(0x04) => data = bool_word_kernel(q.control.gier),
642 Bits::<8>(0x08) => data = q.control.ier.resize(),
643 Bits::<8>(0x0c) => data = q.control.isr.resize(),
644 Bits::<8>(0x10) => data = low_word_kernel(q.control.config.input_base),
645 Bits::<8>(0x14) => data = high_word_kernel(q.control.config.input_base),
646 Bits::<8>(0x1c) => data = low_word_kernel(q.control.config.output_base),
647 Bits::<8>(0x20) => data = high_word_kernel(q.control.config.output_base),
648 Bits::<8>(0x28) => data = low_word_kernel(q.control.config.summary_base),
649 Bits::<8>(0x2c) => data = high_word_kernel(q.control.config.summary_base),
650 Bits::<8>(0x34) => data = q.control.config.batch,
651 Bits::<8>(0x38) => data = q.control.config.abi_word,
652 Bits::<8>(0x3c) => data = low_word_kernel(q.control.config.nonce),
653 Bits::<8>(0x40) => data = high_word_kernel(q.control.config.nonce),
654 Bits::<8>(0x18) => {}
655 Bits::<8>(0x24) => {}
656 Bits::<8>(0x30) => {}
657 Bits::<8>(0x44) => {}
658 Bits::<8>(0x48) => {}
659 Bits::<8>(0x4c) => data = last_status,
660 Bits::<8>(0x50) => data = progress0,
661 Bits::<8>(0x54) => data = progress1,
662 Bits::<8>(0x58) => data = low_word_kernel(q.control.output_stall_cycles),
663 Bits::<8>(0x5c) => data = high_word_kernel(q.control.output_stall_cycles),
664 Bits::<8>(0x60) => data = queue_status_kernel(q.control),
665 Bits::<8>(0x64) => data = q.control.first_error_job,
666 Bits::<8>(0x68) => data = q.control.first_error_detail,
667 Bits::<8>(0x6c) => {
668 data = low_word_kernel(q.control.memory_completion_timing.first);
669 }
670 Bits::<8>(0x70) => {
671 data = high_word_kernel(q.control.memory_completion_timing.first);
672 }
673 Bits::<8>(0x74) => {
674 data = low_word_kernel(q.control.memory_completion_timing.last);
675 }
676 Bits::<8>(0x78) => {
677 data = high_word_kernel(q.control.memory_completion_timing.last);
678 }
679 Bits::<8>(0x7c) => {
680 data = low_word_kernel(q.control.memory_completion_timing.span);
681 }
682 Bits::<8>(0x80) => {
683 data = high_word_kernel(q.control.memory_completion_timing.span);
684 }
685 _ => response = AXI_RESP_DECERR,
686 }
687 }
688 control.axil_rvalid = true;
689 control.axil_rdata = data;
690 control.axil_rresp = response;
691 control.axil_r_ap_ctrl = is_ap_ctrl;
692 }
693
694 if q.control.run_state == RunState::Done {
695 control.run_state = RunState::Idle;
696 }
697
698 if start_command {
699 let retained_config = control.config;
700 let retained_gier = control.gier;
701 let retained_ier = control.ier;
702 let retained_isr = control.isr;
703 let retained_axil_aw_pending = control.axil_aw_pending;
704 let retained_axil_awaddr = control.axil_awaddr;
705 let retained_axil_aw_locked_at_accept = control.axil_aw_locked_at_accept;
706 let retained_axil_w_pending = control.axil_w_pending;
707 let retained_axil_wdata = control.axil_wdata;
708 let retained_axil_wstrb = control.axil_wstrb;
709 let retained_axil_w_locked_at_accept = control.axil_w_locked_at_accept;
710 let retained_axil_bvalid = control.axil_bvalid;
711 let retained_axil_bresp = control.axil_bresp;
712 let retained_axil_rvalid = control.axil_rvalid;
713 let retained_axil_rdata = control.axil_rdata;
714 let retained_axil_rresp = control.axil_rresp;
715 let retained_axil_r_ap_ctrl = control.axil_r_ap_ctrl;
716 control = ShellControl::default();
717 control.config = retained_config;
718 control.gier = retained_gier;
719 control.ier = retained_ier;
720 control.isr = retained_isr;
721 control.axil_aw_pending = retained_axil_aw_pending;
722 control.axil_awaddr = retained_axil_awaddr;
723 control.axil_aw_locked_at_accept = retained_axil_aw_locked_at_accept;
724 control.axil_w_pending = retained_axil_w_pending;
725 control.axil_wdata = retained_axil_wdata;
726 control.axil_wstrb = retained_axil_wstrb;
727 control.axil_w_locked_at_accept = retained_axil_w_locked_at_accept;
728 control.axil_bvalid = retained_axil_bvalid;
729 control.axil_bresp = retained_axil_bresp;
730 control.axil_rvalid = retained_axil_rvalid;
731 control.axil_rdata = retained_axil_rdata;
732 control.axil_rresp = retained_axil_rresp;
733 control.axil_r_ap_ctrl = retained_axil_r_ap_ctrl;
734 control.run_state = RunState::Validate;
735 control.ap_done = false;
736 }
737
738 let scoreboard_occupancy = scoreboard_occupancy_kernel(q.control.scoreboard);
739 let slot0_free = !frame_slot_is_reserved_kernel(
740 q.control.frame_queue,
741 q.control.capture_active,
742 q.control.capture_slot,
743 b1(0),
744 );
745 let slot1_free = !frame_slot_is_reserved_kernel(
746 q.control.frame_queue,
747 q.control.capture_active,
748 q.control.capture_slot,
749 b1(1),
750 );
751 let capture_has_capacity = q.control.capture_active || slot0_free || slot1_free;
752 let ingress_consumed = q.control.run_state == RunState::Execute
753 && q.control.error_flags == b16(0)
754 && q.control.ingress_valid
755 && !abort_command;
756 let ingress_elastic_capacity = !q.control.ingress_valid || ingress_consumed;
757 let hold_final_without_refill = q.control.ingress_valid && q.ingress.beat.last;
758 let signer_output_ready = q.control.run_state == RunState::Execute
759 && q.control.error_flags == b16(0)
760 && capture_has_capacity
761 && ingress_elastic_capacity
762 && !hold_final_without_refill
763 && !abort_command;
764 let signer_start_valid = q.control.run_state == RunState::Execute
765 && q.control.error_flags == b16(0)
766 && q.control.input_job_valid
767 && scoreboard_occupancy < b4(abi::LIVE_JOB_SLOTS as u128)
768 && !abort_command;
769 let signer_start_handshake = signer_start_valid && input.signer.start_ready;
770 let signer_output_handshake = signer_output_ready && input.signer.output_valid;
771 let signer_error_ready = q.control.run_state == RunState::Execute
772 && q.control.error_flags == b16(0)
773 && !abort_command;
774 let signer_error_handshake = signer_error_ready && input.signer.error_valid;
775 let transport_active = q.control.run_state == RunState::Validate
776 || q.control.run_state == RunState::Execute
777 || q.control.run_state == RunState::AbortDrain;
778 let input_ar_handshake =
779 q.control.reader_state == ReaderState::Address && input.input_hbm.arready && !resetting;
780 let input_response_owned =
781 q.control.reader_state == ReaderState::Response || input_ar_handshake;
782 let input_r_handshake = input_response_owned && input.input_hbm.rvalid && !resetting;
783 let writer_w_handshake = q.control.writer_state == WriterState::Data
784 && q.control.prefetch_count != b2(0)
785 && input.output_hbm.wready
786 && !resetting;
787 let writer_final_w_handshake = writer_w_handshake && q.control.writer_beat == b6(34);
788 let output_response_owned = q.control.writer_state == WriterState::Response
789 || (writer_final_w_handshake && !q.control.writer_early_b_drained);
790 let output_b_handshake = output_response_owned && input.output_hbm.bvalid && !resetting;
791 let early_final_b_handshake = q.control.writer_state == WriterState::Data
792 && q.control.writer_beat == b6(34)
793 && !q.control.writer_early_b_drained
794 && input.output_hbm.bvalid
795 && !writer_final_w_handshake
796 && !resetting;
797 let summary_aw_handshake =
798 q.control.run_state == RunState::SummaryAw && input.output_hbm.awready && !resetting;
799 let summary_w_handshake = q.control.run_state == RunState::SummaryW
800 && q.control.summary_aw_handshake
801 && input.output_hbm.wready
802 && !resetting;
803 let summary_response_owned = (q.control.run_state == RunState::SummaryB
808 && q.control.summary_aw_handshake
809 && q.control.summary_w_handshake)
810 || summary_w_handshake;
811 let summary_b_handshake = summary_response_owned && input.output_hbm.bvalid && !resetting;
812 let summary_sequence_active = q.control.run_state == RunState::CoreReset0
813 || q.control.run_state == RunState::CoreReset1
814 || q.control.run_state == RunState::SummaryAw
815 || q.control.run_state == RunState::SummaryW
816 || q.control.run_state == RunState::SummaryB;
817 let premature_summary_b_handshake =
818 summary_sequence_active && !summary_response_owned && input.output_hbm.bvalid && !resetting;
819 let unexpected_input_r = transport_active && input.input_hbm.rvalid && !input_response_owned;
820 let unexpected_output_b = transport_active && input.output_hbm.bvalid && !output_response_owned;
821 let drain_orphan_input_r = transport_active
822 && (q.control.reader_state == ReaderState::Idle
823 || q.control.reader_state == ReaderState::Hold);
824 let drain_orphan_output_b = transport_active && q.control.writer_state == WriterState::Idle;
825
826 let mut new_failure = false;
827 let mut new_error_flags = b16(0);
828 let mut new_terminal_code = TERMINAL_SUCCESS_BITS;
829 let mut new_error_job = b32(0xffff_ffff);
830 let mut new_error_detail = b32(0);
831 let mut scoreboard_events = ScoreboardEvents::default();
832 let mut enqueue_frame = false;
833 let mut enqueue_slot = b1(0);
834 let mut enqueue_job = b32(0);
835 let mut dequeue_frame = false;
836
837 if q.control.run_state == RunState::Validate {
838 let validation = validate_config_kernel(q.control.config);
839 if validation.valid && !abort_command {
840 control.run_state = RunState::Execute;
841 if q.control.config.batch != b32(0) {
842 control.reader_state = ReaderState::Address;
843 }
844 } else if !validation.valid {
845 if !new_failure {
846 new_terminal_code = TERMINAL_CONFIGURATION_BITS;
847 new_error_job = b32(0xffff_ffff);
848 new_error_detail = validation.detail;
849 }
850 new_failure = true;
851 new_error_flags = new_error_flags | b16(ERROR_CONFIGURATION);
852 }
853 }
854
855 if q.control.run_state == RunState::Execute || q.control.run_state == RunState::AbortDrain {
856 control.run_cycle = q.control.run_cycle + b64(1);
857 control.payload_cycles = q.control.payload_cycles + b64(1);
858 }
859
860 if unexpected_input_r {
861 if !new_failure {
862 new_terminal_code = TERMINAL_INPUT_AXI_BITS;
863 new_error_job = if q.control.reader_state == ReaderState::Address {
864 q.control.reader_job
865 } else {
866 b32(0xffff_ffff)
867 };
868 new_error_detail = b32(0x6001)
869 | (input.input_hbm.rresp.resize() << 16)
870 | (input.input_hbm.rid.resize() << 24)
871 | (bool_word_kernel(input.input_hbm.rlast) << 25);
872 }
873 new_failure = true;
874 new_error_flags = new_error_flags | b16(ERROR_INPUT_AXI);
875 if q.control.reader_state == ReaderState::Address && !input_ar_handshake {
876 control.reader_state = ReaderState::Idle;
880 }
881 }
882 if unexpected_output_b {
883 if !new_failure {
884 new_terminal_code = TERMINAL_OUTPUT_AXI_BITS;
885 new_error_job = if q.control.writer_state == WriterState::Idle {
886 b32(0xffff_ffff)
887 } else {
888 q.control.writer_job
889 };
890 new_error_detail = b32(0x6002)
891 | (input.output_hbm.bresp.resize() << 16)
892 | (input.output_hbm.bid.resize() << 24);
893 }
894 new_failure = true;
895 new_error_flags = new_error_flags | b16(ERROR_OUTPUT_AXI);
896 }
897 if premature_summary_b_handshake {
898 if !new_failure {
899 new_terminal_code = TERMINAL_SUMMARY_AXI_BITS;
900 new_error_job = b32(0xffff_ffff);
901 new_error_detail = b32(0x6003)
902 | (input.output_hbm.bresp.resize() << 16)
903 | (input.output_hbm.bid.resize() << 24);
904 }
905 new_failure = true;
906 new_error_flags = new_error_flags | b16(ERROR_SUMMARY_AXI);
907 }
908
909 if input_ar_handshake {
910 control.input_ar_count = q.control.input_ar_count + b16(1);
911 control.reader_state = ReaderState::Response;
912 }
913 if input_r_handshake {
914 let response_ok = input_response_is_canonical_kernel(
915 input.input_hbm.rid,
916 input.input_hbm.rresp,
917 input.input_hbm.rlast,
918 );
919 if response_ok {
920 control.input_r_count = q.control.input_r_count + b16(1);
921 if q.control.run_state == RunState::Execute {
922 input_job = InputJob {
923 data: input.input_hbm.rdata,
924 job_id: q.control.reader_job,
925 };
926 control.input_job_valid = true;
927 control.reader_state = ReaderState::Hold;
928 } else {
929 control.reader_state = ReaderState::Idle;
930 }
931 } else {
932 control.reader_state = ReaderState::Idle;
933 if q.control.run_state == RunState::Execute
934 || q.control.run_state == RunState::AbortDrain
935 {
936 if !new_failure {
937 new_terminal_code = TERMINAL_INPUT_AXI_BITS;
938 new_error_job = q.control.reader_job;
939 new_error_detail = input.input_hbm.rresp.resize()
940 | (input.input_hbm.rid.resize() << 8)
941 | (bool_word_kernel(input.input_hbm.rlast) << 9);
942 }
943 new_failure = true;
944 new_error_flags = new_error_flags | b16(ERROR_INPUT_AXI);
945 }
946 }
947 }
948
949 if q.control.reader_state == ReaderState::Hold {
950 if q.control.run_state == RunState::AbortDrain {
951 control.input_job_valid = false;
952 control.reader_state = ReaderState::Idle;
953 } else if signer_start_handshake {
954 control.input_job_valid = false;
955 control.core_admission_count = q.control.core_admission_count + b16(1);
956 scoreboard_events.admit = true;
957 scoreboard_events.admit_job = q.input_job.job_id;
958 scoreboard_events.admit_cluster = input.signer.start_cluster;
959 if !q.control.first_admission_seen {
960 control.first_admission_seen = true;
961 control.first_admission_cycle = q.control.run_cycle;
962 }
963 let next_job = q.control.reader_job + b32(1);
964 control.reader_job = next_job;
965 if next_job < q.control.config.batch {
966 control.reader_state = ReaderState::Address;
967 } else {
968 control.reader_state = ReaderState::Idle;
969 }
970 }
971 }
972
973 if q.control.run_state == RunState::Execute && q.control.error_flags == b16(0) {
974 if input.signer.start_accepted != signer_start_handshake {
975 if !new_failure {
976 new_terminal_code = TERMINAL_SIGNER_BITS;
977 new_error_job = if signer_start_valid {
978 q.input_job.job_id
979 } else {
980 b32(0xffff_ffff)
981 };
982 new_error_detail = b32(0x1001);
983 }
984 new_failure = true;
985 new_error_flags = new_error_flags | b16(ERROR_SIGNER_FAULT);
986 }
987 if input.signer.delivered_final
988 != (signer_output_handshake && input.signer.output.beat.last)
989 {
990 if !new_failure {
991 new_error_job = input.signer.output.beat.job_id;
992 new_error_detail = b32(0x1002);
993 new_terminal_code = TERMINAL_SIGNER_BITS;
994 }
995 new_failure = true;
996 new_error_flags = new_error_flags | b16(ERROR_SIGNER_FAULT);
997 }
998 if input.signer.delivered_error != signer_error_handshake {
999 if !new_failure {
1000 new_error_job = input.signer.error.job_id;
1001 new_error_detail = b32(0x1003);
1002 new_terminal_code = TERMINAL_SIGNER_BITS;
1003 }
1004 new_failure = true;
1005 new_error_flags = new_error_flags | b16(ERROR_SIGNER_FAULT);
1006 }
1007 if input.signer.fault {
1008 if !new_failure {
1009 new_error_job = b32(0xffff_ffff);
1010 new_error_detail = b32(0x1004);
1011 new_terminal_code = TERMINAL_SIGNER_BITS;
1012 }
1013 new_failure = true;
1014 new_error_flags = new_error_flags | b16(ERROR_SIGNER_FAULT);
1015 }
1016 if input.signer.output_valid && !signer_output_ready {
1017 control.output_stall_cycles = q.control.output_stall_cycles + b64(1);
1018 }
1019 }
1020
1021 if ingress_consumed {
1022 let expected_beat = if q.control.capture_active {
1023 q.control.capture_beat
1024 } else {
1025 b6(0)
1026 };
1027 let canonical = captured_beat_is_canonical_kernel(
1028 q.ingress,
1029 expected_beat,
1030 q.control.capture_active,
1031 q.control.capture_job,
1032 q.control.capture_cluster,
1033 );
1034 control.ingress_valid = false;
1035 if canonical {
1036 let selected_slot = if q.control.capture_active {
1037 q.control.capture_slot
1038 } else if slot0_free {
1039 b1(0)
1040 } else {
1041 b1(1)
1042 };
1043 frame_store.write = SyncBramWrite::<[b8; 64], FRAME_STORE_ADDRESS_BITS> {
1044 addr: frame_store_address_kernel(selected_slot, expected_beat),
1045 value: q.ingress.beat.data,
1046 enable: true,
1047 };
1048 if !q.control.capture_active {
1049 control.capture_active = true;
1050 control.capture_slot = selected_slot;
1051 control.capture_job = q.ingress.beat.job_id;
1052 control.capture_cluster = q.ingress.cluster;
1053 control.capture_beat = b6(1);
1054 scoreboard_events.frame_start = true;
1055 scoreboard_events.frame_start_job = q.ingress.beat.job_id;
1056 scoreboard_events.frame_start_cluster = q.ingress.cluster;
1057 } else if expected_beat == b6(34) {
1058 control.capture_active = false;
1059 control.capture_beat = b6(0);
1060 control.frame_capture_count = q.control.frame_capture_count + b16(1);
1061 enqueue_frame = true;
1062 enqueue_slot = q.control.capture_slot;
1063 enqueue_job = q.control.capture_job;
1064 scoreboard_events.frame_finish = true;
1065 scoreboard_events.frame_finish_job = q.control.capture_job;
1066 if q.control.first_admission_seen {
1067 control.core_latency_cycles =
1068 q.control.run_cycle - q.control.first_admission_cycle + b64(1);
1069 }
1070 if q.control.first_capture_seen {
1071 control.capture_span_cycles =
1072 q.control.run_cycle - q.control.first_capture_cycle;
1073 } else {
1074 control.first_capture_seen = true;
1075 control.first_capture_cycle = q.control.run_cycle;
1076 control.capture_span_cycles = b64(0);
1077 }
1078 } else {
1079 control.capture_beat = expected_beat + b6(1);
1080 }
1081 } else {
1082 if !new_failure {
1083 new_error_job = q.ingress.beat.job_id;
1084 new_error_detail = expected_beat.resize()
1085 | (q.ingress.cluster.resize() << 8)
1086 | (bool_word_kernel(q.ingress.beat.last) << 16);
1087 new_terminal_code = TERMINAL_PROTOCOL_BITS;
1088 }
1089 new_failure = true;
1090 new_error_flags = new_error_flags | b16(ERROR_FRAME);
1091 }
1092 }
1093
1094 if signer_output_handshake {
1095 ingress = input.signer.output;
1096 control.ingress_valid = true;
1097 }
1098
1099 if signer_error_handshake {
1100 scoreboard_events.error = true;
1101 scoreboard_events.error_job = input.signer.error.job_id;
1102 scoreboard_events.error_cluster = input.signer.error.cluster;
1103 control.core_error_count = q.control.core_error_count + b16(1);
1104 if !new_failure {
1105 new_error_job = input.signer.error.job_id;
1106 new_error_detail =
1107 input.signer.error.context.resize() | (input.signer.error.cluster.resize() << 8);
1108 new_terminal_code = TERMINAL_SIGNER_BITS;
1109 }
1110 new_failure = true;
1111 new_error_flags = new_error_flags | b16(ERROR_SIGNER_COMPLETION);
1112 }
1113
1114 let mut next_prefetch_count = q.control.prefetch_count;
1115 let prefetch_push = q.control.read_pending;
1116 if writer_w_handshake && prefetch_push {
1117 if q.control.prefetch_count == b2(1) {
1118 prefetch0 = q.frame_store;
1119 next_prefetch_count = b2(1);
1120 } else if q.control.prefetch_count == b2(2) {
1121 prefetch0 = q.prefetch1;
1122 prefetch1 = q.frame_store;
1123 next_prefetch_count = b2(2);
1124 } else {
1125 if !new_failure {
1126 new_error_job = q.control.writer_job;
1127 new_error_detail = b32(0x2001);
1128 new_terminal_code = TERMINAL_PROTOCOL_BITS;
1129 }
1130 new_failure = true;
1131 new_error_flags = new_error_flags | b16(ERROR_BUFFER);
1132 }
1133 } else if writer_w_handshake {
1134 if q.control.prefetch_count == b2(1) {
1135 next_prefetch_count = b2(0);
1136 } else if q.control.prefetch_count == b2(2) {
1137 prefetch0 = q.prefetch1;
1138 next_prefetch_count = b2(1);
1139 } else {
1140 if !new_failure {
1141 new_error_job = q.control.writer_job;
1142 new_error_detail = b32(0x2002);
1143 new_terminal_code = TERMINAL_PROTOCOL_BITS;
1144 }
1145 new_failure = true;
1146 new_error_flags = new_error_flags | b16(ERROR_BUFFER);
1147 }
1148 } else if prefetch_push {
1149 if q.control.prefetch_count == b2(0) {
1150 prefetch0 = q.frame_store;
1151 next_prefetch_count = b2(1);
1152 } else if q.control.prefetch_count == b2(1) {
1153 prefetch1 = q.frame_store;
1154 next_prefetch_count = b2(2);
1155 } else {
1156 if !new_failure {
1157 new_error_job = q.control.writer_job;
1158 new_error_detail = b32(0x2003);
1159 new_terminal_code = TERMINAL_PROTOCOL_BITS;
1160 }
1161 new_failure = true;
1162 new_error_flags = new_error_flags | b16(ERROR_BUFFER);
1163 }
1164 }
1165 control.prefetch_count = next_prefetch_count;
1166 control.read_pending = false;
1167
1168 if q.control.writer_state == WriterState::Idle
1169 && q.control.run_state == RunState::Execute
1170 && q.control.error_flags == b16(0)
1171 && q.control.frame_queue.count != b2(0)
1172 && !new_failure
1173 && !abort_command
1174 {
1175 control.writer_state = WriterState::Address;
1176 control.writer_slot = q.control.frame_queue.head_slot;
1177 control.writer_job = q.control.frame_queue.head_job;
1178 control.writer_beat = b6(0);
1179 control.read_issue_beat = b6(1);
1180 control.read_pending = true;
1181 control.prefetch_count = b2(0);
1182 control.writer_early_b_drained = false;
1183 frame_store.read_addr = frame_store_address_kernel(q.control.frame_queue.head_slot, b6(0));
1184 } else if q.control.writer_state == WriterState::Address && input.output_hbm.awready {
1185 control.writer_state = WriterState::Data;
1186 control.payload_aw_count = q.control.payload_aw_count + b16(1);
1187 }
1188
1189 if writer_w_handshake {
1190 control.payload_w_count = q.control.payload_w_count + b32(1);
1191 if q.control.writer_beat == b6(34) {
1192 if q.control.writer_early_b_drained {
1193 control.writer_state = WriterState::Idle;
1197 control.writer_early_b_drained = false;
1198 dequeue_frame = true;
1199 } else {
1200 control.writer_state = WriterState::Response;
1201 }
1202 control.writer_beat = b6(0);
1203 } else {
1204 control.writer_beat = q.control.writer_beat + b6(1);
1205 }
1206 }
1207
1208 if early_final_b_handshake {
1209 control.payload_b_count = q.control.payload_b_count + b16(1);
1210 control.writer_early_b_drained = true;
1211 }
1212
1213 if output_b_handshake {
1214 control.payload_b_count = q.control.payload_b_count + b16(1);
1215 control.writer_state = WriterState::Idle;
1216 control.prefetch_count = b2(0);
1217 control.read_pending = false;
1218 control.writer_early_b_drained = false;
1219 dequeue_frame = true;
1220 let response_ok =
1221 output_response_is_canonical_kernel(input.output_hbm.bid, input.output_hbm.bresp);
1222 if response_ok {
1223 control.memory_completed_count = q.control.memory_completed_count + b16(1);
1224 control.memory_completion_timing = record_memory_completion_kernel(
1225 q.control.memory_completion_timing,
1226 q.control.run_cycle,
1227 true,
1228 );
1229 } else {
1230 if !new_failure {
1231 new_error_job = q.control.writer_job;
1232 new_error_detail =
1233 input.output_hbm.bresp.resize() | (input.output_hbm.bid.resize() << 8);
1234 new_terminal_code = TERMINAL_OUTPUT_AXI_BITS;
1235 }
1236 new_failure = true;
1237 new_error_flags = new_error_flags | b16(ERROR_OUTPUT_AXI);
1238 }
1239 }
1240
1241 let writer_still_prefetching = (control.writer_state == WriterState::Address
1242 || control.writer_state == WriterState::Data)
1243 && control.read_issue_beat < b6(35);
1244 if !control.read_pending && writer_still_prefetching && control.prefetch_count < b2(2) {
1245 frame_store.read_addr =
1246 frame_store_address_kernel(control.writer_slot, control.read_issue_beat);
1247 control.read_issue_beat = control.read_issue_beat + b6(1);
1248 control.read_pending = true;
1249 }
1250
1251 let scoreboard_update = scoreboard_update_kernel(q.control.scoreboard, scoreboard_events);
1252 control.scoreboard = scoreboard_update.entries;
1253 if scoreboard_update.fatal {
1254 if !new_failure {
1255 new_error_job = scoreboard_update.failing_job;
1256 new_error_detail = scoreboard_update.detail.resize();
1257 new_terminal_code = TERMINAL_PROTOCOL_BITS;
1258 }
1259 new_failure = true;
1260 new_error_flags = new_error_flags | b16(ERROR_SCOREBOARD);
1261 }
1262
1263 let queue_update = frame_queue_update_kernel(
1264 q.control.frame_queue,
1265 FrameQueueOperation {
1266 dequeue: dequeue_frame,
1267 enqueue: enqueue_frame,
1268 slot: enqueue_slot,
1269 job: enqueue_job,
1270 },
1271 );
1272 control.frame_queue = queue_update.queue;
1273 if queue_update.fatal {
1274 if !new_failure {
1275 new_error_job = enqueue_job;
1276 new_error_detail = b32(0x3001);
1277 new_terminal_code = TERMINAL_PROTOCOL_BITS;
1278 }
1279 new_failure = true;
1280 new_error_flags = new_error_flags | b16(ERROR_BUFFER);
1281 }
1282
1283 if abort_command
1284 && (q.control.run_state == RunState::Validate || q.control.run_state == RunState::Execute)
1285 {
1286 if !new_failure {
1287 new_error_job = b32(0xffff_ffff);
1288 new_error_detail = b32(0x4001);
1289 new_terminal_code = TERMINAL_ABORTED_BITS;
1290 }
1291 new_failure = true;
1292 new_error_flags = new_error_flags | b16(ERROR_SOFT_ABORT);
1293 }
1294
1295 if new_failure {
1296 if q.control.error_flags == b16(0) {
1297 control.first_error_job = new_error_job;
1298 control.first_error_detail = new_error_detail;
1299 control.terminal_code = new_terminal_code;
1300 }
1301 control.error_flags = q.control.error_flags | new_error_flags;
1302 if q.control.run_state == RunState::Execute || q.control.run_state == RunState::Validate {
1303 control.run_state = RunState::AbortDrain;
1304 control.abort_cleaned = false;
1305 }
1306 if q.control.reader_state == ReaderState::Hold {
1309 control.reader_state = ReaderState::Idle;
1310 control.input_job_valid = false;
1311 }
1312 if q.control.writer_state == WriterState::Idle {
1313 control.writer_state = WriterState::Idle;
1314 control.read_pending = false;
1315 control.prefetch_count = b2(0);
1316 control.writer_early_b_drained = false;
1317 }
1318 }
1319
1320 if q.control.run_state == RunState::AbortDrain && !q.control.abort_cleaned {
1321 control.abort_cleaned = true;
1322 control.input_job_valid = false;
1323 control.ingress_valid = false;
1324 control.capture_active = false;
1325 control.capture_beat = b6(0);
1326 if control.reader_state == ReaderState::Hold {
1327 control.reader_state = ReaderState::Idle;
1328 }
1329 if control.writer_state == WriterState::Idle {
1330 control.frame_queue = FrameQueue::default();
1331 control.prefetch_count = b2(0);
1332 control.read_pending = false;
1333 control.writer_early_b_drained = false;
1334 } else {
1335 control.frame_queue = FrameQueue {
1336 count: b2(1),
1337 head_slot: control.writer_slot,
1338 head_job: control.writer_job,
1339 tail_slot: b1(0),
1340 tail_job: b32(0),
1341 };
1342 }
1343 }
1344
1345 if q.control.run_state == RunState::AbortDrain
1346 && control.abort_cleaned
1347 && control.reader_state == ReaderState::Idle
1348 && control.writer_state == WriterState::Idle
1349 && !control.read_pending
1350 {
1351 control.run_state = RunState::CoreReset0;
1352 }
1353 if q.control.run_state == RunState::CoreReset0 {
1354 control.scoreboard = [JobEntry::default(); abi::LIVE_JOB_SLOTS];
1355 control.run_state = RunState::CoreReset1;
1356 }
1357 if q.control.run_state == RunState::CoreReset1 {
1358 control.run_state = RunState::SummaryAw;
1359 }
1360
1361 if q.control.run_state == RunState::Execute && !new_failure {
1362 let batch16: b16 = q.control.config.batch.resize();
1363 let expected_w =
1364 (q.control.config.batch << 5) + (q.control.config.batch << 1) + q.control.config.batch;
1365 let next_occupancy = scoreboard_occupancy_kernel(control.scoreboard);
1366 let closed = control.reader_state == ReaderState::Idle
1367 && !control.input_job_valid
1368 && !control.ingress_valid
1369 && !control.capture_active
1370 && control.frame_queue.count == b2(0)
1371 && control.writer_state == WriterState::Idle
1372 && !control.read_pending
1373 && control.prefetch_count == b2(0)
1374 && next_occupancy == b4(0)
1375 && control.core_admission_count == batch16;
1376 if closed {
1377 let exact = control.input_ar_count == batch16
1378 && control.input_r_count == batch16
1379 && control.core_admission_count == batch16
1380 && control.frame_capture_count == batch16
1381 && control.core_error_count == b16(0)
1382 && control.payload_aw_count == batch16
1383 && control.payload_b_count == batch16
1384 && control.payload_w_count == expected_w
1385 && control.memory_completed_count == batch16
1386 && control.error_flags == b16(0);
1387 if exact {
1388 control.terminal_code = TERMINAL_SUCCESS_BITS;
1389 control.run_state = RunState::SummaryAw;
1390 } else {
1391 control.error_flags = control.error_flags | b16(ERROR_COUNT);
1392 control.terminal_code = TERMINAL_PROTOCOL_BITS;
1393 control.first_error_job = b32(0xffff_ffff);
1394 control.first_error_detail = b32(0x5001);
1395 control.run_state = RunState::AbortDrain;
1396 control.abort_cleaned = false;
1397 }
1398 }
1399 }
1400
1401 if summary_aw_handshake {
1402 control.summary_aw_handshake = true;
1403 control.run_state = RunState::SummaryW;
1404 }
1405 if summary_w_handshake {
1406 control.summary_w_handshake = true;
1407 control.run_state = RunState::SummaryB;
1408 }
1409 if summary_b_handshake {
1410 let summary_ok =
1411 output_response_is_canonical_kernel(input.output_hbm.bid, input.output_hbm.bresp);
1412 control.summary_b_handshake = true;
1413 control.summary_b_canonical = summary_ok;
1414 if !summary_ok {
1415 if q.control.error_flags == b16(0) {
1416 control.first_error_job = b32(0xffff_ffff);
1417 control.first_error_detail =
1418 input.output_hbm.bresp.resize() | (input.output_hbm.bid.resize() << 8);
1419 }
1420 control.error_flags = q.control.error_flags | b16(ERROR_SUMMARY_AXI);
1421 control.terminal_code = TERMINAL_SUMMARY_AXI_BITS;
1422 }
1423 control.ap_done = true;
1424 control.isr = control.isr | b2(1);
1425 if !summary_ok || q.control.error_flags != b16(0) {
1426 control.isr = control.isr | b2(2);
1427 }
1428 control.run_state = RunState::Done;
1429 }
1430 if q.control.run_state == RunState::SummaryB && premature_summary_b_handshake {
1431 control.ap_done = true;
1435 control.isr = control.isr | b2(3);
1436 control.run_state = RunState::Done;
1437 }
1438
1439 let mut seed = [b8(0); 32];
1440 let mut message = [b8(0); 32];
1441 for lane in 0..32 {
1442 seed[lane] = q.input_job.data[lane];
1443 message[lane] = q.input_job.data[32 + lane];
1444 }
1445 let signer = SignerTopInput {
1446 start_valid: signer_start_valid && !resetting,
1447 private_seed: seed,
1448 message,
1449 job_id: q.input_job.job_id,
1450 output_ready: signer_output_ready && !resetting,
1451 error_ready: signer_error_ready && !resetting,
1452 };
1453
1454 let mut input_hbm = idle_read_master_kernel();
1455 input_hbm.rready = (drain_orphan_input_r
1456 || q.control.reader_state == ReaderState::Address
1457 || q.control.reader_state == ReaderState::Response)
1458 && !resetting;
1459 if q.control.reader_state == ReaderState::Address {
1460 input_hbm.arvalid = !resetting;
1461 input_hbm.araddr =
1462 input_job_address_kernel(q.control.config.input_base, q.control.reader_job);
1463 }
1464 if q.control.reader_state == ReaderState::Response {
1465 input_hbm.rready = !resetting;
1466 }
1467
1468 let summary_fields = SummaryFields {
1469 terminal_code: q.control.terminal_code,
1470 nonce: q.control.config.nonce,
1471 payload_cycles: q.control.payload_cycles,
1472 core_latency_cycles: q.control.core_latency_cycles,
1473 capture_span_cycles: q.control.capture_span_cycles,
1474 batch: q.control.config.batch.resize(),
1475 input_ar: q.control.input_ar_count,
1476 input_r: q.control.input_r_count,
1477 core_admissions: q.control.core_admission_count,
1478 frames_captured: q.control.frame_capture_count,
1479 core_errors: q.control.core_error_count,
1480 payload_aw: q.control.payload_aw_count,
1481 payload_b: q.control.payload_b_count,
1482 payload_w: q.control.payload_w_count,
1483 memory_completed: q.control.memory_completed_count,
1484 error_flags: q.control.error_flags,
1485 };
1486 let mut output_hbm = Axi512WriteKernelToMemory::default();
1487 output_hbm.bready = (drain_orphan_output_b
1488 || q.control.writer_state == WriterState::Response
1489 || (q.control.writer_state == WriterState::Data
1490 && q.control.writer_beat == b6(34)
1491 && !q.control.writer_early_b_drained)
1492 || summary_sequence_active)
1496 && !resetting;
1497 if q.control.writer_state == WriterState::Address {
1498 output_hbm.awvalid = !resetting;
1499 output_hbm.awaddr =
1500 output_job_address_kernel(q.control.config.output_base, q.control.writer_job);
1501 output_hbm.awlen = OUTPUT_AXI_LEN;
1502 } else if q.control.writer_state == WriterState::Data {
1503 output_hbm.wvalid = q.control.prefetch_count != b2(0) && !resetting;
1504 output_hbm.wdata = q.prefetch0;
1505 output_hbm.wstrb = if q.control.writer_beat == b6(34) {
1506 b64(0x0000_0000_ffff_ffff)
1507 } else {
1508 b64(0xffff_ffff_ffff_ffff)
1509 };
1510 output_hbm.wlast = q.control.writer_beat == b6(34);
1511 } else if q.control.writer_state == WriterState::Response {
1512 output_hbm.bready = !resetting;
1513 } else if q.control.run_state == RunState::SummaryAw {
1514 output_hbm.awvalid = !resetting;
1515 output_hbm.awaddr = q.control.config.summary_base;
1516 output_hbm.awlen = b8(0);
1517 } else if q.control.run_state == RunState::SummaryW {
1518 output_hbm.wvalid = !resetting;
1519 output_hbm.wdata = summary_line_kernel(summary_fields);
1520 output_hbm.wstrb = b64(0xffff_ffff_ffff_ffff);
1521 output_hbm.wlast = true;
1522 } else if q.control.run_state == RunState::SummaryB {
1523 output_hbm.bready = !resetting;
1524 }
1525 output_hbm.awid = b1(0);
1526 output_hbm.awsize = AXI_SIZE_64_BYTES;
1527 output_hbm.awburst = AXI_BURST_INCR;
1528 output_hbm.awlock = AXI_LOCK_NORMAL;
1529 output_hbm.awcache = AXI_CACHE_NORMAL;
1530 output_hbm.awprot = b3(0);
1531 output_hbm.awqos = b4(0);
1532 output_hbm.awregion = b4(0);
1533
1534 let output = HbmShellOutput {
1535 control: AxiLiteKernelToHost {
1536 awready: axil_awready && !resetting,
1537 wready: axil_wready && !resetting,
1538 bvalid: q.control.axil_bvalid && !resetting,
1539 bresp: q.control.axil_bresp,
1540 arready: axil_arready && !resetting,
1541 rvalid: q.control.axil_rvalid && !resetting,
1542 rdata: q.control.axil_rdata,
1543 rresp: q.control.axil_rresp,
1544 },
1545 input_hbm,
1546 output_hbm,
1547 signer,
1548 signer_soft_reset: !resetting
1549 && (q.control.run_state == RunState::CoreReset0
1550 || q.control.run_state == RunState::CoreReset1),
1551 interrupt: !resetting && q.control.gier && ((q.control.ier & q.control.isr) != b2(0)),
1552 };
1553
1554 if resetting {
1555 control = ShellControl::default();
1556 frame_store = FrameStoreInput::default();
1557 }
1558 let d = U280HbmShellD {
1559 control,
1560 input_job,
1561 ingress,
1562 prefetch0,
1563 prefetch1,
1564 frame_store,
1565 };
1566 (output, d)
1567}
1568
1569#[cfg(test)]
1570mod tests {
1571 use super::*;
1572
1573 fn pending_abort(mut control: ShellControl) -> ShellControl {
1574 control.run_state = RunState::Execute;
1575 control.axil_aw_pending = true;
1576 control.axil_awaddr = b8(abi::register::COMMAND);
1577 control.axil_w_pending = true;
1578 control.axil_wdata = b32(1);
1579 control.axil_wstrb = b4(1);
1580 control
1581 }
1582
1583 fn shell_q(control: ShellControl) -> U280HbmShellQ {
1584 U280HbmShellQ {
1585 control,
1586 input_job: InputJob::default(),
1587 ingress: SignerOutputBeat::default(),
1588 prefetch0: [b8(0); 64],
1589 prefetch1: [b8(0); 64],
1590 frame_store: [b8(0); 64],
1591 }
1592 }
1593
1594 fn active_clock() -> ClockReset {
1595 clock_reset(clock(false), reset(false))
1596 }
1597
1598 fn status_for(control: ShellControl) -> b32 {
1599 last_status_kernel(
1600 control.terminal_code,
1601 control.error_flags,
1602 SummaryWriteStatus {
1603 aw: control.summary_aw_handshake,
1604 w: control.summary_w_handshake,
1605 b: control.summary_b_handshake,
1606 b_canonical: control.summary_b_canonical,
1607 },
1608 )
1609 }
1610
1611 fn summary_response_input(
1612 awready: bool,
1613 wready: bool,
1614 bvalid: bool,
1615 bid: b1,
1616 bresp: b2,
1617 ) -> HbmShellInput {
1618 HbmShellInput {
1619 output_hbm: Axi512WriteMemoryToKernel {
1620 awready,
1621 wready,
1622 bvalid,
1623 bid,
1624 bresp,
1625 },
1626 ..HbmShellInput::default()
1627 }
1628 }
1629
1630 #[test]
1631 fn axil_aw_before_w_cannot_turn_a_busy_start_into_a_stale_idle_start() {
1632 let mut active = ShellControl::default();
1633 active.run_state = RunState::Execute;
1634 active.config.batch = b32(1);
1635 active.reader_state = ReaderState::Hold;
1636 active.input_job_valid = true;
1637 let (_output, aw_d) = u280_hbm_shell_kernel(
1638 active_clock(),
1639 HbmShellInput {
1640 control: AxiLiteHostToKernel {
1641 awvalid: true,
1642 awaddr: b8(abi::register::AP_CTRL),
1643 ..AxiLiteHostToKernel::default()
1644 },
1645 ..HbmShellInput::default()
1646 },
1647 shell_q(active),
1648 );
1649 assert!(aw_d.control.axil_aw_pending);
1650 assert!(aw_d.control.axil_aw_locked_at_accept);
1651
1652 let mut crossed_boundary = aw_d.control;
1653 crossed_boundary.run_state = RunState::Idle;
1654 let (_output, w_d) = u280_hbm_shell_kernel(
1655 active_clock(),
1656 HbmShellInput {
1657 control: AxiLiteHostToKernel {
1658 wvalid: true,
1659 wdata: b32(1),
1660 wstrb: b4(1),
1661 ..AxiLiteHostToKernel::default()
1662 },
1663 ..HbmShellInput::default()
1664 },
1665 shell_q(crossed_boundary),
1666 );
1667 assert!(w_d.control.axil_w_pending);
1668 assert!(!w_d.control.axil_w_locked_at_accept);
1669
1670 let (_output, rejected) = u280_hbm_shell_kernel(
1671 active_clock(),
1672 HbmShellInput::default(),
1673 shell_q(w_d.control),
1674 );
1675 assert!(rejected.control.axil_bvalid);
1676 assert_eq!(rejected.control.axil_bresp, AXI_RESP_SLVERR);
1677 assert_eq!(rejected.control.run_state, RunState::Idle);
1678 }
1679
1680 #[test]
1681 fn axil_w_before_aw_retains_the_busy_configuration_lock() {
1682 let mut active = ShellControl::default();
1683 active.run_state = RunState::Execute;
1684 active.config.batch = b32(7);
1685 active.reader_state = ReaderState::Hold;
1686 active.input_job_valid = true;
1687 let (_output, w_d) = u280_hbm_shell_kernel(
1688 active_clock(),
1689 HbmShellInput {
1690 control: AxiLiteHostToKernel {
1691 wvalid: true,
1692 wdata: b32(99),
1693 wstrb: b4(0xf),
1694 ..AxiLiteHostToKernel::default()
1695 },
1696 ..HbmShellInput::default()
1697 },
1698 shell_q(active),
1699 );
1700 assert!(w_d.control.axil_w_locked_at_accept);
1701
1702 let mut crossed_boundary = w_d.control;
1703 crossed_boundary.run_state = RunState::Idle;
1704 let (_output, aw_d) = u280_hbm_shell_kernel(
1705 active_clock(),
1706 HbmShellInput {
1707 control: AxiLiteHostToKernel {
1708 awvalid: true,
1709 awaddr: b8(abi::register::BATCH),
1710 ..AxiLiteHostToKernel::default()
1711 },
1712 ..HbmShellInput::default()
1713 },
1714 shell_q(crossed_boundary),
1715 );
1716 let (_output, rejected) = u280_hbm_shell_kernel(
1717 active_clock(),
1718 HbmShellInput::default(),
1719 shell_q(aw_d.control),
1720 );
1721 assert_eq!(rejected.control.axil_bresp, AXI_RESP_SLVERR);
1722 assert_eq!(rejected.control.config.batch, b32(7));
1723 }
1724
1725 #[test]
1726 fn axil_simultaneous_idle_pair_commits_but_busy_pair_remains_locked() {
1727 let idle = ShellControl::default();
1728 let pair = HbmShellInput {
1729 control: AxiLiteHostToKernel {
1730 awvalid: true,
1731 awaddr: b8(abi::register::BATCH),
1732 wvalid: true,
1733 wdata: b32(123),
1734 wstrb: b4(0xf),
1735 ..AxiLiteHostToKernel::default()
1736 },
1737 ..HbmShellInput::default()
1738 };
1739 let (_output, captured) = u280_hbm_shell_kernel(active_clock(), pair, shell_q(idle));
1740 assert!(!captured.control.axil_aw_locked_at_accept);
1741 assert!(!captured.control.axil_w_locked_at_accept);
1742 let (_output, committed) = u280_hbm_shell_kernel(
1743 active_clock(),
1744 HbmShellInput::default(),
1745 shell_q(captured.control),
1746 );
1747 assert_eq!(committed.control.axil_bresp, AXI_RESP_OKAY);
1748 assert_eq!(committed.control.config.batch, b32(123));
1749
1750 let mut active = ShellControl::default();
1751 active.run_state = RunState::Execute;
1752 active.config.batch = b32(1);
1753 active.reader_state = ReaderState::Hold;
1754 active.input_job_valid = true;
1755 let (_output, captured_busy) = u280_hbm_shell_kernel(active_clock(), pair, shell_q(active));
1756 assert!(captured_busy.control.axil_aw_locked_at_accept);
1757 assert!(captured_busy.control.axil_w_locked_at_accept);
1758 let mut crossed_boundary = captured_busy.control;
1759 crossed_boundary.run_state = RunState::Idle;
1760 let (_output, rejected) = u280_hbm_shell_kernel(
1761 active_clock(),
1762 HbmShellInput::default(),
1763 shell_q(crossed_boundary),
1764 );
1765 assert_eq!(rejected.control.axil_bresp, AXI_RESP_SLVERR);
1766 assert_eq!(rejected.control.config.batch, b32(1));
1767 }
1768
1769 #[test]
1770 fn axil_abort_intent_is_executed_while_active_and_rejected_after_boundary() {
1771 let mut active = ShellControl::default();
1772 active.run_state = RunState::Execute;
1773 active.config.batch = b32(1);
1774 active.reader_state = ReaderState::Hold;
1775 active.input_job_valid = true;
1776 let abort_pair = HbmShellInput {
1777 control: AxiLiteHostToKernel {
1778 awvalid: true,
1779 awaddr: b8(abi::register::COMMAND),
1780 wvalid: true,
1781 wdata: b32(1),
1782 wstrb: b4(1),
1783 ..AxiLiteHostToKernel::default()
1784 },
1785 ..HbmShellInput::default()
1786 };
1787 let (_output, captured) =
1788 u280_hbm_shell_kernel(active_clock(), abort_pair, shell_q(active));
1789 let (_output, aborted) = u280_hbm_shell_kernel(
1790 active_clock(),
1791 HbmShellInput::default(),
1792 shell_q(captured.control),
1793 );
1794 assert_eq!(aborted.control.axil_bresp, AXI_RESP_OKAY);
1795 assert_eq!(aborted.control.run_state, RunState::AbortDrain);
1796 assert_ne!(aborted.control.error_flags & b16(ERROR_SOFT_ABORT), b16(0));
1797
1798 let mut split_active = active;
1799 let (_output, aw_d) = u280_hbm_shell_kernel(
1800 active_clock(),
1801 HbmShellInput {
1802 control: AxiLiteHostToKernel {
1803 awvalid: true,
1804 awaddr: b8(abi::register::COMMAND),
1805 ..AxiLiteHostToKernel::default()
1806 },
1807 ..HbmShellInput::default()
1808 },
1809 shell_q(split_active),
1810 );
1811 split_active = aw_d.control;
1812 split_active.run_state = RunState::Idle;
1813 let (_output, w_d) = u280_hbm_shell_kernel(
1814 active_clock(),
1815 HbmShellInput {
1816 control: AxiLiteHostToKernel {
1817 wvalid: true,
1818 wdata: b32(1),
1819 wstrb: b4(1),
1820 ..AxiLiteHostToKernel::default()
1821 },
1822 ..HbmShellInput::default()
1823 },
1824 shell_q(split_active),
1825 );
1826 let (_output, stale) = u280_hbm_shell_kernel(
1827 active_clock(),
1828 HbmShellInput::default(),
1829 shell_q(w_d.control),
1830 );
1831 assert_eq!(stale.control.axil_bresp, AXI_RESP_SLVERR);
1832 assert_eq!(stale.control.run_state, RunState::Idle);
1833 assert_eq!(stale.control.error_flags & b16(ERROR_SOFT_ABORT), b16(0));
1834 }
1835
1836 #[test]
1837 fn same_cycle_abort_suppresses_signer_admission_and_next_input_ar() {
1838 let mut control = pending_abort(ShellControl::default());
1839 control.reader_state = ReaderState::Hold;
1840 control.input_job_valid = true;
1841 control.config.batch = b32(2);
1842 let mut q = shell_q(control);
1843 q.input_job.job_id = b32(0);
1844 let input = HbmShellInput {
1845 signer: SignerTopOutput {
1846 start_ready: true,
1847 ..SignerTopOutput::default()
1848 },
1849 ..HbmShellInput::default()
1850 };
1851 let (output, d) = u280_hbm_shell_kernel(active_clock(), input, q);
1852 assert!(!output.signer.start_valid);
1853 assert_eq!(d.control.core_admission_count, b16(0));
1854 assert_ne!(d.control.reader_state, ReaderState::Address);
1855 assert_eq!(d.control.run_state, RunState::AbortDrain);
1856 }
1857
1858 #[test]
1859 fn same_cycle_abort_discards_held_ingress_without_starting_a_frame() {
1860 let mut control = pending_abort(ShellControl::default());
1861 control.ingress_valid = true;
1862 let mut q = shell_q(control);
1863 q.ingress = SignerOutputBeat {
1864 beat: wots_rhdl::LaneLocalBeat {
1865 data: [b8(0x5a); 64],
1866 keep: b64(0xffff_ffff_ffff_ffff),
1867 last: false,
1868 job_id: b32(7),
1869 },
1870 cluster: b2(0),
1871 };
1872 let (output, d) = u280_hbm_shell_kernel(active_clock(), HbmShellInput::default(), q);
1873 assert!(!output.signer.output_ready);
1874 assert!(!d.frame_store.write.enable);
1875 assert!(!d.control.capture_active);
1876 assert_eq!(d.control.frame_queue.count, b2(0));
1877 assert_eq!(d.control.run_state, RunState::AbortDrain);
1878 }
1879
1880 #[test]
1881 fn same_cycle_abort_never_commits_an_idle_writer() {
1882 let mut control = pending_abort(ShellControl::default());
1883 control.frame_queue = FrameQueue {
1884 count: b2(1),
1885 head_slot: b1(0),
1886 head_job: b32(9),
1887 ..FrameQueue::default()
1888 };
1889 let q = shell_q(control);
1890 let (output, d) = u280_hbm_shell_kernel(active_clock(), HbmShellInput::default(), q);
1891 assert!(!output.output_hbm.awvalid);
1892 assert_eq!(d.control.writer_state, WriterState::Idle);
1893 assert_eq!(d.control.payload_aw_count, b16(0));
1894 assert_eq!(d.control.run_state, RunState::AbortDrain);
1895 }
1896
1897 #[test]
1898 fn orphan_input_response_is_drained_and_fails_closed() {
1899 let mut control = ShellControl::default();
1900 control.run_state = RunState::Execute;
1901 let q = shell_q(control);
1902 let input = HbmShellInput {
1903 input_hbm: Axi512ReadMemoryToKernel {
1904 rvalid: true,
1905 rlast: true,
1906 ..Axi512ReadMemoryToKernel::default()
1907 },
1908 ..HbmShellInput::default()
1909 };
1910 let (output, d) = u280_hbm_shell_kernel(active_clock(), input, q);
1911 assert!(output.input_hbm.rready);
1912 assert_eq!(d.control.run_state, RunState::AbortDrain);
1913 assert_ne!(d.control.error_flags & b16(ERROR_INPUT_AXI), b16(0));
1914 }
1915
1916 #[test]
1917 fn orphan_output_response_is_drained_and_fails_closed() {
1918 let mut control = ShellControl::default();
1919 control.run_state = RunState::Execute;
1920 let q = shell_q(control);
1921 let input = HbmShellInput {
1922 output_hbm: Axi512WriteMemoryToKernel {
1923 bvalid: true,
1924 ..Axi512WriteMemoryToKernel::default()
1925 },
1926 ..HbmShellInput::default()
1927 };
1928 let (output, d) = u280_hbm_shell_kernel(active_clock(), input, q);
1929 assert!(output.output_hbm.bready);
1930 assert_eq!(d.control.run_state, RunState::AbortDrain);
1931 assert_ne!(d.control.error_flags & b16(ERROR_OUTPUT_AXI), b16(0));
1932 }
1933
1934 #[test]
1935 fn input_address_and_canonical_response_can_transfer_in_one_cycle() {
1936 let mut control = ShellControl::default();
1937 control.run_state = RunState::Execute;
1938 control.config.batch = b32(1);
1939 control.reader_state = ReaderState::Address;
1940 control.reader_job = b32(0);
1941 let data = [b8(0x5a); 64];
1942 let input = HbmShellInput {
1943 input_hbm: Axi512ReadMemoryToKernel {
1944 arready: true,
1945 rvalid: true,
1946 rdata: data,
1947 rid: b1(0),
1948 rresp: AXI_RESP_OKAY,
1949 rlast: true,
1950 },
1951 ..HbmShellInput::default()
1952 };
1953 let (output, d) = u280_hbm_shell_kernel(active_clock(), input, shell_q(control));
1954 assert!(output.input_hbm.arvalid);
1955 assert!(output.input_hbm.rready);
1956 assert_eq!(d.control.input_ar_count, b16(1));
1957 assert_eq!(d.control.input_r_count, b16(1));
1958 assert_eq!(d.control.reader_state, ReaderState::Hold);
1959 assert!(d.control.input_job_valid);
1960 assert_eq!(d.input_job.data, data);
1961 assert_eq!(d.control.error_flags & b16(ERROR_INPUT_AXI), b16(0));
1962 }
1963
1964 #[test]
1965 fn input_response_without_arready_is_prearmed_drained_and_fails_closed() {
1966 let mut control = ShellControl::default();
1967 control.run_state = RunState::Execute;
1968 control.config.batch = b32(1);
1969 control.reader_state = ReaderState::Address;
1970 control.reader_job = b32(0);
1971 let input = HbmShellInput {
1972 input_hbm: Axi512ReadMemoryToKernel {
1973 arready: false,
1974 rvalid: true,
1975 rid: b1(0),
1976 rresp: AXI_RESP_OKAY,
1977 rlast: true,
1978 ..Axi512ReadMemoryToKernel::default()
1979 },
1980 ..HbmShellInput::default()
1981 };
1982 let (output, d) = u280_hbm_shell_kernel(active_clock(), input, shell_q(control));
1983 assert!(output.input_hbm.arvalid);
1984 assert!(output.input_hbm.rready, "RREADY is pre-armed from q-state");
1985 assert_eq!(d.control.input_ar_count, b16(0));
1986 assert_eq!(d.control.input_r_count, b16(0));
1987 assert_eq!(d.control.reader_state, ReaderState::Idle);
1988 assert_eq!(d.control.run_state, RunState::AbortDrain);
1989 assert_ne!(d.control.error_flags & b16(ERROR_INPUT_AXI), b16(0));
1990 }
1991
1992 #[test]
1993 fn final_payload_w_and_canonical_b_can_retire_in_one_cycle() {
1994 let mut control = ShellControl::default();
1995 control.run_state = RunState::Execute;
1996 control.config.batch = b32(1);
1997 control.writer_state = WriterState::Data;
1998 control.writer_job = b32(0);
1999 control.writer_beat = b6(34);
2000 control.prefetch_count = b2(1);
2001 control.payload_aw_count = b16(1);
2002 control.frame_queue = FrameQueue {
2003 count: b2(1),
2004 head_slot: b1(0),
2005 head_job: b32(0),
2006 ..FrameQueue::default()
2007 };
2008 control.run_cycle = b64(91);
2009 let input = HbmShellInput {
2010 output_hbm: Axi512WriteMemoryToKernel {
2011 wready: true,
2012 bvalid: true,
2013 bid: b1(0),
2014 bresp: AXI_RESP_OKAY,
2015 ..Axi512WriteMemoryToKernel::default()
2016 },
2017 ..HbmShellInput::default()
2018 };
2019 let (output, d) = u280_hbm_shell_kernel(active_clock(), input, shell_q(control));
2020 assert!(output.output_hbm.wvalid);
2021 assert!(output.output_hbm.wlast);
2022 assert!(output.output_hbm.bready);
2023 assert_eq!(d.control.writer_state, WriterState::Idle);
2024 assert_eq!(d.control.payload_w_count, b32(1));
2025 assert_eq!(d.control.payload_b_count, b16(1));
2026 assert_eq!(d.control.memory_completed_count, b16(1));
2027 assert_eq!(d.control.frame_queue.count, b2(0));
2028 assert_eq!(d.control.error_flags & b16(ERROR_OUTPUT_AXI), b16(0));
2029 }
2030
2031 #[test]
2032 fn final_payload_b_without_wready_is_prearmed_drained_and_does_not_deadlock() {
2033 let mut control = ShellControl::default();
2034 control.run_state = RunState::Execute;
2035 control.config.batch = b32(1);
2036 control.writer_state = WriterState::Data;
2037 control.writer_job = b32(0);
2038 control.writer_beat = b6(34);
2039 control.prefetch_count = b2(1);
2040 control.frame_queue = FrameQueue {
2041 count: b2(1),
2042 head_slot: b1(0),
2043 head_job: b32(0),
2044 ..FrameQueue::default()
2045 };
2046 let early_b = HbmShellInput {
2047 output_hbm: Axi512WriteMemoryToKernel {
2048 wready: false,
2049 bvalid: true,
2050 bid: b1(0),
2051 bresp: AXI_RESP_OKAY,
2052 ..Axi512WriteMemoryToKernel::default()
2053 },
2054 ..HbmShellInput::default()
2055 };
2056 let (early_output, early_d) =
2057 u280_hbm_shell_kernel(active_clock(), early_b, shell_q(control));
2058 assert!(early_output.output_hbm.wvalid);
2059 assert!(early_output.output_hbm.wlast);
2060 assert!(
2061 early_output.output_hbm.bready,
2062 "BREADY is pre-armed from q-state, independent of WREADY"
2063 );
2064 assert_eq!(early_d.control.run_state, RunState::AbortDrain);
2065 assert_eq!(early_d.control.writer_state, WriterState::Data);
2066 assert!(early_d.control.writer_early_b_drained);
2067 assert_eq!(early_d.control.payload_b_count, b16(1));
2068 assert_eq!(early_d.control.memory_completed_count, b16(0));
2069 assert_ne!(early_d.control.error_flags & b16(ERROR_OUTPUT_AXI), b16(0));
2070
2071 let (final_output, final_d) = u280_hbm_shell_kernel(
2072 active_clock(),
2073 HbmShellInput {
2074 output_hbm: Axi512WriteMemoryToKernel {
2075 wready: true,
2076 ..Axi512WriteMemoryToKernel::default()
2077 },
2078 ..HbmShellInput::default()
2079 },
2080 shell_q(early_d.control),
2081 );
2082 assert!(final_output.output_hbm.wvalid);
2083 assert!(final_output.output_hbm.wlast);
2084 assert!(!final_output.output_hbm.bready);
2085 assert_eq!(final_d.control.writer_state, WriterState::Idle);
2086 assert!(!final_d.control.writer_early_b_drained);
2087 assert_eq!(final_d.control.frame_queue.count, b2(0));
2088 assert_eq!(final_d.control.payload_w_count, b32(1));
2089 assert_eq!(final_d.control.payload_b_count, b16(1));
2090 }
2091
2092 #[test]
2093 fn payload_b_before_wlast_fails_closed_without_accepting_the_response() {
2094 let mut control = ShellControl::default();
2095 control.run_state = RunState::Execute;
2096 control.config.batch = b32(1);
2097 control.writer_state = WriterState::Data;
2098 control.writer_job = b32(0);
2099 control.writer_beat = b6(33);
2100 control.prefetch_count = b2(1);
2101 control.frame_queue = FrameQueue {
2102 count: b2(1),
2103 head_slot: b1(0),
2104 head_job: b32(0),
2105 ..FrameQueue::default()
2106 };
2107 let input = HbmShellInput {
2108 output_hbm: Axi512WriteMemoryToKernel {
2109 bvalid: true,
2110 bid: b1(0),
2111 bresp: AXI_RESP_OKAY,
2112 ..Axi512WriteMemoryToKernel::default()
2113 },
2114 ..HbmShellInput::default()
2115 };
2116 let (output, d) = u280_hbm_shell_kernel(active_clock(), input, shell_q(control));
2117 assert!(!output.output_hbm.bready);
2118 assert_eq!(d.control.payload_b_count, b16(0));
2119 assert_eq!(d.control.run_state, RunState::AbortDrain);
2120 assert_ne!(d.control.error_flags & b16(ERROR_OUTPUT_AXI), b16(0));
2121 }
2122
2123 #[test]
2124 fn held_canonical_b_from_summary_aw_is_drained_and_never_reports_success() {
2125 let mut control = ShellControl::default();
2126 control.run_state = RunState::SummaryAw;
2127 control.config.summary_base = b64(0x0000_0003_0000_0000);
2128
2129 let (first_output, first_d) = u280_hbm_shell_kernel(
2130 active_clock(),
2131 summary_response_input(false, false, true, b1(0), AXI_RESP_OKAY),
2132 shell_q(control),
2133 );
2134 assert!(first_output.output_hbm.awvalid);
2135 assert!(first_output.output_hbm.bready);
2136 assert_eq!(first_d.control.run_state, RunState::SummaryAw);
2137 assert!(!first_d.control.summary_aw_handshake);
2138 assert!(!first_d.control.summary_w_handshake);
2139 assert!(!first_d.control.summary_b_handshake);
2140 assert_ne!(first_d.control.error_flags & b16(ERROR_SUMMARY_AXI), b16(0));
2141 assert_eq!(first_d.control.terminal_code, TERMINAL_SUMMARY_AXI_BITS);
2142 assert!(!crate::control::last_status_is_host_success_kernel(
2143 status_for(first_d.control)
2144 ));
2145
2146 let (aw_output, aw_d) = u280_hbm_shell_kernel(
2147 active_clock(),
2148 summary_response_input(true, false, true, b1(0), AXI_RESP_OKAY),
2149 shell_q(first_d.control),
2150 );
2151 assert!(aw_output.output_hbm.awvalid);
2152 assert!(aw_output.output_hbm.bready);
2153 assert_eq!(aw_d.control.run_state, RunState::SummaryW);
2154 assert!(aw_d.control.summary_aw_handshake);
2155 assert!(!aw_d.control.summary_w_handshake);
2156 assert!(!aw_d.control.summary_b_handshake);
2157
2158 let (stalled_output, stalled_d) = u280_hbm_shell_kernel(
2159 active_clock(),
2160 summary_response_input(false, false, true, b1(0), AXI_RESP_OKAY),
2161 shell_q(aw_d.control),
2162 );
2163 assert!(stalled_output.output_hbm.wvalid);
2164 assert!(stalled_output.output_hbm.bready);
2165 assert_eq!(stalled_d.control.run_state, RunState::SummaryW);
2166 assert!(!stalled_d.control.summary_w_handshake);
2167 assert!(!stalled_d.control.summary_b_handshake);
2168
2169 let (close_output, close_d) = u280_hbm_shell_kernel(
2170 active_clock(),
2171 summary_response_input(false, true, true, b1(0), AXI_RESP_OKAY),
2172 shell_q(stalled_d.control),
2173 );
2174 assert!(close_output.output_hbm.wvalid);
2175 assert!(close_output.output_hbm.bready);
2176 assert_eq!(close_d.control.run_state, RunState::Done);
2177 assert!(close_d.control.summary_w_handshake);
2178 assert!(close_d.control.summary_b_handshake);
2179 assert!(close_d.control.summary_b_canonical);
2180 assert!(close_d.control.ap_done);
2181 assert_ne!(close_d.control.error_flags & b16(ERROR_SUMMARY_AXI), b16(0));
2182 assert_eq!(close_d.control.terminal_code, TERMINAL_SUMMARY_AXI_BITS);
2183 assert!(!crate::control::last_status_is_host_success_kernel(
2184 status_for(close_d.control)
2185 ));
2186 }
2187
2188 #[test]
2189 fn held_canonical_b_during_stalled_summary_w_is_sticky_failure() {
2190 let mut control = ShellControl::default();
2191 control.run_state = RunState::SummaryW;
2192 control.summary_aw_handshake = true;
2193
2194 let (first_output, first_d) = u280_hbm_shell_kernel(
2195 active_clock(),
2196 summary_response_input(false, false, true, b1(0), AXI_RESP_OKAY),
2197 shell_q(control),
2198 );
2199 assert!(first_output.output_hbm.wvalid);
2200 assert!(first_output.output_hbm.bready);
2201 assert_eq!(first_d.control.run_state, RunState::SummaryW);
2202 assert!(!first_d.control.summary_w_handshake);
2203 assert!(!first_d.control.summary_b_handshake);
2204 assert_ne!(first_d.control.error_flags & b16(ERROR_SUMMARY_AXI), b16(0));
2205
2206 let (held_output, held_d) = u280_hbm_shell_kernel(
2207 active_clock(),
2208 summary_response_input(false, false, true, b1(0), AXI_RESP_OKAY),
2209 shell_q(first_d.control),
2210 );
2211 assert!(held_output.output_hbm.wvalid);
2212 assert!(held_output.output_hbm.bready);
2213 assert_eq!(held_d.control.run_state, RunState::SummaryW);
2214 assert!(!held_d.control.summary_w_handshake);
2215 assert!(!held_d.control.summary_b_handshake);
2216
2217 let (w_output, w_d) = u280_hbm_shell_kernel(
2218 active_clock(),
2219 summary_response_input(false, true, false, b1(0), AXI_RESP_OKAY),
2220 shell_q(held_d.control),
2221 );
2222 assert!(w_output.output_hbm.wvalid);
2223 assert!(w_output.output_hbm.bready);
2224 assert_eq!(w_d.control.run_state, RunState::SummaryB);
2225 assert!(w_d.control.summary_w_handshake);
2226 assert!(!w_d.control.summary_b_handshake);
2227
2228 let (b_output, b_d) = u280_hbm_shell_kernel(
2229 active_clock(),
2230 summary_response_input(false, false, true, b1(0), AXI_RESP_OKAY),
2231 shell_q(w_d.control),
2232 );
2233 assert!(b_output.output_hbm.bready);
2234 assert_eq!(b_d.control.run_state, RunState::Done);
2235 assert!(b_d.control.summary_b_handshake);
2236 assert!(b_d.control.summary_b_canonical);
2237 assert_ne!(b_d.control.error_flags & b16(ERROR_SUMMARY_AXI), b16(0));
2238 assert_eq!(b_d.control.terminal_code, TERMINAL_SUMMARY_AXI_BITS);
2239 assert!(!crate::control::last_status_is_host_success_kernel(
2240 status_for(b_d.control)
2241 ));
2242 }
2243
2244 #[test]
2245 fn premature_summary_b_rejects_wrong_id_and_non_okay_responses() {
2246 for run_state in [RunState::SummaryAw, RunState::SummaryW] {
2247 for (bid, bresp) in [
2248 (b1(1), AXI_RESP_OKAY),
2249 (b1(0), AXI_RESP_SLVERR),
2250 (b1(0), AXI_RESP_DECERR),
2251 (b1(1), AXI_RESP_SLVERR),
2252 ] {
2253 let mut control = ShellControl::default();
2254 control.run_state = run_state;
2255 control.summary_aw_handshake = run_state == RunState::SummaryW;
2256 let (output, d) = u280_hbm_shell_kernel(
2257 active_clock(),
2258 summary_response_input(false, false, true, bid, bresp),
2259 shell_q(control),
2260 );
2261 assert_eq!(output.output_hbm.awvalid, run_state == RunState::SummaryAw);
2262 assert_eq!(output.output_hbm.wvalid, run_state == RunState::SummaryW);
2263 assert!(output.output_hbm.bready);
2264 assert_eq!(d.control.run_state, run_state);
2265 assert!(!d.control.summary_b_handshake);
2266 assert!(!d.control.summary_b_canonical);
2267 assert_eq!(d.control.terminal_code, TERMINAL_SUMMARY_AXI_BITS);
2268 assert_ne!(d.control.error_flags & b16(ERROR_SUMMARY_AXI), b16(0));
2269 assert_eq!(
2270 d.control.first_error_detail,
2271 b32(0x6003) | (bresp.resize() << 16) | (bid.resize() << 24)
2272 );
2273 assert!(!crate::control::last_status_is_host_success_kernel(
2274 status_for(d.control)
2275 ));
2276 }
2277 }
2278 }
2279
2280 #[test]
2281 fn legal_same_cycle_summary_w_and_b_retire_as_success() {
2282 let mut control = ShellControl::default();
2283 control.run_state = RunState::SummaryW;
2284 control.summary_aw_handshake = true;
2285 let (output, d) = u280_hbm_shell_kernel(
2286 active_clock(),
2287 summary_response_input(false, true, true, b1(0), AXI_RESP_OKAY),
2288 shell_q(control),
2289 );
2290 assert!(output.output_hbm.wvalid);
2291 assert!(output.output_hbm.wlast);
2292 assert!(output.output_hbm.bready);
2293 assert_eq!(d.control.run_state, RunState::Done);
2294 assert!(d.control.summary_aw_handshake);
2295 assert!(d.control.summary_w_handshake);
2296 assert!(d.control.summary_b_handshake);
2297 assert!(d.control.summary_b_canonical);
2298 assert_eq!(d.control.error_flags, b16(0));
2299 assert_eq!(d.control.terminal_code, TERMINAL_SUCCESS_BITS);
2300 assert!(crate::control::last_status_is_host_success_kernel(
2301 status_for(d.control)
2302 ));
2303 }
2304
2305 #[test]
2306 fn premature_b_during_core_reset_is_drained_before_summary_ownership() {
2307 let mut control = ShellControl::default();
2308 control.run_state = RunState::CoreReset0;
2309 let (output, first_d) = u280_hbm_shell_kernel(
2310 active_clock(),
2311 summary_response_input(false, false, true, b1(0), AXI_RESP_OKAY),
2312 shell_q(control),
2313 );
2314 assert!(output.output_hbm.bready);
2315 assert_eq!(first_d.control.run_state, RunState::CoreReset1);
2316 assert!(!first_d.control.summary_aw_handshake);
2317 assert!(!first_d.control.summary_w_handshake);
2318 assert!(!first_d.control.summary_b_handshake);
2319 assert_ne!(first_d.control.error_flags & b16(ERROR_SUMMARY_AXI), b16(0));
2320
2321 let (_output, second_d) = u280_hbm_shell_kernel(
2322 active_clock(),
2323 HbmShellInput::default(),
2324 shell_q(first_d.control),
2325 );
2326 assert_eq!(second_d.control.run_state, RunState::SummaryAw);
2327 assert!(!crate::control::last_status_is_host_success_kernel(
2328 status_for(second_d.control)
2329 ));
2330 }
2331
2332 #[test]
2333 fn summary_b_without_request_ownership_drains_and_terminates_failed() {
2334 let mut control = ShellControl::default();
2335 control.run_state = RunState::SummaryB;
2336 let (output, d) = u280_hbm_shell_kernel(
2337 active_clock(),
2338 summary_response_input(false, false, true, b1(0), AXI_RESP_OKAY),
2339 shell_q(control),
2340 );
2341 assert!(output.output_hbm.bready);
2342 assert_eq!(d.control.run_state, RunState::Done);
2343 assert!(d.control.ap_done);
2344 assert!(!d.control.summary_aw_handshake);
2345 assert!(!d.control.summary_w_handshake);
2346 assert!(!d.control.summary_b_handshake);
2347 assert!(!d.control.summary_b_canonical);
2348 assert_ne!(d.control.error_flags & b16(ERROR_SUMMARY_AXI), b16(0));
2349 assert_eq!(d.control.terminal_code, TERMINAL_SUMMARY_AXI_BITS);
2350 assert!(!crate::control::last_status_is_host_success_kernel(
2351 status_for(d.control)
2352 ));
2353 }
2354
2355 #[test]
2356 fn zero_batch_performs_only_the_successful_summary_transaction() {
2357 let mut control = ShellControl::default();
2358 control.run_state = RunState::Validate;
2359 control.config = LaunchConfig {
2360 input_base: b64(0x0000_0001_0000_0000),
2361 output_base: b64(0x0000_0002_0000_0000),
2362 summary_base: b64(0x0000_0003_0000_0000),
2363 batch: b32(0),
2364 abi_word: b32(abi::HASHSIGS_HBM_ABI_V1 as u128),
2365 nonce: b64(0x0123_4567_89ab_cdef),
2366 };
2367
2368 let (validate_output, validate_d) =
2369 u280_hbm_shell_kernel(active_clock(), HbmShellInput::default(), shell_q(control));
2370 assert_eq!(validate_d.control.run_state, RunState::Execute);
2371 assert!(!validate_output.input_hbm.arvalid);
2372 assert!(!validate_output.output_hbm.awvalid);
2373
2374 let (close_output, close_d) = u280_hbm_shell_kernel(
2375 active_clock(),
2376 HbmShellInput::default(),
2377 shell_q(validate_d.control),
2378 );
2379 assert_eq!(close_d.control.run_state, RunState::SummaryAw);
2380 assert!(!close_output.input_hbm.arvalid);
2381 assert!(!close_output.output_hbm.awvalid);
2382
2383 let (aw_output, aw_d) = u280_hbm_shell_kernel(
2384 active_clock(),
2385 HbmShellInput {
2386 output_hbm: Axi512WriteMemoryToKernel {
2387 awready: true,
2388 ..Axi512WriteMemoryToKernel::default()
2389 },
2390 ..HbmShellInput::default()
2391 },
2392 shell_q(close_d.control),
2393 );
2394 assert!(aw_output.output_hbm.awvalid);
2395 assert_eq!(aw_output.output_hbm.awaddr, control.config.summary_base);
2396 assert_eq!(aw_output.output_hbm.awlen, b8(0));
2397 assert_eq!(aw_d.control.run_state, RunState::SummaryW);
2398
2399 let (w_output, w_d) = u280_hbm_shell_kernel(
2400 active_clock(),
2401 HbmShellInput {
2402 output_hbm: Axi512WriteMemoryToKernel {
2403 wready: true,
2404 ..Axi512WriteMemoryToKernel::default()
2405 },
2406 ..HbmShellInput::default()
2407 },
2408 shell_q(aw_d.control),
2409 );
2410 assert!(w_output.output_hbm.wvalid);
2411 assert!(w_output.output_hbm.wlast);
2412 assert_eq!(w_output.output_hbm.wstrb, b64(0xffff_ffff_ffff_ffff));
2413 assert_eq!(w_d.control.run_state, RunState::SummaryB);
2414
2415 let (b_output, b_d) = u280_hbm_shell_kernel(
2416 active_clock(),
2417 HbmShellInput {
2418 output_hbm: Axi512WriteMemoryToKernel {
2419 bvalid: true,
2420 bid: b1(0),
2421 bresp: AXI_RESP_OKAY,
2422 ..Axi512WriteMemoryToKernel::default()
2423 },
2424 ..HbmShellInput::default()
2425 },
2426 shell_q(w_d.control),
2427 );
2428 assert!(b_output.output_hbm.bready);
2429 assert_eq!(b_d.control.run_state, RunState::Done);
2430 assert!(b_d.control.ap_done);
2431 assert_eq!(b_d.control.terminal_code, TERMINAL_SUCCESS_BITS);
2432 assert_eq!(b_d.control.error_flags, b16(0));
2433 assert_eq!(b_d.control.input_ar_count, b16(0));
2434 assert_eq!(b_d.control.input_r_count, b16(0));
2435 assert_eq!(b_d.control.core_admission_count, b16(0));
2436 assert_eq!(b_d.control.frame_capture_count, b16(0));
2437 assert_eq!(b_d.control.payload_aw_count, b16(0));
2438 assert_eq!(b_d.control.payload_w_count, b32(0));
2439 assert_eq!(b_d.control.payload_b_count, b16(0));
2440 assert_eq!(b_d.control.memory_completed_count, b16(0));
2441 let status = last_status_kernel(
2442 b_d.control.terminal_code,
2443 b_d.control.error_flags,
2444 SummaryWriteStatus {
2445 aw: b_d.control.summary_aw_handshake,
2446 w: b_d.control.summary_w_handshake,
2447 b: b_d.control.summary_b_handshake,
2448 b_canonical: b_d.control.summary_b_canonical,
2449 },
2450 );
2451 assert_eq!(status, b32(0x0f00_0000));
2452 assert!(crate::control::last_status_is_host_success_kernel(status));
2453 }
2454}