rhdl_dsig_scalable_core/ubft/
probe.rs1use super::{Axis64, Axis512};
42use crate::slice_signer::FrameBeat;
43use rhdl::prelude::*;
44use rhdl_fpga::core::{
45 dff::DFF,
46 ram::synchronous::{In as RamIn, SyncBRAM, Write as RamWrite},
47};
48
49#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
51pub struct ProbeArgs {
52 pub count: b32,
54 pub gap: b32,
56 pub slot: b4,
58 pub tamper_every: b32,
60 pub go: bool,
62}
63
64#[derive(Clone, Copy, Debug, PartialEq, Digital)]
66pub struct ProbeIn {
67 pub args: ProbeArgs,
69 pub sign_ready: bool,
71 pub frame: Axis512,
73 pub verify_ready: bool,
75 pub verdict: Axis64,
77 pub writer_ready: bool,
79}
80
81#[derive(Clone, Copy, Debug, PartialEq, Digital)]
83pub struct ProbeOut {
84 pub sign: Axis512,
86 pub frame_ready: bool,
88 pub verify: Axis512,
90 pub verdict_ready: bool,
92 pub beat: FrameBeat,
94 pub done: b32,
96 pub matched: b32,
99 pub mismatched: b32,
101 pub first_bad: b64,
103}
104
105const IDLE: b3 = b3(0);
107const SIGN: b3 = b3(1);
109const FRAME: b3 = b3(2);
111const HEADER: b3 = b3(3);
113const BODY: b3 = b3(4);
115const VERDICT: b3 = b3(5);
117const LAST_BEAT: b5 = b5(25);
119const MESSAGE_AT: usize = 42;
121const TAIL: b3 = b3(6);
123const P2: bool = crate::MESSAGE_BYTES == 64;
125const MSG: usize = crate::MESSAGE_BYTES;
127const P1_MSG: usize = if P2 { 0 } else { 16 };
131const P2_CTX: usize = if P2 { 48 } else { 0 };
133const P2_HEAD: usize = if P2 { 22 } else { 0 };
135const P2_TAIL: usize = if P2 { 42 } else { 0 };
137const P2_RUN: usize = if P2 { 16 } else { 0 };
139const SIGN_TAG_AT: usize = if P2 { 0 } else { 16 };
141const ZERO_FROM: usize = MESSAGE_AT + P1_MSG + P2_HEAD;
143
144#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
146#[doc(hidden)]
147pub struct ProbeState {
148 phase: b3,
149 i: b32,
151 beat: b5,
153 wait: b32,
155 tampered: bool,
157 since: b32,
159 done: b32,
160 matched: b32,
161 mismatched: b32,
162 first_bad: b64,
163}
164
165#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
167pub struct UbftProbe {
168 state: DFF<ProbeState>,
170 frame: SyncBRAM<[b8; 64], 5>,
172}
173
174impl Default for UbftProbe {
175 fn default() -> Self {
177 Self {
178 state: DFF::new(ProbeState::default()),
179 frame: SyncBRAM::default(),
180 }
181 }
182}
183
184impl SynchronousIO for UbftProbe {
185 type I = ProbeIn;
186 type O = ProbeOut;
187 type Kernel = ubft_probe_kernel;
188}
189
190#[allow(clippy::needless_range_loop)]
194#[kernel]
195pub fn probe_message_kernel(i: b32) -> [b8; MSG] {
196 let x: b64 = i.resize();
197 let y: b64 = !x;
198 let mut m = [b8(0); MSG];
199 for k in 0..8 {
200 m[k] = (x >> ((8 * k) as u128)).resize();
201 m[8 + k] = (y >> ((8 * k) as u128)).resize();
202 }
203 for k in 0..P2_RUN {
204 m[16 + k] = m[k] ^ b8(0xa5);
205 m[32 + k] = m[k] ^ b8(0x3c);
206 }
207 m
208}
209
210#[allow(clippy::needless_range_loop, clippy::too_many_lines)]
212#[kernel]
213pub fn ubft_probe_kernel(_clock_reset: ClockReset, input: ProbeIn, q: UbftProbeQ) -> (ProbeOut, UbftProbeD) {
214 let mut s = q.state;
215 let args = input.args;
216 let message = probe_message_kernel(q.state.i);
217 let mut sign = Axis512 {
218 valid: false,
219 data: [b8(0); 64],
220 last: true,
221 };
222 let mut verify = Axis512 {
223 valid: false,
224 data: [b8(0); 64],
225 last: false,
226 };
227 let mut frame_ready = false;
228 let mut beat = FrameBeat {
229 valid: false,
230 data: input.frame.data,
231 last: input.frame.last,
232 };
233 let mut write = RamWrite::<[b8; 64], 5> {
234 addr: q.state.beat,
235 value: input.frame.data,
236 enable: false,
237 };
238 let mut read_addr = q.state.beat;
240
241 if q.state.phase == IDLE {
242 if args.go && q.state.i < args.count {
243 if q.state.wait == b32(0) {
244 s.phase = SIGN;
245 let next = q.state.since + b32(1);
248 let tamper = args.tamper_every != b32(0) && next == args.tamper_every;
249 s.tampered = tamper;
250 s.since = if tamper { b32(0) } else { next };
251 } else {
252 s.wait = q.state.wait - b32(1);
253 }
254 }
255 } else if q.state.phase == SIGN {
256 for k in 0..P1_MSG {
259 sign.data[k] = message[k];
260 }
261 for k in 0..P2_CTX {
262 sign.data[8 + k] = message[k];
263 }
264 for k in 0..4 {
265 sign.data[SIGN_TAG_AT + k] = (q.state.i >> ((8 * k) as u128)).resize();
266 }
267 sign.valid = true;
268 if input.sign_ready {
269 s.phase = FRAME;
270 s.beat = b5(0);
271 }
272 } else if q.state.phase == FRAME {
273 frame_ready = input.writer_ready;
274 beat.valid = input.frame.valid && input.writer_ready;
275 if input.frame.valid && input.writer_ready {
276 write.enable = true;
277 if q.state.beat == LAST_BEAT {
278 s.phase = HEADER;
279 s.beat = b5(0);
280 read_addr = b5(0);
281 } else {
282 s.beat = q.state.beat + b5(1);
283 }
284 }
285 } else if q.state.phase == HEADER {
286 verify.valid = true;
287 verify.data[0] = args.slot.resize();
288 for k in 0..4 {
289 verify.data[4 + k] = (q.state.i >> ((8 * k) as u128)).resize();
290 }
291 read_addr = b5(0);
292 if input.verify_ready {
293 s.phase = BODY;
294 }
295 } else if q.state.phase == BODY {
296 verify.valid = true;
297 verify.data = q.frame;
298 let is_last = q.state.beat == LAST_BEAT;
299 if is_last {
300 for k in 0..P1_MSG {
303 verify.data[MESSAGE_AT + k] = message[k];
304 }
305 for k in 0..P2_HEAD {
306 verify.data[MESSAGE_AT + k] = message[k];
307 }
308 for k in ZERO_FROM..64 {
309 verify.data[k] = b8(0);
310 }
311 if q.state.tampered {
312 verify.data[MESSAGE_AT] = message[0] ^ b8(1);
313 }
314 }
315 verify.last = is_last && !P2;
316 if input.verify_ready {
317 if is_last {
318 s.phase = if P2 { TAIL } else { VERDICT };
319 } else {
320 s.beat = q.state.beat + b5(1);
321 read_addr = q.state.beat + b5(1);
322 }
323 }
324 } else if q.state.phase == TAIL {
325 verify.valid = true;
327 for k in 0..P2_TAIL {
328 verify.data[k] = message[P2_HEAD + k];
329 }
330 verify.last = true;
331 if input.verify_ready {
332 s.phase = VERDICT;
333 }
334 } else {
335 if input.verdict.valid {
337 let word = input.verdict.data;
338 let valid = (word & b64(0xff)) == b64(0x80);
339 let tag_ok = (word >> 32) == q.state.i.resize::<64>();
340 let ok = tag_ok && (valid != q.state.tampered);
341 if ok {
342 s.matched = q.state.matched + b32(1);
343 } else {
344 s.mismatched = q.state.mismatched + b32(1);
345 if q.state.mismatched == b32(0) {
346 s.first_bad = word;
347 }
348 }
349 s.done = q.state.done + b32(1);
350 s.i = q.state.i + b32(1);
351 s.wait = args.gap;
352 s.phase = IDLE;
353 }
354 }
355
356 let d = UbftProbeD {
357 state: s,
358 frame: RamIn::<[b8; 64], 5> { read_addr, write },
359 };
360 (
361 ProbeOut {
362 sign,
363 frame_ready,
364 verify,
365 verdict_ready: q.state.phase == VERDICT,
366 beat,
367 done: q.state.done,
368 matched: q.state.matched,
369 mismatched: q.state.mismatched,
370 first_bad: q.state.first_bad,
371 },
372 d,
373 )
374}