Skip to main content

rhdl_ubft/
replica.rs

1//! One rhdl_ubft replica: the kernel that takes the place of ubft_new's.
2//!
3//! ```text
4//!  host ─AXI4-Lite─▶ Control ─args/start─▶ Protocol ◀─status
5//!                                           │  ▲
6//!     kvstore ─input───────────────────────▶│  │──output, remoteUpdate─▶ kvstore
7//!                                           ▼  │
8//!   network_ptr ◀─AXI4─ Fetcher, ShareReader, Writer
9//!   RoCE ◀─tx_meta, tx_data─ Broadcaster ◀─share RAM◀─ sig
10//!   message_ptr ◀─AXI4─ crypto C (setup + service) ◀─sign/verify─ Protocol
11//! ```
12//!
13//! A replica is generic over `N` (the most replicas the build supports) and
14//! `G` (the window). Its signature scheme is fixed per type:
15//! [`define_replica!`] stamps out one replica type for a scheme (RHDL's
16//! derives cannot take a component as a type parameter), with the scheme's
17//! share beats `SB` checked against [`CryptoScheme::SHARE_BEATS`].
18//!
19//! | Type | Scheme | Feature |
20//! |---|---|---|
21//! | [`RhdlUbftEd25519`] | Ed25519, configured verify machines | `ed25519` |
22//! | `RhdlUbftDsig` | DSig | `dsig` (DSig's module) |
23
24use rhdl::prelude::*;
25
26pub use crate::ports::{
27    AxiLiteIn, AxiLiteOut, AxiRead, AxiReadResp, AxiWrite, AxiWriteResp, Axis64, Axis256, Axis512, CryptoIn, CryptoOut, CryptoScheme,
28};
29
30/// The replica's inputs.
31#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
32pub struct ReplicaIn {
33    /// `s_axi_control`.
34    pub ctl: AxiLiteIn,
35    /// `input` (proposals from kvstore).
36    pub input: Axis64,
37    /// `output` `tready`.
38    pub output_ready: bool,
39    /// `remoteUpdate` `tready`.
40    pub remote_ready: bool,
41    /// `m_axis_tx_meta` `tready`.
42    pub tx_meta_ready: bool,
43    /// `m_axis_tx_data` `tready`.
44    pub tx_data_ready: bool,
45    /// `s_axis_tx_status`.
46    pub tx_status: Axis512,
47    /// `network_ptr` reads.
48    pub net_r: AxiReadResp,
49    /// `network_ptr` writes.
50    pub net_w: AxiWriteResp,
51    /// `message_ptr` reads.
52    pub msg_r: AxiReadResp,
53    /// The signature scheme's answers, for a replica whose scheme is outside
54    /// it ([`define_replica!`] with `external`; ignored otherwise).
55    pub crypto_resp: CryptoOut,
56}
57
58/// The replica's outputs.
59#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
60pub struct ReplicaOut {
61    /// `s_axi_control`.
62    pub ctl: AxiLiteOut,
63    /// `input` `tready`.
64    pub input_ready: bool,
65    /// `output`.
66    pub output: Axis256,
67    /// `remoteUpdate`.
68    pub remote: Axis256,
69    /// `m_axis_tx_meta`.
70    pub tx_meta: Axis256,
71    /// `m_axis_tx_data`.
72    pub tx_data: Axis512,
73    /// `s_axis_tx_status` `tready` (statuses are drained).
74    pub tx_status_ready: bool,
75    /// `network_ptr` reads.
76    pub net_r: AxiRead,
77    /// `network_ptr` writes.
78    pub net_w: AxiWrite,
79    /// `message_ptr` reads.
80    pub msg_r: AxiRead,
81    /// Requests to an outside signature scheme (`external` replicas; idle
82    /// otherwise).
83    pub crypto_req: CryptoIn,
84}
85
86/// Define a replica type `$name<N, G>` for signature scheme `$crypto`
87/// with `$sb` share beats.
88#[macro_export]
89macro_rules! define_replica {
90    ($(#[$m:meta])* $name:ident, $crypto:ty, $sb:expr) => {
91        $crate::define_replica!(@impl $(#[$m])* $name, $crypto, $sb, false);
92    };
93    // The scheme is outside the replica: its answers come in on
94    // `ReplicaIn.crypto_resp` and its requests go out on `crypto_req`.
95    ($(#[$m:meta])* $name:ident, external $sb:expr) => {
96        $crate::define_replica!(@impl $(#[$m])* $name, $crate::crypto::external::External<{ $sb }>, $sb, true);
97    };
98    (@impl $(#[$m:meta])* $name:ident, $crypto:ty, $sb:expr, $ext:expr) => {
99        $crate::replica::paste! {
100        /// An rhdl_ubft replica with this scheme (see the module docs).
101        #[derive(Clone, Debug, Synchronous, SynchronousDQ)]
102        pub struct $name<const N: usize, const G: usize> {
103            control: $crate::control::Control,
104            protocol: rhdl_fpga::core::dff::DFF<$crate::protocol::ProtocolState<N, G>>,
105            fetch: $crate::fetch::Fetcher,
106            share: $crate::share::ShareReader,
107            bcast: $crate::broadcast::Broadcaster,
108            write: $crate::writer::Writer,
109            share_ram: rhdl_fpga::core::ram::synchronous::SyncBRAM<[b64; 8], 5>,
110            crypto: $crypto,
111        }
112
113        impl<const N: usize, const G: usize> $name<N, G> {
114            /// A replica, idle until the host starts it.
115            pub fn new() -> Self {
116                assert_eq!($sb, <$crypto as $crate::ports::CryptoScheme>::SHARE_BEATS, "SB must be the scheme's SHARE_BEATS");
117                assert!($sb <= 31 && (1..=16).contains(&N) && (1..=8).contains(&G));
118                Self {
119                    control: $crate::control::Control::default(),
120                    protocol: rhdl_fpga::core::dff::DFF::new($crate::protocol::ProtocolState::<N, G>::idle()),
121                    fetch: $crate::fetch::Fetcher::default(),
122                    share: $crate::share::ShareReader::default(),
123                    bcast: $crate::broadcast::Broadcaster::default(),
124                    write: $crate::writer::Writer::default(),
125                    share_ram: rhdl_fpga::core::ram::synchronous::SyncBRAM::new(std::iter::empty()),
126                    crypto: <$crypto>::default(),
127                }
128            }
129        }
130
131        impl<const N: usize, const G: usize> Default for $name<N, G> {
132            fn default() -> Self {
133                Self::new()
134            }
135        }
136
137        impl<const N: usize, const G: usize> SynchronousIO for $name<N, G> {
138            type I = $crate::replica::ReplicaIn;
139            type O = $crate::replica::ReplicaOut;
140            type Kernel = [<$name:snake _kernel>]<N, G>;
141        }
142
143        /// The wiring.
144        #[kernel]
145        pub fn [<$name:snake _kernel>]<const N: usize, const G: usize>(
146            _cr: ClockReset,
147            i: $crate::replica::ReplicaIn,
148            q: [<$name Q>]<N, G>,
149        ) -> ($crate::replica::ReplicaOut, [<$name D>]<N, G>) {
150            let mut d = [<$name D>]::<N, G>::dont_care();
151            let ctl = q.control;
152            // External schemes answer through the replica's own ports.
153            let cr = if $ext { i.crypto_resp } else { q.crypto };
154
155            // The controller: a pure function of its registers and the engines.
156            let (p, next) = $crate::protocol::protocol_kernel::<N, G, $sb>(
157                $crate::protocol::ProtocolIn {
158                    start: ctl.start,
159                    args: ctl.args,
160                    setup_done: cr.ready,
161                    fetch: q.fetch,
162                    bcast: q.bcast,
163                    write: q.write,
164                    share: q.share,
165                    input: i.input,
166                    output_ready: i.output_ready,
167                    remote_ready: i.remote_ready,
168                    sign_ready: cr.sign_ready,
169                    sig: cr.sig,
170                    verify_ready: cr.verify_ready,
171                    verdict: cr.verdict,
172                },
173                q.protocol,
174            );
175            d.protocol = next;
176            d.control = $crate::control::ControlIn { axi: i.ctl, status: p.status };
177
178            // network_ptr reads: the share reader while it reads, else the fetcher.
179            let share_active = q.share.busy;
180            d.fetch = $crate::fetch::FetchIn {
181                req: p.fetch,
182                axi: $crate::ports::AxiReadResp {
183                    ar_ready: i.net_r.ar_ready && !share_active,
184                    r_valid: i.net_r.r_valid && !share_active,
185                    r_data: i.net_r.r_data,
186                    r_last: i.net_r.r_last,
187                },
188            };
189            d.share = $crate::share::ShareIn {
190                req: p.share,
191                axi: $crate::ports::AxiReadResp {
192                    ar_ready: i.net_r.ar_ready && share_active,
193                    r_valid: i.net_r.r_valid && share_active,
194                    r_data: i.net_r.r_data,
195                    r_last: i.net_r.r_last,
196                },
197                out_ready: p.share_ready,
198            };
199            let net_r = if share_active { q.share.axi } else { q.fetch.axi };
200
201            // RoCE, the share RAM, local writes.
202            d.bcast = $crate::broadcast::BroadcastIn { req: p.bcast, meta_ready: i.tx_meta_ready, data_ready: i.tx_data_ready, ram_data: q.share_ram };
203            d.share_ram.read_addr = q.bcast.ram_addr;
204            d.share_ram.write.addr = p.ram_addr;
205            d.share_ram.write.value = p.ram_data;
206            d.share_ram.write.enable = p.ram_we;
207            d.write = $crate::writer::WriteIn { req: p.write, axi: i.net_w };
208
209            // The signature scheme: set up at start, then the four streams.
210            let creq = $crate::ports::CryptoIn {
211                setup: ctl.start,
212                n: ctl.args.node_num.resize(),
213                id: ctl.args.id.resize(),
214                sk: ctl.args.sk,
215                keys_base: ctl.args.message_ptr,
216                msg: i.msg_r,
217                sign: p.sign,
218                sig_ready: p.sig_ready,
219                verify: p.verify,
220                verdict_ready: p.verdict_ready,
221                announce_ready: true,
222                cfg: ctl.cfg,
223                cert: $crate::ports::Axis512 { valid: false, data: [b64(0); 8], keep: b64(0), last: false },
224            };
225            d.crypto = creq;
226
227            let o = $crate::replica::ReplicaOut {
228                ctl: ctl.axi,
229                input_ready: p.input_ready,
230                output: p.output,
231                remote: p.remote,
232                tx_meta: q.bcast.meta,
233                tx_data: q.bcast.data,
234                tx_status_ready: true,
235                net_r: net_r,
236                net_w: q.write.axi,
237                msg_r: cr.msg,
238                crypto_req: creq,
239            };
240            (o, d)
241        }
242        }
243    };
244}
245
246#[doc(hidden)]
247pub use paste::paste;
248
249#[cfg(feature = "ed25519")]
250define_replica!(
251    /// rhdl_ubft with Ed25519 and the build-selected number of verify machines.
252    RhdlUbftEd25519,
253    crate::crypto::ed25519::Ed25519Crypto<{ crate::build_config::BUILD_VERIFY_MACHINES }>,
254    1
255);
256
257define_replica!(
258    /// rhdl_ubft with its signature scheme outside, one-beat shares
259    /// (Ed25519 framing). A card top wires `crypto_req` / `crypto_resp` to
260    /// a shared or multi-identity signer.
261    RhdlUbftExternal1,
262    external 1
263);