// 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). t_hat (byteDecode12) is re-
        // verified at E6 (V uses it); TDEC is deferred to V-prep so e2 can use
        // bank_t during C/N/U. A_hat equals KeyGen golden (K=2 c0). ----
        // ---- E2: verify y[i], e1[i] (bank_se), e2 (bank_t slot_t) vs ml-kem-r.
        if (KP == 2 && casenum == 0) begin
            verify_e1;
            verify_e2;
        end
        $finish;
    end

    // E1 golden: A_hat[i][j] (KeyGen golden, K=2 c0)
    reg [11:0] ga [0:4*256-1];
    // E2 golden: y0,y1,e1_0,e1_1 (bank_se rel slots 0..3), e2 (bank_t slot 0)
    reg [11:0] gy [0:4*256-1];
    reg [11:0] ge2 [0:255];
    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);
            ce = 0;
            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
            if (ce == 0) $display("K=2 CASE 0 PASS (E1): A_hat (1024) == KeyGen golden");
            else         $display("K=2 CASE 0 FAIL (E1): %0d A mismatches", ce);
        end
    endtask
    // y[i],e1[i] live in bank_se at rel slots 0..K-1 (y), K..2K-1 (e1).
    // dbg slot for bank_se = slot_s_rt + rel. K=2: slot_s_rt=4 -> y0=4,y1=5,e1_0=6,e1_1=7.
    // e2 lives in bank_t rel slot 0 -> dbg slot = slot_t_rt = 8.
    task verify_e2;
        begin
            $readmemh("sync_rtl/top/TB/vectors/encgold/ec_k2_c0_y_0.hex",  gy, 0,    255);
            $readmemh("sync_rtl/top/TB/vectors/encgold/ec_k2_c0_y_1.hex",  gy, 256,  511);
            $readmemh("sync_rtl/top/TB/vectors/encgold/ec_k2_c0_e1_0.hex", gy, 512,  767);
            $readmemh("sync_rtl/top/TB/vectors/encgold/ec_k2_c0_e1_1.hex", gy, 768, 1023);
            $readmemh("sync_rtl/top/TB/vectors/encgold/ec_k2_c0_e2.hex",   ge2);
            ce = 0;
            // y0,y1,e1_0,e1_1 at bank_se dbg slots 4..7
            for (slot = 0; slot < 4; slot = slot + 1)
                for (idx = 0; idx < 256; idx = idx + 1) begin
                    dbg_slot_i = (4+slot); dbg_idx_i = idx[7:0];
                    @(posedge clk); @(posedge clk); @(posedge clk);
                    if (dbg_coeff_o !== gy[slot*256+idx]) begin
                        if (ce < 8) $display("  Y[s%0d,%0d] got=%03x exp=%03x", slot, idx, dbg_coeff_o, gy[slot*256+idx]);
                        ce = ce + 1;
                    end
                end
            // e2 at bank_t dbg slot 8
            for (idx = 0; idx < 256; idx = idx + 1) begin
                dbg_slot_i = 8; dbg_idx_i = idx[7:0];
                @(posedge clk); @(posedge clk); @(posedge clk);
                if (dbg_coeff_o !== ge2[idx]) begin
                    if (ce < 12) $display("  E2[%0d] got=%03x exp=%03x", idx, dbg_coeff_o, ge2[idx]);
                    ce = ce + 1;
                end
            end
            if (ce == 0) $display("K=2 CASE 0 PASS (E2): y[0..1],e1[0..1],e2 == ml-kem-r golden");
            else         $display("K=2 CASE 0 FAIL (E2): %0d coeff mismatches", ce);
        end
    endtask
    initial begin #120000000; $display("FAIL: global timeout"); $finish; end
endmodule
