// 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 wire [255:0] d_rev; // byte-reversed d (FIPS 203 byte order) genvar gi; generate for (gi = 0; gi < 32; gi = gi + 1) begin : gen_d_rev assign d_rev[gi*8 +: 8] = d_reg[(31-gi)*8 +: 8]; end endgenerate 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_rev), .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