Skip to content

Commit 437e80a

Browse files
docs(paper): one-contribution axis, industry-reader content, discovery discussion
Three passes over the paper (plus glosses across the docs), driven by a professor-style structural review and an industry-reader review (crypto protocol companies, hardware companies). No measurement changed; two overclaims corrected. Axis: the contribution is now stated once — a more efficient NTT core, proven correct — with the K-RED butterfly, psi-fold ROM and verification as its components, in two instantiations (streaming retrofit, own-FSM core). Title: 'FoldNTT: A Formally Verified Multiplier- and Twiddle-Lean NTT Core for Proth Primes'. Structure (professor review): whole-core ~1% Fmax now leads the timing paragraph (the -26% butterfly number is framed as the module-level gap it dilutes from); a Correctness guarantee paragraph opens §5 and the obligations table is split into proven vs simulated; the bug story is told three times instead of seven; §6 folded into §4.3; Table 'algebraic fact -> hardware saved' opens §4; a worked K-RED trace (v=5555 -> r=5496, hand-checked) follows the fold equations; §2/§7 positioning de-duplicated; Lemma statements already in Appendix A. Industry readers: §4.3 gains the fold-economics boundary — F stays 2-3 and cost tracks the signed-digit weight of k, with a planner-derived table (Falcon/Kyber/Dilithium/BabyBear/Goldilocks; Goldilocks k^F = 1 needs no ROM scaling) and an explicit 30-64-bit / RNS evaluation boundary; MIT license stated (upstream cfntt_ref is MIT too); a 'what a hardware team can lift' artifact map in Reproducibility; absolute-MHz open-flow hedge; 'generator supports parallel BFU' corrected to 'construction supports'; PWM double-pass cost stated; qualitative energy paragraph (no quantitative claim); explicit not-a-constant-time-certification sentence; reusable-harness paragraph. §8 is now Discussion: it documents the discovery process — the Visioned Vibe Coding loop (formally verify -> regenerate the RTL-grounded 3D model -> VLM critiques the renders -> design change -> verify before accepting) built on visually-3d — with Figure 2, a five-panel strip of the model at v1/v31/v32/v36/v37 composed from the published revision history, and keeps the no-generality/no-ablation caveat. The README gets the same strip and a short 'How it was found' section. Builds: single-column PDF and IEEE two-column (10 pages) both clean — zero LaTeX errors, zero missing glyphs, no overfull >30pt; ieee/build.sh learns the ../../assets path fixup for figures. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 6f1e26d commit 437e80a

12 files changed

Lines changed: 460 additions & 240 deletions

File tree

README.md

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,35 @@
77

88
[![verify](https://github.com/NyxFoundation/FoldNTT/actions/workflows/verify.yml/badge.svg)](https://github.com/NyxFoundation/FoldNTT/actions/workflows/verify.yml)
99

10-
A formally verified NTT accelerator for Falcon / Proth primes, with an
11-
on-board Basys 3 demo and a Vivado-free toolchain.
12-
13-
The name comes from the two core ideas, both of which fold something: K-RED
14-
folds modular reduction into shift-adds (one multiplier per butterfly instead
15-
of three), and the ψ-fold twiddle ROM folds the table in half (a shift-add
16-
derives the other half). Every module is proven equal to its mathematical
17-
spec with z3 + SymbiYosys, and the whole core runs end-to-end on a real FPGA.
10+
FoldNTT is one thing: a more efficient chip design for the polynomial
11+
multiplication at the heart of post-quantum cryptography, proven correct.
12+
13+
The contribution in one sentence: because the cryptographic prime
14+
q = 12289 = 3·2¹² + 1 has a special algebraic shape, and because the table
15+
of precomputed constants has a mirror symmetry, most of the multiplication
16+
hardware and half of the constant storage can be replaced by a few
17+
shift-and-add gates, so the chip computes exactly the same transform with
18+
one hardware multiplier instead of three and half the stored constants, at
19+
the same speed, and every step is machine-proven equal to the mathematics.
20+
21+
No hardware background is needed to see why this matters. Multiplier
22+
circuits are the scarcest resource on these chips, and computing the
23+
remainder after division by q is normally what consumes them. The prime's
24+
shape makes that remainder computable with shifts and additions (the K-RED
25+
trick), and the symmetry w[N/2+j] = ψ·w[j] means half the constant table
26+
is redundant: one shift-and-subtract recreates it. Proving every step
27+
equal to the mathematics, instead of testing a few examples, is also what
28+
exposed a real bug in the original design's inverse transform (reported
29+
upstream).
30+
31+
The name: both ideas fold something. K-RED folds the remainder step into
32+
shift-adds, and the ψ-fold ROM folds the constant table in half.
1833

1934
FoldNTT began as a formal-verification study of the released CFNTT radix-2
2035
accelerator ([xiang-rc/cfntt_ref](https://github.com/xiang-rc/cfntt_ref), Chen
21-
et al., TCHES 2022). That study found a bug in the inverse transform; the
22-
project became an own-FSM architecture that fixes it with fewer multipliers.
36+
et al., TCHES 2022). That study found the inverse-transform bug; the project
37+
became an own-FSM architecture that fixes it with fewer multipliers, running
38+
end-to-end on a real FPGA (a Basys 3 board) with no vendor software.
2339

2440
## Architecture
2541

@@ -76,6 +92,22 @@ twiddle ROM is [`psi-fold-rom/`](psi-fold-rom/), and the FSM + RAM + on-board
7692
self-test are [`ntt-core/`](ntt-core/). The paper's Fig. 1 in [`docs/`](docs/)
7793
shows the same butterfly at gate/register level.
7894

95+
## How it was found
96+
97+
The two folds came out of a recursive loop we call Visioned Vibe Coding:
98+
formally verify the design, render it as a 3D floor-plan model
99+
([visually-3d](https://github.com/NyxFoundation)), show the renders to a
100+
vision-language model, turn its observations into design changes, and
101+
verify again before accepting anything. The ψ-fold was spotted in the
102+
render itself: after K-RED shrank the arithmetic, the twiddle ROM was
103+
visibly the largest block left, and half of it turned out to be
104+
mathematically redundant.
105+
106+
<p align="center">
107+
<img src="docs/assets/discovery-timeline.png" alt="3D model at five points of the discovery loop" width="100%"><br/>
108+
<sub><i>The model across the loop's 37 revisions: first draft (v1); matured CFNTT floor plan, Barrett with 3 DSP (v31); the K-RED butterfly lands (v32); the ψ-fold is spotted (v36); FoldNTT final (v37). The full revision history, with the verification verdicts between steps, is published with the gallery.</i></sub>
109+
</p>
110+
79111
## Inventions
80112

81113
| Folder | Invention | Verified | Result |
@@ -123,6 +155,7 @@ openFPGALoader -b basys3 ntt-core/build/design.bit # LED1 = self-test PASS
123155
bug (missing per-stage halving) was found by verification, reported upstream.
124156
- **Open toolchain** synthesis → place-and-route → bitstream with no Vivado
125157
(yosys + openXC7 nextpnr-xilinx + prjxray).
158+
- MIT-licensed throughout (the upstream `cfntt_ref` submodule is also MIT).
126159

127160
See [`docs/`](docs/) for the paper and [`docs/venue-assessment.md`](docs/venue-assessment.md)
128161
for the submission strategy.

docs/assets/discovery-timeline.png

145 KB
Loading

docs/evaluation.md

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
## §sim: system-level RTL simulation (DONE)
44

5-
`verification/fullcore/run_stream.py` drives the invented RTL
6-
(`compact_bf_v2` + `modular_mul_kred` + `tf_rom_fold`) through a full N=1024
5+
`verification/fullcore/run_stream.py` drives the invented RTL (the
6+
register-level hardware source code:
7+
`compact_bf_v2` + `modular_mul_kred` + `tf_rom_fold`) through a full N=1024
78
NTT and INTT under iverilog. On every tested vector, the RTL `NTT(x)` equals
8-
the reference `DIT_NR_NTT` on the real twiddle table, and
9+
the reference `DIT_NR_NTT` on the real twiddle table (the precomputed
10+
constants the transform multiplies by), and
911
`INTT(NTT(x)) == x` exactly. The issue-#7 halving fix and the 9⁻¹-scaled
1012
folded ROM therefore compose into a correct transform at the system level,
11-
not merely per butterfly.
13+
not merely per butterfly (the transform's small multiply-and-add step).
1214

1315
This closes the gap the module proofs leave. fv_bf_v2 proves the butterfly
1416
matches the spec for every input; `run_stream.py` shows that the specced
@@ -17,7 +19,7 @@ whole transform on real gates (iverilog).
1719

1820
Caveat: cfntt_ref's exact banked-memory schedule is not reproduced, because
1921
the released `fsm.v` is empty (upstream #4). A reconstructed FSM
20-
(`fsm_recon.v`) driving the banked datapath is included but not yet
22+
(control state machine, `fsm_recon.v`) driving the banked datapath is included but not yet
2123
cycle-accurate. It is future work and off the critical path, since the
2224
inventions are drop-in with identical latency. See
2325
`verification/fullcore/README.md`.
@@ -26,7 +28,8 @@ inventions are drop-in with identical latency. See
2628

2729
All numbers come from `yosys` generic synthesis (`synth -flatten -noabc`),
2830
reproducible via `kred-butterfly/cost_report.ys` and the ROM comparison. Cell
29-
counts are technology-independent gate counts, not LUTs.
31+
counts are technology-independent gate counts, not LUTs (the FPGA's
32+
logic cells).
3033

3134
| Block | reference | proposed | Δ |
3235
|---|---|---|---|
@@ -56,7 +59,8 @@ real 7-series primitives resolves both. Per module, flattened
5659

5760
Reading, and why the per-FPGA numbers matter:
5861

59-
1. DSP 3 → 1 holds on real primitives: −67% DSP per butterfly and −27% FF
62+
1. DSP (the FPGA's dedicated multiplier blocks) 3 → 1 holds on real
63+
primitives: −67% DSP per butterfly and −27% FF (register cells)
6064
on the multiplier. NTT accelerators are almost always DSP-bound, since
6165
many parallel butterflies compete for the scarce DSP blocks. This is the
6266
saving that matters, and it scales ×d.
@@ -65,7 +69,8 @@ Reading, and why the per-FPGA numbers matter:
6569
On a LUT-bound design the trade could be neutral-to-negative; on the
6670
usual DSP-bound design it is a clear win. We report both directions.
6771
3. The ROM's −79% was generic gates, not FPGA. Mapped to primitives, the
68-
ROM stays in distributed LUT-RAM (yosys does not infer BRAM for
72+
ROM stays in distributed LUT-RAM (yosys does not infer BRAM — an
73+
on-chip memory block — for
6974
1023×14 here), so `tf_rom_fold` is only −20% LUT (241 → 192), with
7075
fold7 adding CARRY4. The defensible ROM claim is the −50% stored bits,
7176
which converts to a BRAM saving only when the table is large enough to
@@ -109,7 +114,9 @@ Whole-core reading:
109114

110115
### Timing proxy: logic depth (open flow, no PnR needed)
111116

112-
Fmax needs PnR, but yosys' longest-topological-path (`ltp`) gives a
117+
Fmax (the highest clock frequency the routed design sustains) needs PnR
118+
(place and route: mapping the logic onto the chip's physical fabric), but
119+
yosys' longest-topological-path (`ltp`) gives a
113120
technology-mapped logic-depth proxy that already answers the paper's two
114121
timing questions. Per module (post-`synth_xilinx`, LUT/carry levels):
115122

@@ -136,8 +143,9 @@ The first fold7 used three chained conditional subtractions (−4q, −2q, −q)
136143
LTP 31. Since `7x ∈ [0, 7q)`, we replaced them with six parallel constant
137144
comparators that select `m·q` from precomputed multiples, followed by a
138145
single subtraction: LTP 31 → 26, LUT 214 → 192, CARRY4 23 → 18, still
139-
DSP-free. The new datapath was re-verified end-to-end: z3 `VERIFIED`, the
140-
shipped-ROM equivalence miter, the full-transform sim, and the mutation
146+
DSP-free. The new datapath was re-verified end-to-end: z3 (an automated theorem prover) `VERIFIED`, the
147+
shipped-ROM equivalence miter (a circuit comparing the two
148+
implementations output-for-output), the full-transform sim, and the mutation
141149
sweep all pass. A pipelined fold7 (one extra register) would remove the
142150
depth from the ROM read entirely at +1 latency; we keep the combinational
143151
version to preserve the drop-in 1-cycle ROM interface, and flag the

docs/paper/ieee/build.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ pandoc --from gfm+raw_attribute --to latex -s \
1818

1919
python3 "$here/fix_longtables.py" "$here/paper_ieee.tex"
2020

21+
# image paths in paper.md are relative to docs/paper/; we compile one level
22+
# deeper, so point them one directory further up
23+
sed -i 's|{\.\./assets/|{../../assets/|g' "$here/paper_ieee.tex"
24+
2125
cd "$here"
2226
xelatex -interaction=nonstopmode paper_ieee.tex >build.log 2>&1 || true
2327
xelatex -interaction=nonstopmode paper_ieee.tex >build.log 2>&1 || true

0 commit comments

Comments
 (0)