rhdl_dsig_scalable_core/ubft/
verifier.rs1use super::{Axis64, Axis512, KEY_SLOTS, Latency, VERIFY_IN_FLIGHT, latency_kernel};
41use crate::verify::RECORD_BEATS;
42use crate::verify::certs::{CertRequest, CertResult};
43use crate::verify::datapath::{Beat, CertWarm, CoreInput, VerifyCore};
44use rhdl::prelude::*;
45use rhdl_fpga::core::{
46 dff::DFF,
47 ram::synchronous::{In as RamIn, SyncBRAM, Write as RamWrite},
48};
49
50const LAST_BEAT: b5 = b5((RECORD_BEATS - 1) as u128);
52const IN_FLIGHT_LIMIT: b7 = b7(VERIFY_IN_FLIGHT as u128);
54
55#[derive(Clone, Copy, Debug, PartialEq, Digital)]
57pub struct VerifierIn {
58 pub open: bool,
60 pub ticks: b64,
62 pub request: Axis512,
64 pub verdict_ready: bool,
66 pub cert: Axis512,
68 pub cert_taken: bool,
70 pub cert_result: CertResult,
72 pub epochs: [b64; KEY_SLOTS],
74 pub epoch_mask: b8,
76}
77
78#[derive(Clone, Copy, Debug, PartialEq, Digital)]
80pub struct VerifierOut {
81 pub request_ready: bool,
83 pub cert_ready: bool,
85 pub verdict: Axis64,
87 pub cert_request: CertRequest,
89 pub verified: b32,
91 pub valid: b32,
93 pub cert_checks: b32,
95 pub cert_fails: b32,
97 pub framing_fault: bool,
99 pub latency: Latency,
101}
102
103#[derive(Clone, Copy, Debug, PartialEq, Digital)]
106#[doc(hidden)]
107pub struct VerifierState {
108 warm: CertWarm,
110 warm_beat: b2,
112 in_frame: bool,
114 beat: b5,
116 slot: b4,
118 tail: b6,
120 head: b6,
122 in_flight: b7,
124 out_valid: bool,
126 out_data: b64,
128 verified: b32,
130 valid: b32,
132 framing_fault: bool,
134 latency: Latency,
136}
137
138#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
140pub struct UbftVerifier<const TABLE_MASK: usize> {
141 state: DFF<VerifierState>,
143 tags: SyncBRAM<b40, 6>,
145 core: VerifyCore<TABLE_MASK>,
147}
148
149impl Default for VerifierState {
150 fn default() -> Self {
152 Self {
153 warm: CertWarm::none(),
154 warm_beat: b2(0),
155 in_frame: false,
156 beat: b5(0),
157 slot: b4(0),
158 tail: b6(0),
159 head: b6(0),
160 in_flight: b7(0),
161 out_valid: false,
162 out_data: b64(0),
163 verified: b32(0),
164 valid: b32(0),
165 framing_fault: false,
166 latency: Latency::default(),
167 }
168 }
169}
170
171impl<const TABLE_MASK: usize> Default for UbftVerifier<TABLE_MASK> {
172 fn default() -> Self {
174 Self {
175 state: DFF::new(VerifierState::default()),
176 tags: SyncBRAM::default(),
177 core: VerifyCore::default(),
178 }
179 }
180}
181
182impl<const TABLE_MASK: usize> SynchronousIO for UbftVerifier<TABLE_MASK> {
183 type I = VerifierIn;
184 type O = VerifierOut;
185 type Kernel = ubft_verifier_kernel<TABLE_MASK>;
186}
187
188#[allow(clippy::needless_range_loop)]
190#[kernel]
191pub fn ubft_verifier_kernel<const TABLE_MASK: usize>(
192 _clock_reset: ClockReset,
193 input: VerifierIn,
194 q: UbftVerifierQ<TABLE_MASK>,
195) -> (VerifierOut, UbftVerifierD<TABLE_MASK>) {
196 let mut s = q.state;
197 let req = input.request;
198
199 let header_ready = input.open && q.state.in_flight < IN_FLIGHT_LIMIT;
201 let request_ready = if q.state.in_frame { q.core.beat_ready } else { header_ready };
202 let take = req.valid && request_ready;
203 let header = take && !q.state.in_frame;
204 let frame_beat = take && q.state.in_frame;
205 let mut write = RamWrite::<b40, 6> {
206 addr: q.state.tail,
207 value: b40(0),
208 enable: false,
209 };
210 if header {
211 let slot: b4 = (req.data[0] & b8(15)).resize();
212 let tag: b40 = req.data[4].resize::<40>()
213 | (req.data[5].resize::<40>() << 8)
214 | (req.data[6].resize::<40>() << 16)
215 | (req.data[7].resize::<40>() << 24);
216 write = RamWrite::<b40, 6> {
217 addr: q.state.tail,
218 value: tag | (slot.resize::<40>() << 32),
219 enable: true,
220 };
221 s.tail = q.state.tail + b6(1);
222 s.slot = slot;
223 s.in_frame = true;
224 s.beat = b5(0);
225 if req.last {
226 s.framing_fault = true;
227 }
228 }
229 if frame_beat {
230 let last_beat = q.state.beat == LAST_BEAT;
231 if req.last != last_beat {
232 s.framing_fault = true;
233 }
234 if last_beat {
235 s.in_frame = false;
236 } else {
237 s.beat = q.state.beat + b5(1);
238 }
239 }
240
241 if q.state.out_valid && input.verdict_ready {
245 s.out_valid = false;
246 s.verified = q.state.verified + b32(1);
247 if (q.state.out_data & b64(0xff)) == b64(0x80) {
248 s.valid = q.state.valid + b32(1);
249 }
250 }
251 let core_verdict = q.core.verdict.valid && !q.state.out_valid;
252 if core_verdict {
253 let head_word = q.tags;
254 let slot: b64 = (head_word >> 32).resize();
255 let tag: b64 = (head_word & b40(0xffff_ffff)).resize();
256 s.out_valid = true;
257 s.out_data = q.core.verdict.byte.resize::<64>() | (slot << 8) | (tag << 32);
258 s.head = q.state.head + b6(1);
259 }
260 let sent = q.state.out_valid && input.verdict_ready;
261 s.in_flight = q.state.in_flight + (if header { b7(1) } else { b7(0) }) - (if sent { b7(1) } else { b7(0) });
262
263 let cert = input.cert;
267 let cert_ready = !q.state.warm.valid;
268 if cert.valid && cert_ready {
269 if q.state.warm_beat == b2(0) {
270 s.warm.slot = (cert.data[0] & b8(15)).resize();
271 s.warm_beat = b2(1);
272 } else if q.state.warm_beat == b2(1) {
273 s.warm.commitment = cert.data;
274 s.warm_beat = b2(2);
275 } else {
276 s.warm.signature = cert.data;
277 s.warm.valid = true;
278 s.warm_beat = b2(0);
279 if !cert.last {
280 s.framing_fault = true;
281 }
282 }
283 }
284 if q.core.warm_taken {
285 s.warm.valid = false;
286 }
287
288 let begin = header && q.state.in_flight == b7(0);
290 let end = sent && q.state.in_flight == b7(1);
291 let abandon = header && q.state.in_flight != b7(0);
292 s.latency = latency_kernel(q.state.latency, input.ticks, begin, end, abandon);
293
294 let mut expected_epoch = b64(0);
296 let mut epoch_checked = false;
297 for k in 0..KEY_SLOTS {
298 if q.state.slot == b4(k as u128) {
299 expected_epoch = input.epochs[k];
300 epoch_checked = (input.epoch_mask & (b8(1) << b8(k as u128))) != b8(0);
301 }
302 }
303
304 let d = UbftVerifierD::<TABLE_MASK> {
305 state: s,
306 tags: RamIn::<b40, 6> {
307 read_addr: s.head,
308 write,
309 },
310 core: CoreInput {
311 beat: Beat {
312 valid: req.valid && q.state.in_frame,
313 data: req.data,
314 },
315 slot: q.state.slot,
316 check_epoch: epoch_checked,
317 expected_epoch,
318 warm: q.state.warm,
319 verdict_ready: !q.state.out_valid,
320 cert_taken: input.cert_taken,
321 cert_result: input.cert_result,
322 },
323 };
324 (
325 VerifierOut {
326 request_ready,
327 cert_ready,
328 verdict: Axis64 {
329 valid: q.state.out_valid,
330 data: q.state.out_data,
331 },
332 cert_request: q.core.cert_request,
333 verified: q.state.verified,
334 valid: q.state.valid,
335 cert_checks: q.core.cert_checks,
336 cert_fails: q.core.cert_fails,
337 framing_fault: q.state.framing_fault,
338 latency: q.state.latency,
339 },
340 d,
341 )
342}