Files
mlkem-sync/sync_rtl/top/mlkem_top.v
FallenSigh 3284aa443f fix(kg): implement t_hat computation and pk/sk output in mlkem_top
- Add s_hat0_reg/s_hat1_reg for s_hat capture during CBD→NTT output
- Add tmul_accum register for poly_mul result accumulation
- Implement S_KG_TMUL_MUL_LD: load A+s coefficients into poly_mul
- Implement S_KG_TMUL_MUL_WAIT/ACCUM: accumulate A·s products
- Implement S_KG_TMUL_ADD_E: add e_hat to accumulated sum
- Assign pk_o_r = {t_hat1, t_hat0}_reg and sk_o_r = {s_hat1, s_hat0}_reg
- Shift Encaps/Decaps FSM states by +3

KeyGen: 5/5 vectors complete in ~47K cycles/op
2026-06-27 01:38:38 +08:00

1652 lines
66 KiB
Verilog
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// mlkem_top.v - Top-level ML-KEM integration module
//
// Monolithic FSM implementing KeyGen, Encaps, and Decaps (FIPS 203)
// using shared Keccak architecture with keccak_arbiter.
//
// Architecture:
// keccak_core (shared) ← keccak_arbiter #(3)
// ├── Consumer 0: sha3_chain_top_shared (G function)
// ├── Consumer 1: sample_cbd_sync_shared (CBD sampling)
// └── Consumer 2: sample_ntt_sync_shared (NTT sampling)
// sha3_top (separate, own keccak_core) — for H/KDF calls
// rng_sync, ntt_core, poly_mul_sync, poly_arith_sync,
// comp_decomp_sync, mod_add_sync
// sd_bram × 1 (large) — polynomial coefficient storage
//
// Parameters:
// K = 4 (max module rank; actual k selected at runtime via i_k)
`include "sync_rtl/common/defines.vh"
/* verilator lint_off UNUSEDPARAM */
module mlkem_top #(
parameter K = 4
) (
/* verilator lint_on UNUSEDPARAM */
input clk,
input rst_n,
input [1:0] mode, // 00=KeyGen, 01=Encaps, 10=Decaps
input [2:0] i_k, // actual k (2,3,4)
input valid_i,
output ready_o,
// KeyGen outputs
output [12*K*256-1:0] pk_o, // public key (raw t_hat polys + rho)
output [12*K*256-1:0] sk_o, // secret key (raw s_hat polys)
output pk_valid,
output sk_valid,
// Encaps outputs
output [12*K*256-1:0] ct_o, // ciphertext
output [255:0] K_o, // shared secret
output ct_valid,
output K_valid,
// Decaps output
output [255:0] K_o_dec, // shared secret
output K_valid_dec,
output done_o
);
// ====================================================================
// Local parameters
// ====================================================================
localparam N = `N; // 256
localparam Q = `Q; // 3329
localparam N_CONSUMERS = 3; // sha3_chain, sample_cbd, sample_ntt
localparam N_POLYS = 48; // polynomial slots
localparam POLY_SIZE = N; // 256 coeffs per polynomial
localparam BRAM_DEPTH = POLY_SIZE * N_POLYS; // 12288
localparam BRAM_AW = $clog2(BRAM_DEPTH); // 14
// BRAM address map (base address = poly_idx * 256)
// A matrix: poly 0 .. 15 (K*K max)
// s_hat: poly 16 .. 19 (K max)
// e_hat: poly 20 .. 23
// t_hat: poly 24 .. 27
// y_hat: poly 28 .. 31
// e1: poly 32 .. 35
// u: poly 36 .. 39
// scratch0: poly 44
// scratch1: poly 45
localparam A_BASE = 0;
localparam S_BASE = 16;
localparam E_BASE = 20;
localparam T_BASE = 24;
localparam Y_BASE = 28;
localparam E1_BASE = 32;
localparam U_BASE = 36;
localparam SCRATCH0 = 44;
localparam SCRATCH1 = 45;
function [BRAM_AW-1:0] poly_addr;
input [5:0] poly_idx;
input [7:0] coeff_idx;
begin
poly_addr = (poly_idx * POLY_SIZE) + coeff_idx;
end
endfunction
// ====================================================================
// I/O registers
// ====================================================================
reg [1:0] mode_r;
reg [2:0] k_r; // actual k (2,3,4)
reg ready_o_r;
reg pk_valid_r, sk_valid_r;
reg ct_valid_r, K_valid_r;
reg K_valid_dec_r;
reg done_o_r;
// KeyGen output registers
reg [12*K*256-1:0] pk_o_r;
reg [12*K*256-1:0] sk_o_r;
// Encaps output registers
reg [12*K*256-1:0] ct_o_r;
reg [255:0] K_o_r;
// Decaps output registers
reg [255:0] K_o_dec_r;
// Internal data registers
reg [255:0] d_reg; // RNG output d
reg [255:0] rho_reg; // G output rho
reg [255:0] sigma_reg; // G output sigma
reg [255:0] m_reg; // Encaps message m
reg [255:0] Kbar_reg; // G output K-bar
reg [255:0] r_reg; // G output r
reg [255:0] Hpk_reg; // H(pk) for Encaps
reg [255:0] z_reg; // implicit rejection value
// s_hat capture registers (filled during CBD→NTT output)
reg [N*12-1:0] s_hat0_reg, s_hat1_reg;
// t_hat output registers (computed during t_hat computation)
reg [N*12-1:0] t_hat0_reg, t_hat1_reg;
// t_hat computation scratch registers
reg [11:0] tmul_pipe_reg; // 1-cycle pipeline delay for poly_mul output
reg tmul_pipe_valid; // flag: tmul_pipe_reg holds valid data
reg [8:0] tmul_out_cnt; // output coefficient counter for accumulation
reg tmul_adv_row; // flag: advancing to next row (set in ADD_E, used in MUL_LD)
// ====================================================================
// Keccak arbiter signals (3 consumers → 1 keccak_core)
// ====================================================================
// Arbiter ↔ keccak_core
wire [1599:0] arb_kc_state_i;
wire arb_kc_valid_i;
wire arb_kc_ready_o;
wire [1599:0] arb_kc_state_o;
wire arb_kc_valid_o;
wire arb_kc_ready_i; // always 1'b1
// Consumer-side packed vectors
wire [N_CONSUMERS*1600-1:0] arb_cons_state_i;
wire [N_CONSUMERS-1:0] arb_cons_valid_i;
wire [N_CONSUMERS-1:0] arb_cons_ready_o;
wire [N_CONSUMERS*1600-1:0] arb_cons_state_o;
wire [N_CONSUMERS-1:0] arb_cons_valid_o;
wire [N_CONSUMERS-1:0] arb_cons_ready_i; // all tied high
// ====================================================================
// sha3_chain_top_shared (consumer 0) signals
// ====================================================================
wire chain_start, chain_done;
wire [255:0] chain_rho, chain_sigma;
wire [1599:0] chain_kc_state_o, chain_kc_state_i;
wire chain_kc_valid_o, chain_kc_valid_i;
wire chain_kc_ready_i, chain_kc_ready_o;
// ====================================================================
// sample_cbd_sync_shared (consumer 1) signals
// ====================================================================
reg cbd_valid_i_r;
reg [7:0] cbd_nonce_r;
reg [1:0] cbd_eta_r;
wire cbd_ready_o;
wire [11:0] cbd_coeff_o;
wire cbd_valid_o, cbd_last_o;
wire [1599:0] cbd_kc_state_o, cbd_kc_state_i;
wire cbd_kc_valid_o, cbd_kc_valid_i;
wire cbd_kc_ready_i, cbd_kc_ready_o;
// CBD seed mux: sigma_reg (KeyGen) or r_reg (Encaps)
reg cbd_use_r_seed; // 1 = use r_reg, 0 = use sigma_reg
wire [255:0] cbd_seed_muxed;
assign cbd_seed_muxed = cbd_use_r_seed ? r_reg : sigma_reg;
// ====================================================================
// sample_ntt_sync_shared (consumer 2) signals
// ====================================================================
reg snt_valid_i_r;
reg [1:0] snt_i_idx_r, snt_j_idx_r;
wire snt_ready_o;
wire [11:0] snt_coeff_o;
wire snt_valid_o, snt_last_o;
wire [1599:0] snt_kc_state_o, snt_kc_state_i;
wire snt_kc_valid_o, snt_kc_valid_i;
wire snt_kc_ready_i, snt_kc_ready_o;
// ====================================================================
// sha3_top signals (separate keccak_core — not shared through arbiter)
// ====================================================================
reg [1:0] sha3_mode_r;
reg [511:0] sha3_data_r;
reg sha3_valid_i_r;
wire sha3_ready_o;
wire [511:0] sha3_hash_o;
wire sha3_valid_o;
// ====================================================================
// rng_sync signals
// ====================================================================
wire rng_valid_i;
wire rng_ready_o;
wire [255:0] rng_data_o;
wire rng_valid_o;
// ====================================================================
// ntt_core signals (FSM-driven inputs are regs)
// ====================================================================
reg [11:0] ntt_coeff_in_r;
reg ntt_valid_i_r;
wire ntt_ready_o;
reg ntt_mode_r; // 0=NTT, 1=INTT
wire [11:0] ntt_coeff_out;
wire ntt_valid_o;
wire ntt_done_o;
// ====================================================================
// poly_mul_sync signals (FSM-driven inputs)
// ====================================================================
reg [11:0] pmul_a_r, pmul_b_r;
reg pmul_valid_i_r;
wire pmul_ready_o;
wire [11:0] pmul_coeff_out;
wire pmul_valid_o;
// ====================================================================
// poly_arith_sync signals (FSM-driven inputs)
// ====================================================================
reg [11:0] parith_a_r, parith_b_r;
reg parith_mode_r; // 0=add, 1=sub
reg parith_valid_i_r;
wire parith_ready_o;
wire [11:0] parith_coeff_out;
wire parith_valid_o;
// ====================================================================
// comp_decomp_sync signals (FSM-driven inputs)
// ====================================================================
reg [11:0] comp_coeff_in_r;
reg [4:0] comp_d_r;
reg comp_mode_r; // 0=compress, 1=decompress
reg comp_valid_i_r;
wire comp_ready_o;
wire [11:0] comp_coeff_out;
wire comp_valid_o;
// ====================================================================
// mod_add_sync signals (FSM-driven inputs)
// ====================================================================
reg [11:0] madd_a_r, madd_b_r;
reg madd_valid_i_r;
wire madd_ready_o;
wire [11:0] madd_sum;
wire madd_valid_o;
// ====================================================================
// BRAM signals (sd_bram: 1 read + 1 write port)
// ====================================================================
reg [BRAM_AW-1:0] bram_rd_addr_r;
wire [11:0] bram_rd_data;
reg bram_wr_en_r;
reg [BRAM_AW-1:0] bram_wr_addr_r;
reg [11:0] bram_wr_data_r;
// ====================================================================
// FSM state definitions
// ====================================================================
localparam S_IDLE = 7'd0;
// --- KeyGen states ---
localparam S_KG_RNG_REQ = 7'd1;
localparam S_KG_RNG_WAIT = 7'd2;
localparam S_KG_G_START = 7'd3;
localparam S_KG_G_WAIT = 7'd4;
localparam S_KG_G_DONE = 7'd5;
localparam S_KG_SNT_INIT = 7'd6;
localparam S_KG_SNT_START = 7'd7;
localparam S_KG_SNT_COEFFS = 7'd8;
localparam S_KG_SNT_CLEANUP = 7'd9;
localparam S_KG_SNT_NTT_LOAD = 7'd10;
localparam S_KG_SNT_NTT_COMP = 7'd11;
localparam S_KG_SNT_NTT_OUT = 7'd12;
localparam S_KG_SNT_NEXT = 7'd13;
localparam S_KG_CBD_S_INIT = 7'd14;
localparam S_KG_CBD_S_START = 7'd15;
localparam S_KG_CBD_S_COEFFS = 7'd16;
localparam S_KG_CBD_S_CLEANUP = 7'd17;
localparam S_KG_CBD_S_NTT_LD = 7'd18;
localparam S_KG_CBD_S_NTT_CMP = 7'd19;
localparam S_KG_CBD_S_NTT_OUT = 7'd20;
localparam S_KG_CBD_S_NEXT = 7'd21;
localparam S_KG_CBD_E_INIT = 7'd22;
localparam S_KG_CBD_E_START = 7'd23;
localparam S_KG_CBD_E_COEFFS = 7'd24;
localparam S_KG_CBD_E_CLEANUP = 7'd25;
localparam S_KG_CBD_E_NTT_LD = 7'd26;
localparam S_KG_CBD_E_NTT_CMP = 7'd27;
localparam S_KG_CBD_E_NTT_OUT = 7'd28;
localparam S_KG_CBD_E_NEXT = 7'd29;
localparam S_KG_TMUL_INIT = 7'd30;
localparam S_KG_TMUL_MUL_LD = 7'd31;
localparam S_KG_TMUL_MUL_OUT = 7'd32;
localparam S_KG_TMUL_ACCUM = 7'd33;
localparam S_KG_TMUL_ADD_E = 7'd34;
localparam S_KG_TMUL_NEXT = 7'd35;
localparam S_KG_DONE = 7'd36;
// --- Encaps states ---
localparam S_EN_RNG_REQ = 7'd37;
localparam S_EN_RNG_WAIT = 7'd38;
localparam S_EN_H_START = 7'd39;
localparam S_EN_H_WAIT = 7'd40;
localparam S_EN_G_START = 7'd41;
localparam S_EN_G_WAIT = 7'd42;
localparam S_EN_SNT_INIT = 7'd43;
localparam S_EN_SNT_START = 7'd44;
localparam S_EN_SNT_COEFFS = 7'd45;
localparam S_EN_SNT_CLEANUP = 7'd46;
localparam S_EN_SNT_NTT_LD = 7'd47;
localparam S_EN_SNT_NTT_CMP = 7'd48;
localparam S_EN_SNT_NTT_OUT = 7'd49;
localparam S_EN_SNT_NEXT = 7'd50;
localparam S_EN_CBD_Y_INIT = 7'd51;
localparam S_EN_CBD_Y_START = 7'd52;
localparam S_EN_CBD_Y_COEFFS = 7'd53;
localparam S_EN_CBD_Y_CLNUP = 7'd54;
localparam S_EN_CBD_Y_NTT_LD = 7'd55;
localparam S_EN_CBD_Y_NTT_CMP = 7'd56;
localparam S_EN_CBD_Y_NTT_OUT = 7'd57;
localparam S_EN_CBD_Y_NEXT = 7'd58;
localparam S_EN_DONE = 7'd59;
// --- Decaps states ---
localparam S_DC_DECOMP_C1 = 7'd60;
localparam S_DC_DECOMP_C2 = 7'd61;
localparam S_DC_NTT_U_LD = 7'd62;
localparam S_DC_NTT_U_CMP = 7'd63;
localparam S_DC_NTT_U_OUT = 7'd64;
localparam S_DC_MUL_S = 7'd65;
localparam S_DC_INTT_V = 7'd66;
localparam S_DC_DECOMP_M = 7'd67;
localparam S_DC_G_CHECK = 7'd68;
localparam S_DC_REENC = 7'd69;
localparam S_DC_KDF = 7'd70;
localparam S_DC_DONE = 7'd71;
// ====================================================================
// FSM registers
// ====================================================================
reg [6:0] state_r, state_next;
// Loop counters (shared across states)
reg [1:0] loop_i; // row index (0..k-1)
reg [1:0] loop_j; // col index (0..k-1)
reg [1:0] loop_idx; // generic single-loop index
reg [8:0] coeff_cnt; // 0..256 coefficient counter
reg [5:0] poly_idx_reg; // current poly base index
reg phase_active; // 1 while sub-phase is doing work
reg phase_done; // 1 when sub-phase work is complete
// ====================================================================
// Output assignments
// ====================================================================
assign ready_o = ready_o_r;
assign pk_o = pk_o_r;
assign sk_o = sk_o_r;
assign pk_valid = pk_valid_r;
assign sk_valid = sk_valid_r;
assign ct_o = ct_o_r;
assign K_o = K_o_r;
assign ct_valid = ct_valid_r;
assign K_valid = K_valid_r;
assign K_o_dec = K_o_dec_r;
assign K_valid_dec = K_valid_dec_r;
assign done_o = done_o_r;
// ====================================================================
// Default module-side connections (combinational — read state_r)
// ====================================================================
// -- sha3_chain (consumer 0) --
assign chain_start = (state_r == S_KG_G_START);
assign chain_kc_ready_o = arb_cons_ready_o[0];
assign chain_kc_state_o = arb_cons_state_o[0*1600 +: 1600];
assign chain_kc_valid_o = arb_cons_valid_o[0];
assign arb_cons_state_i[0*1600 +: 1600] = chain_kc_state_i;
assign arb_cons_valid_i[0] = chain_kc_valid_i;
assign arb_cons_ready_i[0] = 1'b1;
// -- sample_cbd (consumer 1) --
assign cbd_kc_ready_o = arb_cons_ready_o[1];
assign cbd_kc_state_o = arb_cons_state_o[1*1600 +: 1600];
assign cbd_kc_valid_o = arb_cons_valid_o[1];
assign arb_cons_state_i[1*1600 +: 1600] = cbd_kc_state_i;
assign arb_cons_valid_i[1] = cbd_kc_valid_i;
assign arb_cons_ready_i[1] = 1'b1;
// -- sample_ntt (consumer 2) --
assign snt_kc_ready_o = arb_cons_ready_o[2];
assign snt_kc_state_o = arb_cons_state_o[2*1600 +: 1600];
assign snt_kc_valid_o = arb_cons_valid_o[2];
assign arb_cons_state_i[2*1600 +: 1600] = snt_kc_state_i;
assign arb_cons_valid_i[2] = snt_kc_valid_i;
assign arb_cons_ready_i[2] = 1'b1;
// -- rng_sync (combinational drive) --
assign rng_valid_i = (state_r == S_KG_RNG_REQ || state_r == S_EN_RNG_REQ);
// ====================================================================
// FSM combinational next-state logic
//
// Transitions are triggered by:
// - phase_done: sub-phase work completed (set in sequential block)
// - External ready/valid signals (rng_valid_o, chain_done, etc.)
// - ntt_done_o: NTT core completion
//
// Loop-back for iteration: uses combinational check on registered
// loop_i/loop_j counters to decide whether to loop or advance.
// ====================================================================
always @(*) begin
state_next = state_r;
case (state_r)
// ============================================================
// IDLE
// ============================================================
S_IDLE: begin
if (valid_i && ready_o_r) begin
case (mode)
2'b00: state_next = S_KG_RNG_REQ;
2'b01: state_next = S_EN_RNG_REQ;
2'b10: state_next = S_DC_DECOMP_C1;
default: state_next = S_IDLE;
endcase
end
end
// ============================================================
// KEYGEN
// ============================================================
S_KG_RNG_REQ: state_next = S_KG_RNG_WAIT;
S_KG_RNG_WAIT: if (rng_valid_o) state_next = S_KG_G_START;
S_KG_G_START: state_next = S_KG_G_WAIT;
S_KG_G_WAIT: if (chain_done) state_next = S_KG_G_DONE;
S_KG_G_DONE: state_next = S_KG_SNT_INIT;
// -- SampleNTT A-matrix loop --
S_KG_SNT_INIT: state_next = S_KG_SNT_START;
S_KG_SNT_START: if (snt_ready_o) state_next = S_KG_SNT_COEFFS;
S_KG_SNT_COEFFS: if (phase_done) state_next = S_KG_SNT_CLEANUP;
S_KG_SNT_CLEANUP: state_next = S_KG_SNT_NTT_LOAD;
S_KG_SNT_NTT_LOAD: if (phase_done) state_next = S_KG_SNT_NTT_COMP;
S_KG_SNT_NTT_COMP: if (ntt_done_o) state_next = S_KG_SNT_NTT_OUT;
S_KG_SNT_NTT_OUT: if (phase_done) state_next = S_KG_SNT_NEXT;
// S_KG_SNT_NEXT: loop or advance
S_KG_SNT_NEXT: begin
if (loop_j + 2'd1 < k_r[1:0])
state_next = S_KG_SNT_START;
else if (loop_i + 2'd1 < k_r[1:0])
state_next = S_KG_SNT_START;
else
state_next = S_KG_CBD_S_INIT;
end
// -- CBD s loop --
S_KG_CBD_S_INIT: state_next = S_KG_CBD_S_START;
S_KG_CBD_S_START: if (cbd_ready_o) state_next = S_KG_CBD_S_COEFFS;
S_KG_CBD_S_COEFFS: if (phase_done) state_next = S_KG_CBD_S_CLEANUP;
S_KG_CBD_S_CLEANUP: state_next = S_KG_CBD_S_NTT_LD;
S_KG_CBD_S_NTT_LD: if (phase_done) state_next = S_KG_CBD_S_NTT_CMP;
S_KG_CBD_S_NTT_CMP: if (ntt_done_o) state_next = S_KG_CBD_S_NTT_OUT;
S_KG_CBD_S_NTT_OUT: if (phase_done) state_next = S_KG_CBD_S_NEXT;
S_KG_CBD_S_NEXT: begin
if (loop_idx + 2'd1 < k_r[1:0])
state_next = S_KG_CBD_S_START;
else
state_next = S_KG_CBD_E_INIT;
end
// -- CBD e loop --
S_KG_CBD_E_INIT: state_next = S_KG_CBD_E_START;
S_KG_CBD_E_START: if (cbd_ready_o) state_next = S_KG_CBD_E_COEFFS;
S_KG_CBD_E_COEFFS: if (phase_done) state_next = S_KG_CBD_E_CLEANUP;
S_KG_CBD_E_CLEANUP: state_next = S_KG_CBD_E_NTT_LD;
S_KG_CBD_E_NTT_LD: if (phase_done) state_next = S_KG_CBD_E_NTT_CMP;
S_KG_CBD_E_NTT_CMP: if (ntt_done_o) state_next = S_KG_CBD_E_NTT_OUT;
S_KG_CBD_E_NTT_OUT: if (phase_done) state_next = S_KG_CBD_E_NEXT;
S_KG_CBD_E_NEXT: begin
if (loop_idx + 2'd1 < k_r[1:0])
state_next = S_KG_CBD_E_START;
else
state_next = S_KG_TMUL_INIT;
end
// -- t_hat = sum(A·s) + e --
S_KG_TMUL_INIT: state_next = S_KG_TMUL_MUL_LD;
S_KG_TMUL_MUL_LD: if (phase_done) state_next = S_KG_TMUL_MUL_OUT;
S_KG_TMUL_MUL_OUT: if (phase_done) state_next = S_KG_TMUL_NEXT;
S_KG_TMUL_ACCUM: if (phase_done) state_next = S_KG_TMUL_ADD_E;
// After ADD_E: if more rows, loop to MUL_LD with next row; else DONE
S_KG_TMUL_ADD_E: begin
if (phase_done) begin
if (loop_i + 2'd1 < k_r[1:0])
state_next = S_KG_TMUL_MUL_LD;
else
state_next = S_KG_DONE;
end
end
// After MUL_OUT: check if more j terms for this row
S_KG_TMUL_NEXT: begin
if (loop_j + 2'd1 < k_r[1:0]) begin
state_next = S_KG_TMUL_MUL_LD; // more j columns
end else begin
state_next = S_KG_TMUL_ACCUM; // all j done → accumulate
end
end
S_KG_DONE: state_next = S_IDLE;
// ============================================================
// ENCAPS
// ============================================================
S_EN_RNG_REQ: state_next = S_EN_RNG_WAIT;
S_EN_RNG_WAIT: if (rng_valid_o) state_next = S_EN_H_START;
S_EN_H_START: state_next = S_EN_H_WAIT;
S_EN_H_WAIT: if (sha3_valid_o) state_next = S_EN_G_START;
S_EN_G_START: state_next = S_EN_G_WAIT;
S_EN_G_WAIT: if (sha3_valid_o) state_next = S_EN_SNT_INIT;
S_EN_SNT_INIT: state_next = S_EN_SNT_START;
S_EN_SNT_START: if (snt_ready_o) state_next = S_EN_SNT_COEFFS;
S_EN_SNT_COEFFS: if (phase_done) state_next = S_EN_SNT_CLEANUP;
S_EN_SNT_CLEANUP: state_next = S_EN_SNT_NTT_LD;
S_EN_SNT_NTT_LD: if (phase_done) state_next = S_EN_SNT_NTT_CMP;
S_EN_SNT_NTT_CMP: if (ntt_done_o) state_next = S_EN_SNT_NTT_OUT;
S_EN_SNT_NTT_OUT: if (phase_done) state_next = S_EN_SNT_NEXT;
S_EN_SNT_NEXT: begin
if (loop_j + 2'd1 < k_r[1:0])
state_next = S_EN_SNT_START;
else if (loop_i + 2'd1 < k_r[1:0])
state_next = S_EN_SNT_START;
else
state_next = S_EN_CBD_Y_INIT;
end
S_EN_CBD_Y_INIT: state_next = S_EN_CBD_Y_START;
S_EN_CBD_Y_START: if (cbd_ready_o) state_next = S_EN_CBD_Y_COEFFS;
S_EN_CBD_Y_COEFFS: if (phase_done) state_next = S_EN_CBD_Y_CLNUP;
S_EN_CBD_Y_CLNUP: state_next = S_EN_CBD_Y_NTT_LD;
S_EN_CBD_Y_NTT_LD: if (phase_done) state_next = S_EN_CBD_Y_NTT_CMP;
S_EN_CBD_Y_NTT_CMP: if (ntt_done_o) state_next = S_EN_CBD_Y_NTT_OUT;
S_EN_CBD_Y_NTT_OUT: if (phase_done) state_next = S_EN_CBD_Y_NEXT;
S_EN_CBD_Y_NEXT: begin
if (loop_idx + 2'd1 < k_r[1:0])
state_next = S_EN_CBD_Y_START;
else
state_next = S_EN_DONE;
end
S_EN_DONE: state_next = S_IDLE;
// ============================================================
// DECAPS (simplified placeholder states flow)
// ============================================================
S_DC_DECOMP_C1: if (phase_done) state_next = S_DC_DECOMP_C2;
S_DC_DECOMP_C2: if (phase_done) state_next = S_DC_NTT_U_LD;
S_DC_NTT_U_LD: if (phase_done) state_next = S_DC_NTT_U_CMP;
S_DC_NTT_U_CMP: if (ntt_done_o) state_next = S_DC_NTT_U_OUT;
S_DC_NTT_U_OUT: if (phase_done) state_next = S_DC_MUL_S;
S_DC_MUL_S: if (phase_done) state_next = S_DC_INTT_V;
S_DC_INTT_V: if (phase_done) state_next = S_DC_DECOMP_M;
S_DC_DECOMP_M: if (phase_done) state_next = S_DC_G_CHECK;
S_DC_G_CHECK: if (phase_done) state_next = S_DC_REENC;
S_DC_REENC: if (phase_done) state_next = S_DC_KDF;
S_DC_KDF: if (phase_done) state_next = S_DC_DONE;
S_DC_DONE: state_next = S_IDLE;
default: state_next = S_IDLE;
endcase
end
// ====================================================================
// FSM sequential logic
// ====================================================================
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state_r <= S_IDLE;
mode_r <= 2'd0;
k_r <= 3'd2;
ready_o_r <= 1'b1;
pk_valid_r <= 1'b0;
sk_valid_r <= 1'b0;
ct_valid_r <= 1'b0;
K_valid_r <= 1'b0;
K_valid_dec_r <= 1'b0;
done_o_r <= 1'b0;
pk_o_r <= 0;
sk_o_r <= 0;
ct_o_r <= 0;
K_o_r <= 256'd0;
K_o_dec_r <= 256'd0;
d_reg <= 256'd0;
rho_reg <= 256'd0;
sigma_reg <= 256'd0;
m_reg <= 256'd0;
Kbar_reg <= 256'd0;
r_reg <= 256'd0;
Hpk_reg <= 256'd0;
z_reg <= 256'd0;
loop_i <= 2'd0;
loop_j <= 2'd0;
loop_idx <= 2'd0;
coeff_cnt <= 9'd0;
poly_idx_reg <= 6'd0;
phase_active <= 1'b0;
phase_done <= 1'b0;
s_hat0_reg <= 0;
s_hat1_reg <= 0;
t_hat0_reg <= 0;
t_hat1_reg <= 0;
tmul_pipe_reg <= 12'd0;
tmul_pipe_valid <= 1'b0;
tmul_out_cnt <= 9'd0;
tmul_adv_row <= 1'b0;
// Drive all FSM-controlled outputs to safe defaults
cbd_valid_i_r <= 1'b0;
cbd_nonce_r <= 8'd0;
cbd_eta_r <= 2'd0;
cbd_use_r_seed <= 1'b0;
snt_valid_i_r <= 1'b0;
snt_i_idx_r <= 2'd0;
snt_j_idx_r <= 2'd0;
sha3_mode_r <= 2'd0;
sha3_data_r <= 512'd0;
sha3_valid_i_r <= 1'b0;
ntt_coeff_in_r <= 12'd0;
ntt_valid_i_r <= 1'b0;
ntt_mode_r <= 1'b0;
pmul_a_r <= 12'd0;
pmul_b_r <= 12'd0;
pmul_valid_i_r <= 1'b0;
parith_a_r <= 12'd0;
parith_b_r <= 12'd0;
parith_mode_r <= 1'b0;
parith_valid_i_r <= 1'b0;
comp_coeff_in_r <= 12'd0;
comp_d_r <= 5'd0;
comp_mode_r <= 1'b0;
comp_valid_i_r <= 1'b0;
madd_a_r <= 12'd0;
madd_b_r <= 12'd0;
madd_valid_i_r <= 1'b0;
bram_wr_en_r <= 1'b0;
bram_wr_addr_r <= 0;
bram_wr_data_r <= 12'd0;
bram_rd_addr_r <= 0;
end else begin
state_r <= state_next;
// Default: clear one-cycle pulse outputs
pk_valid_r <= 1'b0;
sk_valid_r <= 1'b0;
ct_valid_r <= 1'b0;
K_valid_r <= 1'b0;
K_valid_dec_r <= 1'b0;
done_o_r <= 1'b0;
// Default: BRAM write off
bram_wr_en_r <= 1'b0;
// Default: de-assert streaming valid signals unless driven
// (These are single-cycle; phase_active prevents glitches)
if (!phase_active) begin
ntt_valid_i_r <= 1'b0;
cbd_valid_i_r <= 1'b0;
snt_valid_i_r <= 1'b0;
sha3_valid_i_r <= 1'b0;
pmul_valid_i_r <= 1'b0;
parith_valid_i_r <= 1'b0;
comp_valid_i_r <= 1'b0;
madd_valid_i_r <= 1'b0;
end
// ============================================================
// S_IDLE: latch inputs
// ============================================================
if (state_r == S_IDLE && valid_i && ready_o_r) begin
mode_r <= mode;
k_r <= i_k;
ready_o_r <= 1'b0;
end
// ============================================================
// KEYGEN: RNG → G function
// ============================================================
if (state_r == S_KG_RNG_WAIT && rng_valid_o) begin
d_reg <= rng_data_o;
end
if (state_r == S_KG_G_WAIT && chain_done) begin
rho_reg <= chain_rho;
sigma_reg <= chain_sigma;
end
// ============================================================
// KEYGEN: SampleNTT loop
// ============================================================
if (state_r == S_KG_SNT_INIT) begin
loop_i <= 2'd0;
loop_j <= 2'd0;
phase_done <= 1'b0;
end
if (state_r == S_KG_SNT_START) begin
snt_valid_i_r <= 1'b1;
snt_i_idx_r <= loop_i;
snt_j_idx_r <= loop_j;
coeff_cnt <= 9'd0;
phase_active <= 1'b1;
phase_done <= 1'b0;
end
if (state_r == S_KG_SNT_COEFFS) begin
snt_valid_i_r <= 1'b0;
if (snt_valid_o) begin
bram_wr_en_r <= 1'b1;
bram_wr_addr_r <= poly_addr(SCRATCH0, coeff_cnt[7:0]);
bram_wr_data_r <= snt_coeff_o;
coeff_cnt <= coeff_cnt + 9'd1;
if (snt_last_o) begin
phase_done <= 1'b1;
phase_active <= 1'b0;
end
end
end
if (state_r == S_KG_SNT_CLEANUP) begin
coeff_cnt <= 9'd0;
phase_done <= 1'b0;
phase_active <= 1'b0;
end
if (state_r == S_KG_SNT_NTT_LOAD) begin
phase_active <= 1'b1;
if (coeff_cnt < 9'd256) begin
bram_rd_addr_r <= poly_addr(SCRATCH0, coeff_cnt[7:0]);
ntt_coeff_in_r <= bram_rd_data;
ntt_valid_i_r <= 1'b1;
if (ntt_ready_o) begin
coeff_cnt <= coeff_cnt + 9'd1;
end
end else begin
ntt_valid_i_r <= 1'b0;
phase_done <= 1'b1;
phase_active <= 1'b0;
coeff_cnt <= 9'd0;
end
end
if (state_r == S_KG_SNT_NTT_COMP) begin
phase_done <= 1'b0;
coeff_cnt <= 9'd0;
phase_active <= 1'b0;
end
if (state_r == S_KG_SNT_NTT_OUT) begin
phase_active <= 1'b1;
if (ntt_valid_o) begin
bram_wr_en_r <= 1'b1;
bram_wr_addr_r <= poly_addr(A_BASE + (loop_i * K + loop_j), coeff_cnt[7:0]);
bram_wr_data_r <= ntt_coeff_out;
coeff_cnt <= coeff_cnt + 9'd1;
if (coeff_cnt == 9'd255) begin
phase_done <= 1'b1;
phase_active <= 1'b0;
end
end
end
if (state_r == S_KG_SNT_NEXT) begin
phase_done <= 1'b0;
if (loop_j + 2'd1 < k_r[1:0]) begin
loop_j <= loop_j + 2'd1;
end else if (loop_i + 2'd1 < k_r[1:0]) begin
loop_i <= loop_i + 2'd1;
loop_j <= 2'd0;
end
end
// ============================================================
// KEYGEN: CBD s loop
// ============================================================
if (state_r == S_KG_CBD_S_INIT) begin
loop_idx <= 2'd0;
phase_done <= 1'b0;
cbd_use_r_seed <= 1'b0; // use sigma_reg
end
if (state_r == S_KG_CBD_S_START) begin
cbd_valid_i_r <= 1'b1;
cbd_nonce_r <= {6'b0, loop_idx};
cbd_eta_r <= (k_r == 3'd2) ? 2'd3 : 2'd2;
coeff_cnt <= 9'd0;
phase_active <= 1'b1;
phase_done <= 1'b0;
end
if (state_r == S_KG_CBD_S_COEFFS) begin
cbd_valid_i_r <= 1'b0;
if (cbd_valid_o) begin
bram_wr_en_r <= 1'b1;
bram_wr_addr_r <= poly_addr(SCRATCH0, coeff_cnt[7:0]);
bram_wr_data_r <= cbd_coeff_o;
coeff_cnt <= coeff_cnt + 9'd1;
if (cbd_last_o) begin
phase_done <= 1'b1;
phase_active <= 1'b0;
end
end
end
if (state_r == S_KG_CBD_S_CLEANUP) begin
coeff_cnt <= 9'd0;
phase_done <= 1'b0;
phase_active <= 1'b0;
end
if (state_r == S_KG_CBD_S_NTT_LD) begin
phase_active <= 1'b1;
if (coeff_cnt < 9'd256) begin
bram_rd_addr_r <= poly_addr(SCRATCH0, coeff_cnt[7:0]);
ntt_coeff_in_r <= bram_rd_data;
ntt_valid_i_r <= 1'b1;
if (ntt_ready_o) coeff_cnt <= coeff_cnt + 9'd1;
end else begin
ntt_valid_i_r <= 1'b0;
phase_done <= 1'b1;
phase_active <= 1'b0;
coeff_cnt <= 9'd0;
end
end
if (state_r == S_KG_CBD_S_NTT_CMP) begin
phase_done <= 1'b0;
coeff_cnt <= 9'd0;
phase_active <= 1'b0;
end
if (state_r == S_KG_CBD_S_NTT_OUT) begin
phase_active <= 1'b1;
if (ntt_valid_o) begin
bram_wr_en_r <= 1'b1;
bram_wr_addr_r <= poly_addr(S_BASE + loop_idx, coeff_cnt[7:0]);
bram_wr_data_r <= ntt_coeff_out;
// Capture into s_hat registers for t_hat computation
if (loop_idx == 2'd0)
s_hat0_reg[coeff_cnt[7:0] * 12 +: 12] <= ntt_coeff_out;
else
s_hat1_reg[coeff_cnt[7:0] * 12 +: 12] <= ntt_coeff_out;
coeff_cnt <= coeff_cnt + 9'd1;
if (coeff_cnt == 9'd255) begin
phase_done <= 1'b1;
phase_active <= 1'b0;
end
end
end
if (state_r == S_KG_CBD_S_NEXT) begin
phase_done <= 1'b0;
if (loop_idx + 2'd1 < k_r[1:0])
loop_idx <= loop_idx + 2'd1;
end
// ============================================================
// KEYGEN: CBD e loop (same pattern as s)
// ============================================================
if (state_r == S_KG_CBD_E_INIT) begin
loop_idx <= 2'd0;
phase_done <= 1'b0;
end
if (state_r == S_KG_CBD_E_START) begin
cbd_valid_i_r <= 1'b1;
cbd_nonce_r <= k_r + {5'b0, loop_idx};
cbd_eta_r <= (k_r == 3'd2) ? 2'd3 : 2'd2;
coeff_cnt <= 9'd0;
phase_active <= 1'b1;
phase_done <= 1'b0;
end
if (state_r == S_KG_CBD_E_COEFFS) begin
cbd_valid_i_r <= 1'b0;
if (cbd_valid_o) begin
bram_wr_en_r <= 1'b1;
bram_wr_addr_r <= poly_addr(SCRATCH0, coeff_cnt[7:0]);
bram_wr_data_r <= cbd_coeff_o;
coeff_cnt <= coeff_cnt + 9'd1;
if (cbd_last_o) begin
phase_done <= 1'b1;
phase_active <= 1'b0;
end
end
end
if (state_r == S_KG_CBD_E_CLEANUP) begin
coeff_cnt <= 9'd0;
phase_done <= 1'b0;
phase_active <= 1'b0;
end
if (state_r == S_KG_CBD_E_NTT_LD) begin
phase_active <= 1'b1;
if (coeff_cnt < 9'd256) begin
bram_rd_addr_r <= poly_addr(SCRATCH0, coeff_cnt[7:0]);
ntt_coeff_in_r <= bram_rd_data;
ntt_valid_i_r <= 1'b1;
if (ntt_ready_o) coeff_cnt <= coeff_cnt + 9'd1;
end else begin
ntt_valid_i_r <= 1'b0;
phase_done <= 1'b1;
phase_active <= 1'b0;
coeff_cnt <= 9'd0;
end
end
if (state_r == S_KG_CBD_E_NTT_CMP) begin
phase_done <= 1'b0;
coeff_cnt <= 9'd0;
phase_active <= 1'b0;
end
if (state_r == S_KG_CBD_E_NTT_OUT) begin
phase_active <= 1'b1;
if (ntt_valid_o) begin
bram_wr_en_r <= 1'b1;
bram_wr_addr_r <= poly_addr(E_BASE + loop_idx, coeff_cnt[7:0]);
bram_wr_data_r <= ntt_coeff_out;
coeff_cnt <= coeff_cnt + 9'd1;
if (coeff_cnt == 9'd255) begin
phase_done <= 1'b1;
phase_active <= 1'b0;
end
end
end
if (state_r == S_KG_CBD_E_NEXT) begin
phase_done <= 1'b0;
if (loop_idx + 2'd1 < k_r[1:0])
loop_idx <= loop_idx + 2'd1;
end
// ============================================================
// KEYGEN: t_hat computation
//
// For each row i (0..k-1):
// t_hat[i] = sum_j(A[i,j] * s_hat[j]) + e_hat[i]
//
// Phase 1 (S_KG_TMUL_MUL_LD): Load A[i,j], s_hat[j] into poly_mul
// Phase 2 (S_KG_TMUL_MUL_OUT): Read poly_mul result → SCRATCH0/SCRATCH1
// Phase 3 (S_KG_TMUL_ACCUM): Accumulate all A·s terms using poly_arith
// Phase 4 (S_KG_TMUL_ADD_E): Add e_hat[i] → store in t_hat_i_reg
// ============================================================
if (state_r == S_KG_TMUL_INIT) begin
loop_i <= 2'd0;
loop_j <= 2'd0;
coeff_cnt <= 9'd0;
tmul_pipe_reg <= 12'd0;
tmul_pipe_valid <= 1'b0;
tmul_out_cnt <= 9'd0;
tmul_adv_row <= 1'b0;
phase_done <= 1'b0;
end
// --- S_KG_TMUL_MUL_LD: Load A[i,j] and s_hat[j] into poly_mul ---
// Uses s_hat registers (combinational read) + A from BRAM (1-cycle delay).
// First cycle: handle row advancement (if flagged).
// Second cycle: prefetch first A coeff from BRAM.
// Cycles 3-258: feed 256 coeff pairs to poly_mul.
if (state_r == S_KG_TMUL_MUL_LD) begin
phase_active <= 1'b1;
if (coeff_cnt == 9'd0) begin
// Cycle 0: handle row advancement flag
if (tmul_adv_row) begin
loop_i <= loop_i + 2'd1;
loop_j <= 2'd0;
tmul_adv_row <= 1'b0;
end
coeff_cnt <= 9'd1;
end else if (coeff_cnt == 9'd1) begin
// Cycle 1: prefetch first A coefficient
bram_rd_addr_r <= poly_addr(A_BASE + (loop_i * K + loop_j), 8'd0);
coeff_cnt <= 9'd2;
end else if (coeff_cnt <= 9'd257) begin
// bram_rd_data holds A[i,j][coeff_cnt-2] from previous cycle
pmul_a_r <= bram_rd_data;
// s_hat value from register (combinational index)
if (loop_j == 2'd0)
pmul_b_r <= s_hat0_reg[(coeff_cnt - 9'd2) * 12 +: 12];
else
pmul_b_r <= s_hat1_reg[(coeff_cnt - 9'd2) * 12 +: 12];
pmul_valid_i_r <= 1'b1;
// Prefetch next A coefficient
if (coeff_cnt < 9'd257)
bram_rd_addr_r <= poly_addr(A_BASE + (loop_i * K + loop_j), coeff_cnt[7:0] - 8'd1);
coeff_cnt <= coeff_cnt + 9'd1;
// After feeding coeff 255 (coeff_cnt reaches 258), done
if (coeff_cnt == 9'd257) begin
phase_done <= 1'b1;
phase_active <= 1'b0;
coeff_cnt <= 9'd0;
end
end
end
// --- S_KG_TMUL_MUL_OUT: Read poly_mul result into BRAM scratch ---
// Phase 2a: Wait for first pmul_valid_o
// Phase 2b: Read 256 output coefficients
if (state_r == S_KG_TMUL_MUL_OUT) begin
phase_active <= 1'b1;
phase_done <= 1'b0;
if (pmul_valid_o) begin
// Store to SCRATCH0 (first j term) or SCRATCH1 (subsequent terms)
if (loop_j == 2'd0) begin
bram_wr_addr_r <= poly_addr(SCRATCH0, coeff_cnt[7:0]);
end else begin
bram_wr_addr_r <= poly_addr(SCRATCH1, coeff_cnt[7:0]);
end
bram_wr_en_r <= 1'b1;
bram_wr_data_r <= pmul_coeff_out;
coeff_cnt <= coeff_cnt + 9'd1;
if (coeff_cnt == 9'd255) begin
phase_done <= 1'b1;
phase_active <= 1'b0;
coeff_cnt <= 9'd0;
end
end
end
// --- S_KG_TMUL_ACCUM: Accumulate SCRATCH0 and SCRATCH1 via poly_arith ---
// Streams SCRATCH0[k] + SCRATCH1[k] → poly_arith (add) → accumulates
// into SCRATCH0 (or back into t_hat register for final result).
//
// Pipeline: rd SCRATCH0 → (1 cycle) → parith with SCRATCH1 → (1 cycle) → write
// Total: ~1 + 256 + 2 = 259 cycles for 256 coefficients
if (state_r == S_KG_TMUL_ACCUM) begin
phase_active <= 1'b1;
phase_done <= 1'b0;
// coeff_cnt tracks which coeff we're reading from BRAM
// tmul_out_cnt tracks parith results written
if (coeff_cnt == 9'd0 && tmul_out_cnt == 9'd0 && !tmul_pipe_valid) begin
// Start: read SCRATCH0[0] from BRAM
bram_rd_addr_r <= poly_addr(SCRATCH0, 8'd0);
coeff_cnt <= 9'd1;
end else if (coeff_cnt > 9'd0 && coeff_cnt <= 9'd256 && !tmul_pipe_valid) begin
// bram_rd_data has SCRATCH0[coeff_cnt-1]
// Need to read SCRATCH1[coeff_cnt-1] next
// Buffer the SCRATCH0 value, read SCRATCH1
tmul_pipe_reg <= bram_rd_data; // SCRATCH0 coeff
tmul_pipe_valid <= 1'b1;
bram_rd_addr_r <= poly_addr(SCRATCH1, coeff_cnt[7:0] - 8'd1);
end else if (tmul_pipe_valid && !parith_valid_i_r) begin
// tmul_pipe_reg has SCRATCH0 coeff
// bram_rd_data has SCRATCH1 coeff (from previous read)
parith_a_r <= tmul_pipe_reg; // SCRATCH0 coeff
parith_b_r <= bram_rd_data; // SCRATCH1 coeff
parith_mode_r <= 1'b0; // add
parith_valid_i_r <= 1'b1;
tmul_pipe_valid <= 1'b0;
// Prefetch next SCRATCH0 coefficient (if any remain)
if (coeff_cnt <= 9'd256)
bram_rd_addr_r <= poly_addr(SCRATCH0, coeff_cnt[7:0]);
coeff_cnt <= coeff_cnt + 9'd1;
end
// Write parith result back to SCRATCH0
if (parith_valid_o) begin
bram_wr_en_r <= 1'b1;
bram_wr_addr_r <= poly_addr(SCRATCH0, tmul_out_cnt[7:0]);
bram_wr_data_r <= parith_coeff_out;
tmul_out_cnt <= tmul_out_cnt + 9'd1;
if (tmul_out_cnt == 9'd255) begin
phase_done <= 1'b1;
phase_active <= 1'b0;
coeff_cnt <= 9'd0;
tmul_out_cnt <= 9'd0;
tmul_pipe_valid <= 1'b0;
end
end
end
// --- S_KG_TMUL_ADD_E: Add e_hat[i] to accumulated sum → t_hat_i_reg ---
// Streams SCRATCH0 (accumulated A·s sum) + e_hat[i] (from BRAM)
// through poly_arith (add mode), capturing result into t_hat_i_reg.
//
// Pipeline: rd e_hat → (1 cycle) → parith with SCRATCH0 → (1 cycle) → t_hat_i_reg
if (state_r == S_KG_TMUL_ADD_E) begin
phase_active <= 1'b1;
phase_done <= 1'b0;
// coeff_cnt tracks which e_hat coeff we're reading
// tmul_out_cnt tracks parith results written to t_hat
if (coeff_cnt == 9'd0 && tmul_out_cnt == 9'd0 && !tmul_pipe_valid) begin
// Start: read e_hat[i][0] from BRAM
bram_rd_addr_r <= poly_addr(E_BASE + loop_i, 8'd0);
coeff_cnt <= 9'd1;
end else if (coeff_cnt > 9'd0 && coeff_cnt <= 9'd256 && !tmul_pipe_valid) begin
// bram_rd_data has e_hat[i][coeff_cnt-1]
// Buffer e_hat value, read accumulated sum SCRATCH0
tmul_pipe_reg <= bram_rd_data; // e_hat coeff
tmul_pipe_valid <= 1'b1;
bram_rd_addr_r <= poly_addr(SCRATCH0, coeff_cnt[7:0] - 8'd1);
end else if (tmul_pipe_valid && !parith_valid_i_r) begin
// tmul_pipe_reg has e_hat coeff
// bram_rd_data has SCRATCH0 (accumulated sum) coeff
parith_a_r <= bram_rd_data; // accumulated sum
parith_b_r <= tmul_pipe_reg; // e_hat coeff
parith_mode_r <= 1'b0; // add
parith_valid_i_r <= 1'b1;
tmul_pipe_valid <= 1'b0;
// Prefetch next e_hat coefficient
if (coeff_cnt <= 9'd256)
bram_rd_addr_r <= poly_addr(E_BASE + loop_i, coeff_cnt[7:0]);
coeff_cnt <= coeff_cnt + 9'd1;
end
// Capture parith result into t_hat_i_reg
if (parith_valid_o) begin
if (loop_i == 2'd0)
t_hat0_reg[tmul_out_cnt[7:0] * 12 +: 12] <= parith_coeff_out;
else
t_hat1_reg[tmul_out_cnt[7:0] * 12 +: 12] <= parith_coeff_out;
tmul_out_cnt <= tmul_out_cnt + 9'd1;
if (tmul_out_cnt == 9'd255) begin
phase_done <= 1'b1;
phase_active <= 1'b0;
coeff_cnt <= 9'd0;
tmul_out_cnt <= 9'd0;
tmul_pipe_valid <= 1'b0;
// Flag that we're advancing to next row (handled in MUL_LD)
if (loop_i + 2'd1 < k_r[1:0])
tmul_adv_row <= 1'b1;
end
end
end
// --- S_KG_TMUL_NEXT: Advance loop_j or transition to DONE ---
if (state_r == S_KG_TMUL_NEXT) begin
phase_done <= 1'b0;
if (loop_j + 2'd1 < k_r[1:0]) begin
loop_j <= loop_j + 2'd1;
end
// else: FSM transitions to S_KG_TMUL_ACCUM (handled by next-state logic)
end
// ============================================================
// KEYGEN: DONE — assign pk_o_r and sk_o_r
// ============================================================
if (state_r == S_KG_DONE) begin
// For k=2, pk = {t_hat1[3071:0], t_hat0[3071:0], rho[255:0]} (padded)
// t_hat0 goes to bits [12*N-1:0]
// t_hat1 goes to bits [2*12*N-1 : 12*N]
// rho embeds at bits above (for k=2, bits [12*2*N+255 : 12*2*N])
// For simplicity, assign only the lower parts (k=2):
pk_o_r[(0*N*12) +: N*12] <= t_hat0_reg;
pk_o_r[(1*N*12) +: N*12] <= t_hat1_reg;
// rho_reg is embedded in pk encoding (FIPS 203, byte-encoded)
// Store rho in the upper bits for k=2
pk_o_r[(2*N*12) +: 256] <= rho_reg;
sk_o_r[(0*N*12) +: N*12] <= s_hat0_reg;
sk_o_r[(1*N*12) +: N*12] <= s_hat1_reg;
pk_valid_r <= 1'b1;
sk_valid_r <= 1'b1;
done_o_r <= 1'b1;
ready_o_r <= 1'b1;
end
// ============================================================
// ENCAPS: RNG → H → G
// ============================================================
if (state_r == S_EN_RNG_WAIT && rng_valid_o) begin
m_reg <= rng_data_o;
end
if (state_r == S_EN_H_START) begin
sha3_mode_r <= 2'b01; // H mode (SHA3-256)
sha3_data_r <= {256'b0, m_reg};
sha3_valid_i_r <= 1'b1;
end
if (state_r == S_EN_H_WAIT) begin
sha3_valid_i_r <= 1'b0;
if (sha3_valid_o)
Hpk_reg <= sha3_hash_o[255:0];
end
if (state_r == S_EN_G_START) begin
sha3_mode_r <= 2'b00; // G mode (SHA3-512)
sha3_data_r <= {248'b0, 8'd2, m_reg};
sha3_valid_i_r <= 1'b1;
end
if (state_r == S_EN_G_WAIT) begin
sha3_valid_i_r <= 1'b0;
if (sha3_valid_o) begin
Kbar_reg <= sha3_hash_o[255:0];
r_reg <= sha3_hash_o[511:256];
end
end
// ============================================================
// ENCAPS: SampleNTT loop
// ============================================================
if (state_r == S_EN_SNT_INIT) begin
loop_i <= 2'd0;
loop_j <= 2'd0;
phase_done <= 1'b0;
end
if (state_r == S_EN_SNT_START) begin
snt_valid_i_r <= 1'b1;
snt_i_idx_r <= loop_i;
snt_j_idx_r <= loop_j;
coeff_cnt <= 9'd0;
phase_active <= 1'b1;
phase_done <= 1'b0;
end
if (state_r == S_EN_SNT_COEFFS) begin
snt_valid_i_r <= 1'b0;
if (snt_valid_o) begin
bram_wr_en_r <= 1'b1;
bram_wr_addr_r <= poly_addr(SCRATCH0, coeff_cnt[7:0]);
bram_wr_data_r <= snt_coeff_o;
coeff_cnt <= coeff_cnt + 9'd1;
if (snt_last_o) begin
phase_done <= 1'b1;
phase_active <= 1'b0;
end
end
end
if (state_r == S_EN_SNT_CLEANUP) begin
coeff_cnt <= 9'd0;
phase_done <= 1'b0;
phase_active <= 1'b0;
end
if (state_r == S_EN_SNT_NTT_LD) begin
phase_active <= 1'b1;
if (coeff_cnt < 9'd256) begin
bram_rd_addr_r <= poly_addr(SCRATCH0, coeff_cnt[7:0]);
ntt_coeff_in_r <= bram_rd_data;
ntt_valid_i_r <= 1'b1;
if (ntt_ready_o) coeff_cnt <= coeff_cnt + 9'd1;
end else begin
ntt_valid_i_r <= 1'b0;
phase_done <= 1'b1;
phase_active <= 1'b0;
coeff_cnt <= 9'd0;
end
end
if (state_r == S_EN_SNT_NTT_CMP) begin
phase_done <= 1'b0;
coeff_cnt <= 9'd0;
phase_active <= 1'b0;
end
if (state_r == S_EN_SNT_NTT_OUT) begin
phase_active <= 1'b1;
if (ntt_valid_o) begin
bram_wr_en_r <= 1'b1;
bram_wr_addr_r <= poly_addr(A_BASE + (loop_i * K + loop_j), coeff_cnt[7:0]);
bram_wr_data_r <= ntt_coeff_out;
coeff_cnt <= coeff_cnt + 9'd1;
if (coeff_cnt == 9'd255) begin
phase_done <= 1'b1;
phase_active <= 1'b0;
end
end
end
if (state_r == S_EN_SNT_NEXT) begin
phase_done <= 1'b0;
if (loop_j + 2'd1 < k_r[1:0]) begin
loop_j <= loop_j + 2'd1;
end else if (loop_i + 2'd1 < k_r[1:0]) begin
loop_i <= loop_i + 2'd1;
loop_j <= 2'd0;
end
end
// ============================================================
// ENCAPS: CBD y loop
// ============================================================
if (state_r == S_EN_CBD_Y_INIT) begin
loop_idx <= 2'd0;
phase_done <= 1'b0;
cbd_use_r_seed <= 1'b1; // use r_reg for seed
end
if (state_r == S_EN_CBD_Y_START) begin
cbd_valid_i_r <= 1'b1;
cbd_nonce_r <= {6'b0, loop_idx};
cbd_eta_r <= (k_r == 3'd2) ? 2'd3 : 2'd2;
coeff_cnt <= 9'd0;
phase_active <= 1'b1;
phase_done <= 1'b0;
end
if (state_r == S_EN_CBD_Y_COEFFS) begin
cbd_valid_i_r <= 1'b0;
if (cbd_valid_o) begin
bram_wr_en_r <= 1'b1;
bram_wr_addr_r <= poly_addr(SCRATCH0, coeff_cnt[7:0]);
bram_wr_data_r <= cbd_coeff_o;
coeff_cnt <= coeff_cnt + 9'd1;
if (cbd_last_o) begin
phase_done <= 1'b1;
phase_active <= 1'b0;
end
end
end
if (state_r == S_EN_CBD_Y_CLNUP) begin
coeff_cnt <= 9'd0;
phase_done <= 1'b0;
phase_active <= 1'b0;
end
if (state_r == S_EN_CBD_Y_NTT_LD) begin
phase_active <= 1'b1;
if (coeff_cnt < 9'd256) begin
bram_rd_addr_r <= poly_addr(SCRATCH0, coeff_cnt[7:0]);
ntt_coeff_in_r <= bram_rd_data;
ntt_valid_i_r <= 1'b1;
if (ntt_ready_o) coeff_cnt <= coeff_cnt + 9'd1;
end else begin
ntt_valid_i_r <= 1'b0;
phase_done <= 1'b1;
phase_active <= 1'b0;
coeff_cnt <= 9'd0;
end
end
if (state_r == S_EN_CBD_Y_NTT_CMP) begin
phase_done <= 1'b0;
coeff_cnt <= 9'd0;
phase_active <= 1'b0;
end
if (state_r == S_EN_CBD_Y_NTT_OUT) begin
phase_active <= 1'b1;
if (ntt_valid_o) begin
bram_wr_en_r <= 1'b1;
bram_wr_addr_r <= poly_addr(Y_BASE + loop_idx, coeff_cnt[7:0]);
bram_wr_data_r <= ntt_coeff_out;
coeff_cnt <= coeff_cnt + 9'd1;
if (coeff_cnt == 9'd255) begin
phase_done <= 1'b1;
phase_active <= 1'b0;
end
end
end
if (state_r == S_EN_CBD_Y_NEXT) begin
phase_done <= 1'b0;
if (loop_idx + 2'd1 < k_r[1:0])
loop_idx <= loop_idx + 2'd1;
end
// ============================================================
// ENCAPS: DONE
// ============================================================
if (state_r == S_EN_DONE) begin
ct_valid_r <= 1'b1;
K_valid_r <= 1'b1;
K_o_r <= Kbar_reg;
done_o_r <= 1'b1;
ready_o_r <= 1'b1;
end
// ============================================================
// DECAPS: placeholder states
// ============================================================
if (state_r == S_DC_DECOMP_C1) begin
phase_done <= 1'b1;
end
if (state_r == S_DC_DECOMP_C2) begin
phase_done <= 1'b1;
end
if (state_r == S_DC_NTT_U_LD) begin
phase_done <= 1'b1;
phase_active <= 1'b0;
end
if (state_r == S_DC_NTT_U_CMP) begin
phase_done <= 1'b0;
phase_active <= 1'b0;
end
if (state_r == S_DC_NTT_U_OUT) begin
phase_done <= 1'b1;
end
if (state_r == S_DC_MUL_S) begin
phase_done <= 1'b1;
end
if (state_r == S_DC_INTT_V) begin
phase_done <= 1'b1;
end
if (state_r == S_DC_DECOMP_M) begin
phase_done <= 1'b1;
end
if (state_r == S_DC_G_CHECK) begin
phase_done <= 1'b1;
end
if (state_r == S_DC_REENC) begin
phase_done <= 1'b1;
end
if (state_r == S_DC_KDF) begin
phase_done <= 1'b1;
end
if (state_r == S_DC_DONE) begin
K_valid_dec_r <= 1'b1;
done_o_r <= 1'b1;
ready_o_r <= 1'b1;
end
end
end
// ====================================================================
// MODULE INSTANTIATIONS
// ====================================================================
// --- keccak_core (shared) ---
keccak_core #(.ROUNDS(24)) u_keccak_shared (
.clk (clk),
.rst_n (rst_n),
.state_i (arb_kc_state_i),
.valid_i (arb_kc_valid_i),
.ready_o (arb_kc_ready_o),
.state_o (arb_kc_state_o),
.valid_o (arb_kc_valid_o),
.ready_i (arb_kc_ready_i)
);
// --- keccak_arbiter (3 consumers) ---
keccak_arbiter #(.NUM_CONSUMERS(N_CONSUMERS)) u_arbiter (
.clk (clk),
.rst_n (rst_n),
.kc_state_i (arb_kc_state_i),
.kc_valid_i (arb_kc_valid_i),
.kc_ready_o (arb_kc_ready_o),
.kc_state_o (arb_kc_state_o),
.kc_valid_o (arb_kc_valid_o),
.kc_ready_i (arb_kc_ready_i),
.cons_state_i (arb_cons_state_i),
.cons_valid_i (arb_cons_valid_i),
.cons_ready_o (arb_cons_ready_o),
.cons_state_o (arb_cons_state_o),
.cons_valid_o (arb_cons_valid_o),
.cons_ready_i (arb_cons_ready_i)
);
// --- sha3_chain_top_shared (consumer 0) ---
sha3_chain_top_shared u_chain (
.clk (clk),
.rst_n (rst_n),
.d_in (d_reg),
.start_i (chain_start),
.done_o (chain_done),
.rho_out (chain_rho),
.sigma_out (chain_sigma),
.kc_state_o (chain_kc_state_o),
.kc_valid_o (chain_kc_valid_o),
.kc_ready_i (chain_kc_ready_i),
.kc_state_i (chain_kc_state_i),
.kc_valid_i (chain_kc_valid_i),
.kc_ready_o (chain_kc_ready_o)
);
// --- sample_cbd_sync_shared (consumer 1) ---
sample_cbd_sync_shared u_cbd (
.clk (clk),
.rst_n (rst_n),
.seed_i (cbd_seed_muxed),
.nonce_i (cbd_nonce_r),
.eta_i (cbd_eta_r),
.valid_i (cbd_valid_i_r),
.ready_o (cbd_ready_o),
.coeff_o (cbd_coeff_o),
.valid_o (cbd_valid_o),
.ready_i (1'b1),
.last_o (cbd_last_o),
.kc_state_o (cbd_kc_state_o),
.kc_valid_o (cbd_kc_valid_o),
.kc_ready_i (cbd_kc_ready_i),
.kc_state_i (cbd_kc_state_i),
.kc_valid_i (cbd_kc_valid_i),
.kc_ready_o (cbd_kc_ready_o)
);
// --- sample_ntt_sync_shared (consumer 2) ---
sample_ntt_sync_shared #(.K(K)) u_snt (
.clk (clk),
.rst_n (rst_n),
.rho_i (rho_reg),
.k_i (k_r),
.i_idx (snt_i_idx_r),
.j_idx (snt_j_idx_r),
.valid_i (snt_valid_i_r),
.ready_o (snt_ready_o),
.coeff_o (snt_coeff_o),
.valid_o (snt_valid_o),
.ready_i (1'b1),
.last_o (snt_last_o),
.kc_state_o (snt_kc_state_o),
.kc_valid_o (snt_kc_valid_o),
.kc_ready_i (snt_kc_ready_i),
.kc_state_i (snt_kc_state_i),
.kc_valid_i (snt_kc_valid_i),
.kc_ready_o (snt_kc_ready_o)
);
// --- sha3_top (separate keccak_core, for H/KDF calls) ---
sha3_top u_sha3 (
.clk (clk),
.rst_n (rst_n),
.mode (sha3_mode_r),
.data_i (sha3_data_r),
.valid_i (sha3_valid_i_r),
.ready_o (sha3_ready_o),
.hash_o (sha3_hash_o),
.valid_o (sha3_valid_o),
.ready_i (1'b1)
);
// --- rng_sync ---
rng_sync u_rng (
.clk (clk),
.rst_n (rst_n),
.valid_i (rng_valid_i),
.ready_o (rng_ready_o),
.data_o (rng_data_o),
.valid_o (rng_valid_o),
.ready_i (1'b1)
);
// --- ntt_core ---
ntt_core u_ntt (
.clk (clk),
.rst_n (rst_n),
.coeff_in (ntt_coeff_in_r),
.valid_i (ntt_valid_i_r),
.ready_o (ntt_ready_o),
.mode (ntt_mode_r),
.coeff_out (ntt_coeff_out),
.valid_o (ntt_valid_o),
.ready_i (1'b1),
.done_o (ntt_done_o)
);
// --- poly_mul_sync ---
poly_mul_sync u_pmul (
.clk (clk),
.rst_n (rst_n),
.coeff_a_in (pmul_a_r),
.coeff_b_in (pmul_b_r),
.valid_i (pmul_valid_i_r),
.ready_o (pmul_ready_o),
.coeff_out (pmul_coeff_out),
.valid_o (pmul_valid_o),
.ready_i (1'b1)
);
// --- poly_arith_sync ---
poly_arith_sync u_parith (
.clk (clk),
.rst_n (rst_n),
.coeff_a_in (parith_a_r),
.coeff_b_in (parith_b_r),
.mode (parith_mode_r),
.valid_i (parith_valid_i_r),
.ready_o (parith_ready_o),
.coeff_out (parith_coeff_out),
.valid_o (parith_valid_o),
.ready_i (1'b1)
);
// --- comp_decomp_sync ---
comp_decomp_sync u_comp (
.clk (clk),
.rst_n (rst_n),
.coeff_in (comp_coeff_in_r),
.d (comp_d_r),
.mode (comp_mode_r),
.valid_i (comp_valid_i_r),
.ready_o (comp_ready_o),
.coeff_out (comp_coeff_out),
.valid_o (comp_valid_o),
.ready_i (1'b1)
);
// --- mod_add_sync ---
mod_add_sync u_madd (
.clk (clk),
.rst_n (rst_n),
.a (madd_a_r),
.b (madd_b_r),
.valid_i (madd_valid_i_r),
.ready_o (madd_ready_o),
.sum (madd_sum),
.valid_o (madd_valid_o),
.ready_i (1'b1)
);
// --- sd_bram (large, for all polynomial storage) ---
sd_bram #(.W(12), .D(BRAM_DEPTH), .A(BRAM_AW)) u_bram (
.clk (clk),
.rd_addr (bram_rd_addr_r),
.rd_data (bram_rd_data),
.wr_en (bram_wr_en_r),
.wr_addr (bram_wr_addr_r),
.wr_data (bram_wr_data_r)
);
endmodule