refactor(kg): share a single keccak_core across G/H, SampleNTT, CBD (4->1)
KeyGen's Keccak consumers (G/H via sha3, A via SampleNTT, C via CBD) run in disjoint top-FSM phases, so one keccak_core suffices. Add sha3_top_shared (keccak_core externalised as kc_* ports, like the existing sample_*_shared variants); mlkem_top now instantiates one keccak_core and a phase mux that routes kc_state_i/kc_valid_i from the active consumer and gates kc_valid_o per consumer (inactive samplers latch squeeze state unconditionally). Cuts the KeyGen datapath from 4 keccak_core (1600-bit state + 24-round logic each) to 1 -- the dominant ASIC area win. 11/11 KAT PASS (K=2 c0-4, K=3/4 c0-2), byte-exact, 0 file-not-found.
This commit is contained in:
@@ -9,9 +9,9 @@
|
||||
# ---- Step 1: compile RTL ----
|
||||
xvlog -sv --relax -i . sync_rtl/sha3/keccak_round.v
|
||||
xvlog -sv --relax -i . sync_rtl/sha3/keccak_core.v
|
||||
xvlog -sv --relax -i . sync_rtl/sha3/sha3_top.v
|
||||
xvlog -sv --relax -i . sync_rtl/sample_ntt/sample_ntt_sync.v
|
||||
xvlog -sv --relax -i . sync_rtl/sample_cbd/sample_cbd_sync.v
|
||||
xvlog -sv --relax -i . sync_rtl/sha3/sha3_top_shared.v
|
||||
xvlog -sv --relax -i . sync_rtl/sample_ntt/sample_ntt_sync_shared.v
|
||||
xvlog -sv --relax -i . sync_rtl/sample_cbd/sample_cbd_sync_shared.v
|
||||
xvlog -sv --relax -i . sync_rtl/ntt/barrett_mul.v
|
||||
xvlog -sv --relax -i . sync_rtl/ntt/zeta_rom.v
|
||||
xvlog -sv --relax -i . sync_rtl/ntt/butterfly_unit.v
|
||||
|
||||
@@ -179,13 +179,63 @@ module mlkem_top #(
|
||||
reg sha3_ack; // consumer ready for hash
|
||||
wire [511:0] g_data = {248'b0, 5'b0, k_r, d_i}; // data_i[263:256]=k, [255:0]=d
|
||||
|
||||
// ================================================================
|
||||
// Shared keccak_core + phase mux (3 consumers -> 1 core)
|
||||
//
|
||||
// G/H (u_sha3), SampleNTT (u_snt) and CBD (u_cbd) each need a Keccak
|
||||
// permutation, but they run in DISJOINT top-FSM phases (ST_G/ST_H, ST_A,
|
||||
// ST_C respectively), so at most one is ever active. One keccak_core
|
||||
// serves all three: kc_state_i/kc_valid_i are muxed by phase; kc_valid_o
|
||||
// is broadcast but GATED per consumer so an inactive consumer never
|
||||
// latches a permutation result meant for another (the samplers latch
|
||||
// squeeze state unconditionally on their kc_valid_o input).
|
||||
// ================================================================
|
||||
wire [1599:0] kc_state_o; // shared core output (to all consumers)
|
||||
wire kc_valid_o; // shared core output-valid
|
||||
wire kc_ready_o; // shared core input-ready
|
||||
|
||||
// per-consumer drives toward the core
|
||||
wire [1599:0] sha3_kc_state_i, snt_kc_state_i, cbd_kc_state_i;
|
||||
wire sha3_kc_valid_i, snt_kc_valid_i, cbd_kc_valid_i;
|
||||
/* verilator lint_off UNUSEDSIGNAL */
|
||||
wire sha3_kc_ready_i, snt_kc_ready_i, cbd_kc_ready_i; // all 1'b1
|
||||
/* verilator lint_on UNUSEDSIGNAL */
|
||||
|
||||
// phase selects (mutually exclusive)
|
||||
wire sel_sha3 = (st == ST_G) || (st == ST_H);
|
||||
wire sel_snt = (st == ST_A);
|
||||
wire sel_cbd = (st == ST_C);
|
||||
|
||||
// gated output-valid: only the active consumer sees kc_valid_o
|
||||
wire kc_valid_o_sha3 = kc_valid_o & sel_sha3;
|
||||
wire kc_valid_o_snt = kc_valid_o & sel_snt;
|
||||
wire kc_valid_o_cbd = kc_valid_o & sel_cbd;
|
||||
|
||||
// input mux: route the active consumer's request to the core
|
||||
wire [1599:0] kc_state_i_mux = sel_snt ? snt_kc_state_i :
|
||||
sel_cbd ? cbd_kc_state_i :
|
||||
sha3_kc_state_i;
|
||||
wire kc_valid_i_mux = sel_snt ? snt_kc_valid_i :
|
||||
sel_cbd ? cbd_kc_valid_i :
|
||||
sel_sha3 ? sha3_kc_valid_i : 1'b0;
|
||||
|
||||
keccak_core #(.ROUNDS(24)) u_keccak (
|
||||
.clk(clk), .rst_n(rst_n),
|
||||
.state_i(kc_state_i_mux),
|
||||
.valid_i(kc_valid_i_mux),
|
||||
.ready_o(kc_ready_o),
|
||||
.state_o(kc_state_o),
|
||||
.valid_o(kc_valid_o),
|
||||
.ready_i(1'b1) // consumers always accept (kc_ready_i=1)
|
||||
);
|
||||
|
||||
// ---- single shared sha3_top serving BOTH G and H ----
|
||||
// G (ST_G) uses single-block mode (mb_en=0); H(ek) (ST_H) uses the
|
||||
// multi-block absorb path (mb_en=1). These phases are disjoint in the
|
||||
// top FSM, so one sha3_top (one keccak_core) is sufficient. mb_en and
|
||||
// ready_i are muxed by phase; data_i/mode only matter while mb_en=0.
|
||||
wire sha3_mb_en = (st == ST_H);
|
||||
sha3_top u_sha3 (
|
||||
sha3_top_shared u_sha3 (
|
||||
.clk(clk), .rst_n(rst_n),
|
||||
.mode(2'b00), // G = SHA3-512 (only used when mb_en=0)
|
||||
.data_i(g_data),
|
||||
@@ -198,7 +248,14 @@ module mlkem_top #(
|
||||
.mb_block_i(h_block_r),
|
||||
.mb_valid_i(h_mbvalid),
|
||||
.mb_last_i(h_mblast),
|
||||
.mb_ready_o(h_mbready)
|
||||
.mb_ready_o(h_mbready),
|
||||
// shared keccak_core interface (gated by phase in the mux below)
|
||||
.kc_state_o(kc_state_o),
|
||||
.kc_valid_o(kc_valid_o_sha3),
|
||||
.kc_ready_o(kc_ready_o),
|
||||
.kc_state_i(sha3_kc_state_i),
|
||||
.kc_valid_i(sha3_kc_valid_i),
|
||||
.kc_ready_i(sha3_kc_ready_i)
|
||||
);
|
||||
|
||||
// ---- multi-block H(ek) state (SHA3-256, 6/9/12 blocks); fed to shared u_sha3 ----
|
||||
@@ -265,7 +322,7 @@ module mlkem_top #(
|
||||
wire snt_last;
|
||||
reg snt_ack; // we accept coeffs
|
||||
|
||||
sample_ntt_sync #(.K(KMAX)) u_snt (
|
||||
sample_ntt_sync_shared #(.K(KMAX)) u_snt (
|
||||
.clk(clk), .rst_n(rst_n),
|
||||
.rho_i(rho_r),
|
||||
.k_i(k_r),
|
||||
@@ -276,7 +333,14 @@ module mlkem_top #(
|
||||
.coeff_o(snt_coeff),
|
||||
.valid_o(snt_vo),
|
||||
.ready_i(snt_ack),
|
||||
.last_o(snt_last)
|
||||
.last_o(snt_last),
|
||||
// shared keccak_core interface
|
||||
.kc_state_o(kc_state_o),
|
||||
.kc_valid_o(kc_valid_o_snt),
|
||||
.kc_ready_o(kc_ready_o),
|
||||
.kc_state_i(snt_kc_state_i),
|
||||
.kc_valid_i(snt_kc_valid_i),
|
||||
.kc_ready_i(snt_kc_ready_i)
|
||||
);
|
||||
|
||||
// ---- sample_cbd_sync: s[i]=CBD3(PRF(sigma,i)), e[i]=CBD3(PRF(sigma,K+i)) ----
|
||||
@@ -287,7 +351,7 @@ module mlkem_top #(
|
||||
wire cbd_last;
|
||||
reg cbd_ack;
|
||||
|
||||
sample_cbd_sync u_cbd (
|
||||
sample_cbd_sync_shared u_cbd (
|
||||
.clk(clk), .rst_n(rst_n),
|
||||
.seed_i(sigma_r),
|
||||
.nonce_i(c_nonce),
|
||||
@@ -297,7 +361,14 @@ module mlkem_top #(
|
||||
.coeff_o(cbd_coeff),
|
||||
.valid_o(cbd_vo),
|
||||
.ready_i(cbd_ack),
|
||||
.last_o(cbd_last)
|
||||
.last_o(cbd_last),
|
||||
// shared keccak_core interface
|
||||
.kc_state_o(kc_state_o),
|
||||
.kc_valid_o(kc_valid_o_cbd),
|
||||
.kc_ready_o(kc_ready_o),
|
||||
.kc_state_i(cbd_kc_state_i),
|
||||
.kc_valid_i(cbd_kc_valid_i),
|
||||
.kc_ready_i(cbd_kc_ready_i)
|
||||
);
|
||||
|
||||
// signed (two's complement) -> [0,Q): add Q when negative
|
||||
|
||||
Reference in New Issue
Block a user