- 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.
144 lines
5.8 KiB
Verilog
144 lines
5.8 KiB
Verilog
// 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
|