rhdl_ed25519_scalable_core/
fe17_kara.rs1use rhdl::prelude::*;
45use rhdl_fpga::core::dff::DFF;
46
47use super::fe17::{FieldMulIn, FieldMulOut, Fe17, MUL_LATENCY, MulNorm};
48
49#[derive(Copy, Clone, Debug, PartialEq, Digital)]
52pub struct KaraRow {
53 pub x: [s18; 5],
55 pub y: [s18; 5],
57}
58
59#[derive(Copy, Clone, Debug, PartialEq, Digital)]
66pub struct KaraIo {
67 pub rows: [KaraRow; 6],
69 pub win: [[s40; 5]; 6],
71 pub done: [[s40; 5]; 6],
73}
74
75#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
77pub struct KaraStage {
78 regs: DFF<KaraIo>,
80}
81
82impl Default for KaraStage {
84 fn default() -> Self {
85 Self { regs: DFF::resetless() }
86 }
87}
88
89impl SynchronousIO for KaraStage {
91 type I = KaraIo;
92 type O = KaraIo;
93 type Kernel = kara_stage_kernel;
94}
95
96#[kernel]
99pub fn kara_stage_kernel(_cr: ClockReset, i: KaraIo, q: KaraStageQ) -> (KaraIo, KaraStageD) {
100 let mut o = i;
101 for m in 0..6 {
102 let x = i.rows[m].x[0];
103 let mut win = i.win[m];
104 for j in 0..5 {
105 let p: s36 = x.xmul(i.rows[m].y[j]).as_signed_bits();
106 win[j] = i.win[m][j] + p.resize::<40>();
107 }
108 let mut done = i.done[m];
111 for j in 0..4 {
112 done[j] = i.done[m][j + 1];
113 }
114 done[4] = win[0];
115 let mut next = [s40(0); 5];
116 for j in 0..4 {
117 next[j] = win[j + 1];
118 }
119 let mut x_next = [s18(0); 5];
120 for j in 0..4 {
121 x_next[j] = i.rows[m].x[j + 1];
122 }
123 o.rows[m].x = x_next;
124 o.win[m] = next;
125 o.done[m] = done;
126 }
127 (q.regs, KaraStageD { regs: o })
128}
129
130#[derive(Copy, Clone, Debug, PartialEq, Digital)]
132#[doc(hidden)]
133pub struct KaraData {
134 a0: [b17; 15],
136 b0: [b17; 15],
138 rows: KaraIo,
140 sum8: [[s42; 9]; 7],
143 sum9: [[s42; 9]; 6],
145 g: [[s42; 9]; 5],
147 cols: [b39; 29],
149 folded: [b43; 15],
151 delay: [[b17; 15]; 5],
153}
154
155#[derive(Copy, Clone, Debug, PartialEq, Digital)]
157#[doc(hidden)]
158pub struct KaraCtl {
159 valid: [bool; 23],
161 tag: [b16; 23],
163}
164
165#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
168#[rhdl(dq_no_prefix)]
169pub struct FieldMulKara {
170 ctl: DFF<KaraCtl>,
172 data: DFF<KaraData>,
174 stages: [KaraStage; 5],
176 norm: MulNorm,
178}
179
180impl Default for FieldMulKara {
182 fn default() -> Self {
183 assert!(MUL_LATENCY == 23);
184 Self {
185 ctl: DFF::new(KaraCtl { valid: [false; 23], tag: [b16(0); 23] }),
186 data: DFF::resetless(),
187 stages: std::array::from_fn(|_| KaraStage::default()),
188 norm: MulNorm::default(),
189 }
190 }
191}
192
193impl SynchronousIO for FieldMulKara {
196 type I = FieldMulIn;
197 type O = FieldMulOut;
198 type Kernel = field_mul_kara_kernel;
199}
200
201#[kernel]
203pub fn digit_s18(x: b17) -> s18 {
204 x.resize::<18>().as_signed()
205}
206
207#[kernel]
209pub fn times19_col(x: b39) -> b43 {
210 let w = x.resize::<43>();
211 (w << 4) + (w << 1) + w
212}
213
214#[kernel]
216pub fn field_mul_kara_kernel(_cr: ClockReset, i: FieldMulIn, q: Q) -> (FieldMulOut, D) {
217 let c = q.ctl;
218 let r = q.data;
219 let mut nc = c;
220 let mut n = r;
221 nc.valid[0] = i.valid;
222 nc.tag[0] = i.tag;
223 for s in 1..23 {
224 nc.valid[s] = c.valid[s - 1];
225 nc.tag[s] = c.tag[s - 1];
226 }
227 n.a0 = i.a.d;
229 n.b0 = i.b.d;
230 let mut rows = r.rows;
233 for j in 0..5 {
234 for u in 0..3 {
235 rows.rows[u].x[j] = digit_s18(r.a0[5 * u + j]);
236 rows.rows[u].y[j] = digit_s18(r.b0[5 * u + j]);
237 }
238 rows.rows[3].x[j] = digit_s18(r.a0[j]) - digit_s18(r.a0[5 + j]);
239 rows.rows[3].y[j] = digit_s18(r.b0[j]) - digit_s18(r.b0[5 + j]);
240 rows.rows[4].x[j] = digit_s18(r.a0[j]) - digit_s18(r.a0[10 + j]);
241 rows.rows[4].y[j] = digit_s18(r.b0[j]) - digit_s18(r.b0[10 + j]);
242 rows.rows[5].x[j] = digit_s18(r.a0[5 + j]) - digit_s18(r.a0[10 + j]);
243 rows.rows[5].y[j] = digit_s18(r.b0[5 + j]) - digit_s18(r.b0[10 + j]);
244 }
245 rows.win = [[s40(0); 5]; 6];
246 rows.done = [[s40(0); 5]; 6];
247 n.rows = rows;
248 let st = q.stages[4];
251 let mut p = [[s42(0); 9]; 6];
252 for m in 0..6 {
253 for k in 0..5 {
254 p[m][k] = st.done[m][k].resize::<42>();
255 }
256 for k in 0..4 {
257 p[m][5 + k] = st.win[m][k].resize::<42>();
258 }
259 }
260 for k in 0..9 {
262 n.sum8[0][k] = p[0][k] + p[1][k];
263 n.sum8[1][k] = p[1][k] + p[2][k];
264 n.sum8[2][k] = p[0][k];
265 n.sum8[3][k] = p[2][k];
266 n.sum8[4][k] = p[3][k];
267 n.sum8[5][k] = p[4][k];
268 n.sum8[6][k] = p[5][k];
269 }
270 for k in 0..9 {
272 n.sum9[0][k] = r.sum8[2][k];
273 n.sum9[1][k] = r.sum8[0][k] - r.sum8[4][k];
274 n.sum9[2][k] = r.sum8[0][k] + r.sum8[3][k];
275 n.sum9[3][k] = r.sum8[1][k] - r.sum8[6][k];
276 n.sum9[4][k] = r.sum8[3][k];
277 n.sum9[5][k] = r.sum8[5][k];
278 }
279 for k in 0..9 {
281 n.g[0][k] = r.sum9[0][k];
282 n.g[1][k] = r.sum9[1][k];
283 n.g[2][k] = r.sum9[2][k] - r.sum9[5][k];
284 n.g[3][k] = r.sum9[3][k];
285 n.g[4][k] = r.sum9[4][k];
286 }
287 let mut cv = [s42(0); 29];
291 for u in 0..5 {
292 for s in 0..5 {
293 cv[5 * u + s] = r.g[u][s];
294 }
295 }
296 for u in 1..5 {
297 for s in 0..4 {
298 cv[5 * u + s] = cv[5 * u + s] + r.g[u - 1][s + 5];
299 }
300 }
301 for s in 0..4 {
302 cv[25 + s] = r.g[4][s + 5];
303 }
304 for t in 0..29 {
305 n.cols[t] = cv[t].as_unsigned().resize::<39>();
306 }
307 for k in 0..14 {
309 n.folded[k] = r.cols[k].resize::<43>() + times19_col(r.cols[k + 15]);
310 }
311 n.folded[14] = r.cols[14].resize::<43>();
312 n.delay[0] = q.norm;
314 for s in 1..5 {
315 n.delay[s] = r.delay[s - 1];
316 }
317 let d = D { ctl: nc, data: n, stages: [r.rows, q.stages[0], q.stages[1], q.stages[2], q.stages[3]], norm: r.folded };
318 let out = FieldMulOut { valid: c.valid[22], r: Fe17 { d: r.delay[4] }, tag: c.tag[22] };
319 (out, d)
320}
321
322#[cfg(test)]
324mod tests {
325 use super::*;
326 use crate::fe17::FieldMulPar;
327 use crate::host::Fp;
328 use crate::simulation::CycleSim;
329
330 fn sample(n: usize) -> Vec<Fp> {
332 let mut s = 0x2545_f491_4f6c_dd1du64;
333 let mut next = || {
334 s ^= s << 13;
335 s ^= s >> 7;
336 s ^= s << 17;
337 s
338 };
339 let max_block = |u: usize| {
342 let mut d = [0u32; 15];
343 for j in 0..5 {
344 d[5 * u + j] = 0x1ffff;
345 }
346 Fp::from_digits17(&d)
347 };
348 let mut v = vec![
349 Fp::ZERO,
350 Fp::ONE,
351 Fp::ZERO.sub(Fp::ONE),
352 Fp::ZERO.sub(Fp([19, 0, 0, 0])),
353 max_block(0),
354 max_block(1),
355 max_block(2),
356 ];
357 while v.len() < n {
358 let bytes: [u8; 32] = std::array::from_fn(|_| next() as u8);
359 v.push(Fp::from_bytes(&bytes));
360 }
361 v
362 }
363
364 #[test]
367 fn kara_matches_host_and_the_225_dsp_unit() {
368 let xs = sample(48);
369 let pairs: Vec<(Fp, Fp)> = xs.iter().flat_map(|a| xs.iter().take(10).map(move |b| (*a, *b))).collect();
370 let mut kara = CycleSim::new(FieldMulKara::default());
371 let mut par = CycleSim::new(FieldMulPar::default());
372 let mut got = 0;
373 for cycle in 0..pairs.len() + 40 {
374 let (valid, a, b) = match pairs.get(cycle) {
376 Some((a, b)) if cycle % 7 != 3 => (true, Fe17::from_fp(*a), Fe17::from_fp(*b)),
377 _ => (false, Fe17::zero(), Fe17::zero()),
378 };
379 let input = FieldMulIn { valid, a, b, tag: b16((cycle & 0xffff) as u128) };
380 let k = kara.tick(input);
381 let p = par.tick(input);
382 assert_eq!(k.valid, p.valid, "cycle {cycle}: valid");
383 if k.valid {
384 assert_eq!(k.tag, p.tag, "cycle {cycle}: tag");
385 let idx = k.tag.raw() as usize;
386 let (a, b) = pairs[idx];
387 assert_eq!(k.r.to_fp(), a.mul(b), "pair {idx}");
388 assert_eq!(k.r, p.r, "pair {idx}: same digits as FieldMulPar");
389 got += 1;
390 }
391 }
392 assert_eq!(got, (0..pairs.len()).filter(|c| c % 7 != 3).count());
393 }
394
395 #[test]
397 fn kara_lowers_to_verilog() -> Result<(), RHDLError> {
398 let text = FieldMulKara::default().descriptor("field_mul_kara".into())?.hdl_unchecked()?.modules.pretty();
399 assert!(text.contains("module field_mul_kara"));
400 Ok(())
401 }
402}