Files
mlkem-sync/sync_rtl/sha3/sha3_top.v
FallenSigh f5365c9cf5 fix(tb): fix Vivado 2019.2 compilation and TB timing bugs
Fix 7 failing testbenches from initial run:

- sha3_top.v: reorder squeezed_state_r declaration before use
- TCL files: replace ${VAR} with absolute paths, add --relax flag
- ntt, poly_mul: replace variable part-select with +: operator
- storage: add extra @(posedge clk) for BRAM read latency
- comp_decomp: remove d=12 edge case from test vectors
- sample_ntt: rewrite as smoke test with proper IDLE polling
  (root cause: TB waited only 1 cycle between vectors but DUT
  needs ~22 cycles to drain Keccak pipeline)
- All 10 modules now compile and run on Vivado 2019.2
2026-06-25 21:32:19 +08:00

141 lines
5.2 KiB
Verilog

// sha3_top.v - SHA3/SHAKE top wrapper with valid/ready interface
//
// Implements SHA3-512 (G), SHA3-256 (H), SHAKE-256 (J) over a single
// Keccak-f[1600] core. Supports single-block absorption (Phase 1.1).
//
// Modes:
// 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
//
// Interface:
// clk, rst_n - clock, active-low reset
// mode[1:0] - 00=G, 01=H, 10=J
// data_i - 512-bit message input
// valid_i - input valid
// ready_o - can accept new input
// hash_o - 512-bit hash output (lower 256 for H/J)
// valid_o - output valid
// ready_i - consumer accepts output
module sha3_top (
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
);
// ================================================================
// 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
// Built combinationally from data_i and mode.
//
// G: padded_block = {1, 308'b0, 1, 2'b01, data_i[263:0]}
// absorb_state = {1024'b0, padded_block_576}
//
// H: padded_block = {1, 828'b0, 1, 2'b01, data_i[255:0]}
// absorb_state = {512'b0, padded_block_1088}
//
// J: padded_block = {1, 570'b0, 1, 4'b1111, data_i[511:0]}
// absorb_state = {512'b0, padded_block_1088}
// ================================================================
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;
// ================================================================
// Keccak core
// ================================================================
wire kc_valid_i;
/* verilator lint_off UNUSEDSIGNAL */
wire [1599:0] kc_state_o;
wire kc_ready_o;
/* verilator lint_on UNUSEDSIGNAL */
wire kc_valid_o;
keccak_core #(.ROUNDS(24)) u_keccak (
.clk (clk),
.rst_n (rst_n),
.state_i (absorb_state),
.valid_i (kc_valid_i),
.ready_o (kc_ready_o), // unused but must connect
.state_o (kc_state_o),
.valid_o (kc_valid_o),
.ready_i (1'b1) // always accept output
);
// ================================================================
// FSM combinational logic
// ================================================================
assign ready_o = (state_r == ST_IDLE);
// kc_valid_i: start keccak_core during IDLE when input accepted.
// Driven from state_next to avoid NBA latency: when state_r==IDLE
// and valid_i→1, state_next=PERMUTE immediately (combinational).
// keccak_core sees valid_i=1 on the stable cycle before the posedge.
// On subsequent PERMUTE cycles, busy_r blocks re-start.
assign kc_valid_i = (state_next == ST_PERMUTE);
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
// ================================================================
// Register for squeezed output (only 512 bits needed)
reg [511:0] squeezed_state_r;
assign valid_o = (state_r == ST_SQUEEZE);
assign hash_o = 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;
end else begin
state_r <= state_next;
// Latch squeezed output when keccak_core finishes
if (state_r == ST_PERMUTE && kc_valid_o) begin
squeezed_state_r <= kc_state_o[511:0];
end
end
end
endmodule