1use rhdl::prelude::*;
21use rhdl_fpga::core::{
22 dff::DFF,
23 ram::synchronous::{In as SyncBramIn, SyncBRAM, Write as SyncBramWrite},
24};
25use rhdl_primitives::NoResetDff;
26use sha256_rhdl::lane::{CompressionInput, CompressionOutput};
27
28use crate::{tag::decode_tag_kernel, verify::tasks::verification_tag_is_well_formed_kernel};
29
30pub const VERIFY_CLUSTER_CONTEXTS: usize = 4;
32pub const VERIFY_EXPECTED_TAG_ADDRESS_BITS: usize = 6;
34
35pub const VERIFY_DIAGNOSTIC_ISSUE_REJECT: u128 = 1 << 0;
37pub const VERIFY_DIAGNOSTIC_RETURN_TIMING: u128 = 1 << 1;
39pub const VERIFY_DIAGNOSTIC_RETURN_TAG: u128 = 1 << 2;
41pub const VERIFY_DIAGNOSTIC_RETURN_FORMAT: u128 = 1 << 3;
43pub const VERIFY_DIAGNOSTIC_RETURN_OWNER: u128 = 1 << 4;
45
46const VERIFY_EXPECTED_DUE_INSERT: u128 = 1_u128 << 63;
47
48const _: () = {
49 assert!(VERIFY_CLUSTER_CONTEXTS == 4);
50 assert!(VERIFY_EXPECTED_TAG_ADDRESS_BITS == 6);
51};
52
53#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
55pub struct VerifyTransportControl {
56 pub issue_cursor: b2,
58 pub launch_valid: bool,
60 pub launch_owner: b2,
62 pub response_due: b64,
64 pub pointer: b6,
66}
67
68#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
70pub struct VerifyTransportInput {
71 pub requests: [CompressionInput; VERIFY_CLUSTER_CONTEXTS],
73 pub assigned_generations: [b8; VERIFY_CLUSTER_CONTEXTS],
75 pub lane_response: CompressionOutput,
77 pub abort: bool,
79}
80
81#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
83pub struct VerifyTransportOutput {
84 pub lane_request: CompressionInput,
86 pub request_accepted: [bool; VERIFY_CLUSTER_CONTEXTS],
88 pub request_rejected: [bool; VERIFY_CLUSTER_CONTEXTS],
90 pub responses: [CompressionOutput; VERIFY_CLUSTER_CONTEXTS],
92 pub response_routed: [bool; VERIFY_CLUSTER_CONTEXTS],
94 pub launch_pending: bool,
96 pub expected_valid: bool,
98 pub expected_tag: b32,
100 pub fatal: bool,
102 pub diagnostics: b16,
104}
105
106#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
108pub struct VerifyTransportSelection {
109 pub valid: bool,
111 pub owner: b2,
113 pub next_cursor: b2,
115}
116
117#[kernel]
119#[allow(clippy::assign_op_pattern)] pub fn compression_input_equal_kernel(left: CompressionInput, right: CompressionInput) -> bool {
121 let mut equal = left.valid == right.valid && left.tag == right.tag;
122 for word in 0..8 {
123 equal = equal && left.chaining[word] == right.chaining[word];
124 }
125 for word in 0..16 {
126 equal = equal && left.block[word] == right.block[word];
127 }
128 equal
129}
130
131#[kernel]
133pub fn verify_transport_request_is_canonical_kernel(
134 request: CompressionInput,
135 physical_owner: b2,
136 assigned_generation: b8,
137) -> bool {
138 let fields = decode_tag_kernel(request.tag);
139 let physical_owner_wide: b4 = physical_owner.resize();
140 request.valid
141 && verification_tag_is_well_formed_kernel(request.tag)
142 && fields.context == physical_owner_wide
143 && fields.generation == assigned_generation
144}
145
146#[kernel]
148#[allow(clippy::assign_op_pattern)] pub fn select_verify_transport_request_kernel(
150 eligible: [bool; VERIFY_CLUSTER_CONTEXTS],
151 cursor: b2,
152 exclude_valid: bool,
153 excluded_owner: b2,
154) -> VerifyTransportSelection {
155 let mut selection = VerifyTransportSelection {
156 next_cursor: cursor,
157 ..VerifyTransportSelection::default()
158 };
159 let mut scan = cursor;
160 for _offset in 0..VERIFY_CLUSTER_CONTEXTS {
161 if !selection.valid && eligible[scan] && (!exclude_valid || scan != excluded_owner) {
162 selection.valid = true;
163 selection.owner = scan;
164 selection.next_cursor = scan + b2(1);
165 }
166 scan = scan + b2(1);
167 }
168 selection
169}
170
171#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
173#[rhdl(dq_no_prefix)]
174pub struct VerifyTransport {
175 control: DFF<VerifyTransportControl>,
176 launch: NoResetDff<CompressionInput>,
177 expected_tags: SyncBRAM<b32, VERIFY_EXPECTED_TAG_ADDRESS_BITS>,
178}
179
180impl Default for VerifyTransport {
181 fn default() -> Self {
182 Self {
183 control: DFF::new(VerifyTransportControl::default()),
184 launch: NoResetDff::new(),
185 expected_tags: SyncBRAM::default(),
186 }
187 }
188}
189
190impl SynchronousIO for VerifyTransport {
191 type I = VerifyTransportInput;
192 type O = VerifyTransportOutput;
193 type Kernel = verify_transport_kernel;
194}
195
196#[kernel]
198#[allow(clippy::assign_op_pattern, clippy::needless_range_loop)] pub fn verify_transport_kernel(
200 clock_reset: ClockReset,
201 input: VerifyTransportInput,
202 q: Q,
203) -> (VerifyTransportOutput, D) {
204 let resetting = clock_reset.reset.any();
205 let mut control = q.control;
206 let mut launch = q.launch;
207 let mut request_accepted = [false; VERIFY_CLUSTER_CONTEXTS];
208 let mut request_rejected = [false; VERIFY_CLUSTER_CONTEXTS];
209 let mut responses = [CompressionOutput::default(); VERIFY_CLUSTER_CONTEXTS];
210 let mut response_routed = [false; VERIFY_CLUSTER_CONTEXTS];
211 let mut lane_request = CompressionInput::default();
212 let mut diagnostics = b16(0);
213
214 let canonical = [
215 verify_transport_request_is_canonical_kernel(
216 input.requests[0],
217 b2(0),
218 input.assigned_generations[0],
219 ),
220 verify_transport_request_is_canonical_kernel(
221 input.requests[1],
222 b2(1),
223 input.assigned_generations[1],
224 ),
225 verify_transport_request_is_canonical_kernel(
226 input.requests[2],
227 b2(2),
228 input.assigned_generations[2],
229 ),
230 verify_transport_request_is_canonical_kernel(
231 input.requests[3],
232 b2(3),
233 input.assigned_generations[3],
234 ),
235 ];
236 for context in 0..VERIFY_CLUSTER_CONTEXTS {
237 request_rejected[context] = input.requests[context].valid && !canonical[context];
238 }
239
240 let held_request = match q.control.launch_owner {
241 Bits::<2>(0) => input.requests[0],
242 Bits::<2>(1) => input.requests[1],
243 Bits::<2>(2) => input.requests[2],
244 _ => input.requests[3],
245 };
246 let held_generation = match q.control.launch_owner {
247 Bits::<2>(0) => input.assigned_generations[0],
248 Bits::<2>(1) => input.assigned_generations[1],
249 Bits::<2>(2) => input.assigned_generations[2],
250 _ => input.assigned_generations[3],
251 };
252 let held_canonical = verify_transport_request_is_canonical_kernel(
253 held_request,
254 q.control.launch_owner,
255 held_generation,
256 );
257 let held_unchanged = compression_input_equal_kernel(q.launch, held_request);
258 let held_failure = q.control.launch_valid && (!held_canonical || !held_unchanged);
259 if held_failure {
260 request_rejected[q.control.launch_owner] = true;
261 }
262 let issue_failure =
263 request_rejected[0] || request_rejected[1] || request_rejected[2] || request_rejected[3];
264 if issue_failure {
265 diagnostics = diagnostics | b16(VERIFY_DIAGNOSTIC_ISSUE_REJECT);
266 }
267
268 let expected_valid = q.control.response_due & b64(1) != b64(0);
269 let response_fields = decode_tag_kernel(input.lane_response.tag);
270 let response_timing_failure = expected_valid != input.lane_response.valid;
271 let response_tag_failure =
272 expected_valid && input.lane_response.valid && input.lane_response.tag != q.expected_tags;
273 let response_format_failure = input.lane_response.valid
274 && !verification_tag_is_well_formed_kernel(input.lane_response.tag);
275 let response_owner_failure = input.lane_response.valid && response_fields.context >= b4(4);
276 if response_timing_failure {
277 diagnostics = diagnostics | b16(VERIFY_DIAGNOSTIC_RETURN_TIMING);
278 }
279 if response_tag_failure {
280 diagnostics = diagnostics | b16(VERIFY_DIAGNOSTIC_RETURN_TAG);
281 }
282 if response_format_failure {
283 diagnostics = diagnostics | b16(VERIFY_DIAGNOSTIC_RETURN_FORMAT);
284 }
285 if response_owner_failure {
286 diagnostics = diagnostics | b16(VERIFY_DIAGNOSTIC_RETURN_OWNER);
287 }
288 let return_failure = response_timing_failure
289 || response_tag_failure
290 || response_format_failure
291 || response_owner_failure;
292 let fatal = !resetting && !input.abort && (issue_failure || return_failure);
293
294 let exact_return = expected_valid
295 && input.lane_response.valid
296 && input.lane_response.tag == q.expected_tags
297 && !response_format_failure
298 && !response_owner_failure;
299 if !resetting && !input.abort && !fatal && exact_return {
300 let expected_fields = decode_tag_kernel(q.expected_tags);
301 let expected_owner: b2 = expected_fields.context.resize();
302 responses[expected_owner] = input.lane_response;
303 response_routed[expected_owner] = true;
304 }
305
306 let mut expected_write = SyncBramWrite::<b32, VERIFY_EXPECTED_TAG_ADDRESS_BITS> {
307 addr: q.control.pointer,
308 value: b32(0),
309 enable: false,
310 };
311 if !resetting && !input.abort && !fatal {
312 control.response_due = q.control.response_due >> 1;
313 control.pointer = q.control.pointer + b6(1);
314
315 if q.control.launch_valid {
316 lane_request = q.launch;
317 lane_request.valid = true;
318 request_accepted[q.control.launch_owner] = true;
319 control.response_due = control.response_due | b64(VERIFY_EXPECTED_DUE_INSERT);
320 expected_write = SyncBramWrite::<b32, VERIFY_EXPECTED_TAG_ADDRESS_BITS> {
321 addr: q.control.pointer,
322 value: q.launch.tag,
323 enable: true,
324 };
325 }
326
327 let capture = select_verify_transport_request_kernel(
328 canonical,
329 q.control.issue_cursor,
330 q.control.launch_valid,
331 q.control.launch_owner,
332 );
333 control.launch_valid = capture.valid;
334 if capture.valid {
335 control.launch_owner = capture.owner;
336 control.issue_cursor = capture.next_cursor;
337 launch = match capture.owner {
338 Bits::<2>(0) => input.requests[0],
339 Bits::<2>(1) => input.requests[1],
340 Bits::<2>(2) => input.requests[2],
341 _ => input.requests[3],
342 };
343 }
344 } else {
345 control.launch_valid = false;
346 control.response_due = b64(0);
347 lane_request.valid = false;
348 }
349
350 if resetting {
351 control = VerifyTransportControl::default();
352 expected_write.enable = false;
353 }
354
355 let output = VerifyTransportOutput {
356 lane_request,
357 request_accepted,
358 request_rejected,
359 responses,
360 response_routed,
361 launch_pending: !resetting && q.control.launch_valid,
362 expected_valid: !resetting && !input.abort && expected_valid,
363 expected_tag: q.expected_tags,
364 fatal,
365 diagnostics: if resetting || input.abort {
366 b16(0)
367 } else {
368 diagnostics
369 },
370 };
371 let d = D {
372 control,
373 launch,
374 expected_tags: SyncBramIn::<b32, VERIFY_EXPECTED_TAG_ADDRESS_BITS> {
375 read_addr: q.control.pointer + b6(1),
378 write: expected_write,
379 },
380 };
381 (output, d)
382}