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}