1use super::signer::{SignConfig, SignerIn, UbftSigner};
23use super::verifier::{UbftVerifier, VerifierIn};
24use super::*;
25use crate::axi4::{AxiLiteIn, AxiLiteOut};
26use crate::cert_unit::SLICES;
27use crate::engine::set_half_kernel;
28use crate::verify::certs::CertResult;
29use crate::verify::engine::{CertCore, words64_kernel};
30use rhdl::prelude::*;
31use rhdl_ed25519_live::sha_ring::{ShaDone, ShaRingOut};
32use rhdl_ed25519_live::verify_core::VerifyIn;
33use rhdl_ed25519_live::wire::{ScalarWords, VerifyRequest, Words512};
34use rhdl_fpga::core::dff::DFF;
35
36#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
39pub struct CfgWrite {
40 pub valid: bool,
43 pub addr: b12,
45 pub data: b32,
47}
48
49#[derive(Clone, Copy, Debug, PartialEq, Digital)]
51pub struct UbftInput {
52 pub cfg: CfgWrite,
55 pub axi: AxiLiteIn,
57 pub sign: Axis512,
59 pub frame_ready: bool,
61 pub verify: Axis512,
63 pub verdict_ready: bool,
65 pub cert_in: Axis512,
67 pub cert_out_ready: bool,
69}
70
71#[derive(Clone, Copy, Debug, PartialEq, Digital)]
73pub struct UbftOutput {
74 pub axi: AxiLiteOut,
76 pub sign_ready: bool,
78 pub frame: Axis512,
80 pub verify_ready: bool,
82 pub verdict: Axis64,
84 pub cert_out: Axis512,
86 pub cert_in_ready: bool,
88 pub ready: bool,
90 pub fault: bool,
92 pub cfg_ready: bool,
94}
95
96#[derive(Clone, Copy, Debug, PartialEq, Digital)]
98#[doc(hidden)]
99pub struct UbftArgs {
100 sign_count: b32,
101 verify_count: b32,
102 batches: b32,
103 epoch: b64,
104 first_batch: b64,
105 prefix_cv: [b32; 8],
106 seed: [b32; 8],
107 key_mask: b8,
108 keys: [[b32; 8]; KEY_SLOTS],
109 epochs: [b64; KEY_SLOTS],
110 epoch_mask: b8,
111}
112
113#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
115#[doc(hidden)]
116pub struct EdHeld {
117 message: [b64; 8],
118 signature: [b64; 8],
119 public_key: [b64; 4],
120 tag: b16,
121}
122
123#[derive(Clone, Copy, Debug, PartialEq, Digital)]
125#[doc(hidden)]
126pub struct UbftControl {
127 args: UbftArgs,
128 running: bool,
129 done: bool,
130 done_unread: bool,
131 gie: bool,
132 ier: bool,
133 isr: bool,
134 ticks: b64,
135 start_tick: b64,
136 done_tick: b64,
137 key_tick: b64,
138 ticks_hi: b32,
139 kl_slot: b5,
141 kl_wait: bool,
143 kl_pulse: bool,
145 keys_settled: bool,
147 key_bad: bool,
149 aw_addr: b12,
150 aw_have: bool,
151 w_data: b32,
152 w_have: bool,
153 b_valid: bool,
154 r_valid: bool,
155 r_data: b32,
156 ed_valid: bool,
158 ed_request: EdHeld,
159 ed_slot: b4,
160}
161
162impl Default for UbftControl {
163 fn default() -> Self {
165 Self {
166 args: UbftArgs {
167 sign_count: b32(0),
168 verify_count: b32(0),
169 batches: b32(0),
170 epoch: b64(0),
171 first_batch: b64(0),
172 prefix_cv: [b32(0); 8],
173 seed: [b32(0); 8],
174 key_mask: b8(0),
175 keys: [[b32(0); 8]; KEY_SLOTS],
176 epochs: [b64(0); KEY_SLOTS],
177 epoch_mask: b8(0),
178 },
179 running: false,
180 done: false,
181 done_unread: false,
182 gie: false,
183 ier: false,
184 isr: false,
185 ticks: b64(0),
186 start_tick: b64(0),
187 done_tick: b64(0),
188 key_tick: b64(0),
189 ticks_hi: b32(0),
190 kl_slot: b5(0),
191 kl_wait: false,
192 kl_pulse: false,
193 keys_settled: false,
194 key_bad: false,
195 aw_addr: b12(0),
196 aw_have: false,
197 w_data: b32(0),
198 w_have: false,
199 b_valid: false,
200 r_valid: false,
201 r_data: b32(0),
202 ed_valid: false,
203 ed_request: EdHeld::default(),
204 ed_slot: b4(0),
205 }
206 }
207}
208
209#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
211pub struct UbftEngine<const TABLE_MASK: usize> {
212 control: DFF<UbftControl>,
214 signer: UbftSigner<TABLE_MASK>,
216 verifier: UbftVerifier<TABLE_MASK>,
218 ed: CertCore,
220}
221
222impl<const TABLE_MASK: usize> Default for UbftEngine<TABLE_MASK> {
223 fn default() -> Self {
225 Self {
226 control: DFF::new(UbftControl::default()),
227 signer: UbftSigner::default(),
228 verifier: UbftVerifier::default(),
229 ed: CertCore::default(),
230 }
231 }
232}
233
234impl<const TABLE_MASK: usize> SynchronousIO for UbftEngine<TABLE_MASK> {
235 type I = UbftInput;
236 type O = UbftOutput;
237 type Kernel = ubft_engine_kernel<TABLE_MASK>;
238}
239
240const SHAPE_BASE: usize = SLICES | (KEY_SLOTS << 24);
242
243#[kernel]
246pub fn latency_word_kernel(l: Latency, i: b3) -> b32 {
247 if i == b3(0) {
248 l.n
249 } else if i == b3(1) {
250 l.last
251 } else if i == b3(2) {
252 l.min
253 } else if i == b3(3) {
254 l.max
255 } else if i == b3(4) {
256 l.sum.resize()
257 } else {
258 (l.sum >> 32).resize()
259 }
260}
261
262#[allow(clippy::needless_range_loop, clippy::too_many_lines)]
264#[kernel]
265pub fn ubft_engine_kernel<const TABLE_MASK: usize>(
266 clock_reset: ClockReset,
267 input: UbftInput,
268 q: UbftEngineQ<TABLE_MASK>,
269) -> (UbftOutput, UbftEngineD<TABLE_MASK>) {
270 let resetting = clock_reset.reset.any();
271 let mut c = q.control;
272 let args = q.control.args;
273 c.ticks = q.control.ticks + b64(1);
274 let ed = q.ed;
275 let sig = q.signer;
276 let ver = q.verifier;
277 c.kl_pulse = false;
278
279 let fault_bits: b32 = (if sig.slice_fault { b32(1) } else { b32(0) })
281 | (if sig.cert_fault { b32(2) } else { b32(0) })
282 | (if ed.internal_error { b32(4) } else { b32(0) })
283 | (if q.control.key_bad { b32(8) } else { b32(0) })
284 | (if ver.framing_fault { b32(16) } else { b32(0) });
285 let settled_start = q.control.ticks - q.control.start_tick > b64(16);
286 if q.control.running
287 && settled_start
288 && q.control.keys_settled
289 && sig.signed == args.sign_count
290 && ver.verified == args.verify_count
291 {
292 c.running = false;
293 c.done = true;
294 c.done_unread = true;
295 c.isr = true;
296 c.done_tick = q.control.ticks;
297 }
298
299 let mut kl_key = [b64(0); 4];
303 let mut kl_masked = false;
304 for s in 0..KEY_SLOTS {
305 if q.control.kl_slot == b5(s as u128) {
306 kl_masked = (args.key_mask & (b8(1) << b8(s as u128))) != b8(0);
307 for w in 0..4 {
308 kl_key[w] = args.keys[s][2 * w].resize::<64>() | (args.keys[s][2 * w + 1].resize::<64>() << 32);
309 }
310 }
311 }
312 let mut kl_state = b2(0);
313 for s in 0..KEY_SLOTS {
314 if q.control.kl_slot == b5(s as u128) {
315 kl_state = ed.key_state[s];
316 }
317 }
318 let loading = q.control.running && !q.control.keys_settled && !resetting;
319 if loading {
320 if q.control.kl_slot == b5(KEY_SLOTS as u128) {
321 c.keys_settled = true;
322 c.key_tick = q.control.ticks;
323 } else if !kl_masked {
324 c.kl_slot = q.control.kl_slot + b5(1);
325 } else if !q.control.kl_wait {
326 if ed.load_key_ready && !q.control.kl_pulse && !q.control.ed_valid {
327 c.kl_pulse = true;
328 c.kl_wait = true;
329 c.ed_request.public_key = kl_key;
330 }
331 } else if !q.control.kl_pulse && (kl_state == b2(2) || kl_state == b2(3)) {
332 c.kl_wait = false;
333 c.kl_slot = q.control.kl_slot + b5(1);
334 if kl_state == b2(3) {
335 c.key_bad = true;
336 }
337 }
338 }
339
340 if q.control.ed_valid && ed.ready {
342 c.ed_valid = false;
343 }
344 let cr = ver.cert_request;
345 let issue = cr.valid && !q.control.ed_valid && q.control.keys_settled && !resetting;
346 if issue {
347 let mut key = [b64(0); 4];
348 for s in 0..KEY_SLOTS {
349 if cr.slot == b4(s as u128) {
350 for w in 0..4 {
351 key[w] = args.keys[s][2 * w].resize::<64>() | (args.keys[s][2 * w + 1].resize::<64>() << 32);
352 }
353 }
354 }
355 c.ed_valid = true;
356 c.ed_slot = cr.slot;
357 c.ed_request = EdHeld {
358 message: words64_kernel(cr.commitment),
359 signature: words64_kernel(cr.signature),
360 public_key: key,
361 tag: cr.entry.resize(),
362 };
363 }
364
365 let axi = input.axi;
367 if axi.aw_valid && !q.control.aw_have {
368 c.aw_addr = axi.aw_addr;
369 c.aw_have = true;
370 }
371 if axi.w_valid && !q.control.w_have {
372 c.w_data = axi.w_data;
373 c.w_have = true;
374 }
375 if q.control.b_valid && axi.b_ready {
376 c.b_valid = false;
377 }
378 let axi_write = q.control.aw_have && q.control.w_have && !q.control.b_valid;
382 let cfg_write = input.cfg.valid && !axi_write;
383 if axi_write || cfg_write {
384 let a = if axi_write { q.control.aw_addr } else { input.cfg.addr };
385 let v = if axi_write { q.control.w_data } else { input.cfg.data };
386 if axi_write {
387 c.aw_have = false;
388 c.w_have = false;
389 c.b_valid = true;
390 }
391 if a == b12(0x000) && (v & b32(1)) == b32(1) && !q.control.running && !q.control.done {
392 c.running = true;
393 c.start_tick = q.control.ticks;
394 }
395 if a == b12(0x004) {
396 c.gie = (v & b32(1)) == b32(1);
397 }
398 if a == b12(0x008) {
399 c.ier = (v & b32(1)) == b32(1);
400 }
401 if a == b12(0x00c) && (v & b32(1)) == b32(1) {
402 c.isr = !q.control.isr;
403 }
404 if !q.control.running && !q.control.done {
405 if a == b12(ARG_SIGN_COUNT as u128) {
406 c.args.sign_count = v;
407 }
408 if a == b12(ARG_VERIFY_COUNT as u128) {
409 c.args.verify_count = v;
410 }
411 if a == b12(ARG_BATCHES as u128) {
412 c.args.batches = v;
413 }
414 if a == b12(ARG_EPOCH as u128) || a == b12((ARG_EPOCH + 4) as u128) {
415 c.args.epoch = set_half_kernel(args.epoch, v, a == b12((ARG_EPOCH + 4) as u128));
416 }
417 if a == b12(ARG_FIRST_BATCH as u128) || a == b12((ARG_FIRST_BATCH + 4) as u128) {
418 c.args.first_batch = set_half_kernel(args.first_batch, v, a == b12((ARG_FIRST_BATCH + 4) as u128));
419 }
420 for w in 0..8 {
421 if a == b12((ARG_CV + 4 * w) as u128) {
422 c.args.prefix_cv[w] = v;
423 }
424 if a == b12((ARG_SEED + 4 * w) as u128) {
425 c.args.seed[w] = v;
426 }
427 }
428 if a == b12(ARG_KEY_MASK as u128) {
429 c.args.key_mask = v.resize();
430 }
431 for s in 0..KEY_SLOTS {
432 for w in 0..8 {
433 if a == b12((ARG_KEYS + 0x20 * s + 4 * w) as u128) {
434 c.args.keys[s][w] = v;
435 }
436 }
437 if a == b12((ARG_EPOCHS + 8 * s) as u128) {
438 c.args.epochs[s] = (q.control.args.epochs[s] & (b64(0xffff_ffff) << 32)) | v.resize::<64>();
439 }
440 if a == b12((ARG_EPOCHS + 8 * s + 4) as u128) {
441 c.args.epochs[s] = (q.control.args.epochs[s] & b64(0xffff_ffff)) | (v.resize::<64>() << 32);
442 }
443 }
444 if a == b12(ARG_EPOCH_MASK as u128) {
445 c.args.epoch_mask = v.resize();
446 }
447 }
448 }
449
450 if q.control.r_valid && axi.r_ready {
452 c.r_valid = false;
453 }
454 let ar_ready = !q.control.r_valid;
455 if axi.ar_valid && ar_ready {
456 let a = axi.ar_addr;
457 let mut value = b32(0);
458 if a == b12(0x000) {
459 value = (if q.control.running { b32(1) } else { b32(0) })
460 | (if q.control.done_unread { b32(2) } else { b32(0) })
461 | (if !q.control.running { b32(4) } else { b32(0) })
462 | (if q.control.done_unread { b32(8) } else { b32(0) });
463 c.done_unread = false;
464 }
465 if a == b12(0x004) {
466 value = if q.control.gie { b32(1) } else { b32(0) };
467 }
468 if a == b12(0x008) {
469 value = if q.control.ier { b32(1) } else { b32(0) };
470 }
471 if a == b12(0x00c) {
472 value = if q.control.isr { b32(1) } else { b32(0) };
473 }
474 if a == b12(REG_MAGIC as u128) {
475 value = MAGIC;
476 }
477 if a == b12(REG_ABI as u128) {
478 value = ABI_VERSION;
479 }
480 if a == b12(REG_SHAPE as u128) {
481 value = b32((SHAPE_BASE | (TABLE_MASK << 8)) as u128);
482 }
483 if a == b12(REG_FAULT as u128) {
484 value = fault_bits;
485 }
486 if a == b12(REG_TICKS as u128) {
487 value = q.control.ticks.resize();
488 c.ticks_hi = (q.control.ticks >> 32).resize();
489 }
490 if a == b12((REG_TICKS + 4) as u128) {
491 value = q.control.ticks_hi;
492 }
493 if a == b12(REG_START_TICK as u128) {
494 value = q.control.start_tick.resize();
495 }
496 if a == b12((REG_START_TICK + 4) as u128) {
497 value = (q.control.start_tick >> 32).resize();
498 }
499 if a == b12(REG_DONE_TICK as u128) {
500 value = q.control.done_tick.resize();
501 }
502 if a == b12((REG_DONE_TICK + 4) as u128) {
503 value = (q.control.done_tick >> 32).resize();
504 }
505 if a == b12(REG_KEY_TICK as u128) {
506 value = q.control.key_tick.resize();
507 }
508 if a == b12((REG_KEY_TICK + 4) as u128) {
509 value = (q.control.key_tick >> 32).resize();
510 }
511 if a == b12(REG_SIGNED as u128) {
512 value = sig.signed;
513 }
514 if a == b12(REG_SIGN_TAKEN as u128) {
515 value = sig.taken;
516 }
517 if a == b12(REG_VERIFIED as u128) {
518 value = ver.verified;
519 }
520 if a == b12(REG_VALID as u128) {
521 value = ver.valid;
522 }
523 if a == b12(REG_CERT_CHECKS as u128) {
524 value = ver.cert_checks;
525 }
526 if a == b12(REG_CERT_FAILS as u128) {
527 value = ver.cert_fails;
528 }
529 if a == b12(REG_CERTIFIED as u128) {
530 value = sig.certified;
531 }
532 if a == b12(REG_ANNOUNCED as u128) {
533 value = sig.announced;
534 }
535 for i in 0..6 {
536 if a == b12((REG_SIGN_LATENCY + 4 * i) as u128) {
537 value = latency_word_kernel(sig.latency, b3(i as u128));
538 }
539 if a == b12((REG_VERIFY_LATENCY + 4 * i) as u128) {
540 value = latency_word_kernel(ver.latency, b3(i as u128));
541 }
542 }
543 for w in 0..4 {
544 if a == b12((REG_PUBLIC_KEY + 8 * w) as u128) {
545 value = sig.public_key.words[w].resize();
546 }
547 if a == b12((REG_PUBLIC_KEY + 8 * w + 4) as u128) {
548 value = (sig.public_key.words[w] >> 32).resize();
549 }
550 }
551 c.r_valid = true;
552 c.r_data = value;
553 }
554
555 let mut seed_words = [b64(0); 4];
557 for w in 0..4 {
558 seed_words[w] = args.seed[2 * w].resize::<64>() | (args.seed[2 * w + 1].resize::<64>() << 32);
559 }
560 let held = q.control.ed_request;
561 let d = UbftEngineD::<TABLE_MASK> {
562 control: c,
563 signer: SignerIn {
564 config: SignConfig {
565 prefix_cv: args.prefix_cv,
566 epoch: args.epoch,
567 first_batch: args.first_batch,
568 batches: args.batches,
569 seed: seed_words,
570 },
571 run: q.control.running,
572 ticks: q.control.ticks,
573 request: input.sign,
574 frame_ready: input.frame_ready,
575 cert_ready: input.cert_out_ready,
576 },
577 verifier: VerifierIn {
578 open: q.control.running && q.control.keys_settled,
579 ticks: q.control.ticks,
580 request: input.verify,
581 verdict_ready: input.verdict_ready,
582 cert: input.cert_in,
583 epochs: q.control.args.epochs,
584 epoch_mask: q.control.args.epoch_mask,
585 cert_taken: issue,
586 cert_result: CertResult {
587 valid: ed.valid,
588 entry: ed.tag.resize(),
589 ok: ed.ok,
590 },
591 },
592 ed: VerifyIn {
593 valid: q.control.ed_valid,
594 request: VerifyRequest {
595 message: Words512 { words: held.message },
596 public_key: ScalarWords { words: held.public_key },
597 signature: Words512 { words: held.signature },
598 tag: held.tag,
599 },
600 load_key_valid: q.control.kl_pulse,
601 load_key: ScalarWords { words: held.public_key },
602 load_slot: q.control.kl_slot.resize(),
603 sha_ext: ShaRingOut {
604 ready: false,
605 done: ShaDone {
606 valid: false,
607 tag: b16(0),
608 state: [b64(0); 8],
609 },
610 },
611 },
612 };
613 (
614 UbftOutput {
615 axi: AxiLiteOut {
616 aw_ready: !q.control.aw_have,
617 w_ready: !q.control.w_have,
618 b_valid: q.control.b_valid,
619 b_resp: b2(0),
620 ar_ready,
621 r_valid: q.control.r_valid,
622 r_data: q.control.r_data,
623 r_resp: b2(0),
624 interrupt: q.control.gie && q.control.ier && q.control.isr,
625 },
626 sign_ready: sig.request_ready,
627 frame: sig.frame,
628 verify_ready: ver.request_ready,
629 verdict: ver.verdict,
630 cert_out: sig.cert,
631 cert_in_ready: ver.cert_ready,
632 ready: q.control.running && q.control.keys_settled,
633 fault: fault_bits != b32(0),
634 cfg_ready: !axi_write,
635 },
636 d,
637 )
638}
639
640const _: () = assert!(KEY_SLOTS <= rhdl_ed25519_live::machine::KEY_SLOTS, "more trusted keys than the Ed25519 verify core has slots");