Skip to main content

rhdl_dsig_scalable_core/ubft/
card.rs

1//! [`UbftCard`]: the uBFT service with its probe, as one card kernel for
2//! measurement.
3//!
4//! ```text
5//!   host ── AXI-Lite ──▶ front: 0x000..0x7ff ──▶ UbftEngine registers
6//!                              0x800..0xfff ──▶ probe registers (below)
7//!   UbftProbe ◀── four streams ──▶ UbftEngine
8//!   UbftProbe ── frames ──▶ HbmWriter ── m_axi_frames ──▶ HBM
9//! ```
10//!
11//! `ap_start` goes to the engine; the run is done when the engine is (set
12//! its `sign_count` and `verify_count` to the probe's `count`). The probe
13//! starts once its `go` argument is set and the engine accepts requests.
14//!
15//! # Probe registers
16//!
17//! | Offset | Name | Access |
18//! |---|---|---|
19//! | `0x800` | arg `probe_count` | W |
20//! | `0x804` | arg `probe_gap`: idle cycles between requests | W |
21//! | `0x808` | arg `probe_slot`: key slot of the verify requests | W |
22//! | `0x80c` | arg `probe_tamper`: tamper every n-th (0 never) | W |
23//! | `0x810` | arg `probe_go` | W |
24//! | `0x814` | arg `probe_announce`: feed the signer's certificate announce back into the verifier (as a peer's) | W |
25//! | `0x818` | arg `frames` (u64): HBM address of frame 0 (2 KiB stride) | W |
26//! | `0x840` | `PROBE_DONE`: verdicts received | R |
27//! | `0x844` | `PROBE_MATCHED` | R |
28//! | `0x848` | `PROBE_MISMATCHED` | R |
29//! | `0x84c`/`0x850` | `PROBE_FIRST_BAD` lo/hi: first mismatching verdict word | R |
30//! | `0x854` | `FRAMES_WRITTEN`: frames acknowledged by HBM | R |
31//! | `0x858` | `WRITE_ERROR` | R |
32
33use super::Axis512;
34use super::engine::{CfgWrite, UbftEngine, UbftInput, UbftOutput};
35use super::probe::{ProbeArgs, ProbeIn, UbftProbe};
36use crate::axi4::{AxiLiteIn, AxiLiteOut, WriteRequest, WriteResponse};
37use crate::engine::set_half_kernel;
38use crate::hbm_writer::{HbmWriter, WriterInput};
39use rhdl::prelude::*;
40use rhdl_fpga::core::dff::DFF;
41
42/// Offset of arg `probe_count`.
43pub const ARG_PROBE_COUNT: usize = 0x800;
44/// `probe_gap`.
45pub const ARG_PROBE_GAP: usize = 0x804;
46/// `probe_slot`.
47pub const ARG_PROBE_SLOT: usize = 0x808;
48/// `probe_tamper`.
49pub const ARG_PROBE_TAMPER: usize = 0x80c;
50/// `probe_go`.
51pub const ARG_PROBE_GO: usize = 0x810;
52/// `probe_announce`.
53pub const ARG_PROBE_ANNOUNCE: usize = 0x814;
54/// `frames` (u64).
55pub const ARG_FRAMES: usize = 0x818;
56/// `PROBE_DONE`.
57pub const REG_PROBE_DONE: usize = 0x840;
58/// `PROBE_MATCHED`.
59pub const REG_PROBE_MATCHED: usize = 0x844;
60/// `PROBE_MISMATCHED`.
61pub const REG_PROBE_MISMATCHED: usize = 0x848;
62/// `PROBE_FIRST_BAD` lo.
63pub const REG_PROBE_FIRST_BAD: usize = 0x84c;
64/// `FRAMES_WRITTEN`.
65pub const REG_FRAMES_WRITTEN: usize = 0x854;
66/// `WRITE_ERROR`.
67pub const REG_WRITE_ERROR: usize = 0x858;
68
69/// Card kernel inputs.
70#[derive(Clone, Copy, Debug, PartialEq, Digital)]
71pub struct CardInput {
72    /// Host control channel.
73    pub axi: AxiLiteIn,
74    /// Frame write channel.
75    pub frames: WriteResponse,
76}
77
78/// Card kernel outputs.
79#[derive(Clone, Copy, Debug, PartialEq, Digital)]
80pub struct CardOutput {
81    /// Host control channel.
82    pub axi: AxiLiteOut,
83    /// Frame write channel.
84    pub frames: WriteRequest,
85}
86
87/// The AXI-Lite front and the probe's arguments.
88#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
89#[doc(hidden)]
90pub struct CardControl {
91    args: ProbeArgs,
92    /// Loop the certificate announce back (`probe_announce`).
93    announce: bool,
94    /// Announce loop: 0 header next, 1 commitment, 2 signature.
95    ann_phase: b2,
96    frames_base: b64,
97    aw_have: bool,
98    aw_addr: b12,
99    w_have: bool,
100    w_data: b32,
101    b_valid: bool,
102    r_valid: bool,
103    r_data: b32,
104    /// A write is being passed to the engine.
105    w_busy: bool,
106    /// Its AW is not yet accepted by the engine.
107    fwd_aw: bool,
108    /// Its W is not yet accepted by the engine.
109    fwd_w: bool,
110    /// A read is being passed to the engine.
111    r_busy: bool,
112    /// Its AR is not yet accepted.
113    fwd_ar: bool,
114    ar_addr: b12,
115}
116
117/// The card kernel.
118#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
119pub struct UbftCard<const TABLE_MASK: usize> {
120    /// The AXI-Lite front and probe arguments.
121    control: DFF<CardControl>,
122    /// The service.
123    engine: UbftEngine<TABLE_MASK>,
124    /// The uBFT stand-in.
125    probe: UbftProbe,
126    /// Frames to HBM.
127    writer: HbmWriter,
128}
129
130impl<const TABLE_MASK: usize> Default for UbftCard<TABLE_MASK> {
131    /// Power-on.
132    fn default() -> Self {
133        Self {
134            control: DFF::new(CardControl::default()),
135            engine: UbftEngine::default(),
136            probe: UbftProbe::default(),
137            writer: HbmWriter::default(),
138        }
139    }
140}
141
142impl<const TABLE_MASK: usize> SynchronousIO for UbftCard<TABLE_MASK> {
143    type I = CardInput;
144    type O = CardOutput;
145    type Kernel = ubft_card_kernel<TABLE_MASK>;
146}
147
148/// The card's logic.
149#[allow(clippy::too_many_lines)]
150#[kernel]
151pub fn ubft_card_kernel<const TABLE_MASK: usize>(
152    _clock_reset: ClockReset,
153    input: CardInput,
154    q: UbftCardQ<TABLE_MASK>,
155) -> (CardOutput, UbftCardD<TABLE_MASK>) {
156    let mut c = q.control;
157    let axi = input.axi;
158    let eng: UbftOutput = q.engine;
159    let probe = q.probe;
160
161    // ------------------------------------------------ writes
162    if axi.aw_valid && !q.control.aw_have {
163        c.aw_addr = axi.aw_addr;
164        c.aw_have = true;
165    }
166    if axi.w_valid && !q.control.w_have {
167        c.w_data = axi.w_data;
168        c.w_have = true;
169    }
170    if q.control.b_valid && axi.b_ready {
171        c.b_valid = false;
172    }
173    let mut eng_axi = AxiLiteIn {
174        aw_valid: q.control.fwd_aw,
175        aw_addr: q.control.aw_addr,
176        w_valid: q.control.fwd_w,
177        w_data: q.control.w_data,
178        w_strb: b4(0xf),
179        b_ready: true,
180        ar_valid: q.control.fwd_ar,
181        ar_addr: q.control.ar_addr,
182        r_ready: true,
183    };
184    if q.control.fwd_aw && eng.axi.aw_ready {
185        c.fwd_aw = false;
186    }
187    if q.control.fwd_w && eng.axi.w_ready {
188        c.fwd_w = false;
189    }
190    if q.control.w_busy && eng.axi.b_valid {
191        c.w_busy = false;
192        c.b_valid = true;
193        c.aw_have = false;
194        c.w_have = false;
195    }
196    if q.control.aw_have && q.control.w_have && !q.control.b_valid && !q.control.w_busy {
197        let a = q.control.aw_addr;
198        let v = q.control.w_data;
199        if (a & b12(0x800)) != b12(0) {
200            if a == b12(0x800) {
201                c.args.count = v;
202            }
203            if a == b12(0x804) {
204                c.args.gap = v;
205            }
206            if a == b12(0x808) {
207                c.args.slot = v.resize();
208            }
209            if a == b12(0x80c) {
210                c.args.tamper_every = v;
211            }
212            if a == b12(0x810) {
213                c.args.go = (v & b32(1)) == b32(1);
214            }
215            if a == b12(0x814) {
216                c.announce = (v & b32(1)) == b32(1);
217            }
218            if a == b12(0x818) || a == b12(0x81c) {
219                c.frames_base = set_half_kernel(q.control.frames_base, v, a == b12(0x81c));
220            }
221            c.b_valid = true;
222            c.aw_have = false;
223            c.w_have = false;
224        } else {
225            c.w_busy = true;
226            c.fwd_aw = true;
227            c.fwd_w = true;
228        }
229    }
230
231    // ------------------------------------------------ reads
232    if q.control.r_valid && axi.r_ready {
233        c.r_valid = false;
234    }
235    if q.control.fwd_ar && eng.axi.ar_ready {
236        c.fwd_ar = false;
237    }
238    if q.control.r_busy && eng.axi.r_valid {
239        c.r_busy = false;
240        c.r_valid = true;
241        c.r_data = eng.axi.r_data;
242    }
243    let ar_ready = !q.control.r_valid && !q.control.r_busy;
244    if axi.ar_valid && ar_ready {
245        let a = axi.ar_addr;
246        if (a & b12(0x800)) != b12(0) {
247            let mut value = b32(0);
248            if a == b12(0x840) {
249                value = probe.done;
250            }
251            if a == b12(0x844) {
252                value = probe.matched;
253            }
254            if a == b12(0x848) {
255                value = probe.mismatched;
256            }
257            if a == b12(0x84c) {
258                value = probe.first_bad.resize();
259            }
260            if a == b12(0x850) {
261                value = (probe.first_bad >> 32).resize();
262            }
263            if a == b12(0x854) {
264                value = q.writer.frames_written;
265            }
266            if a == b12(0x858) {
267                value = if q.writer.error { b32(1) } else { b32(0) };
268            }
269            c.r_valid = true;
270            c.r_data = value;
271        } else {
272            c.r_busy = true;
273            c.fwd_ar = true;
274            c.ar_addr = a;
275        }
276    }
277    // The forwarded channels are presented from registers only.
278    eng_axi.ar_addr = q.control.ar_addr;
279
280    // ------------------------------------------------ announce loop
281    // The signer's announce goes back into its own verifier as if a peer in
282    // the probe's key slot had sent it: a header, then the two beats.
283    let mut cert_in = Axis512 {
284        valid: false,
285        data: [b8(0); 64],
286        last: false,
287    };
288    let mut cert_out_ready = true;
289    if q.control.announce {
290        cert_out_ready = false;
291        if q.control.ann_phase == b2(0) {
292            if eng.cert_out.valid {
293                cert_in.valid = true;
294                cert_in.data[0] = q.control.args.slot.resize();
295                if eng.cert_in_ready {
296                    c.ann_phase = b2(1);
297                }
298            }
299        } else {
300            cert_in = eng.cert_out;
301            cert_out_ready = eng.cert_in_ready;
302            if eng.cert_out.valid && eng.cert_in_ready {
303                c.ann_phase = if eng.cert_out.last { b2(0) } else { b2(2) };
304            }
305        }
306    }
307
308    let d = UbftCardD::<TABLE_MASK> {
309        control: c,
310        engine: UbftInput {
311            cfg: CfgWrite {
312                valid: false,
313                addr: b12(0),
314                data: b32(0),
315            },
316            axi: eng_axi,
317            sign: probe.sign,
318            frame_ready: probe.frame_ready,
319            verify: probe.verify,
320            verdict_ready: probe.verdict_ready,
321            cert_in,
322            cert_out_ready,
323        },
324        probe: ProbeIn {
325            args: q.control.args,
326            sign_ready: eng.sign_ready,
327            frame: eng.frame,
328            verify_ready: eng.verify_ready,
329            verdict: eng.verdict,
330            writer_ready: q.writer.beat_ready,
331        },
332        writer: WriterInput {
333            base: q.control.frames_base,
334            beat: probe.beat,
335            memory: input.frames,
336        },
337    };
338    (
339        CardOutput {
340            axi: AxiLiteOut {
341                aw_ready: !q.control.aw_have,
342                w_ready: !q.control.w_have,
343                b_valid: q.control.b_valid,
344                b_resp: b2(0),
345                ar_ready,
346                r_valid: q.control.r_valid,
347                r_data: q.control.r_data,
348                r_resp: b2(0),
349                interrupt: eng.axi.interrupt,
350            },
351            frames: q.writer.memory,
352        },
353        d,
354    )
355}