feat(tb): add independent KG/EN/DE testbenches

- sync_rtl/kg/TB/tb_kg_xsim.v + xsim_run.tcl
- sync_rtl/en/TB/tb_en_xsim.v + xsim_run.tcl
- sync_rtl/de/TB/tb_de_xsim.v + xsim_run.tcl

Run individual tests:
  ./run_tb.sh kg   (KeyGen only, ~47K cycles)
  ./run_tb.sh en   (Encaps only)
  ./run_tb.sh de   (Decaps only)
This commit is contained in:
2026-06-27 02:27:22 +08:00
parent 6721c3c0c1
commit f211dc3c55
6 changed files with 1095 additions and 0 deletions

223
sync_rtl/de/TB/tb_de_xsim.v Normal file
View File

@@ -0,0 +1,223 @@
// tb_de_xsim.v - Decaps-only KAT testbench for mlkem_top
//
// Runs full ML-KEM flow (kgende) but only CHECKS Decaps results.
// Uses existing vector files from sync_rtl/top/TB/vectors/.
//
// Usage:
// xvlog -sv -i . <all_deps>.v sync_rtl/de/TB/tb_de_xsim.v
// xelab tb_de_xsim -s tb_de_xsim --timescale 1ns/1ps
// xsim tb_de_xsim -R
`timescale 1ns / 1ps
module tb_de_xsim;
parameter VECTOR_FILE = "sync_rtl/top/TB/vectors/mlkem_top_input.hex";
parameter EXPECTED_FILE = "sync_rtl/top/TB/vectors/mlkem_top_expected.hex";
parameter MAX_VECTORS = 16;
parameter TIMEOUT_CYCLES = 10000000;
parameter K_PARAM = 4;
localparam PK_WIDTH = 12 * K_PARAM * 256;
localparam SK_WIDTH = 12 * K_PARAM * 256;
localparam EXP_PK_WIDTH = 6400;
localparam EXP_SK_WIDTH = 13056;
localparam EXP_CT_WIDTH = 6144;
localparam EXP_SS_WIDTH = 256;
// DUT signals
reg clk;
reg rst_n;
reg [1:0] mode;
reg [2:0] i_k;
reg valid_i;
wire ready_o;
wire [PK_WIDTH-1:0] pk_o;
wire [SK_WIDTH-1:0] sk_o;
wire pk_valid;
wire sk_valid;
wire [EXP_CT_WIDTH*K_PARAM/2-1:0] ct_o;
wire [255:0] K_o;
wire ct_valid;
wire K_valid;
wire [255:0] K_o_dec;
wire K_valid_dec;
wire done_o;
// DUT instantiation
mlkem_top #(.K(K_PARAM)) u_dut (
.clk(clk), .rst_n(rst_n), .mode(mode), .i_k(i_k),
.valid_i(valid_i), .ready_o(ready_o),
.pk_o(pk_o), .sk_o(sk_o), .pk_valid(pk_valid), .sk_valid(sk_valid),
.ct_o(ct_o), .K_o(K_o), .ct_valid(ct_valid), .K_valid(K_valid),
.K_o_dec(K_o_dec), .K_valid_dec(K_valid_dec), .done_o(done_o)
);
initial clk = 1'b0;
always #5 clk = ~clk;
reg [767:0] input_mem [0:MAX_VECTORS-1];
reg [25855:0] expected_mem [0:MAX_VECTORS-1];
integer vec_count, idx, dc_pass, dc_fail;
function [7:0] nibble_to_ascii;
input [3:0] nibble;
nibble_to_ascii = (nibble < 4'd10) ? (8'h30 + {4'd0, nibble}) : (8'h41 + ({4'd0, nibble} - 4'd10));
endfunction
task print_hex256;
input [255:0] val;
input [256*8:1] label;
integer bi;
begin
$write("%s: ", label);
for (bi = 63; bi >= 0; bi = bi - 1)
$write("%c", nibble_to_ascii(val[(bi*4)+:4]));
$write("\n");
end
endtask
integer wfd_result;
task wait_for_done;
input [256*8:1] op_name;
integer cyc;
begin
cyc = 0;
while (!done_o && cyc < TIMEOUT_CYCLES) begin @(posedge clk); cyc = cyc + 1; end
if (cyc >= TIMEOUT_CYCLES) begin
$display("ERROR: %s timeout after %0d cycles", op_name, TIMEOUT_CYCLES);
wfd_result = 0;
end else begin
$display("INFO: %s done after %0d cycles", op_name, cyc);
wfd_result = 1;
end
end
endtask
initial begin
vec_count = 0;
$readmemh(VECTOR_FILE, input_mem);
$readmemh(EXPECTED_FILE, expected_mem);
begin
integer found_end = 0;
for (idx = 0; idx < MAX_VECTORS; idx = idx + 1) begin
if (!found_end && (input_mem[idx] === 768'hx || input_mem[idx] === 768'hz))
found_end = 1;
else if (!found_end)
vec_count = vec_count + 1;
end
end
if (vec_count == 0) begin
$display("ERROR: No vectors loaded from %s", VECTOR_FILE);
$finish;
end
$display("====================================================");
$display("MLKEM_TOP Decaps-ONLY KAT TESTBENCH");
$display(" Vectors loaded: %0d", vec_count);
$display("====================================================");
mode <= 2'd0;
i_k <= 3'd2;
valid_i <= 1'b0;
rst_n <= 1'b0;
repeat (5) @(posedge clk);
rst_n <= 1'b1;
@(posedge clk);
force u_dut.chain_kc_ready_o = 1'b1;
force u_dut.ntt_valid_o = 1'b1;
dc_pass = 0; dc_fail = 0;
for (idx = 0; idx < vec_count; idx = idx + 1) begin
reg [255:0] d_val, msg_val, z_val;
d_val = input_mem[idx][767:512];
msg_val = input_mem[idx][511:256];
z_val = input_mem[idx][255:0];
// KeyGen (run but don't check)
force u_dut.d_reg = d_val;
mode <= 2'b00;
i_k <= 3'd2;
valid_i <= 1'b1;
@(posedge clk);
valid_i <= 1'b0;
wait_for_done("KeyGen");
if (wfd_result) release u_dut.d_reg;
else begin
release u_dut.d_reg;
rst_n <= 1'b0; repeat (5) @(posedge clk); rst_n <= 1'b1;
force u_dut.chain_kc_ready_o = 1'b1; force u_dut.ntt_valid_o = 1'b1;
@(posedge clk);
end
repeat (2) @(posedge clk);
// Encaps (run but don't check)
force u_dut.m_reg = msg_val;
mode <= 2'b01;
i_k <= 3'd2;
valid_i <= 1'b1;
@(posedge clk);
valid_i <= 1'b0;
wait_for_done("Encaps");
if (wfd_result) release u_dut.m_reg;
else begin
release u_dut.m_reg;
rst_n <= 1'b0; repeat (5) @(posedge clk); rst_n <= 1'b1;
force u_dut.chain_kc_ready_o = 1'b1; force u_dut.ntt_valid_o = 1'b1;
@(posedge clk);
end
repeat (2) @(posedge clk);
// Decaps (check this)
$display("--- Vector %0d: Decaps ---", idx);
print_hex256(z_val, " z ");
force u_dut.z_reg = z_val;
mode <= 2'b10;
i_k <= 3'd2;
valid_i <= 1'b1;
@(posedge clk);
valid_i <= 1'b0;
wait_for_done("Decaps");
if (wfd_result) begin
release u_dut.z_reg;
if (K_valid_dec) begin
$display(" PASS: Decaps completed (K_valid_dec asserted)");
dc_pass = dc_pass + 1;
end else begin
$display(" FAIL: Decaps K_valid_dec not asserted");
dc_fail = dc_fail + 1;
end
end else begin
release u_dut.z_reg;
$display(" FAIL: Decaps timeout (placeholder states — expected)");
dc_fail = dc_fail + 1;
rst_n <= 1'b0; repeat (5) @(posedge clk); rst_n <= 1'b1;
force u_dut.chain_kc_ready_o = 1'b1; force u_dut.ntt_valid_o = 1'b1;
@(posedge clk);
end
repeat (2) @(posedge clk);
end
release u_dut.chain_kc_ready_o;
release u_dut.ntt_valid_o;
$display("====================================================");
$display("DECAPS TEST COMPLETE");
$display(" PASS: %0d FAIL: %0d", dc_pass, dc_fail);
$display("====================================================");
$finish;
end
initial begin
#(TIMEOUT_CYCLES * 10 * 100);
$display("FATAL: Global simulation timeout");
$finish;
end
endmodule

132
sync_rtl/de/TB/xsim_run.tcl Normal file
View File

@@ -0,0 +1,132 @@
# NOTE: On some systems, you may need:
# export LD_PRELOAD=/usr/lib64/libtinfo.so.5
# before running this script.
# xsim_run.tcl - Vivado xsim compilation and simulation for Decaps-only testbench
#
# Compiles ALL RTL dependencies for mlkem_top plus the Decaps-only testbench.
# Run from the project root: ~/Dev/mlkem/
#
# Prerequisites:
# source /opt/Xilinx/Vivado/2019.2/settings64.sh
#
# Usage:
# vivado -mode batch -source sync_rtl/de/TB/xsim_run.tcl
# ================================================================
# Configuration
# ================================================================
set COMMON_DIR sync_rtl/common
set SHA3_DIR sync_rtl/sha3
set SHA3C_DIR sync_rtl/sha3_chain
set RNG_DIR sync_rtl/rng
set NTT_DIR sync_rtl/ntt
set PA_DIR sync_rtl/poly_arith
set PM_DIR sync_rtl/poly_mul
set CBD_DIR sync_rtl/sample_cbd
set SNT_DIR sync_rtl/sample_ntt
set CD_DIR sync_rtl/comp_decomp
set MA_DIR sync_rtl/mod_add
set STOR_DIR sync_rtl/storage
set TOP_DIR sync_rtl/top
set TB_DIR sync_rtl/de/TB
# ================================================================
# Step 1: Compile common infrastructure
# ================================================================
puts "=== Compiling common infrastructure ==="
xvlog -sv -i . ${COMMON_DIR}/pipeline_reg.v
xvlog -sv -i . ${COMMON_DIR}/skid_buffer.v
# ================================================================
# Step 2: Compile SHA3 / Keccak core
# ================================================================
puts "=== Compiling SHA3/Keccak core ==="
xvlog -sv ${SHA3_DIR}/keccak_round.v
xvlog -sv ${SHA3_DIR}/keccak_core.v
xvlog -sv -i . ${SHA3_DIR}/sha3_top.v
# ================================================================
# Step 3: Compile SHA3 chain (G function)
# ================================================================
puts "=== Compiling SHA3 chain ==="
xvlog -sv -i . ${SHA3C_DIR}/sha3_chain_top_shared.v
# ================================================================
# Step 4: Compile RNG
# ================================================================
puts "=== Compiling RNG ==="
xvlog -sv ${RNG_DIR}/rng_sync.v
# ================================================================
# Step 5: Compile NTT core and dependencies
# ================================================================
puts "=== Compiling NTT core ==="
xvlog -sv ${NTT_DIR}/zeta_rom.v
xvlog -sv ${NTT_DIR}/barrett_mul.v
xvlog -sv ${NTT_DIR}/butterfly_unit.v
xvlog -sv -i . ${NTT_DIR}/ntt_core.v
# ================================================================
# Step 6: Compile polynomial arithmetic
# ================================================================
puts "=== Compiling polynomial arithmetic ==="
xvlog -sv ${PA_DIR}/poly_arith_sync.v
# ================================================================
# Step 7: Compile polynomial multiplication
# ================================================================
puts "=== Compiling polynomial multiplication ==="
xvlog -sv ${PM_DIR}/poly_mul_zeta_rom.v
xvlog -sv ${PM_DIR}/basecase_mul.v
xvlog -sv -i . ${PM_DIR}/poly_mul_sync.v
# ================================================================
# Step 8: Compile sampling modules
# ================================================================
puts "=== Compiling sampling modules ==="
xvlog -sv -i . ${CBD_DIR}/sample_cbd_sync_shared.v
xvlog -sv -i . ${SNT_DIR}/sample_ntt_sync_shared.v
# ================================================================
# Step 9: Compile compression and modular arithmetic
# ================================================================
puts "=== Compiling compression and modular arithmetic ==="
xvlog -sv ${CD_DIR}/comp_decomp_sync.v
xvlog -sv ${MA_DIR}/mod_add_sync.v
# ================================================================
# Step 10: Compile storage (BRAM)
# ================================================================
puts "=== Compiling storage BRAMs ==="
xvlog -sv ${STOR_DIR}/s_bram.v
xvlog -sv ${STOR_DIR}/sd_bram.v
# ================================================================
# Step 11: Compile top-level integration
# ================================================================
puts "=== Compiling top-level integration ==="
xvlog -sv -i . ${TOP_DIR}/keccak_arbiter.v
xvlog -sv -i . ${TOP_DIR}/mlkem_top.v
# ================================================================
# Step 12: Compile testbench
# ================================================================
puts "=== Compiling Decaps-only testbench ==="
xvlog -sv ${TB_DIR}/tb_de_xsim.v
# ================================================================
# Step 13: Elaborate snapshot (xelab)
# ================================================================
puts "=== Elaborating snapshot ==="
xelab tb_de_xsim -s tb_de_xsim --timescale 1ns/1ps
# ================================================================
# Step 14: Run simulation
# ================================================================
puts ""
puts "=== Running Decaps-only simulation ==="
xsim tb_de_xsim -R
puts ""
puts "=== Decaps simulation complete ==="

238
sync_rtl/en/TB/tb_en_xsim.v Normal file
View File

@@ -0,0 +1,238 @@
// tb_en_xsim.v - Encaps-only KAT testbench for mlkem_top
//
// Runs full ML-KEM flow (kgende) but only CHECKS Encaps results.
// Uses existing vector files from sync_rtl/top/TB/vectors/.
//
// Usage:
// xvlog -sv -i . <all_deps>.v sync_rtl/en/TB/tb_en_xsim.v
// xelab tb_en_xsim -s tb_en_xsim --timescale 1ns/1ps
// xsim tb_en_xsim -R
`timescale 1ns / 1ps
module tb_en_xsim;
parameter VECTOR_FILE = "sync_rtl/top/TB/vectors/mlkem_top_input.hex";
parameter EXPECTED_FILE = "sync_rtl/top/TB/vectors/mlkem_top_expected.hex";
parameter MAX_VECTORS = 16;
parameter TIMEOUT_CYCLES = 10000000;
parameter K_PARAM = 4;
localparam PK_WIDTH = 12 * K_PARAM * 256;
localparam SK_WIDTH = 12 * K_PARAM * 256;
localparam EXP_PK_WIDTH = 6400;
localparam EXP_SK_WIDTH = 13056;
localparam EXP_CT_WIDTH = 6144;
localparam EXP_SS_WIDTH = 256;
// DUT signals
reg clk;
reg rst_n;
reg [1:0] mode;
reg [2:0] i_k;
reg valid_i;
wire ready_o;
wire [PK_WIDTH-1:0] pk_o;
wire [SK_WIDTH-1:0] sk_o;
wire pk_valid;
wire sk_valid;
wire [EXP_CT_WIDTH*K_PARAM/2-1:0] ct_o;
wire [255:0] K_o;
wire ct_valid;
wire K_valid;
wire [255:0] K_o_dec;
wire K_valid_dec;
wire done_o;
// DUT instantiation
mlkem_top #(.K(K_PARAM)) u_dut (
.clk(clk), .rst_n(rst_n), .mode(mode), .i_k(i_k),
.valid_i(valid_i), .ready_o(ready_o),
.pk_o(pk_o), .sk_o(sk_o), .pk_valid(pk_valid), .sk_valid(sk_valid),
.ct_o(ct_o), .K_o(K_o), .ct_valid(ct_valid), .K_valid(K_valid),
.K_o_dec(K_o_dec), .K_valid_dec(K_valid_dec), .done_o(done_o)
);
initial clk = 1'b0;
always #5 clk = ~clk;
reg [767:0] input_mem [0:MAX_VECTORS-1];
reg [25855:0] expected_mem [0:MAX_VECTORS-1];
integer vec_count, idx, en_pass, en_fail;
function [7:0] nibble_to_ascii;
input [3:0] nibble;
nibble_to_ascii = (nibble < 4'd10) ? (8'h30 + {4'd0, nibble}) : (8'h41 + ({4'd0, nibble} - 4'd10));
endfunction
task print_hex256;
input [255:0] val;
input [256*8:1] label;
integer bi;
begin
$write("%s: ", label);
for (bi = 63; bi >= 0; bi = bi - 1)
$write("%c", nibble_to_ascii(val[(bi*4)+:4]));
$write("\n");
end
endtask
integer wfd_result;
task wait_for_done;
input [256*8:1] op_name;
integer cyc;
begin
cyc = 0;
while (!done_o && cyc < TIMEOUT_CYCLES) begin @(posedge clk); cyc = cyc + 1; end
if (cyc >= TIMEOUT_CYCLES) begin
$display("ERROR: %s timeout after %0d cycles", op_name, TIMEOUT_CYCLES);
wfd_result = 0;
end else begin
$display("INFO: %s done after %0d cycles", op_name, cyc);
wfd_result = 1;
end
end
endtask
initial begin
vec_count = 0;
$readmemh(VECTOR_FILE, input_mem);
$readmemh(EXPECTED_FILE, expected_mem);
begin
integer found_end = 0;
for (idx = 0; idx < MAX_VECTORS; idx = idx + 1) begin
if (!found_end && (input_mem[idx] === 768'hx || input_mem[idx] === 768'hz))
found_end = 1;
else if (!found_end)
vec_count = vec_count + 1;
end
end
if (vec_count == 0) begin
$display("ERROR: No vectors loaded from %s", VECTOR_FILE);
$finish;
end
$display("====================================================");
$display("MLKEM_TOP Encaps-ONLY KAT TESTBENCH");
$display(" Vectors loaded: %0d", vec_count);
$display("====================================================");
mode <= 2'd0;
i_k <= 3'd2;
valid_i <= 1'b0;
rst_n <= 1'b0;
repeat (5) @(posedge clk);
rst_n <= 1'b1;
@(posedge clk);
force u_dut.chain_kc_ready_o = 1'b1;
force u_dut.ntt_valid_o = 1'b1;
en_pass = 0; en_fail = 0;
for (idx = 0; idx < vec_count; idx = idx + 1) begin
reg [255:0] d_val, msg_val, z_val;
reg [EXP_CT_WIDTH-1:0] exp_ct;
reg [EXP_SS_WIDTH-1:0] exp_ss;
d_val = input_mem[idx][767:512];
msg_val = input_mem[idx][511:256];
z_val = input_mem[idx][255:0];
exp_ss = expected_mem[idx][0 +: EXP_SS_WIDTH];
exp_ct = expected_mem[idx][EXP_SS_WIDTH +: EXP_CT_WIDTH];
// KeyGen (run but don't check)
force u_dut.d_reg = d_val;
mode <= 2'b00;
i_k <= 3'd2;
valid_i <= 1'b1;
@(posedge clk);
valid_i <= 1'b0;
wait_for_done("KeyGen");
if (wfd_result) release u_dut.d_reg;
else begin
release u_dut.d_reg;
rst_n <= 1'b0; repeat (5) @(posedge clk); rst_n <= 1'b1;
force u_dut.chain_kc_ready_o = 1'b1; force u_dut.ntt_valid_o = 1'b1;
@(posedge clk);
end
repeat (2) @(posedge clk);
// Encaps (check this)
$display("--- Vector %0d: Encaps ---", idx);
print_hex256(msg_val, " msg");
force u_dut.m_reg = msg_val;
mode <= 2'b01;
i_k <= 3'd2;
valid_i <= 1'b1;
@(posedge clk);
valid_i <= 1'b0;
wait_for_done("Encaps");
if (wfd_result) begin
release u_dut.m_reg;
if (ct_valid && K_valid) begin
if (ct_o[EXP_CT_WIDTH-1:0] == exp_ct) begin
$display(" PASS: ct matches expected");
en_pass = en_pass + 1;
end else begin
$display(" FAIL: ct mismatch");
en_fail = en_fail + 1;
end
if (K_o == exp_ss)
$display(" PASS: K matches expected ss");
else begin
$display(" FAIL: K mismatch");
en_fail = en_fail + 1;
end
end else begin
$display(" FAIL: ct_valid=%b K_valid=%b", ct_valid, K_valid);
en_fail = en_fail + 1;
end
end else begin
release u_dut.m_reg;
$display(" FAIL: Encaps timeout");
en_fail = en_fail + 1;
rst_n <= 1'b0; repeat (5) @(posedge clk); rst_n <= 1'b1;
force u_dut.chain_kc_ready_o = 1'b1; force u_dut.ntt_valid_o = 1'b1;
@(posedge clk);
end
repeat (2) @(posedge clk);
// Decaps (run but don't check)
force u_dut.z_reg = z_val;
mode <= 2'b10;
i_k <= 3'd2;
valid_i <= 1'b1;
@(posedge clk);
valid_i <= 1'b0;
wait_for_done("Decaps");
if (wfd_result) release u_dut.z_reg;
else begin
release u_dut.z_reg;
rst_n <= 1'b0; repeat (5) @(posedge clk); rst_n <= 1'b1;
force u_dut.chain_kc_ready_o = 1'b1; force u_dut.ntt_valid_o = 1'b1;
@(posedge clk);
end
repeat (2) @(posedge clk);
end
release u_dut.chain_kc_ready_o;
release u_dut.ntt_valid_o;
$display("====================================================");
$display("ENCAPS TEST COMPLETE");
$display(" PASS: %0d FAIL: %0d", en_pass, en_fail);
$display("====================================================");
$finish;
end
initial begin
#(TIMEOUT_CYCLES * 10 * 100);
$display("FATAL: Global simulation timeout");
$finish;
end
endmodule

132
sync_rtl/en/TB/xsim_run.tcl Normal file
View File

@@ -0,0 +1,132 @@
# NOTE: On some systems, you may need:
# export LD_PRELOAD=/usr/lib64/libtinfo.so.5
# before running this script.
# xsim_run.tcl - Vivado xsim compilation and simulation for Encaps-only testbench
#
# Compiles ALL RTL dependencies for mlkem_top plus the Encaps-only testbench.
# Run from the project root: ~/Dev/mlkem/
#
# Prerequisites:
# source /opt/Xilinx/Vivado/2019.2/settings64.sh
#
# Usage:
# vivado -mode batch -source sync_rtl/en/TB/xsim_run.tcl
# ================================================================
# Configuration
# ================================================================
set COMMON_DIR sync_rtl/common
set SHA3_DIR sync_rtl/sha3
set SHA3C_DIR sync_rtl/sha3_chain
set RNG_DIR sync_rtl/rng
set NTT_DIR sync_rtl/ntt
set PA_DIR sync_rtl/poly_arith
set PM_DIR sync_rtl/poly_mul
set CBD_DIR sync_rtl/sample_cbd
set SNT_DIR sync_rtl/sample_ntt
set CD_DIR sync_rtl/comp_decomp
set MA_DIR sync_rtl/mod_add
set STOR_DIR sync_rtl/storage
set TOP_DIR sync_rtl/top
set TB_DIR sync_rtl/en/TB
# ================================================================
# Step 1: Compile common infrastructure
# ================================================================
puts "=== Compiling common infrastructure ==="
xvlog -sv -i . ${COMMON_DIR}/pipeline_reg.v
xvlog -sv -i . ${COMMON_DIR}/skid_buffer.v
# ================================================================
# Step 2: Compile SHA3 / Keccak core
# ================================================================
puts "=== Compiling SHA3/Keccak core ==="
xvlog -sv ${SHA3_DIR}/keccak_round.v
xvlog -sv ${SHA3_DIR}/keccak_core.v
xvlog -sv -i . ${SHA3_DIR}/sha3_top.v
# ================================================================
# Step 3: Compile SHA3 chain (G function)
# ================================================================
puts "=== Compiling SHA3 chain ==="
xvlog -sv -i . ${SHA3C_DIR}/sha3_chain_top_shared.v
# ================================================================
# Step 4: Compile RNG
# ================================================================
puts "=== Compiling RNG ==="
xvlog -sv ${RNG_DIR}/rng_sync.v
# ================================================================
# Step 5: Compile NTT core and dependencies
# ================================================================
puts "=== Compiling NTT core ==="
xvlog -sv ${NTT_DIR}/zeta_rom.v
xvlog -sv ${NTT_DIR}/barrett_mul.v
xvlog -sv ${NTT_DIR}/butterfly_unit.v
xvlog -sv -i . ${NTT_DIR}/ntt_core.v
# ================================================================
# Step 6: Compile polynomial arithmetic
# ================================================================
puts "=== Compiling polynomial arithmetic ==="
xvlog -sv ${PA_DIR}/poly_arith_sync.v
# ================================================================
# Step 7: Compile polynomial multiplication
# ================================================================
puts "=== Compiling polynomial multiplication ==="
xvlog -sv ${PM_DIR}/poly_mul_zeta_rom.v
xvlog -sv ${PM_DIR}/basecase_mul.v
xvlog -sv -i . ${PM_DIR}/poly_mul_sync.v
# ================================================================
# Step 8: Compile sampling modules
# ================================================================
puts "=== Compiling sampling modules ==="
xvlog -sv -i . ${CBD_DIR}/sample_cbd_sync_shared.v
xvlog -sv -i . ${SNT_DIR}/sample_ntt_sync_shared.v
# ================================================================
# Step 9: Compile compression and modular arithmetic
# ================================================================
puts "=== Compiling compression and modular arithmetic ==="
xvlog -sv ${CD_DIR}/comp_decomp_sync.v
xvlog -sv ${MA_DIR}/mod_add_sync.v
# ================================================================
# Step 10: Compile storage (BRAM)
# ================================================================
puts "=== Compiling storage BRAMs ==="
xvlog -sv ${STOR_DIR}/s_bram.v
xvlog -sv ${STOR_DIR}/sd_bram.v
# ================================================================
# Step 11: Compile top-level integration
# ================================================================
puts "=== Compiling top-level integration ==="
xvlog -sv -i . ${TOP_DIR}/keccak_arbiter.v
xvlog -sv -i . ${TOP_DIR}/mlkem_top.v
# ================================================================
# Step 12: Compile testbench
# ================================================================
puts "=== Compiling Encaps-only testbench ==="
xvlog -sv ${TB_DIR}/tb_en_xsim.v
# ================================================================
# Step 13: Elaborate snapshot (xelab)
# ================================================================
puts "=== Elaborating snapshot ==="
xelab tb_en_xsim -s tb_en_xsim --timescale 1ns/1ps
# ================================================================
# Step 14: Run simulation
# ================================================================
puts ""
puts "=== Running Encaps-only simulation ==="
xsim tb_en_xsim -R
puts ""
puts "=== Encaps simulation complete ==="

238
sync_rtl/kg/TB/tb_kg_xsim.v Normal file
View File

@@ -0,0 +1,238 @@
// tb_kg_xsim.v - KeyGen-only KAT testbench for mlkem_top
//
// Runs full ML-KEM flow (kgende) but only CHECKS KeyGen results.
// Uses existing vector files from sync_rtl/top/TB/vectors/.
//
// Usage:
// xvlog -sv -i . <all_deps>.v sync_rtl/kg/TB/tb_kg_xsim.v
// xelab tb_kg_xsim -s tb_kg_xsim --timescale 1ns/1ps
// xsim tb_kg_xsim -R
`timescale 1ns / 1ps
module tb_kg_xsim;
parameter VECTOR_FILE = "sync_rtl/top/TB/vectors/mlkem_top_input.hex";
parameter EXPECTED_FILE = "sync_rtl/top/TB/vectors/mlkem_top_expected.hex";
parameter MAX_VECTORS = 16;
parameter TIMEOUT_CYCLES = 10000000;
parameter K_PARAM = 4;
localparam PK_WIDTH = 12 * K_PARAM * 256;
localparam SK_WIDTH = 12 * K_PARAM * 256;
localparam EXP_PK_WIDTH = 6400;
localparam EXP_SK_WIDTH = 13056;
localparam EXP_CT_WIDTH = 6144;
localparam EXP_SS_WIDTH = 256;
// DUT signals
reg clk;
reg rst_n;
reg [1:0] mode;
reg [2:0] i_k;
reg valid_i;
wire ready_o;
wire [PK_WIDTH-1:0] pk_o;
wire [SK_WIDTH-1:0] sk_o;
wire pk_valid;
wire sk_valid;
wire [EXP_CT_WIDTH*K_PARAM/2-1:0] ct_o;
wire [255:0] K_o;
wire ct_valid;
wire K_valid;
wire [255:0] K_o_dec;
wire K_valid_dec;
wire done_o;
// DUT instantiation
mlkem_top #(.K(K_PARAM)) u_dut (
.clk(clk), .rst_n(rst_n), .mode(mode), .i_k(i_k),
.valid_i(valid_i), .ready_o(ready_o),
.pk_o(pk_o), .sk_o(sk_o), .pk_valid(pk_valid), .sk_valid(sk_valid),
.ct_o(ct_o), .K_o(K_o), .ct_valid(ct_valid), .K_valid(K_valid),
.K_o_dec(K_o_dec), .K_valid_dec(K_valid_dec), .done_o(done_o)
);
initial clk = 1'b0;
always #5 clk = ~clk;
reg [767:0] input_mem [0:MAX_VECTORS-1];
reg [25855:0] expected_mem [0:MAX_VECTORS-1];
integer vec_count, idx, kg_pass, kg_fail;
function [7:0] nibble_to_ascii;
input [3:0] nibble;
nibble_to_ascii = (nibble < 4'd10) ? (8'h30 + {4'd0, nibble}) : (8'h41 + ({4'd0, nibble} - 4'd10));
endfunction
task print_hex256;
input [255:0] val;
input [256*8:1] label;
integer bi;
begin
$write("%s: ", label);
for (bi = 63; bi >= 0; bi = bi - 1)
$write("%c", nibble_to_ascii(val[(bi*4)+:4]));
$write("\n");
end
endtask
integer wfd_result;
task wait_for_done;
input [256*8:1] op_name;
integer cyc;
begin
cyc = 0;
while (!done_o && cyc < TIMEOUT_CYCLES) begin @(posedge clk); cyc = cyc + 1; end
if (cyc >= TIMEOUT_CYCLES) begin
$display("ERROR: %s timeout after %0d cycles", op_name, TIMEOUT_CYCLES);
wfd_result = 0;
end else begin
$display("INFO: %s done after %0d cycles", op_name, cyc);
wfd_result = 1;
end
end
endtask
initial begin
vec_count = 0;
$readmemh(VECTOR_FILE, input_mem);
$readmemh(EXPECTED_FILE, expected_mem);
begin
integer found_end = 0;
for (idx = 0; idx < MAX_VECTORS; idx = idx + 1) begin
if (!found_end && (input_mem[idx] === 768'hx || input_mem[idx] === 768'hz))
found_end = 1;
else if (!found_end)
vec_count = vec_count + 1;
end
end
if (vec_count == 0) begin
$display("ERROR: No vectors loaded from %s", VECTOR_FILE);
$finish;
end
$display("====================================================");
$display("MLKEM_TOP KeyGen-ONLY KAT TESTBENCH");
$display(" Vectors loaded: %0d", vec_count);
$display("====================================================");
mode <= 2'd0;
i_k <= 3'd2;
valid_i <= 1'b0;
rst_n <= 1'b0;
repeat (5) @(posedge clk);
rst_n <= 1'b1;
@(posedge clk);
force u_dut.chain_kc_ready_o = 1'b1;
force u_dut.ntt_valid_o = 1'b1;
kg_pass = 0; kg_fail = 0;
for (idx = 0; idx < vec_count; idx = idx + 1) begin
reg [255:0] d_val, msg_val, z_val;
reg [EXP_PK_WIDTH-1:0] exp_pk;
reg [EXP_SK_WIDTH-1:0] exp_sk;
d_val = input_mem[idx][767:512];
msg_val = input_mem[idx][511:256];
z_val = input_mem[idx][255:0];
exp_sk = expected_mem[idx][EXP_SS_WIDTH + EXP_CT_WIDTH +: EXP_SK_WIDTH];
exp_pk = expected_mem[idx][EXP_SS_WIDTH + EXP_CT_WIDTH + EXP_SK_WIDTH +: EXP_PK_WIDTH];
$display("--- Vector %0d: KeyGen ---", idx);
print_hex256(d_val, " d ");
// KeyGen
force u_dut.d_reg = d_val;
mode <= 2'b00;
i_k <= 3'd2;
valid_i <= 1'b1;
@(posedge clk);
valid_i <= 1'b0;
wait_for_done("KeyGen");
if (wfd_result) begin
release u_dut.d_reg;
if (pk_valid && sk_valid) begin
if (pk_o[EXP_PK_WIDTH-1:0] == exp_pk) begin
$display(" PASS: pk matches expected");
kg_pass = kg_pass + 1;
end else begin
$display(" FAIL: pk mismatch");
kg_fail = kg_fail + 1;
end
if (sk_o[SK_WIDTH-1:0] == exp_sk[SK_WIDTH-1:0])
$display(" PASS: sk matches expected");
else begin
$display(" FAIL: sk mismatch");
kg_fail = kg_fail + 1;
end
end else begin
$display(" FAIL: pk_valid=%b sk_valid=%b", pk_valid, sk_valid);
kg_fail = kg_fail + 1;
end
end else begin
release u_dut.d_reg;
$display(" FAIL: KeyGen timeout");
kg_fail = kg_fail + 1;
rst_n <= 1'b0; repeat (5) @(posedge clk); rst_n <= 1'b1;
force u_dut.chain_kc_ready_o = 1'b1; force u_dut.ntt_valid_o = 1'b1;
@(posedge clk);
end
repeat (2) @(posedge clk);
// Encaps (run but don't check)
force u_dut.m_reg = msg_val;
mode <= 2'b01;
i_k <= 3'd2;
valid_i <= 1'b1;
@(posedge clk);
valid_i <= 1'b0;
wait_for_done("Encaps");
if (wfd_result) release u_dut.m_reg;
else begin
release u_dut.m_reg;
rst_n <= 1'b0; repeat (5) @(posedge clk); rst_n <= 1'b1;
force u_dut.chain_kc_ready_o = 1'b1; force u_dut.ntt_valid_o = 1'b1;
@(posedge clk);
end
repeat (2) @(posedge clk);
// Decaps (run but don't check)
force u_dut.z_reg = z_val;
mode <= 2'b10;
i_k <= 3'd2;
valid_i <= 1'b1;
@(posedge clk);
valid_i <= 1'b0;
wait_for_done("Decaps");
if (wfd_result) release u_dut.z_reg;
else begin
release u_dut.z_reg;
rst_n <= 1'b0; repeat (5) @(posedge clk); rst_n <= 1'b1;
force u_dut.chain_kc_ready_o = 1'b1; force u_dut.ntt_valid_o = 1'b1;
@(posedge clk);
end
repeat (2) @(posedge clk);
end
release u_dut.chain_kc_ready_o;
release u_dut.ntt_valid_o;
$display("====================================================");
$display("KEYGEN TEST COMPLETE");
$display(" PASS: %0d FAIL: %0d", kg_pass, kg_fail);
$display("====================================================");
$finish;
end
initial begin
#(TIMEOUT_CYCLES * 10 * 100);
$display("FATAL: Global simulation timeout");
$finish;
end
endmodule

132
sync_rtl/kg/TB/xsim_run.tcl Normal file
View File

@@ -0,0 +1,132 @@
# NOTE: On some systems, you may need:
# export LD_PRELOAD=/usr/lib64/libtinfo.so.5
# before running this script.
# xsim_run.tcl - Vivado xsim compilation and simulation for KeyGen-only testbench
#
# Compiles ALL RTL dependencies for mlkem_top plus the KeyGen-only testbench.
# Run from the project root: ~/Dev/mlkem/
#
# Prerequisites:
# source /opt/Xilinx/Vivado/2019.2/settings64.sh
#
# Usage:
# vivado -mode batch -source sync_rtl/kg/TB/xsim_run.tcl
# ================================================================
# Configuration
# ================================================================
set COMMON_DIR sync_rtl/common
set SHA3_DIR sync_rtl/sha3
set SHA3C_DIR sync_rtl/sha3_chain
set RNG_DIR sync_rtl/rng
set NTT_DIR sync_rtl/ntt
set PA_DIR sync_rtl/poly_arith
set PM_DIR sync_rtl/poly_mul
set CBD_DIR sync_rtl/sample_cbd
set SNT_DIR sync_rtl/sample_ntt
set CD_DIR sync_rtl/comp_decomp
set MA_DIR sync_rtl/mod_add
set STOR_DIR sync_rtl/storage
set TOP_DIR sync_rtl/top
set TB_DIR sync_rtl/kg/TB
# ================================================================
# Step 1: Compile common infrastructure
# ================================================================
puts "=== Compiling common infrastructure ==="
xvlog -sv -i . ${COMMON_DIR}/pipeline_reg.v
xvlog -sv -i . ${COMMON_DIR}/skid_buffer.v
# ================================================================
# Step 2: Compile SHA3 / Keccak core
# ================================================================
puts "=== Compiling SHA3/Keccak core ==="
xvlog -sv ${SHA3_DIR}/keccak_round.v
xvlog -sv ${SHA3_DIR}/keccak_core.v
xvlog -sv -i . ${SHA3_DIR}/sha3_top.v
# ================================================================
# Step 3: Compile SHA3 chain (G function)
# ================================================================
puts "=== Compiling SHA3 chain ==="
xvlog -sv -i . ${SHA3C_DIR}/sha3_chain_top_shared.v
# ================================================================
# Step 4: Compile RNG
# ================================================================
puts "=== Compiling RNG ==="
xvlog -sv ${RNG_DIR}/rng_sync.v
# ================================================================
# Step 5: Compile NTT core and dependencies
# ================================================================
puts "=== Compiling NTT core ==="
xvlog -sv ${NTT_DIR}/zeta_rom.v
xvlog -sv ${NTT_DIR}/barrett_mul.v
xvlog -sv ${NTT_DIR}/butterfly_unit.v
xvlog -sv -i . ${NTT_DIR}/ntt_core.v
# ================================================================
# Step 6: Compile polynomial arithmetic
# ================================================================
puts "=== Compiling polynomial arithmetic ==="
xvlog -sv ${PA_DIR}/poly_arith_sync.v
# ================================================================
# Step 7: Compile polynomial multiplication
# ================================================================
puts "=== Compiling polynomial multiplication ==="
xvlog -sv ${PM_DIR}/poly_mul_zeta_rom.v
xvlog -sv ${PM_DIR}/basecase_mul.v
xvlog -sv -i . ${PM_DIR}/poly_mul_sync.v
# ================================================================
# Step 8: Compile sampling modules
# ================================================================
puts "=== Compiling sampling modules ==="
xvlog -sv -i . ${CBD_DIR}/sample_cbd_sync_shared.v
xvlog -sv -i . ${SNT_DIR}/sample_ntt_sync_shared.v
# ================================================================
# Step 9: Compile compression and modular arithmetic
# ================================================================
puts "=== Compiling compression and modular arithmetic ==="
xvlog -sv ${CD_DIR}/comp_decomp_sync.v
xvlog -sv ${MA_DIR}/mod_add_sync.v
# ================================================================
# Step 10: Compile storage (BRAM)
# ================================================================
puts "=== Compiling storage BRAMs ==="
xvlog -sv ${STOR_DIR}/s_bram.v
xvlog -sv ${STOR_DIR}/sd_bram.v
# ================================================================
# Step 11: Compile top-level integration
# ================================================================
puts "=== Compiling top-level integration ==="
xvlog -sv -i . ${TOP_DIR}/keccak_arbiter.v
xvlog -sv -i . ${TOP_DIR}/mlkem_top.v
# ================================================================
# Step 12: Compile testbench
# ================================================================
puts "=== Compiling KeyGen-only testbench ==="
xvlog -sv ${TB_DIR}/tb_kg_xsim.v
# ================================================================
# Step 13: Elaborate snapshot (xelab)
# ================================================================
puts "=== Elaborating snapshot ==="
xelab tb_kg_xsim -s tb_kg_xsim --timescale 1ns/1ps
# ================================================================
# Step 14: Run simulation
# ================================================================
puts ""
puts "=== Running KeyGen-only simulation ==="
xsim tb_kg_xsim -R
puts ""
puts "=== KeyGen simulation complete ==="