Skip to main content

rhdl_fpga_planner/
decisions.rs

1//! # How the planner decides: from your knobs to a build
2//!
3//! This chapter follows one question from the moment you press a key in
4//! the TUI to the `build-config.toml` that W writes:
5//!
6//! > *With this many LUTs, DSPs, BRAM and URAM, which modules do we build,
7//! > how big is each one, and how fast does the chip sign and verify?*
8//!
9//! Read it once top to bottom and you will know which function to open
10//! for any number the screen shows. Every heuristic is named with the
11//! reason it exists and the problem it solved, because each one was added
12//! after a real number on the screen looked wrong.
13//!
14//! ```text
15//!  you: budget %, powers, background, limits, knobs
16//!        │
17//!        ▼
18//!  1. shares        powers → guaranteed LUT/DSP/BRAM/URAM per module   shares()
19//!        │
20//!        ▼
21//!  2. best builds   each module searches its own build space           Component::plan
22//!        │
23//!        ▼
24//!  3. lending       unused resources → modules that can use them       plan_chip()
25//!        │
26//!        ▼
27//!  4. delivery      capacity ÷ need per side, SHA-512 supply           deliver()
28//!        │
29//!        ▼
30//!  5. balancing     B key: sign/verify split; DSig background          balance_mix(), layout()
31//!        │
32//!        ▼
33//!  6. build flags   W key: each module's compile-time settings         build_config()
34//! ```
35//!
36//! # Chapter 1: A budget becomes shares
37//!
38//! The budget is four numbers: LUTs (`LUT %` of 485k), DSPs (`DSP %` of
39//! 4,520), BRAM (`BRAM %` of the 1,776 BRAM36 in the U280 shell's dynamic
40//! region) and URAM (`URAM %` of 960). See [`Budget`](crate::Budget).
41//!
42//! *Why 1,776 and not the device's 2,016:* DSig's build v4 failed
43//! placement asking for 4,033 BRAM18 in a region that has 3,552. The rest
44//! belongs to the shell. A plan that counts the whole device promises RAM
45//! we cannot have.
46//!
47//! The SHA-512 line is in the budget. Its allowance (65k LUTs and 20 DSPs
48//! by default) comes off the top, and the modules with powers divide the
49//! rest ([`Budget::rest`](crate::Budget::rest)). 485k and 4,520 are the old
50//! 420k and 4,500 plus that default, so the default chip gives the engines
51//! what it always did; but now a bigger line costs the engines LUTs, and a
52//! smaller one gives them LUTs. *Why it changed:* with the line outside
53//! the budget, shrinking it bought nothing, and the line's own unused LUTs
54//! (65k holds one 43.2k pipeline) could not be lent.
55//!
56//! The TUI shows this as the **LUT split** row, in % of the LUT budget:
57//!
58//! ```text
59//!  Ed25519 paper:  SHA x %  |  Ed25519 (100 - x) %
60//!  DSig paper:     SHA x %  |  DSig (100 - x)(100 - B)/100 %  |  Ed25519 (100 - x)·B/100 %
61//! ```
62//!
63//! **The line sizes itself (auto, the default).** The SHA-512 line should
64//! never be what limits the chip, and it should not hold LUTs it does not
65//! need. So [`plan_chip`](crate::plan_chip) asks it, through
66//! [`Component::size_for`](crate::Component::size_for), for the smallest
67//! build that hashes what the engines want
68//! ([`Delivered::sha_wanted`](crate::Delivered::sha_wanted), the blocks per
69//! second at the rates they deliver before any SHA limit), plans the engines
70//! in what that leaves, and repeats (a smaller line frees LUTs, the engines
71//! grow and want more) at most four times. The plan the line does not limit,
72//! delivering the most, wins, and
73//! [`apply_layout`](crate::apply_layout) tells the line the size it settled
74//! on ([`Component::resize`](crate::Component::resize)), so the next plan
75//! starts there. The Ed25519 paper at 100 % wants about 25M blocks per
76//! second, an eighth of a block per clock: a ~13.6k-LUT line instead of 65k.
77//!
78//! **By hand.** Left/Right on the row sets the line's LUT allowance (and its
79//! "size" knob to manual) in 0.5 % steps of the budget, from 0 up to 432k
80//! (ten full pipelines). A bigger line takes its LUTs from the engines; the
81//! "size" knob returns it to auto. B, the EdDSA background below, is a % of
82//! what the line leaves.
83//!
84//! **Every split sums to 100 %.** You never set a module's share directly.
85//! You set splits, and [`apply_layout`](crate::apply_layout) turns them
86//! into module powers:
87//!
88//! ```text
89//!  Ed25519 paper:  sign s %  +  verify (100 - s) %
90//!
91//!  DSig paper:     HBSS foreground (100 - B) %  +  EdDSA background B %
92//!                  foreground = DSig sign s % + DSig verify (100 - s) %
93//!                  background = Ed25519 sign e % + Ed25519 verify (100 - e) %
94//!
95//!  e.g. B = 8.5, s = 44, e = 75:
96//!    DSig sign 40.3 %, DSig verify 51.2 %, Ed25519 sign 6.4 %, Ed25519 verify 2.1 %
97//! ```
98//!
99//! A module's **guaranteed share** is its power % of each resource
100//! ([`shares`](crate::shares)). With power 40.3 % at the default budget that
101//! is 169.1k LUT, 1,811 DSP, 715 BRAM36 and 386 URAM.
102//!
103//! **Manual limits** ([`Slot::limits`](crate::Slot::limits)) override this
104//! per resource. "DSig sign URAM 10 %" gives DSig sign exactly 96 URAM, and
105//! the other modules split the remaining URAM by their powers. A limit is
106//! a hard cap: lending (Chapter 3) never gives that module more of that
107//! resource. Leave limits on auto unless you are testing a specific
108//! partition; auto plans better.
109//!
110//! # Chapter 2: Each module finds its best build
111//!
112//! Each module is a [`Component`](crate::Component). Given a share, it
113//! returns the fastest build that fits, or `None`. The planner knows
114//! nothing about Ed25519 or DSig; all the architecture choices below live
115//! in the modules' own crates. That is deliberate: the people who build a
116//! module know its costs.
117//!
118//! ## Ed25519 sign (`rhdl_ed25519_scalable_core::planner::Ed25519Sign`)
119//!
120//! A signing core is a profile `fb/sha/sc/cp`:
121//! - `fb`: fixed-base field machines, 14,615 LUT and 225 DSP each (routed).
122//! - `sha`: SHA-512 rings; 0 on the shared SHA-512 line.
123//! - `sc`: scalar rings.
124//! - `cp`: one batch-compress machine (1), or none (0).
125//!
126//! The search (`frontier::plan_sign_shaped`) tries every profile up to 40
127//! machines, with both multipliers (225 DSP, and the lean 150-DSP
128//! Karatsuba that costs +4,440 LUT and saves 75 DSP, routed), and keeps the
129//! fastest that fits. A tie goes
130//! to fewer LUTs. Three refinements shape what you see:
131//!
132//! 1. **The cycle model picks the bottleneck.** Cycles per signature is
133//!    the slowest stage (`config::Profile::cycles_per_sign`):
134//!    - machines: `392 bundles / fb / 0.97`;
135//!    - SHA: `243 / sha / 0.886`;
136//!    - scalar: `64 / sc / 0.9`;
137//!    - admission: 2;
138//!    - compress credits: `(392·32 + 423·64 + 8000) / 2048 = 23.25`.
139//!
140//!    That last stage caps one core at **8.82M signatures/s** however many
141//!    machines it has.
142//! 2. **Cores.** Because of that cap, a module may be 1 to
143//!    `MAX_CORES` (4) identical cores, each planned in `1/n` of the share.
144//!    Before this, the signing rate stopped growing near 8.8M whatever the
145//!    budget.
146//! 3. **Memory placement.** Every block starts in its preferred memory
147//!    layout (machine banks in URAM). If the share's URAM runs out, a
148//!    greedy placer (`frontier::place`) moves one block at a time to
149//!    whichever layout shrinks the overflow most:
150//!    - the block-RAM layout of a machine;
151//!    - a controller, key table or key loader with its URAM converted at
152//!      8 BRAM36 per URAM;
153//!    - and when BRAM runs out too, machine banks in LUT RAM (+22,830 LUT,
154//!      routed).
155//!
156//!    That is how spare LUTs still buy machines when RAM is short. The
157//!    plan detail says where memory went ("some URAM moved to BRAM",
158//!    "some banks in LUT RAM").
159//!
160//! Your shape knobs restrict the search; they never add builds it would
161//! not consider:
162//! - **block ratio manual**: only profiles whose `fb : sha : sc` follows
163//!   your weights (rounded, at least 1).
164//! - **batch compress on/off**: only `cp = 1` or `cp = 0` builds. Off
165//!   means each machine inverts and encodes its own point: the 657-bundle
166//!   program, a model. The measured small signer counts as off.
167//!
168//! The **small signer** (`Ed25519SignCoreSmall`, routed, 29,055 LUT,
169//! 248 DSP, 40 BRAM36, 32 URAM, 895 cycles = 229k signatures/s) is a separate
170//! candidate. It wins any share too small for a full core, which is
171//! exactly DSig's certificate signer.
172//!
173//! ## Ed25519 verify (`Ed25519Verify`)
174//!
175//! The same structure: a profile `machines/sha/sc/cp` (`cp` 0–2), verify
176//! modes with different program lengths (known-key 775 bundles, strict
177//! ~3,050, batch ~1,300, the last two models), cores, shape knobs and
178//! memory placement. Known-key adds a key loader and a 24-URAM key table
179//! per machine, which is why the verifier is the first to run out of URAM.
180//!
181//! ## DSig sign (`rhdl_dsig_scalable_core::planner::Dsig`)
182//!
183//! DSig scales in **slices**, 1 to 16. The largest count that fits wins
184//! (`Dsig::plan`).
185//! - One slice: 127.5k LUT (3 chain lanes of 23.9k Haraka plus a 16.4k
186//!   BLAKE3 factory lane plus the rest) and 72 URAM.
187//! - Rate: `slices × 205 MHz / 68` = 3.01M signatures/s per slice.
188//! - Built: 2, 4 and 8 slices; 2 is measured on the card at 6.02M/s.
189//!   Every other count is a model with the same per-slice cost.
190//!
191//! The knobs change the slice's cost, and only through cost do they change
192//! the rate. A cheaper chain hash (BLAKE3 lanes, 16.4k) or S-boxes in RAM
193//! fit more slices in the same LUTs; the key period (68 cycles) stays the
194//! same. This is why changing the hash at the default budget may show the
195//! same rate: the saving is real but not yet a whole slice.
196//!
197//! "Ed25519 certs" decides who signs the batch roots:
198//! - **The chip's Ed25519 sign module** (default): DSig's cost has no
199//!   Ed25519 core.
200//! - **Inside DSig**, as built: +25.4k LUT, a 29.3M/s certificate ceiling,
201//!   and the plan [covers](crate::Plan::covers) Ed25519 signing, so the
202//!   chip's Ed25519 sign module is not needed.
203//!
204//! The HBSS knob offers HORS. It plans nothing and says so
205//! ([`Component::unavailable`](crate::Component::unavailable)) until
206//! someone writes its cost model.
207//!
208//! ## DSig verify (`DsigVerify`, a model)
209//!
210//! Identical verify units, as many as the LUTs and BRAM allow. One unit is
211//! 3 chain lanes, a BLAKE3 lane and ~20k of glue: 108k LUT with Haraka. It
212//! takes 35 cycles per verification (105 chain steps over 3 lanes), so
213//! 5.86M/s per unit.
214//!
215//! ## SHA-512 line (`rhdl_sha512_pipelined::planner::Sha512`)
216//!
217//! One core, because the clients' arbiter drives a single in-order core:
218//! a full pipeline (62,252 LUT, 1 block per clock) or a folded ring of
219//! 2–40 stages (`rhdl_sha512_pipelined::folded`, 3,509 to 40,047 LUT,
220//! `S / 80` blocks per clock); LUTs routed out of context at 4.878 ns, not
221//! in-chip. `fastest(luts)` fills a manual LUT cap; on **auto** (the
222//! default), `smallest(units)` gives the cheapest core that hashes what the
223//! chip wants (below).
224//!
225//! # Chapter 3: Lending: real units, not percentages
226//!
227//! Percentages hand every module the same fraction of *every* resource,
228//! and modules are lopsided:
229//! - DSig signing uses no DSPs.
230//! - An Ed25519 verifier exhausts URAM long before LUTs.
231//! - A 40.3 % share holds 1 DSig slice (148k of its 169k LUT), and the
232//!   last 21k does nothing.
233//!
234//! The first planner wasted all of that, and the TUI used about half the
235//! LUTs you gave it.
236//!
237//! [`plan_chip`](crate::plan_chip) fixes it in rounds. After every module
238//! plans in its guaranteed share:
239//! 1. Count what is unused, per resource, in real units.
240//! 2. Re-plan each module in its own build's cost plus that free pool.
241//! 3. Commit the re-plan that raises the delivered rates most (relative
242//!    gains of both sides, summed), and repeat until nobody gains.
243//!
244//! Two rules keep it fair:
245//! - **Own side first.** Phase 0 lends only within a side (DSig sign and
246//!   the Ed25519 signer share the sign side's leftovers). Phase 1 opens
247//!   whatever is still free, which that side could not use, to either side.
248//!   Without this, the verify side once took the LUTs the sign side needed
249//!   for its next slice, and 200 % / 200 % delivered less than 200 % / 100 %.
250//! - **Borrow only what the gain needs.** The committed module takes the
251//!   smallest slice of the free pool (1/16, 1/8, 1/4, 1/2 or all) that
252//!   gains as much. Without this, the Ed25519 verifier in the DSig paper
253//!   took 123k spare LUTs (6 machines, 1.54M/s) to deliver 45.8k/s.
254//!
255//! The card line "uses resources other modules left free" marks a
256//! borrower; its gauges show the share it ended up with.
257//!
258//! ## The remainder: nothing idle that a module could use
259//!
260//! Lending only commits what raises the *delivered* rates. That leaves
261//! resources idle whenever the bottleneck is elsewhere: in the DSig paper
262//! the DSig signer uses almost no DSPs, but the Ed25519 modules behind it
263//! are not the bottleneck, so lending never gave them those DSPs. Two
264//! slices' worth of LUTs short of a third slice also sat idle.
265//!
266//! So after lending, a third pass (`fill_remainder`, inside
267//! [`plan_chip`](crate::plan_chip)) hands out whatever is still free:
268//! 1. **Pro rata.** Every module of the paper, the SHA-512 line included,
269//!    is offered its slice of each free resource, in proportion to its
270//!    guaranteed share of that resource, on top of its build. It keeps a
271//!    larger build if its capacity rises.
272//! 2. **Greedy.** If no pro-rata slice buys anything (the next core or
273//!    slice costs more than a slice), modules in descending guaranteed LUTs
274//!    may take the whole pool; the first that grows keeps what it uses.
275//! 3. Repeat (at most 6 rounds) until nothing grows.
276//!
277//! **The SHA-512 line takes part.** Its allowance is what it is
278//! guaranteed, not a ceiling: it borrows in lending's phase 1 when it is
279//! the bottleneck (a line shrunk to 2k grows back out of LUTs the engines
280//! leave), and it grows in the remainder pass up to its largest build, 2
281//! blocks per clock (two full pipelines, 86.4k LUT). In the Ed25519 paper
282//! at LUT 150 % / DSP 100 % the engines are DSP-bound, so the line takes
283//! the idle LUTs and goes from 1 to 2 blocks per clock.
284//!
285//! A step is kept only if neither side delivers less (one side's growth
286//! can take SHA-512 blocks from the other). The delivered rates rarely
287//! change in this pass; what changes is **capacity**, the headroom each
288//! module has above what the chip delivers, which the cards show as
289//! "capacity". *Why not lend it in the first place:* lending's rule,
290//! borrow only what raises the delivered rates, is what keeps a helper from
291//! taking the LUTs the bottleneck needs. The remainder pass runs after the
292//! bottleneck has taken everything it can use, so it cannot starve it.
293//!
294//! # Chapter 4: What the chip delivers
295//!
296//! Capacities are not results: on each side the slowest link sets the
297//! pace ([`deliver`](crate::deliver)). Each paper says how many operations
298//! of each role one delivered operation needs
299//! ([`Mode::need`](crate::Mode::need)):
300//!
301//! ```text
302//!  DSig sign side:    DSig sign × 1,  Ed25519 sign × 1/K          (K = 128)
303//!  DSig verify side:  DSig verify × 1, Ed25519 verify × (1 - h),  h = b·(K-1)/K
304//! ```
305//!
306//! A side delivers `min over its roles (capacity / need)`. Then the SHA-512
307//! line: if both sides together want more blocks than it supplies, both
308//! slow down in proportion and the screen marks it "limits the chip".
309//! Latency is the slowest module of the side plus one line pass per block.
310//!
311//! # Chapter 5: Balancing: what "100 %" means
312//!
313//! **The B key** ([`balance_mix`](crate::balance_mix)) searches the
314//! sign/verify split for the most `min(sign, verify / mix)`: 4 % steps,
315//! then 1 % and 0.5 % around the best. Ties go to the larger total. This
316//! is how a budget too small for both sides still shows something instead
317//! of zeros everywhere.
318//!
319//! **The DSig background on auto** ([`layout`](crate::layout)) asks how
320//! much EdDSA DSig really needs:
321//! 1. A quick search over the background `B` and its sign share `e`, with
322//!    every module in its guaranteed share.
323//! 2. A refinement over `B` in 0.5 % steps with the full planner,
324//!    lending included, because a borrowed slice can make a much smaller
325//!    background enough. Without this step the quick score reserved 20 %
326//!    and cost the foreground a whole slice.
327//!
328//! The smallest best `B` is shown as **needed** (8.5 % at the defaults).
329//!
330//! # Chapter 6: The build config
331//!
332//! W calls [`build_config`](crate::build_config): one TOML table per module
333//! with that module's
334//! [`build_flags`](crate::Component::build_flags), plus a header with the
335//! budget and the delivered rates.
336//!
337//! | Module | Keys | Meaning |
338//! |---|---|---|
339//! | Ed25519 sign | `ED25519_SCALABLE_PROFILE = "fb,sha,sc,cp"`, `cores`, `SH`, `memory`, `multiplier`, `uram_machines`, `lut_machines`, `buildable`, `not_buildable` | the profile `build.rs` reads; `1,1,1,0` is the small signer; `SH = 1` on the shared line; `SPECIALIZED` or `AS_BUILT` memory; `DSP225` or `LEAN150`; how many machines keep their banks in UltraRAM and how many in LUT RAM (the rest in block RAM), which the export reads as `ED25519_URAM_MACHINES` / `ED25519_LUT_MACHINES` |
340//! | Ed25519 verify | `verify_profile`, `cores`, `verify_mode`, `SH`, `memory`, `multiplier`, `uram_machines`, `lut_machines`, `buildable`, `not_buildable` | the same, for `KnownKeyVerifyCore` (`machines,sha,sc,1`: it always has one compress stage) |
341//! | DSig sign | `dsig.DSIG_SLICES = N`, `dsig.DSIG_CHAIN = "haraka"\|"blake3"\|"sha512"`, `dsig.DSIG_SBOX_PLACEMENT`, `dsig.DSIG_TABLE_MASK` (the T-table rounds "auto" chose, e.g. `0x1f8` for 6), `dsig.certificates`, `buildable`, `not_buildable` | build variables the DSig crate's `build.rs` reads (any other value fails the build, so host and kernel cannot silently disagree); `buildable = false` marks a model configuration with no build behind it, and `not_buildable` lists why (as for Ed25519) |
342//! | SHA-512 line | `sha512.full_pipelines`, `sha512.folded_pipelines` | the pipelines that fill the cap |
343//!
344//! *Why placement and the multiplier are in the file:* the first L200
345//! verifier plan (27,0,2,1) was exported with every machine in its
346//! preferred layout, and Vivado stopped before placement asking for 2,646
347//! BRAM36 on a device with 2,016 (job 023, 2026-10-18). A plan the file
348//! cannot reproduce is a plan nobody can build. Since then every Ed25519
349//! plan on screen is exported exactly as planned, and
350//! `measure/export_build.py` turns the table into the export's
351//! environment.
352//!
353//! # Chapter 7: Why a rate stays flat
354//!
355//! Builds come in whole blocks:
356//! - an Ed25519 machine is ~14.6k LUT and 225 DSP;
357//! - a DSig slice is ~128k LUT;
358//! - a DSig verify unit is ~108k LUT.
359//!
360//! Between steps nothing changes, and that is physical, not a bug. Every
361//! card shows **next: +N LUT -> rate**, the distance to the next faster
362//! build ([`tui::next_step`](crate::tui::next_step)), and its limiting
363//! resource. If "next" is far, look at the limit: often DSP or URAM, not
364//! LUT, is the wall.
365//!
366//! Two walls are common:
367//!
368//! - **DSPs in the Ed25519 paper.** Every Ed25519 machine needs its DSP
369//!   multiplier, so at DSP 100 % the chip is DSP-bound: LUT 150 % and 200 %
370//!   both deliver 4.57M signatures / 4.50M verifications (fix9 costs,
371//!   `measure/configs/predictions.tsv`), with 4,4xx of 4,520 DSPs used and
372//!   LUTs idle that no module can turn into a machine. Raise DSP and the
373//!   LUTs fill: 200 % / 200 % uses 956k of 970k LUTs for 6.09M / 6.61M.
374//!   (A LUT-only multiplier would turn those LUTs into machines; open
375//!   problem.)
376//! - **Slices in the DSig paper.** A slice is ~95k–125k LUT with T-tables.
377//!   At 100 % (485k, 65k of it the SHA line) the chip holds 2 slices and 3
378//!   verify units: 6.03M / 17.57M, all but 2k LUTs used. A third slice would
379//!   need a verify unit back, and 9.04M / 5.86M balances worse. At 150 % it
380//!   is 4 slices (12.06M), at 200 % 6 slices (18.09M). The sixth slice
381//!   needs DSig's "table RAM" knob on "block RAM, then URAM (model)" (the
382//!   default), which moves T-tables that block RAM cannot hold into spare
383//!   URAM; on "block RAM (built)" 200 % holds 5 slices (15.07M).
384//!
385//! In the DSig paper the Ed25519 modules get almost every DSP (DSig uses
386//! none), but they cannot build more machines without LUTs, which the
387//! slices hold; their capacity stays a little above what DSig needs.
388//!
389//! # Chapter 8: Working on it
390//!
391//! | You want to… | Change | Then check |
392//! |---|---|---|
393//! | correct a block's cost | the module's cost table (`frontier::slimmed_costs`, DSig `config.rs`) | `chip` test `rates_scale_with_luts_and_dsps` |
394//! | add a knob | the component's `knobs`/`set_knob`, and its search | the module's own tests; the knob appears in the TUI by itself |
395//! | share a knob across modules | `Knob::shared()` with the same name in each | `dsig_scheme_knobs_reach_both_modules` |
396//! | add a module | a [`Component`](crate::Component) and a slot in `rhdl_signatures::paper::slots`, behind its crate's feature | `this_builds_papers_balance_and_render` |
397//! | change how resources move | `plan_chip` (lending), `shares` (limits), `layout` (background) | planner tests `unused_resources_are_lent_in_real_units`, `manual_limits_are_exact_caps` |
398//!
399//! Three things bite newcomers:
400//! - **The plan cache** ([`plan_slot`](crate::plan_slot)) keys on the
401//!   component's name and knob values. A component whose plan depends on
402//!   state that is not a knob must put it in a knob, or stale plans
403//!   appear.
404//! - **Speed.** Balancing calls the planner thousands of times. Run the
405//!   chip tests with `--profile sim` (optimized); the debug build can
406//!   exceed the 20-minute bound of `tools/bounded.sh`.
407//! - **One tuner, two builds.** `cargo tuner` (the root crate
408//!   `rhdl_signatures`) has both papers; `cargo tuner-ed25519` is the same
409//!   binary built without the `dsig` feature, so it shows the Ed25519 paper
410//!   only. A paper is offered only when its modules are in the slot list
411//!   ([`App::has_paper`](crate::tui::App::has_paper)).
412//!
413//! # Open problems
414//!
415//! 1. A `cp = 0` known-key verifier (machines encode their own points)
416//!    would save the 16.6k-LUT compress machine at small sizes. It is not
417//!    built, so the search offers known-key verifiers only with `cp = 1`.
418//! 2. Measure the 8 BRAM per URAM conversion of controllers and key tables.
419//!    (URAM, block-RAM and LUT-RAM machine banks are routed at 205 MHz:
420//!    fix9 jobs 007zy–007zzb.)
421//! 3. Model HORS (a cost model for the HBSS knob's second choice).
422//! 4. Replace the DSig per-slice estimate with build v5's measured slice
423//!    (DSig expects ~75–80k LUT, which moves every step in Chapter 7).
424//! 5. Lending is greedy. It is checked by the scaling tests, not proven
425//!    optimal; an exact search over the few modules is possible if it
426//!    ever matters.
427//!
428//! # Where to go next
429//!
430//! - The chip guide (the root crate `rhdl_signatures`) for the papers, the features and the team.
431//! - [`crate::tui`], the Tuner guide: the screen, one plan start to finish,
432//!   every cost constant and the measured accuracy.
433//! - The Ed25519 crate's `frontier` and `planner` modules for its search.
434//! - DSig's `planner` module for slices, the HBSS and the verifier model.