Skip to main content

rhdl_fpga_planner/
workload.rs

1//! What the chip is for: the paper ([`Mode`]), its two sides ([`Side`]),
2//! the roles that make them up ([`Role`]) and the workload ([`Context`]).
3
4use crate::Component;
5
6/// Which paper the chip is planned for.
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8pub enum Mode {
9    /// Ed25519 alone.
10    Ed25519,
11    /// DSig with the Ed25519 signing and verification it needs.
12    Dsig,
13    /// Ed25519 as a service beside a uBFT replica: the uBFT modules only
14    /// ([`Component::ubft`]), headline idle latency rather than rate.
15    UbftEd25519,
16    /// DSig (with its Ed25519 work) as a service beside a uBFT replica.
17    UbftDsig,
18}
19
20/// What each paper's chip contains and needs.
21impl Mode {
22    /// Every mode, in the order the TUI offers them.
23    pub const ALL: [Mode; 4] = [Mode::Dsig, Mode::Ed25519, Mode::UbftDsig, Mode::UbftEd25519];
24    /// Display name.
25    pub fn name(self) -> &'static str {
26        match self {
27            Mode::Ed25519 => "Ed25519 paper (Ed25519 alone)",
28            Mode::Dsig => "DSig paper (DSig + Ed25519)",
29            Mode::UbftEd25519 => "uBFT with Ed25519 (beside a uBFT replica)",
30            Mode::UbftDsig => "uBFT with DSig (beside a uBFT replica)",
31        }
32    }
33    /// Short name for the headline.
34    pub fn short(self) -> &'static str {
35        match self {
36            Mode::Ed25519 => "Ed25519 paper",
37            Mode::Dsig => "DSig paper",
38            Mode::UbftEd25519 => "uBFT+Ed25519",
39            Mode::UbftDsig => "uBFT+DSig",
40        }
41    }
42    /// The paper whose needs and splits this mode follows: a uBFT mode
43    /// signs and verifies like the paper of its scheme.
44    pub fn paper(self) -> Mode {
45        match self {
46            Mode::Ed25519 | Mode::UbftEd25519 => Mode::Ed25519,
47            Mode::Dsig | Mode::UbftDsig => Mode::Dsig,
48        }
49    }
50    /// A uBFT mode: only [uBFT modules](Component::ubft) take part.
51    pub fn is_ubft(self) -> bool {
52        matches!(self, Mode::UbftEd25519 | Mode::UbftDsig)
53    }
54    /// What one delivered operation of the side is called.
55    pub fn op(self, side: Side) -> &'static str {
56        match (self.paper(), side) {
57            (Mode::Dsig, Side::Sign) => "DSig signatures",
58            (Mode::Dsig, Side::Verify) => "DSig verifications",
59            (_, Side::Sign) => "Ed25519 signatures",
60            (_, Side::Verify) => "Ed25519 verifications",
61        }
62    }
63    /// Operations of `role` needed per delivered operation of its side (0:
64    /// the role takes no part). `batch` is DSig's batch size `K`.
65    ///
66    /// - Ed25519 paper: one Ed25519 signature per signature, one Ed25519
67    ///   verification per verification.
68    /// - DSig paper: one DSig signature and `1/K` Ed25519 signature per
69    ///   signature; one DSig verification and `1 − h` Ed25519 verification
70    ///   per verification, `h = (b/100)·(K − 1)/K` ([`Context::cache_hit`]).
71    /// - A uBFT mode: as its [paper](Self::paper); one uBFT certificate
72    ///   share is one signature, checking a peer's share one verification.
73    pub fn need(self, role: Role, cx: &Context, batch: f64) -> f64 {
74        match (self.paper(), role) {
75            (Mode::Ed25519, Role::Ed25519Sign) => 1.0,
76            (Mode::Ed25519, Role::Ed25519Verify) => 1.0,
77            (Mode::Dsig, Role::DsigSign) => 1.0,
78            (Mode::Dsig, Role::DsigVerify) => 1.0,
79            (Mode::Dsig, Role::Ed25519Sign) => 1.0 / batch,
80            (Mode::Dsig, Role::Ed25519Verify) => 1.0 - cx.cache_hit(batch),
81            _ => 0.0,
82        }
83    }
84    /// Whether components of `role` belong in this mode's chip.
85    pub fn uses(self, role: Role) -> bool {
86        match role {
87            Role::Sha512 | Role::Other => true,
88            // The uBFT DSig services already contain their certificate cores.
89            // Generic DSig can still use external Ed25519 components.
90            Role::Ed25519Sign | Role::Ed25519Verify => self != Mode::UbftDsig,
91            Role::DsigSign | Role::DsigVerify => self.paper() == Mode::Dsig,
92        }
93    }
94    /// Whether `c` belongs in this mode's chip: its role is [used](Self::uses)
95    /// and it is a uBFT module exactly when this is a uBFT mode. So the
96    /// papers never plan a uBFT service, and a uBFT chip holds only the
97    /// replica and its services (each with its own SHA-512, no shared line).
98    pub fn admits(self, c: &dyn Component) -> bool {
99        self.uses(c.role()) && c.ubft() == self.is_ubft()
100    }
101}
102
103/// Signing or verification.
104#[derive(Clone, Copy, Debug, PartialEq, Eq)]
105pub enum Side {
106    /// Producing signatures.
107    Sign,
108    /// Checking signatures.
109    Verify,
110}
111
112/// The roles that make up each side.
113impl Side {
114    /// Both sides.
115    pub const BOTH: [Side; 2] = [Side::Sign, Side::Verify];
116    /// Display name.
117    pub fn name(self) -> &'static str {
118        match self {
119            Side::Sign => "sign",
120            Side::Verify => "verify",
121        }
122    }
123    /// The operation roles on this side.
124    pub fn roles(self) -> [Role; 2] {
125        match self {
126            Side::Sign => [Role::Ed25519Sign, Role::DsigSign],
127            Side::Verify => [Role::Ed25519Verify, Role::DsigVerify],
128        }
129    }
130}
131
132/// What a component does.
133#[derive(Clone, Copy, Debug, PartialEq, Eq)]
134pub enum Role {
135    /// Ed25519 signing.
136    Ed25519Sign,
137    /// Ed25519 verification.
138    Ed25519Verify,
139    /// DSig signing.
140    DsigSign,
141    /// DSig verification.
142    DsigVerify,
143    /// The SHA-512 line.
144    Sha512,
145    /// Anything else (no operations counted).
146    Other,
147}
148
149/// Role metadata for display and iteration.
150impl Role {
151    /// Every role that counts operations.
152    pub const OPS: [Role; 4] = [Role::Ed25519Sign, Role::Ed25519Verify, Role::DsigSign, Role::DsigVerify];
153    /// Display name.
154    pub fn name(self) -> &'static str {
155        match self {
156            Role::Ed25519Sign => "Ed25519 sign",
157            Role::Ed25519Verify => "Ed25519 verify",
158            Role::DsigSign => "DSig sign",
159            Role::DsigVerify => "DSig verify",
160            Role::Sha512 => "SHA-512 line",
161            Role::Other => "other",
162        }
163    }
164    /// The side the role works for (`None` for the SHA line and others).
165    pub fn side(self) -> Option<Side> {
166        match self {
167            Role::Ed25519Sign | Role::DsigSign => Some(Side::Sign),
168            Role::Ed25519Verify | Role::DsigVerify => Some(Side::Verify),
169            _ => None,
170        }
171    }
172}
173
174/// Workload inputs every component sees.
175#[derive(Clone, Copy, Debug, PartialEq)]
176pub struct Context {
177    /// Clock in MHz.
178    pub mhz: f64,
179    /// Verifications wanted per signature: the target of [`balance_mix`](crate::balance_mix)
180    /// (it does not tie the delivered rates together).
181    pub mix: f64,
182    /// Best-case expectation, 0..100 %: how often a DSig verification finds
183    /// its batch already verified (signatures arrive grouped by batch).
184    pub best_case: f64,
185}
186
187/// 205 MHz, one verification per signature, best case 100 %.
188impl Default for Context {
189    fn default() -> Self {
190        Context { mhz: 205.0, mix: 1.0, best_case: 100.0 }
191    }
192}
193
194/// Quantities derived from the workload inputs.
195impl Context {
196    /// Fraction of DSig verifications that skip Ed25519 with batch size
197    /// `batch`: at best every signature after the first of its batch,
198    /// `(b/100)·(K − 1)/K`.
199    pub fn cache_hit(&self, batch: f64) -> f64 {
200        (self.best_case.clamp(0.0, 100.0) / 100.0) * (batch - 1.0).max(0.0) / batch.max(1.0)
201    }
202}