rhdl_ubft/ports.rs
1//! The kernel's ports: the same as ubft_new's `ubft` kernel, so the Vitis
2//! link (`config.cfg`) connects rhdl_ubft where ubft_new's kernel was.
3//!
4//! | Port | Kind | ubft_new | Here |
5//! |---|---|---|---|
6//! | `input` | AXI4-Stream in, 64 bits | `hls::stream<ProposedValue>` | [`Axis64`] |
7//! | `output`, `remoteUpdate` | AXI4-Stream out, 256 bits | `hls::stream<ap_uint<256>>` | [`Axis256`] |
8//! | `m_axis_tx_meta` | AXI4-Stream out, 256 + keep/last | `pkt256` | [`Axis256`] + [`TxSide`] |
9//! | `m_axis_tx_data` | AXI4-Stream out, 512 + keep/last | `pkt512` | [`Axis512`] + [`TxSide`] |
10//! | `s_axis_tx_status` | AXI4-Stream in, 512 | `pkt512` (drained) | [`Axis512`] |
11//! | `network_ptr` | AXI4 master, 512-bit data | `m_axi gmem0` | [`AxiRead`]/[`AxiWrite`] |
12//! | `message_ptr` | AXI4 master (reads) | `m_axi gmem1` | [`AxiRead`] |
13//! | `s_axi_control` | AXI4-Lite | arguments and `ap_ctrl_hs` | [`AxiLiteIn`]/[`AxiLiteOut`] |
14//!
15//! Words are little-endian 64-bit lanes: lane 0 holds bits 0..64.
16
17use rhdl::prelude::*;
18
19/// A 64-bit stream beat.
20#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
21pub struct Axis64 {
22 /// `tvalid`.
23 pub valid: bool,
24 /// `tdata`.
25 pub data: b64,
26}
27
28/// A 256-bit stream beat.
29#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
30pub struct Axis256 {
31 /// `tvalid`.
32 pub valid: bool,
33 /// `tdata`, four lanes.
34 pub data: [b64; 4],
35 /// `tlast`.
36 pub last: bool,
37}
38
39/// A 512-bit stream beat.
40#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
41pub struct Axis512 {
42 /// `tvalid`.
43 pub valid: bool,
44 /// `tdata`, eight lanes.
45 pub data: [b64; 8],
46 /// `tkeep`: one bit per byte (byte 0 in bit 0).
47 pub keep: b64,
48 /// `tlast`.
49 pub last: bool,
50}
51
52/// The `tkeep` of a meta beat (32 bytes, all kept).
53#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
54pub struct TxSide {
55 /// `tkeep` of the 256-bit meta beat.
56 pub keep: b32,
57}
58
59/// AXI4 read requests from the kernel (INCR bursts of 64-byte beats).
60#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
61pub struct AxiRead {
62 /// AR `valid`.
63 pub ar_valid: bool,
64 /// AR byte address (64-byte aligned).
65 pub ar_addr: b64,
66 /// AR `len`: beats − 1.
67 pub ar_len: b8,
68 /// R `ready`.
69 pub r_ready: bool,
70}
71
72/// AXI4 read responses to the kernel.
73#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
74pub struct AxiReadResp {
75 /// AR `ready`.
76 pub ar_ready: bool,
77 /// R `valid`.
78 pub r_valid: bool,
79 /// R `data`, eight lanes.
80 pub r_data: [b64; 8],
81 /// R `last`.
82 pub r_last: bool,
83}
84
85/// AXI4 write requests from the kernel (single 64-byte beats with byte
86/// strobes).
87#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
88pub struct AxiWrite {
89 /// AW `valid`.
90 pub aw_valid: bool,
91 /// AW byte address (64-byte aligned).
92 pub aw_addr: b64,
93 /// W `valid`.
94 pub w_valid: bool,
95 /// W `data`.
96 pub w_data: [b64; 8],
97 /// W `strb`: one bit per byte.
98 pub w_strb: b64,
99 /// B `ready`.
100 pub b_ready: bool,
101}
102
103/// AXI4 write responses to the kernel.
104#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
105pub struct AxiWriteResp {
106 /// AW `ready`.
107 pub aw_ready: bool,
108 /// W `ready`.
109 pub w_ready: bool,
110 /// B `valid`.
111 pub b_valid: bool,
112}
113
114/// AXI4-Lite requests (the layout of the Ed25519 and DSig services' port).
115#[derive(Clone, Copy, Debug, Default, Digital, PartialEq)]
116pub struct AxiLiteIn {
117 /// AW `valid`.
118 pub aw_valid: bool,
119 /// Write byte address (4 KiB aperture).
120 pub aw_addr: b12,
121 /// W `valid`.
122 pub w_valid: bool,
123 /// Write word.
124 pub w_data: b32,
125 /// Byte enables (every write is a whole word).
126 pub w_strb: b4,
127 /// B `ready`.
128 pub b_ready: bool,
129 /// AR `valid`.
130 pub ar_valid: bool,
131 /// Read byte address.
132 pub ar_addr: b12,
133 /// R `ready`.
134 pub r_ready: bool,
135}
136
137/// AXI4-Lite responses.
138#[derive(Clone, Copy, Debug, Default, Digital, PartialEq)]
139pub struct AxiLiteOut {
140 /// AW `ready`.
141 pub aw_ready: bool,
142 /// W `ready`.
143 pub w_ready: bool,
144 /// B `valid`.
145 pub b_valid: bool,
146 /// B `resp` (always OKAY).
147 pub b_resp: b2,
148 /// AR `ready`.
149 pub ar_ready: bool,
150 /// R `valid`.
151 pub r_valid: bool,
152 /// R `data`.
153 pub r_data: b32,
154 /// R `resp` (always OKAY).
155 pub r_resp: b2,
156 /// Level interrupt (`ap_done` with `GIE` and `IER` set).
157 pub interrupt: bool,
158}
159
160/// The kernel's control registers (byte offsets), those of ubft_new's HLS
161/// kernel (`xubft_hw.h`), so its host code drives rhdl_ubft unchanged.
162pub mod regs {
163 /// `ap_ctrl`: bit 0 `ap_start`, 1 `ap_done`, 2 `ap_idle`, 3 `ap_ready`.
164 pub const AP_CTRL: usize = 0x00;
165 /// Global interrupt enable.
166 pub const GIE: usize = 0x04;
167 /// Interrupt enable.
168 pub const IER: usize = 0x08;
169 /// Interrupt status.
170 pub const ISR: usize = 0x0c;
171 /// `network_ptr` (64 bits).
172 pub const NETWORK_PTR: usize = 0x10;
173 /// `message_ptr` (64 bits).
174 pub const MESSAGE_PTR: usize = 0x1c;
175 /// `node_num`.
176 pub const NODE_NUM: usize = 0x28;
177 /// `id`.
178 pub const ID: usize = 0x30;
179 /// `execution_cycles`.
180 pub const EXECUTION_CYCLES: usize = 0x38;
181 /// `wait`.
182 pub const WAIT: usize = 0x40;
183 /// `sk0` (64 bits); `sk1`, `sk2`, `sk3` follow at +0x0c each.
184 pub const SK0: usize = 0x48;
185 /// rhdl_ubft's `session` (64 bits): a host-chosen id, never reused, that
186 /// every signed share carries (outside ubft_new's map, which ends at 0x74).
187 pub const SESSION: usize = 0x80;
188 /// rhdl_ubft's own read-only status words start here (the HLS kernel
189 /// has nothing at or above 0x100).
190 pub const STATUS: usize = 0x100;
191}
192
193/// One host write into a scheme's configuration window.
194#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
195pub struct CfgWrite {
196 /// A write this cycle.
197 pub valid: bool,
198 /// Offset within the window (s_axi_control offset − 0x800).
199 pub addr: b12,
200 /// The word.
201 pub data: b32,
202}
203
204/// What the consensus controller asks of a signature scheme: the agreed
205/// four streams, plus a one-time setup (seed and peers' keys). A scheme is
206/// any `Synchronous` component with `I = CryptoIn, O = CryptoOut`
207/// (`crypto::ed25519::Ed25519Crypto`, DSig's `crypto::dsig`), and the
208/// replica is generic over it.
209///
210/// Streams (the layout both services share), sign layout v2 (ABI 2). The
211/// signed context is 48 bytes, all LE: bytes 0..8 `"RUB2"` (low 4 bytes)
212/// and the role (byte 4; 1 = certify share), 8..16 the session, 16..24
213/// `view | slot << 32`, 24..32 `k | m << 32`, 32..48 reserved (zero today;
214/// tail-CB instance context goes here, which DSig profile 2 also signs).
215/// * `sign`: one beat. Bytes 0..8 are the tag, bytes 8..56 the context.
216/// Ed25519 signs the context zero-extended to 64 bytes.
217/// * `sig`: two beats. Beat 0 is the 64-byte share, beat 1 bytes 0..8 the
218/// tag; `tlast` on beat 1.
219/// * `verify`: three beats. Beat 0 has byte 0 = the signer's replica index
220/// (its key slot) and bytes 4..8 the tag; beat 1 is the context at bytes
221/// 0..48, zero-extended to 64 (the message as signed); beat 2 the share.
222/// `tlast` on beat 2.
223/// * `verdict`: one word. Bits 0..8 are 0x80 if valid (else `0x80 | code`),
224/// 8..12 the slot, 32..64 the tag.
225#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
226pub struct CryptoIn {
227 /// Set up (one-cycle pulse).
228 pub setup: bool,
229 /// Replicas.
230 pub n: b5,
231 /// This replica.
232 pub id: b5,
233 /// This replica's private key (`sk0..sk3`).
234 pub sk: [b64; 4],
235 /// `message_ptr`: every replica's public key, 8 words each.
236 pub keys_base: b64,
237 /// `message_ptr` read answers.
238 pub msg: AxiReadResp,
239 /// Sign requests.
240 pub sign: Axis512,
241 /// `sig` `tready`.
242 pub sig_ready: bool,
243 /// Verify requests.
244 pub verify: Axis512,
245 /// `verdict` `tready`.
246 pub verdict_ready: bool,
247 /// `announce` `tready`.
248 pub announce_ready: bool,
249 /// A host write into the scheme's configuration window (s_axi_control
250 /// 0x800..0xFFF, only while stopped). DSig takes its per-session
251 /// namespace here; Ed25519 ignores it.
252 pub cfg: CfgWrite,
253 /// A peer's announce, fed back to the scheme: header beat (byte 0 =
254 /// the peer's key slot), then its two announce beats; `tlast` on the
255 /// third. Schemes without announces keep `cert_ready` high.
256 pub cert: Axis512,
257}
258
259/// A signature scheme's answers to the controller.
260#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
261pub struct CryptoOut {
262 /// Setup finished (level, until the next `setup`).
263 pub ready: bool,
264 /// `message_ptr` read requests.
265 pub msg: AxiRead,
266 /// `sign` `tready`.
267 pub sign_ready: bool,
268 /// Signature beats.
269 pub sig: Axis512,
270 /// `verify` `tready`.
271 pub verify_ready: bool,
272 /// Verdicts.
273 pub verdict: Axis64,
274 /// Sticky fault (the service's `FAULT` or a framing error).
275 pub fault: bool,
276 /// Best-effort announce for the peers (DSig: a new batch's 64-byte
277 /// commitment, then its 64-byte Ed25519 signature; `tlast` on beat 1).
278 /// Never valid for Ed25519.
279 pub announce: Axis512,
280 /// `cert` `tready`.
281 pub cert_ready: bool,
282}
283
284/// A signature scheme's compile-time shape. The replica takes `SB =
285/// C::SHARE_BEATS` as a const generic (kernels cannot read associated
286/// constants), and its constructor asserts they agree.
287pub trait CryptoScheme: rhdl::prelude::Synchronous<I = CryptoIn, O = CryptoOut> + Default + Clone {
288 /// 64-byte beats in one certify share (Ed25519 1, DSig 26). Shares sit
289 /// at `certify[(slot·N + node)·16·SHARE_BEATS]`; the share stream
290 /// (`sig`) carries this many beats and then the tag beat, and a verify
291 /// request is header, message, then this many share beats.
292 const SHARE_BEATS: usize;
293 /// Scheme number reported in `SHAPE` (1 Ed25519, 2 DSig).
294 const SCHEME: u8;
295}
296
297// Zero values usable inside kernels (`Default::default()` does not lower).
298
299/// No 64-bit beat.
300#[kernel]
301pub fn idle64() -> Axis64 {
302 Axis64 { valid: false, data: b64(0) }
303}
304
305/// No 256-bit beat.
306#[kernel]
307pub fn idle256() -> Axis256 {
308 Axis256 { valid: false, data: [b64(0); 4], last: false }
309}
310
311/// No 512-bit beat.
312#[kernel]
313pub fn idle512() -> Axis512 {
314 Axis512 { valid: false, data: [b64(0); 8], keep: b64(0), last: false }
315}
316
317/// No read request.
318#[kernel]
319pub fn idle_read() -> AxiRead {
320 AxiRead { ar_valid: false, ar_addr: b64(0), ar_len: b8(0), r_ready: false }
321}
322
323/// No read answer.
324#[kernel]
325pub fn idle_read_resp() -> AxiReadResp {
326 AxiReadResp { ar_ready: false, r_valid: false, r_data: [b64(0); 8], r_last: false }
327}
328
329/// No write request.
330#[kernel]
331pub fn idle_write() -> AxiWrite {
332 AxiWrite { aw_valid: false, aw_addr: b64(0), w_valid: false, w_data: [b64(0); 8], w_strb: b64(0), b_ready: false }
333}
334
335/// No AXI4-Lite request.
336#[kernel]
337pub fn idle_lite_in() -> AxiLiteIn {
338 AxiLiteIn {
339 aw_valid: false,
340 aw_addr: b12(0),
341 w_valid: false,
342 w_data: b32(0),
343 w_strb: b4(0),
344 b_ready: false,
345 ar_valid: false,
346 ar_addr: b12(0),
347 r_ready: false,
348 }
349}
350
351/// No AXI4-Lite answer.
352#[kernel]
353pub fn idle_lite_out() -> AxiLiteOut {
354 AxiLiteOut {
355 aw_ready: false,
356 w_ready: false,
357 b_valid: false,
358 b_resp: b2(0),
359 ar_ready: false,
360 r_valid: false,
361 r_data: b32(0),
362 r_resp: b2(0),
363 interrupt: false,
364 }
365}