1use rhdl::prelude::*;
4
5use crate::abi::{
6 ABI_WORD_BITS, INPUT_ALIGNMENT_MASK, MAX_BATCH_BITS, OUTPUT_ALIGNMENT_MASK,
7 SHA256_PROFILE_WIRE_TAG_BITS, SUMMARY_ABI_REVISION_HI_BITS, SUMMARY_ABI_REVISION_LO_BITS,
8};
9
10#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
12pub enum RunState {
13 #[default]
15 Idle,
16 Validate,
18 Execute,
20 AbortDrain,
22 CoreReset0,
24 CoreReset1,
26 SummaryAw,
28 SummaryW,
30 SummaryB,
32 Done,
34}
35
36#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
38pub struct LaunchConfig {
39 pub input_base: b64,
41 pub output_base: b64,
43 pub summary_base: b64,
45 pub batch: b32,
47 pub abi_word: b32,
49 pub nonce: b64,
51}
52
53#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
55pub struct ConfigValidation {
56 pub valid: bool,
58 pub detail: b32,
60}
61
62#[kernel]
64#[allow(clippy::assign_op_pattern)] pub fn validate_config_kernel(config: LaunchConfig) -> ConfigValidation {
66 let batch64: b64 = config.batch.resize();
67 let input_bytes = batch64 << 6;
68 let output_bytes = batch64 << 12;
69 let max64 = b64(0xffff_ffff_ffff_ffff);
70 let input_overflow = config.input_base > max64 - input_bytes;
71 let output_overflow = config.output_base > max64 - output_bytes;
72 let summary_overflow = config.summary_base > max64 - b64(64);
73 let output_end = config.output_base + output_bytes;
74 let summary_end = config.summary_base + b64(64);
75 let overlaps_output = config.batch != b32(0)
76 && config.summary_base < output_end
77 && config.output_base < summary_end;
78
79 let mut detail = b32(0);
80 if config.abi_word != ABI_WORD_BITS {
81 detail = detail | b32(1 << 0);
82 }
83 if config.batch > MAX_BATCH_BITS {
84 detail = detail | b32(1 << 1);
85 }
86 if config.input_base == b64(0) {
87 detail = detail | b32(1 << 2);
88 }
89 if config.output_base == b64(0) {
90 detail = detail | b32(1 << 3);
91 }
92 if config.summary_base == b64(0) {
93 detail = detail | b32(1 << 4);
94 }
95 if config.input_base & INPUT_ALIGNMENT_MASK != b64(0) {
96 detail = detail | b32(1 << 5);
97 }
98 if config.output_base & OUTPUT_ALIGNMENT_MASK != b64(0) {
99 detail = detail | b32(1 << 6);
100 }
101 if config.summary_base & b64(63) != b64(0) {
102 detail = detail | b32(1 << 7);
103 }
104 if input_overflow {
105 detail = detail | b32(1 << 8);
106 }
107 if output_overflow {
108 detail = detail | b32(1 << 9);
109 }
110 if summary_overflow {
111 detail = detail | b32(1 << 10);
112 }
113 if overlaps_output {
114 detail = detail | b32(1 << 11);
115 }
116 ConfigValidation {
117 valid: detail == b32(0),
118 detail,
119 }
120}
121
122#[kernel]
124#[allow(clippy::cast_sign_loss, clippy::needless_range_loop)] pub fn apply_write_strobes_kernel(previous: b32, value: b32, strobes: b4) -> b32 {
126 let mut next = previous;
127 for lane in 0..4 {
128 let lane_selected = ((strobes >> b2(lane as u128)) & b4(1)) != b4(0);
129 if lane_selected {
130 let mask = b32(0xff) << b5((lane * 8) as u128);
131 next = (next & !mask) | (value & mask);
132 }
133 }
134 next
135}
136
137#[kernel]
139pub fn low_word_kernel(value: b64) -> b32 {
140 value.resize()
141}
142
143#[kernel]
145pub fn high_word_kernel(value: b64) -> b32 {
146 (value >> 32).resize()
147}
148
149#[kernel]
151pub fn replace_low_word_kernel(previous: b64, value: b32, strobes: b4) -> b64 {
152 let prior_low: b32 = previous.resize();
153 let next_low = apply_write_strobes_kernel(prior_low, value, strobes);
154 (previous & b64(0xffff_ffff_0000_0000)) | next_low.resize()
155}
156
157#[kernel]
159pub fn replace_high_word_kernel(previous: b64, value: b32, strobes: b4) -> b64 {
160 let prior_high: b32 = (previous >> 32).resize();
161 let next_high = apply_write_strobes_kernel(prior_high, value, strobes);
162 (previous & b64(0x0000_0000_ffff_ffff)) | (next_high.resize() << 32)
163}
164
165#[allow(clippy::struct_excessive_bools)] #[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
168pub struct SummaryWriteStatus {
169 pub aw: bool,
171 pub w: bool,
173 pub b: bool,
175 pub b_canonical: bool,
177}
178
179#[kernel]
181pub fn last_status_kernel(terminal_code: b8, error_flags: b16, summary: SummaryWriteStatus) -> b32 {
182 let aw = if summary.aw { b32(1) } else { b32(0) };
183 let w = if summary.w { b32(1) } else { b32(0) };
184 let b = if summary.b { b32(1) } else { b32(0) };
185 let canonical = if summary.b_canonical { b32(1) } else { b32(0) };
186 terminal_code.resize()
187 | (error_flags.resize() << 8)
188 | (aw << 24)
189 | (w << 25)
190 | (b << 26)
191 | (canonical << 27)
192}
193
194#[kernel]
196pub fn last_status_is_host_success_kernel(status: b32) -> bool {
197 status == b32(0x0f00_0000)
198}
199
200#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
202pub struct SummaryFields {
203 pub terminal_code: b8,
205 pub nonce: b64,
207 pub payload_cycles: b64,
209 pub core_latency_cycles: b64,
211 pub capture_span_cycles: b64,
213 pub batch: b16,
215 pub input_ar: b16,
217 pub input_r: b16,
219 pub core_admissions: b16,
221 pub frames_captured: b16,
223 pub core_errors: b16,
225 pub payload_aw: b16,
227 pub payload_b: b16,
229 pub payload_w: b32,
231 pub memory_completed: b16,
233 pub error_flags: b16,
235}
236
237#[kernel]
239#[allow(clippy::needless_range_loop)] pub fn summary_line_kernel(fields: SummaryFields) -> [b8; 64] {
241 let mut data = [b8(0); 64];
242 data[0] = b8(0x48);
243 data[1] = b8(0x53);
244 data[2] = b8(0x52);
245 data[3] = b8(0x31);
246 data[4] = SUMMARY_ABI_REVISION_LO_BITS;
247 data[5] = SUMMARY_ABI_REVISION_HI_BITS;
248 data[6] = SHA256_PROFILE_WIRE_TAG_BITS;
249 data[7] = fields.terminal_code;
250
251 for byte in 0..8 {
252 let shift = b6((byte * 8) as u128);
253 data[8 + byte] = (fields.nonce >> shift).resize();
254 data[16 + byte] = (fields.payload_cycles >> shift).resize();
255 data[24 + byte] = (fields.core_latency_cycles >> shift).resize();
256 data[32 + byte] = (fields.capture_span_cycles >> shift).resize();
257 }
258
259 let word5: b64 = fields.batch.resize()
260 | (fields.input_ar.resize() << 16)
261 | (fields.input_r.resize() << 32)
262 | (fields.core_admissions.resize() << 48);
263 let word6: b64 = fields.frames_captured.resize()
264 | (fields.core_errors.resize() << 16)
265 | (fields.payload_aw.resize() << 32)
266 | (fields.payload_b.resize() << 48);
267 let word7: b64 = fields.payload_w.resize()
268 | (fields.memory_completed.resize() << 32)
269 | (fields.error_flags.resize() << 48);
270 for byte in 0..8 {
271 let shift = b6((byte * 8) as u128);
272 data[40 + byte] = (word5 >> shift).resize();
273 data[48 + byte] = (word6 >> shift).resize();
274 data[56 + byte] = (word7 >> shift).resize();
275 }
276 data
277}