Decouple the ENC_C1/C2 compress micro-sequence into a streaming
producer/collector instead of feeding one coeff and draining the whole
6-stage comp_decomp pipeline per coeff.
- comp_decomp_sync: ready_o = 1'b1 (fully pipelined, consumer never
backpressures since ready_i is tied high), so feeders can issue a new
coeff every cycle.
- mlkem_top ENC_C1/C2: producer feeds one coeff every 2 cycles
(<=5.5 bit/cyc, under the single-port ct drain of 8 bit/cyc so the bit
buffer stays bounded); a per-cycle collector appends compressed bits
and drains one ct byte/cycle.
Verified byte-exact against NIST KAT (Vivado 2019.2 XSIM) for KeyGen,
Encaps (9/9), Decaps (9/9 incl. reject path + DECOMP), hello_world e2e.
K=2 Encaps: ENC_C1 6273->1035, ENC_C2 2945->522, total 27851->20190.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The previous Barrett multiplication-based division was incorrect. Both
compress and decompress used the same wrong implementation, so they
happened to be consistent with each other, but the math was wrong.
Replace with synthesizer-native / and % operators. This is cleaner
and the synthesis tool handles integer division/remainder correctly
for these small operand widths (12-bit dividend, 12-bit divisor).
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Wrap mlkem_top in an AXI4 burst slave (mlkem_axi_slave) mapped to the
freed FFT slot 6 at 0x1f400000 (virtual 0xbf400000). The core runs on
sys_clk/2 (25 MHz); the wrapper handles the clock-domain crossing:
start toggle->pulse, wait-stated byte-preload handshake, busy/done
sync, and direct readback taps (ek/dk/ct/ss). CPU drives it via a
register map (CTRL/STATUS/CONFIG, d/z/msg, preload + readback windows).
Wire into soc_top on the existing axiOut_6 nets (no crossbar change),
add mlkem_intr on intrpt[3]. Add sdk mlkem_test example running a
KeyGen->Encaps->Decaps self-consistency round trip.