feat(top): add shared keccak variants, arbiter, and mlkem_top integration

- sha3_chain_top_shared.v: external keccak_core interface (6 ports)
- sample_cbd_sync_shared.v: shared keccak variant (6 ports)
- sample_ntt_sync_shared.v: shared keccak variant (6 ports)
- keccak_arbiter.v: fixed-priority arbiter for 3 keccak consumers
- mlkem_top.v: 1403-line monolithic FSM with KeyGen/Encaps/Decaps

Architecture:
  keccak_arbiter → keccak_core → keccak_round (shared)
  sha3_chain_top_shared (consumer 0)
  sample_cbd_sync_shared (consumer 1)
  sample_ntt_sync_shared (consumer 2)
  sha3_top (separate, own keccak_core)
  rng_sync, ntt_core, poly_arith, poly_mul, comp_decomp, mod_add
  sd_bram for polynomial storage

All original RTL files preserved unchanged.
This commit is contained in:
2026-06-26 03:35:37 +08:00
parent 1983d840a7
commit 03b4707879
5 changed files with 2348 additions and 0 deletions

View File

@@ -0,0 +1,143 @@
// keccak_arbiter.v - Fixed-priority arbiter for sharing a single keccak_core
//
// Allows N consumers (sha3_chain, sample_cbd, sample_ntt, sha3_top) to
// share one keccak_core instance. Consumer 0 has highest priority used
// for sha3_chain which needs fast turnaround during KeyGen.
//
// State machine:
// IDLE: Wait for any cons_valid_i. Grant to highest-priority consumer
// (lowest index). Assert kc_valid_i to start permutation.
// BUSY: Hold grant until kc_valid_o fires (permutation done), then
// return to IDLE. cons_valid_o pulses for the granted consumer.
//
// Parameters:
// NUM_CONSUMERS = 4
//
// Interface:
// Keccak side: kc_state_i/o, kc_valid_i/o, kc_ready_i/o (single instance)
// Consumer side: packed per-consumer valid/ready/state vectors
module keccak_arbiter #(
parameter NUM_CONSUMERS = 4
) (
input clk,
input rst_n,
// Keccak core side (single keccak_core instance)
output [1599:0] kc_state_i,
output kc_valid_i,
input kc_ready_o,
input [1599:0] kc_state_o,
input kc_valid_o,
output kc_ready_i,
// Consumer side (N copies, packed)
input [NUM_CONSUMERS*1600-1:0] cons_state_i,
input [NUM_CONSUMERS-1:0] cons_valid_i,
output [NUM_CONSUMERS-1:0] cons_ready_o,
output [NUM_CONSUMERS*1600-1:0] cons_state_o,
output [NUM_CONSUMERS-1:0] cons_valid_o,
/* verilator lint_off UNUSEDSIGNAL */
input [NUM_CONSUMERS-1:0] cons_ready_i
/* verilator lint_on UNUSEDSIGNAL */
);
// ================================================================
// State machine
// ================================================================
localparam ST_IDLE = 1'b0;
localparam ST_BUSY = 1'b1;
reg state_r, state_next;
// ================================================================
// Grant logic
// ================================================================
localparam GRANT_W = $clog2(NUM_CONSUMERS);
reg [GRANT_W-1:0] grant_r; // registered grant (held during BUSY)
reg [GRANT_W-1:0] grant_comb; // priority-encoded index (combinational)
// Any consumer requesting
wire any_valid;
assign any_valid = |cons_valid_i;
// Priority encoder: consumer 0 has highest priority (lowest index).
// Reverse iteration so last assignment wins lowest-index priority.
integer i;
always @(*) begin
grant_comb = {GRANT_W{1'b0}};
for (i = NUM_CONSUMERS - 1; i >= 0; i = i - 1) begin
if (cons_valid_i[i]) begin
/* verilator lint_off WIDTHTRUNC */
grant_comb = i; // intentional truncation: i ∈ [0,NUM_CONSUMERS-1] fits GRANT_W
/* verilator lint_on WIDTHTRUNC */
end
end
end
// Active grant: combinational in IDLE, registered (held) in BUSY
wire [GRANT_W-1:0] active_grant;
assign active_grant = (state_r == ST_IDLE) ? grant_comb : grant_r;
// ================================================================
// FSM next-state logic
// ================================================================
always @(*) begin
state_next = state_r;
case (state_r)
ST_IDLE: if (kc_ready_o && any_valid) state_next = ST_BUSY;
ST_BUSY: if (kc_valid_o) state_next = ST_IDLE;
default: state_next = ST_IDLE;
endcase
end
// ================================================================
// Sequential logic
// ================================================================
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state_r <= ST_IDLE;
grant_r <= {GRANT_W{1'b0}};
end else begin
state_r <= state_next;
// Capture grant on IDLE→BUSY transition
if (state_r == ST_IDLE && state_next == ST_BUSY)
grant_r <= grant_comb;
end
end
// ================================================================
// Keccak core side
// ================================================================
// Route selected consumer's state to keccak
assign kc_state_i = cons_state_i[active_grant * 1600 +: 1600];
// Start permutation when IDLE, keccak ready, and any consumer wants access
assign kc_valid_i = (state_r == ST_IDLE) && kc_ready_o && any_valid;
// Always accept keccak output (keccak_core's ready_i)
assign kc_ready_i = 1'b1;
// ================================================================
// Consumer side (generated per consumer)
// ================================================================
genvar g;
generate
for (g = 0; g < NUM_CONSUMERS; g = g + 1) begin : gen_consumer
// cons_ready_o: this consumer is granted AND keccak is ready
assign cons_ready_o[g] = (active_grant == g)
&& any_valid
&& (state_r == ST_IDLE)
&& kc_ready_o;
// cons_valid_o: this consumer was granted AND keccak finished
assign cons_valid_o[g] = (grant_r == g) && kc_valid_o;
// cons_state_o: broadcast keccak output to all consumers.
// Each consumer latches only when its own valid_o is high.
assign cons_state_o[g*1600 +: 1600] = kc_state_o;
end
endgenerate
endmodule

1403
sync_rtl/top/mlkem_top.v Normal file

File diff suppressed because it is too large Load Diff