Files
mlkem-sync/.claude/plans/phase2_polymem_bram.md
FallenSigh 4d3adc6b57 refactor(kg): split polymem into 3 banks {a, se, t} (async, stage 2a)
Replace the single async-read polymem[0:28*256-1] with 3 polynomial-indexed
banks (bank_a A_hat / bank_se s_hat||e_hat / bank_t t_hat), addressed by
abs_slot - base_slot. Still async-read here -- a pure refactor that validates
bank sizing and base-relative addressing with zero timing change before
stage 2b converts them to registered sd_bram + read-ahead pipelines.

11/11 KAT PASS, byte-exact, 0 file-not-found.
2026-06-28 15:55:26 +08:00

2.4 KiB

Phase 2: polymem -> BRAM banks

Goal

Replace the single multi-port async-read reg [11:0] polymem [0:28*256-1] with registered-read sd_bram banks that infer real BRAM (ASIC: compiled SRAM).

Bank split (all sd_bram, W=12, 1R+1W registered)

  • bank_a : A_hat[i][j], slots 0..K^2-1 -> D=4096 (KMAX^2*256), A=12
  • bank_se: s_hat[i] then e_hat[i] -> D=2048 (2KMAX256), A=11 relative slot = abs_slot - slot_s_rt (s: 0..K-1, e: K..2K-1)
  • bank_t : t_hat[i], slots slot_t_rt.. -> D=1024 (KMAX*256), A=10 relative slot = abs_slot - slot_t_rt

Port budget (verified disjoint)

  • poly_mul LOAD vs ACCUMULATE never overlap; ST_N/ST_M/ST_E are disjoint top-states. => each bank needs only 1R+1W. sd_bram fits.
  • bank_t ST_M-acc reads t_hat[idx] (j>0 source) and writes t_hat[idx] result same cycle: 1R+1W, read-old/write-new. Prefetch keeps read AHEAD of write (monotonic idx), so no same-pass RAW hazard.

Read sites (all need 1-cycle read-ahead vs async today)

  • ntt_in (ST_N load) <- bank_se[n_slot]
  • e_c0,e_c1 (ST_E) <- bank_se (dk) or bank_t (ek); SERIALIZE the two reads across the 3-byte/pair window
  • pm_a_in (ST_M load) <- bank_a[m_aslot]
  • pm_b_in (ST_M load) <- bank_se[m_j]
  • m_acc_src (ST_M acc) <- bank_se[K+m_i] (j==0) or bank_t[m_i] (j>0)
  • dbg_coeff_o (not on KAT path) <- route by slot range (compile-only)

Write sites

  • ST_A: bank_a[a_slot] <= snt_coeff
  • ST_C: bank_se[c_slot-slot_s] <= cbd_modq
  • ST_N: bank_se[n_slot] <= ntt_coeff (write-back, same slot)
  • ST_M-acc: bank_t[m_i] <= m_accq

Read-ahead strategy

Cores hold ready_o high through entire LOAD (no mid-stream backpressure) => fixed 1-cycle skew. Pattern: advance a read-address pointer 1 cycle ahead of the consumer index; delay the consumer's valid by 1 cycle ("prime the pipe"). For ST_M-acc (irregular pm_vo cadence 0,1,1): 2-entry prefetch skid buffer, read pointer runs monotonically ahead of write pointer.

Checkpoints (KAT-gated: 11 cases, byte-exact, 0 file-not-found)

  • 2a: split polymem -> 4 ASYNC-read banks (pure refactor, zero timing change). Validates bank sizing + base-relative addressing + debug mux. COMMIT.
  • 2b: convert banks to registered sd_bram + add read-ahead pipelines to every consumer FSM (ST_N, ST_E, ST_M load, ST_M acc). COMMIT.