Skip to main content

rhdl_ed25519_fast_core/
result_arbiter.rs

1//! Lossless retirement arbitration for signing and verification results.
2//!
3//! The external black box retires one result per cycle. Signing has priority;
4//! verification completions that coincide with signing are retained in a
5//! compact 64-entry queue. The queue stores only tag, decision, and error.
6
7use rhdl::prelude::*;
8use rhdl_fpga::core::dff::DFF;
9
10use crate::protocol::{CoreResult, Words512, idle_result};
11
12/// A completed signing operation.
13#[derive(Copy, Clone, Debug, PartialEq, Digital)]
14pub struct SignCompletion {
15    /// Marks the completion valid.
16    pub valid: bool,
17    /// Request tag.
18    pub tag: b16,
19    /// Generated Ed25519 signature.
20    pub signature: Words512,
21}
22
23/// A completed strict-verification operation.
24#[derive(Copy, Clone, Debug, PartialEq, Digital)]
25pub struct VerifyCompletion {
26    /// Marks the completion valid.
27    pub valid: bool,
28    /// Request tag.
29    pub tag: b16,
30    /// Strict-verification decision.
31    pub ok: bool,
32    /// Stable verification error code.
33    pub error: b8,
34}
35
36/// Inputs to [`ResultArbiter`].
37#[derive(Copy, Clone, Debug, PartialEq, Digital)]
38pub struct ResultArbiterIn {
39    /// Optional signing completion.
40    pub sign: SignCompletion,
41    /// Optional verification completion.
42    pub verify: VerifyCompletion,
43    /// Sticky-error contribution from the signing pipeline.
44    pub sign_internal_error: bool,
45    /// Sticky-error contribution from the verification pipeline.
46    pub verify_internal_error: bool,
47}
48
49/// Outputs from [`ResultArbiter`].
50#[derive(Copy, Clone, Debug, PartialEq, Digital)]
51pub struct ResultArbiterOut {
52    /// High unless an impossible 64-result collision backlog is full.
53    pub verify_ready: bool,
54    /// Registered black-box result.
55    pub result: CoreResult,
56    /// Sticky internal error flag.
57    pub internal_error: bool,
58}
59
60#[derive(Copy, Clone, Debug, PartialEq, Digital)]
61#[doc(hidden)]
62pub struct ResultArbiterRegs {
63    queue: [VerifyCompletion; 64],
64    read: b6,
65    write: b6,
66    count: b7,
67    result: CoreResult,
68    internal_error: bool,
69}
70
71fn empty_verify() -> VerifyCompletion {
72    VerifyCompletion {
73        valid: false,
74        tag: b16(0),
75        ok: false,
76        error: b8(0),
77    }
78}
79
80fn reset_regs() -> ResultArbiterRegs {
81    ResultArbiterRegs {
82        queue: [empty_verify(); 64],
83        read: b6(0),
84        write: b6(0),
85        count: b7(0),
86        result: idle_result(),
87        internal_error: false,
88    }
89}
90
91/// Sign-priority arbiter with a 64-completion verifier backlog.
92#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
93pub struct ResultArbiter {
94    regs: DFF<ResultArbiterRegs>,
95}
96
97impl Default for ResultArbiter {
98    fn default() -> Self {
99        Self {
100            regs: DFF::new(reset_regs()),
101        }
102    }
103}
104
105impl SynchronousIO for ResultArbiter {
106    type I = ResultArbiterIn;
107    type O = ResultArbiterOut;
108    type Kernel = result_arbiter_kernel;
109}
110
111/// State transition for [`ResultArbiter`].
112#[kernel]
113pub fn result_arbiter_kernel(
114    _cr: ClockReset,
115    i: ResultArbiterIn,
116    q: ResultArbiterQ,
117) -> (ResultArbiterOut, ResultArbiterD) {
118    let r = q.regs;
119    let mut n = r;
120    n.result.valid = false;
121
122    let drain_verify = !i.sign.valid && r.count != b7(0);
123    let direct_verify = !i.sign.valid && r.count == b7(0) && i.verify.valid;
124    let enqueue_verify = i.verify.valid && !direct_verify;
125
126    if i.sign.valid {
127        n.result = CoreResult {
128            valid: true,
129            tag: i.sign.tag,
130            signature: i.sign.signature,
131            is_verify: false,
132            verify_ok: false,
133            error: b8(0),
134        };
135    } else if drain_verify {
136        let completion = r.queue[r.read];
137        n.result = CoreResult {
138            valid: true,
139            tag: completion.tag,
140            signature: Words512 { words: [b64(0); 8] },
141            is_verify: true,
142            verify_ok: completion.ok,
143            error: completion.error,
144        };
145        n.read = r.read + b6(1);
146    } else if direct_verify {
147        n.result = CoreResult {
148            valid: true,
149            tag: i.verify.tag,
150            signature: Words512 { words: [b64(0); 8] },
151            is_verify: true,
152            verify_ok: i.verify.ok,
153            error: i.verify.error,
154        };
155    }
156
157    if enqueue_verify {
158        if r.count != b7(64) || drain_verify {
159            n.queue[r.write] = i.verify;
160            n.write = r.write + b6(1);
161        } else {
162            n.internal_error = true;
163        }
164    }
165    if enqueue_verify && !drain_verify && r.count != b7(64) {
166        n.count = r.count + b7(1);
167    } else if !enqueue_verify && drain_verify {
168        n.count = r.count - b7(1);
169    }
170
171    if i.sign_internal_error || i.verify_internal_error {
172        n.internal_error = true;
173    }
174    let output = ResultArbiterOut {
175        verify_ready: r.count != b7(64) || drain_verify,
176        result: r.result,
177        internal_error: r.internal_error,
178    };
179    (output, ResultArbiterD { regs: n })
180}
181
182#[cfg(test)]
183mod tests {
184    use super::*;
185
186    fn idle() -> ResultArbiterIn {
187        ResultArbiterIn {
188            sign: SignCompletion {
189                valid: false,
190                tag: b16(0),
191                signature: Words512::zero(),
192            },
193            verify: empty_verify(),
194            sign_internal_error: false,
195            verify_internal_error: false,
196        }
197    }
198
199    #[test]
200    fn simultaneous_verification_is_retained_behind_signing() {
201        let mut both = idle();
202        both.sign.valid = true;
203        both.sign.tag = b16(0x51);
204        both.sign.signature.words[0] = b64(0x5151);
205        both.verify = VerifyCompletion {
206            valid: true,
207            tag: b16(0x71),
208            ok: true,
209            error: b8(0),
210        };
211        let results: Vec<_> = ResultArbiter::default()
212            .run(
213                [idle(), both, idle(), idle()]
214                    .into_iter()
215                    .with_reset(1)
216                    .clock_pos_edge(100),
217            )
218            .synchronous_sample()
219            .map(|sample| sample.output.result)
220            .filter(|result| result.valid)
221            .collect();
222        assert_eq!(results.len(), 2);
223        assert_eq!(results[0].tag, b16(0x51));
224        assert!(!results[0].is_verify);
225        assert_eq!(results[1].tag, b16(0x71));
226        assert!(results[1].is_verify);
227        assert!(results[1].verify_ok);
228    }
229}