ST_ENC_C reuses the ST_C CBD datapath with Encaps muxes: seed=r (not sigma),
eta=eta1 for y[0..K-1] then eta2 for e1[0..K-1]/e2, nonce 0..2K. 2K+1 polys
(vs KeyGen 2K). y/e1 -> bank_se rel slots 0..2K-1; e2 -> bank_t rel slot 0
(free during C/N/U since TDEC is deferred to V-prep so the 28-slot banks hold
all of A+y_hat+e1+e2 at K=4 without resizing).
Bring-up golden via ml-kem-r dump_encaps_full (working-tree example):
vectors/encgold/ec_k2_c0_{y,e1,e2}.hex. Verified (K=2 c0) y[0..1],e1[0..1],e2
== ml-kem-r; A_hat (E1) and ss (E0) still pass; K=3/4 no timeout.
ST_ENC_LOAD: stream rho (32B) from ek_bram[384k..] into rho_r (read-ahead,
1-cyc bram latency). ST_ENC_A: regenerate A_hat via SampleNTT into bank_a
(reuses ST_A datapath, gated on st==ST_A||ST_ENC_A). ST_ENC_TDEC:
byteDecode12 ek[i*384..] -> t_hat[i] into bank_t (5-cycle micro-phase per
3-byte/2-coeff triple; bt write port muxed with ST_M).
Verified (K=2 c0) A_hat (1024 coeffs) + t_hat (512) == KeyGen golden via
dbg_coeff_o; E0 ss==KAT.ss still passes all K/cases (no timeout).
Extend mlkem_top with a runtime op_i select (0=KeyGen, 1=Encaps) and the
first Encaps stages, reusing the shared keccak_core and the ST_H multi-block
SHA3-256 machinery:
ST_ENC_H: H(ek) over preloaded ek_bram (same FSM as KeyGen ST_H)
ST_ENC_G: (K,r) = G(m||H(ek)) via new 64-byte single-block SHA3-512
- sha3_top_shared: add mode=2'b11 = SHA3-512 over a full 512-bit message
(g512_pad). Standalone tb_sha3_g512 confirms it byte-exact.
- mlkem_top: new ports op_i, msg_i, ek_in_{we,addr,byte} (ek preload), ss_o,
dbg_ct_*, dbg_r_o/dbg_hek_o. st widened 4->5 bits; ST_ENC_* states added.
Renamed message port to msg_i to avoid collision with ST_M counter m_i.
- TB tb_mlkem_enc_katK + gen_encaps_vectors.py (per-byte ek/m/ct/ss vectors).
Verified ss==KAT.ss for K=2/3/4, cases 0-2 (all PASS). KeyGen unaffected
(K=2 c0 still ek==pk, dk==sk byte-exact).