refactor(kg): share a single keccak_core across G/H, SampleNTT, CBD (4->1)
KeyGen's Keccak consumers (G/H via sha3, A via SampleNTT, C via CBD) run in disjoint top-FSM phases, so one keccak_core suffices. Add sha3_top_shared (keccak_core externalised as kc_* ports, like the existing sample_*_shared variants); mlkem_top now instantiates one keccak_core and a phase mux that routes kc_state_i/kc_valid_i from the active consumer and gates kc_valid_o per consumer (inactive samplers latch squeeze state unconditionally). Cuts the KeyGen datapath from 4 keccak_core (1600-bit state + 24-round logic each) to 1 -- the dominant ASIC area win. 11/11 KAT PASS (K=2 c0-4, K=3/4 c0-2), byte-exact, 0 file-not-found.
This commit is contained in:
183
sync_rtl/sha3/sha3_top_shared.v
Normal file
183
sync_rtl/sha3/sha3_top_shared.v
Normal file
@@ -0,0 +1,183 @@
|
||||
// sha3_top_shared.v - SHA3/SHAKE wrapper with EXTERNAL (shared) keccak_core.
|
||||
//
|
||||
// Identical to sha3_top.v except the internal keccak_core instance is
|
||||
// replaced by explicit kc_* ports, so several Keccak consumers can share a
|
||||
// single keccak_core via a phase mux (see mlkem_top). The sponge state
|
||||
// (mb_state_r) and all FSM/squeeze logic stay here; only the 24-round
|
||||
// permutation is external. Bit-identical to sha3_top when wired to a
|
||||
// dedicated keccak_core.
|
||||
//
|
||||
// Single-block modes (mb_en=0):
|
||||
// 00 = G (SHA3-512): rate=576, suffix=01, msg_len=264, out=512
|
||||
// 01 = H (SHA3-256): rate=1088, suffix=01, msg_len=256, out=256
|
||||
// 10 = J (SHAKE-256): rate=1088, suffix=1111,msg_len=512,out=256
|
||||
//
|
||||
// Multi-block SHA3-256 (mb_en=1): streams pre-padded 1088-bit rate blocks;
|
||||
// the CALLER applies SHA3-256 padding to the final block.
|
||||
//
|
||||
// Keccak interface (port names mirror the keccak_core port each connects to):
|
||||
// kc_state_o[1599:0] - keccak result (input, from keccak_core.state_o)
|
||||
// kc_valid_o - keccak output valid (input, from keccak_core.valid_o)
|
||||
// kc_ready_o - keccak ready for input (input, from keccak_core.ready_o)
|
||||
// kc_state_i[1599:0] - keccak input state (output, to keccak_core.state_i)
|
||||
// kc_valid_i - request permutation (output, to keccak_core.valid_i)
|
||||
// kc_ready_i - accept keccak output (output, to keccak_core.ready_i, always 1)
|
||||
|
||||
module sha3_top_shared (
|
||||
input clk,
|
||||
input rst_n,
|
||||
input [1:0] mode,
|
||||
input [511:0] data_i,
|
||||
input valid_i,
|
||||
output ready_o,
|
||||
output [511:0] hash_o,
|
||||
output valid_o,
|
||||
input ready_i,
|
||||
// --- multi-block SHA3-256 absorb (tie mb_en=0 to disable) ---
|
||||
input mb_en,
|
||||
input [1087:0] mb_block_i,
|
||||
input mb_valid_i,
|
||||
input mb_last_i,
|
||||
output mb_ready_o,
|
||||
// --- external (shared) keccak_core interface ---
|
||||
input [1599:0] kc_state_o,
|
||||
input kc_valid_o,
|
||||
/* verilator lint_off UNUSEDSIGNAL */
|
||||
input kc_ready_o,
|
||||
/* verilator lint_on UNUSEDSIGNAL */
|
||||
output [1599:0] kc_state_i,
|
||||
output kc_valid_i,
|
||||
output kc_ready_i
|
||||
);
|
||||
|
||||
// ================================================================
|
||||
// FSM state encoding
|
||||
// ================================================================
|
||||
localparam ST_IDLE = 2'd0;
|
||||
localparam ST_PERMUTE = 2'd1;
|
||||
localparam ST_SQUEEZE = 2'd2;
|
||||
|
||||
reg [1:0] state_r, state_next;
|
||||
|
||||
// ================================================================
|
||||
// Absorb state: message || suffix || pad10*1 into rate bits
|
||||
// ================================================================
|
||||
wire [575:0] g_pad;
|
||||
wire [1087:0] h_pad;
|
||||
wire [1087:0] j_pad;
|
||||
|
||||
assign g_pad = {1'b1, {308{1'b0}}, 1'b1, 2'b10, data_i[263:0]};
|
||||
assign h_pad = {1'b1, {828{1'b0}}, 1'b1, 2'b10, data_i[255:0]};
|
||||
// J: SHAKE suffix is "1111" — all ones, order irrelevant
|
||||
assign j_pad = {1'b1, {570{1'b0}}, 1'b1, 4'b1111, data_i[511:0]};
|
||||
|
||||
wire [1599:0] absorb_state;
|
||||
assign absorb_state = (mode == 2'b00) ? {{(1600-576){1'b0}}, g_pad} :
|
||||
(mode == 2'b01) ? {{(1600-1088){1'b0}}, h_pad} :
|
||||
(mode == 2'b10) ? {{(1600-1088){1'b0}}, j_pad} :
|
||||
1600'd0;
|
||||
|
||||
// ================================================================
|
||||
// Multi-block SHA3-256 absorb FSM (active only when mb_en=1)
|
||||
// ================================================================
|
||||
localparam MB_IDLE = 2'd0; // ready for a block (or first block)
|
||||
localparam MB_PERMUTE = 2'd1; // keccak running on xored state
|
||||
localparam MB_DONE = 2'd2; // squeeze: present 256-bit digest
|
||||
|
||||
reg [1:0] mb_state, mb_state_next;
|
||||
reg [1599:0] mb_state_r; // running sponge state
|
||||
reg mb_last_r; // captured last-block flag
|
||||
reg [255:0] mb_digest_r; // latched 256-bit digest (sticky in MB_DONE)
|
||||
|
||||
// XOR the incoming block into the low 1088 bits (rate) of the state.
|
||||
wire [1599:0] mb_xored;
|
||||
assign mb_xored = mb_state_r ^ {{(1600-1088){1'b0}}, mb_block_i};
|
||||
|
||||
// Accept a block only in MB_IDLE while enabled.
|
||||
assign mb_ready_o = mb_en && (mb_state == MB_IDLE);
|
||||
wire mb_accept = mb_en && (mb_state == MB_IDLE) && mb_valid_i;
|
||||
wire mb_kc_valid = mb_accept; // start keccak on the accept cycle
|
||||
|
||||
always @(*) begin
|
||||
mb_state_next = mb_state;
|
||||
case (mb_state)
|
||||
MB_IDLE: if (mb_accept) mb_state_next = MB_PERMUTE;
|
||||
MB_PERMUTE: if (kc_valid_o) mb_state_next = mb_last_r ? MB_DONE : MB_IDLE;
|
||||
MB_DONE: if (ready_i) mb_state_next = MB_IDLE;
|
||||
default: mb_state_next = MB_IDLE;
|
||||
endcase
|
||||
end
|
||||
|
||||
// ================================================================
|
||||
// External keccak_core interface (was internal keccak_core)
|
||||
// ================================================================
|
||||
// Keccak input: multi-block xored state when mb_en, else single-block absorb.
|
||||
assign kc_state_i = mb_en ? mb_xored : absorb_state;
|
||||
// Always accept keccak output (matches the dedicated-core ready_i=1'b1).
|
||||
assign kc_ready_i = 1'b1;
|
||||
// kc_valid_i: single-block start (state_next==PERMUTE) OR multi-block accept.
|
||||
assign kc_valid_i = mb_en ? mb_kc_valid : (state_next == ST_PERMUTE);
|
||||
|
||||
// ================================================================
|
||||
// FSM combinational logic
|
||||
// ================================================================
|
||||
assign ready_o = !mb_en && (state_r == ST_IDLE);
|
||||
|
||||
always @(*) begin
|
||||
state_next = state_r;
|
||||
case (state_r)
|
||||
ST_IDLE: if (valid_i && ready_o) state_next = ST_PERMUTE;
|
||||
ST_PERMUTE: if (kc_valid_o) state_next = ST_SQUEEZE;
|
||||
ST_SQUEEZE: if (valid_o && ready_i) state_next = ST_IDLE;
|
||||
default: state_next = ST_IDLE;
|
||||
endcase
|
||||
end
|
||||
|
||||
// ================================================================
|
||||
// Output
|
||||
// ================================================================
|
||||
reg [511:0] squeezed_state_r;
|
||||
|
||||
// valid_o / hash_o serve both paths, selected by mb_en.
|
||||
assign valid_o = mb_en ? (mb_state == MB_DONE) : (state_r == ST_SQUEEZE);
|
||||
assign hash_o = mb_en ? {256'b0, mb_digest_r} : squeezed_state_r;
|
||||
|
||||
// ================================================================
|
||||
// Sequential logic
|
||||
// ================================================================
|
||||
always @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
state_r <= ST_IDLE;
|
||||
squeezed_state_r <= 512'd0;
|
||||
mb_state <= MB_IDLE;
|
||||
mb_state_r <= 1600'd0;
|
||||
mb_last_r <= 1'b0;
|
||||
mb_digest_r <= 256'd0;
|
||||
end else begin
|
||||
state_r <= state_next;
|
||||
mb_state <= mb_state_next;
|
||||
|
||||
// --- single-block: latch squeezed output ---
|
||||
if (state_r == ST_PERMUTE && kc_valid_o) begin
|
||||
squeezed_state_r <= kc_state_o[511:0];
|
||||
end
|
||||
|
||||
// --- multi-block: capture last flag on accept ---
|
||||
if (mb_accept) begin
|
||||
mb_last_r <= mb_last_i;
|
||||
end
|
||||
|
||||
// --- multi-block: latch permuted state when keccak finishes ---
|
||||
if (mb_state == MB_PERMUTE && kc_valid_o) begin
|
||||
mb_state_r <= kc_state_o;
|
||||
if (mb_last_r) mb_digest_r <= kc_state_o[255:0];
|
||||
end
|
||||
|
||||
// --- multi-block: clear running state after digest consumed ---
|
||||
if (mb_state == MB_DONE && ready_i) begin
|
||||
mb_state_r <= 1600'd0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
Reference in New Issue
Block a user