Skip to main content

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}