ST_ENC_LOAD: stream rho (32B) from ek_bram[384k..] into rho_r (read-ahead, 1-cyc bram latency). ST_ENC_A: regenerate A_hat via SampleNTT into bank_a (reuses ST_A datapath, gated on st==ST_A||ST_ENC_A). ST_ENC_TDEC: byteDecode12 ek[i*384..] -> t_hat[i] into bank_t (5-cycle micro-phase per 3-byte/2-coeff triple; bt write port muxed with ST_M). Verified (K=2 c0) A_hat (1024 coeffs) + t_hat (512) == KeyGen golden via dbg_coeff_o; E0 ss==KAT.ss still passes all K/cases (no timeout).
141 lines
6.8 KiB
Verilog
141 lines
6.8 KiB
Verilog
// tb_mlkem_enc_katK_xsim.v - ML-KEM Encaps vs NIST KAT, parametric K (KP) + CASE.
|
|
// E0 stage: verify H(ek), G(m||H(ek)) -> (ss=K, r). Preloads ek into ek_bram,
|
|
// pulses start with op=1, waits for done, checks ss == KAT.ss and dumps H(ek)/r.
|
|
//
|
|
// xelab -generic_top KP=2|3|4 ; xsim -testplusarg CASE=n
|
|
// ek/m/ct/ss vectors: sync_rtl/top/TB/vectors/enc_k{K}_c{N}_{ek,m,ct,ss}.hex
|
|
// (per-byte hex, byte 0 first).
|
|
`timescale 1ns/1ps
|
|
module tb_mlkem_enc_katK_xsim;
|
|
parameter KP = 2;
|
|
localparam EKB = 384*KP + 32; // ek (=pk) bytes
|
|
localparam CTB = (KP==4) ? 1568 : (32*(10*KP+4)); // ct bytes: K2 768,K3 1088,K4 1568
|
|
|
|
reg clk=0, rst_n=0, start_i=0;
|
|
reg [2:0] k_i;
|
|
reg [255:0] d_i, z_i, m_i;
|
|
wire busy_o, done_o;
|
|
// ek preload port
|
|
reg ek_in_we=0; reg [10:0] ek_in_addr=0; reg [7:0] ek_in_byte=0;
|
|
wire [255:0] ss_o;
|
|
reg [10:0] dbg_ct_idx_i=0; wire [7:0] dbg_ct_o;
|
|
reg [3:0] dbg_slot_i=0; reg [7:0] dbg_idx_i=0; wire [11:0] dbg_coeff_o;
|
|
reg dbg_byte_sel_i=0; reg [10:0] dbg_byte_idx_i=0; wire [7:0] dbg_byte_o;
|
|
reg [11:0] dbg_dk_idx_i=0; wire [7:0] dbg_dk_o;
|
|
wire [255:0] dbg_rho_o, dbg_sigma_o, dbg_r_o, dbg_hek_o;
|
|
|
|
mlkem_top dut (
|
|
.clk(clk), .rst_n(rst_n), .k_i(k_i), .op_i(1'b1),
|
|
.d_i(d_i), .z_i(z_i), .msg_i(m_i), .start_i(start_i),
|
|
.busy_o(busy_o), .done_o(done_o),
|
|
.ek_in_we(ek_in_we), .ek_in_addr(ek_in_addr), .ek_in_byte(ek_in_byte),
|
|
.ss_o(ss_o), .dbg_ct_idx_i(dbg_ct_idx_i), .dbg_ct_o(dbg_ct_o),
|
|
.dbg_slot_i(dbg_slot_i), .dbg_idx_i(dbg_idx_i), .dbg_coeff_o(dbg_coeff_o),
|
|
.dbg_byte_sel_i(dbg_byte_sel_i), .dbg_byte_idx_i(dbg_byte_idx_i), .dbg_byte_o(dbg_byte_o),
|
|
.dbg_dk_idx_i(dbg_dk_idx_i), .dbg_dk_o(dbg_dk_o),
|
|
.dbg_rho_o(dbg_rho_o), .dbg_sigma_o(dbg_sigma_o),
|
|
.dbg_r_o(dbg_r_o), .dbg_hek_o(dbg_hek_o)
|
|
);
|
|
always #5 clk = ~clk;
|
|
|
|
reg [7:0] ek_b [0:EKB-1];
|
|
reg [7:0] m_b [0:31];
|
|
reg [7:0] ss_b [0:31];
|
|
integer c, i, errors, casenum, j;
|
|
reg [8*80-1:0] tag, ekfile, mfile, ssfile;
|
|
|
|
initial begin
|
|
if (!$value$plusargs("CASE=%d", casenum)) casenum = 0;
|
|
$sformat(tag, "k%0d", KP);
|
|
$sformat(ekfile, "sync_rtl/top/TB/vectors/enc_%0s_c%0d_ek.hex", tag, casenum);
|
|
$sformat(mfile, "sync_rtl/top/TB/vectors/enc_%0s_c%0d_m.hex", tag, casenum);
|
|
$sformat(ssfile, "sync_rtl/top/TB/vectors/enc_%0s_c%0d_ss.hex", tag, casenum);
|
|
$readmemh(ekfile, ek_b);
|
|
$readmemh(mfile, m_b);
|
|
$readmemh(ssfile, ss_b);
|
|
|
|
// build m_i: byte i in m_i[8*i +: 8]
|
|
m_i = 256'd0;
|
|
for (j = 0; j < 32; j = j + 1) m_i[8*j +: 8] = m_b[j];
|
|
k_i = KP[2:0];
|
|
|
|
$display("=== ML-KEM K=%0d Encaps KAT case %0d (E0) ===", KP, casenum);
|
|
$write(" m = "); for (j=0;j<32;j=j+1) $write("%02x", m_b[j]); $write("\n");
|
|
|
|
rst_n=0; repeat(4) @(posedge clk); rst_n=1; @(posedge clk);
|
|
|
|
// ---- preload ek into ek_bram (1 byte/cycle) ----
|
|
for (i = 0; i < EKB; i = i + 1) begin
|
|
ek_in_we = 1'b1; ek_in_addr = i[10:0]; ek_in_byte = ek_b[i];
|
|
@(posedge clk);
|
|
end
|
|
ek_in_we = 1'b0; @(posedge clk);
|
|
|
|
// ---- run Encaps ----
|
|
start_i=1; @(posedge clk); start_i=0;
|
|
c=0; while(!done_o && c<2000000) begin @(posedge clk); c=c+1; end
|
|
if(!done_o) begin $display("FAIL K=%0d case %0d: timeout", KP, casenum); $finish; end
|
|
$display("=== Encaps E0 done in %0d cyc ===", c);
|
|
|
|
$write(" H(ek) = "); for (j=0;j<32;j=j+1) $write("%02x", dbg_hek_o[8*j +: 8]); $write("\n");
|
|
$write(" r = "); for (j=0;j<32;j=j+1) $write("%02x", dbg_r_o[8*j +: 8]); $write("\n");
|
|
$write(" ss = "); for (j=0;j<32;j=j+1) $write("%02x", ss_o[8*j +: 8]); $write("\n");
|
|
|
|
// ---- check ss == KAT.ss ----
|
|
errors = 0;
|
|
for (j = 0; j < 32; j = j + 1)
|
|
if (ss_o[8*j +: 8] !== ss_b[j]) begin
|
|
if (errors < 8) $display(" SS[%0d] got=%02x exp=%02x", j, ss_o[8*j +: 8], ss_b[j]);
|
|
errors = errors + 1;
|
|
end
|
|
|
|
if (errors == 0) $display("K=%0d CASE %0d PASS (E0): ss == KAT.ss", KP, casenum);
|
|
else $display("K=%0d CASE %0d FAIL (E0): %0d ss mismatches", KP, casenum, errors);
|
|
|
|
// ---- E1: verify A_hat (slots 0..K^2-1) and t_hat (slots slot_t..) ----
|
|
// The KAT case-0 Encaps reuses ek=pk from KeyGen case-0, so regenerated
|
|
// A_hat and decoded t_hat equal KeyGen's golden vectors (K=2 c0 only).
|
|
if (KP == 2 && casenum == 0) verify_e1;
|
|
$finish;
|
|
end
|
|
|
|
// golden coeff arrays (K=2 c0): A_hat[i][j] and t_hat[i], 256 coeffs each
|
|
reg [11:0] ga [0:4*256-1]; // A00,A01,A10,A11 concatenated (slot*256+idx)
|
|
reg [11:0] gt [0:2*256-1]; // t_hat0, t_hat1
|
|
integer ce, slot, idx;
|
|
task verify_e1;
|
|
begin
|
|
$readmemh("test_framework/modules/mlkem_keygen/golden/c000_Ahat_0_0.hex", ga, 0, 255);
|
|
$readmemh("test_framework/modules/mlkem_keygen/golden/c000_Ahat_0_1.hex", ga, 256, 511);
|
|
$readmemh("test_framework/modules/mlkem_keygen/golden/c000_Ahat_1_0.hex", ga, 512, 767);
|
|
$readmemh("test_framework/modules/mlkem_keygen/golden/c000_Ahat_1_1.hex", ga, 768, 1023);
|
|
$readmemh("test_framework/modules/mlkem_keygen/golden/c000_that_0.hex", gt, 0, 255);
|
|
$readmemh("test_framework/modules/mlkem_keygen/golden/c000_that_1.hex", gt, 256, 511);
|
|
ce = 0;
|
|
// A_hat: slots 0..3 (i*k+j)
|
|
for (slot = 0; slot < 4; slot = slot + 1)
|
|
for (idx = 0; idx < 256; idx = idx + 1) begin
|
|
dbg_slot_i = slot[3:0]; dbg_idx_i = idx[7:0];
|
|
@(posedge clk); @(posedge clk); @(posedge clk);
|
|
if (dbg_coeff_o !== ga[slot*256+idx]) begin
|
|
if (ce < 8) $display(" A[s%0d,%0d] got=%03x exp=%03x", slot, idx, dbg_coeff_o, ga[slot*256+idx]);
|
|
ce = ce + 1;
|
|
end
|
|
end
|
|
// t_hat: slots slot_t_rt(=8) .. +1
|
|
for (slot = 0; slot < 2; slot = slot + 1)
|
|
for (idx = 0; idx < 256; idx = idx + 1) begin
|
|
dbg_slot_i = (8+slot); dbg_idx_i = idx[7:0];
|
|
@(posedge clk); @(posedge clk); @(posedge clk);
|
|
if (dbg_coeff_o !== gt[slot*256+idx]) begin
|
|
if (ce < 12) $display(" T[%0d,%0d] got=%03x exp=%03x", slot, idx, dbg_coeff_o, gt[slot*256+idx]);
|
|
ce = ce + 1;
|
|
end
|
|
end
|
|
if (ce == 0) $display("K=2 CASE 0 PASS (E1): A_hat (1024) + t_hat (512) == KeyGen golden");
|
|
else $display("K=2 CASE 0 FAIL (E1): %0d coeff mismatches", ce);
|
|
end
|
|
endtask
|
|
initial begin #120000000; $display("FAIL: global timeout"); $finish; end
|
|
endmodule
|