diff --git a/sync_rtl/top/TB/tb_mlkem_kg_2d_xsim.v b/sync_rtl/top/TB/tb_mlkem_kg_2d_xsim.v new file mode 100644 index 0000000..1b00236 --- /dev/null +++ b/sync_rtl/top/TB/tb_mlkem_kg_2d_xsim.v @@ -0,0 +1,57 @@ +// tb_mlkem_kg_2d_xsim.v - Stage 2d: verify forward NTT of s/e in mlkem_top. +// After ST_N, slots S0,S1,E0,E1 must hold shat_0,shat_1,ehat_0,ehat_1. +// Golden: kg_c000_sehat.hex (4 polys x 256 = 1024 lines, mod-q). +`timescale 1ns/1ps +module tb_mlkem_kg_2d_xsim; + reg clk=0, rst_n=0, start_i=0; + reg [255:0] d_i, z_i=0; + wire busy_o, done_o; + reg [3:0] dbg_slot_i=0; reg [7:0] dbg_idx_i=0; wire [11:0] dbg_coeff_o; + wire [255:0] dbg_rho_o, dbg_sigma_o; + + mlkem_top #(.K(2)) dut ( + .clk(clk), .rst_n(rst_n), .d_i(d_i), .z_i(z_i), .start_i(start_i), + .busy_o(busy_o), .done_o(done_o), + .dbg_slot_i(dbg_slot_i), .dbg_idx_i(dbg_idx_i), .dbg_coeff_o(dbg_coeff_o), + .dbg_rho_o(dbg_rho_o), .dbg_sigma_o(dbg_sigma_o) + ); + always #5 clk = ~clk; + + localparam [255:0] D_LIT = 256'h2426f1941779574d3f1b163bd57f7e173e229e630ec7f7073bdf365137c4bb6d; + + reg [11:0] gold [0:1023]; + reg [3:0] slot_of [0:3]; // S0,S1,E0,E1 + integer c, p, idx, errors, gi; + + initial begin + $readmemh("sync_rtl/top/TB/vectors/kg_c000_sehat.hex", gold); + slot_of[0]=4'd4; slot_of[1]=4'd5; slot_of[2]=4'd6; slot_of[3]=4'd7; + + d_i = D_LIT; + rst_n=0; repeat(4) @(posedge clk); rst_n=1; @(posedge clk); + start_i=1; @(posedge clk); start_i=0; + c=0; while(!done_o && c<300000) begin @(posedge clk); c=c+1; end + if(!done_o) begin $display("FAIL: timeout after %0d cyc", c); $finish; end + $display("=== Stage 2d: NTT(s/e) -> shat/ehat === done in %0d cyc", c); + + errors = 0; + for (p = 0; p < 4; p = p + 1) begin + for (idx = 0; idx < 256; idx = idx + 1) begin + dbg_slot_i = slot_of[p]; + dbg_idx_i = idx[7:0]; + @(posedge clk); @(posedge clk); + gi = p*256 + idx; + if (dbg_coeff_o !== gold[gi]) begin + if (errors < 8) + $display(" MISMATCH slot%0d[%0d]: got=%03x exp=%03x", + slot_of[p], idx, dbg_coeff_o, gold[gi]); + errors = errors + 1; + end + end + end + if (errors == 0) $display("ALL TESTS PASSED (1024/1024 shat/ehat coeffs)"); + else $display("TESTS FAILED: %0d mismatches", errors); + $finish; + end + initial begin #10000000; $display("FAIL: global timeout"); $finish; end +endmodule diff --git a/sync_rtl/top/TB/vectors/kg_c000_sehat.hex b/sync_rtl/top/TB/vectors/kg_c000_sehat.hex new file mode 100644 index 0000000..1d1287d --- /dev/null +++ b/sync_rtl/top/TB/vectors/kg_c000_sehat.hex @@ -0,0 +1,1024 @@ +afa +6ad +379 +79a +bd2 +36c +6c8 +455 +40c +6f4 +444 +b88 +66a +0c5 +061 +567 +04d +2e0 +44b +077 +63f +a66 +afb +576 +1bc +bee +192 +a1a +1b5 +c63 +01f +885 +02a +840 +550 +428 +1a1 +c3e +9b3 +50a +7f2 +2e0 +69a +541 +64e +ca4 +820 +4b2 +260 +a61 +92c +7a6 +8c8 +615 +85e +1e1 +01e +b5a +05b +210 +919 +0b0 +77b +198 +920 +b8e +094 +7ab +228 +691 +1dc +5fa +aaf +094 +0b0 +4c0 +1ac +a26 +7ce +3fa +528 +7d0 +2bf +191 +7d0 +477 +1b3 +cf8 +50d +2ae +3ba +0cd +5a0 +78f +273 +bfb +2a0 +bd1 +25b +48c +305 +65f +07a +bad +46b +98c +98c +c6d +9de +2c4 +30d +3c4 +ae5 +32a +68b +c6d +0cb +245 +463 +0d3 +0c5 +29e +8d2 +71c +83d +0f7 +431 +41f +cc2 +404 +500 +67e +699 +bca +466 +a1b +793 +3cc +6ed +0f5 +48a +3fe +09b +752 +7ce +78b +b97 +7f6 +a11 +cb1 +338 +06a +7f2 +0ab +82b +75d +22e +808 +a86 +0fb +13e +1a8 +b8c +0d3 +b5f +908 +572 +9e1 +a56 +1eb +810 +000 +591 +af9 +858 +5aa +73e +1f2 +c00 +25a +3cc +23f +83c +71b +867 +ad6 +798 +b1e +9f2 +0d8 +7b6 +0f1 +12b +4f6 +156 +93c +138 +ae7 +11a +304 +a24 +9ac +721 +793 +a5c +2e8 +59e +281 +10f +08f +3ae +28e +723 +515 +9e5 +ca6 +763 +82a +91f +ad2 +026 +6b7 +223 +241 +1d9 +605 +5bd +227 +0dd +bfe +0e6 +a8e +926 +741 +a88 +2f5 +1df +7f2 +68c +c41 +cf6 +0e7 +29f +a2b +3f7 +0c0 +8af +1d8 +b2c +2e8 +86a +636 +190 +926 +b10 +008 +042 +b06 +aad +07e +3c0 +49b +8d5 +118 +c0a +bb9 +b69 +399 +b52 +11b +72c +c8c +7d9 +59a +5b9 +4c5 +91f +1e9 +5a1 +1ff +14c +786 +8e4 +273 +4c5 +9b1 +7da +7f0 +6bd +9a7 +13b +8d8 +af7 +421 +786 +830 +3fd +3d2 +61b +a92 +76a +3e5 +b99 +91e +b5f +c16 +18d +3d8 +4ed +a31 +104 +35c +78e +054 +270 +54d +678 +b6a +aba +c52 +861 +902 +5b5 +69a +6e5 +332 +542 +9c4 +ab9 +76c +274 +28f +939 +546 +4af +8d7 +09d +135 +253 +714 +439 +cc0 +059 +6e1 +20b +19b +442 +06b +66f +bf8 +c5d +3c9 +6f1 +c0b +286 +c59 +8b9 +1b8 +729 +109 +888 +b4a +215 +6ce +54a +672 +b58 +2d8 +6df +8db +411 +6f1 +0cf +b4f +9cb +c57 +774 +018 +58c +542 +21d +673 +6a1 +400 +0e4 +bf3 +beb +4fb +745 +99a +5c9 +54f +6d1 +9db +770 +9dc +217 +4f1 +19c +cf0 +63a +56d +79e +a47 +6e1 +57b +004 +107 +506 +2f4 +745 +2fa +832 +c69 +260 +0f8 +01c +6a2 +0f7 +c90 +1a6 +922 +93c +113 +1cd +8e5 +402 +042 +0c8 +c37 +3d3 +b6b +a83 +821 +a53 +316 +370 +4e3 +931 +b05 +9c9 +035 +36c +2c4 +4f5 +84b +cb1 +752 +3cc +c9c +36c +28e +afb +79b +7e2 +24f +a78 +8d9 +727 +aff +08b +86b +245 +c9d +8a3 +7fe +280 +c34 +762 +192 +c4a +0e3 +014 +91d +029 +69d +7d5 +9d3 +0bd +656 +965 +15d +ca3 +98b +882 +74e +023 +62a +a00 +5f0 +54c +235 +4ec +479 +1ce +64a +73a +ad9 +133 +98c +662 +ad4 +291 +73e +2ce +ac3 +4af +53f +935 +659 +737 +210 +66a +076 +4ec +759 +9be +37d +cfe +3f7 +c66 +56a +b58 +369 +96a +485 +1ed +3b7 +8cd +a60 +951 +379 +2b1 +ab4 +c54 +703 +c33 +0bc +cca +1df +0e2 +5ee +09b +93e +54a +bb9 +7e0 +8f9 +0b4 +6e9 +176 +9b4 +633 +0ac +133 +0c8 +633 +b48 +98e +244 +a97 +b21 +a6d +8a3 +088 +55d +890 +3a5 +1be +b1a +84a +12c +c89 +3da +195 +b31 +650 +b14 +a16 +2c9 +9b5 +24f +3cc +963 +203 +7fc +4f7 +a6e +390 +824 +a17 +474 +294 +939 +9dd +744 +760 +420 +54b +7a7 +b18 +042 +23a +693 +74d +80f +b97 +910 +aed +c70 +28d +c96 +bd2 +70c +bb7 +673 +a1b +c9e +aab +414 +217 +496 +a8d +5dd +597 +271 +cad +02a +055 +815 +176 +9c0 +6b5 +986 +4ea +c05 +593 +75d +b97 +246 +3f3 +4d4 +75d +64a +ac2 +013 +8b8 +283 +1c8 +565 +096 +0e9 +53e +a83 +a8b +573 +826 +95d +a33 +7a4 +ba5 +508 +b3e +4b8 +2a8 +282 +9fd +95a +8f1 +1b0 +51a +a60 +535 +6f5 +6c6 +8a2 +ab9 +048 +6e9 +2d7 +b10 +5c6 +747 +3b3 +1b4 +a30 +739 +304 +b0d +2d6 +0d9 +0d9 +9eb +273 +621 +450 +7d8 +032 +7e0 +b47 +43e +816 +905 +86b +201 +9d4 +94a +57f +746 +943 +8ae +2d2 +656 +82a +cd9 +bd3 +902 +7da +13a +7d0 +a9c +b99 +32e +423 +4bc +427 +91f +c8f +932 +a92 +78e +4cd +964 +344 +5f4 +8bd +111 +247 +a7d +194 +4a2 +b59 +967 +622 +b2f +8d8 +796 +888 +83a +46d +5fc +027 +a6d +2a3 +335 +316 +09d +a43 +b94 +156 +7d5 +601 +5b5 +8e6 +484 +4e5 +ca1 +760 +c98 +395 +12d +6e7 +cfd +04b +267 +6b2 +57e +942 +216 +7d5 +c6e +bba +ad5 +051 +3c2 +1b0 +0f7 +98e +12a +7cb +b68 +380 +792 +469 +b9b +250 +ac6 +cc2 +9ec +929 +110 +28b +cd5 +2a0 +585 +ccf +bc0 +40f +310 +106 +083 +4cd +684 +41f +8d4 +771 +748 +72f +48c +8aa +01c +9d8 +395 +5d3 +866 +00c +450 +2bf +902 +680 +c6a +b84 +131 +0ac +08d +bc9 +9a2 +b97 +2b3 +659 +385 +624 +878 +993 +5f0 +1ed +760 +cf0 +2e9 +72f +955 +0bc +ab5 +baa +bb4 +496 +016 +7f3 +5dc +5fd +bef +a74 +293 +18b +8a3 +451 +08c +9e4 +703 +5c1 +af5 +8de +94b +5d0 +bcf +2cb +436 +89a +895 +782 +540 +38d +421 +0da +7b9 +a1e +765 +b20 +182 +49e +b70 +051 +028 +29d +2f6 +288 +7ca +8ff +4af +2e1 +031 +396 +19f +03a +bcb +641 +166 +c26 +ce5 +c04 +224 +923 +68e +618 +12a +956 +167 +657 +7e0 +05b +506 +67d +c20 +616 +21c +b11 +04e +b97 +397 +776 +2d5 +106 +c52 +84c +7bf +388 +1a5 +69d +67e +32e +2c1 +a83 +b38 +749 +b65 +046 +227 +9fd +b2a +31f +42f +17b +01c +61e +a89 +8e9 +2d9 +65d +912 +5c7 +580 +ac8 +c87 +c3e +8bf +2f3 +406 +601 +632 +ca6 +2cf +1f4 +9d6 +80d +4a3 +208 +bdf +add +9be +1c0 +3cc +ba7 +08d +36b +23f +67e +302 +549 +2e8 +976 +5a0 +6dd +013 +589 +0c9 +894 +92e +882 +8ca +0c0 +a62 +225 +a61 +835 +617 +7d4 +17a +b00 +7b8 +b26 +368 +1a1 +028 +31e +639 +7d4 +af5 +309 +981 +c31 +8d6 +9bc +126 +cab diff --git a/sync_rtl/top/mlkem_top.v b/sync_rtl/top/mlkem_top.v index 6bf5a13..a25846a 100644 --- a/sync_rtl/top/mlkem_top.v +++ b/sync_rtl/top/mlkem_top.v @@ -72,6 +72,7 @@ module mlkem_top #( localparam ST_G = 4'd1; // run G(d||K), capture rho/sigma localparam ST_A = 4'd2; // generate A_hat[i][j] via SampleNTT localparam ST_C = 4'd3; // generate s[i],e[i] via CBD + localparam ST_N = 4'd4; // forward NTT of s[i],e[i] in place localparam ST_DONE = 4'd15; reg [3:0] st, st_next; @@ -167,13 +168,41 @@ module mlkem_top #( // signed (two's complement) -> [0,Q): add Q when negative wire [11:0] cbd_modq = cbd_coeff[11] ? (cbd_coeff + 12'(Q)) : cbd_coeff; + // ---- ntt_core: forward NTT (mode=0, no scaling) of s[i],e[i] in place ---- + // N-stage bookkeeping: process slots S0,S1,E0,E1 (= SLOT_S0 + n_slot). + reg [2:0] n_slot; // 0..2K (4 polys) + reg [8:0] n_ridx; // load read index 0..256 + reg [7:0] n_widx; // output write index 0..255 + reg n_valid; // feeding coeffs to ntt_core + reg n_pending; // waiting for ntt_core IDLE to start next slot + wire [3:0] n_slot_addr = SLOT_S0 + {1'b0, n_slot}; + + wire ntt_ready; + wire [11:0] ntt_coeff; + wire ntt_vo; + wire ntt_done; + wire [11:0] ntt_in = polymem[n_slot_addr*256 + n_ridx[7:0]]; + + ntt_core u_ntt ( + .clk(clk), .rst_n(rst_n), + .coeff_in(ntt_in), + .valid_i(n_valid), + .ready_o(ntt_ready), + .mode(1'b0), // forward NTT, no scaling + .coeff_out(ntt_coeff), + .valid_o(ntt_vo), + .ready_i(1'b1), // always accept output + .done_o(ntt_done) + ); + always @(*) begin st_next = st; case (st) ST_IDLE: if (start_i) st_next = ST_G; ST_G: if (sha3_vo) st_next = ST_A; ST_A: if (a_pair >= K*K) st_next = ST_C; - ST_C: if (c_poly >= 2*K) st_next = ST_DONE; + ST_C: if (c_poly >= 2*K) st_next = ST_N; + ST_N: if (n_slot >= 2*K) st_next = ST_DONE; ST_DONE: st_next = ST_IDLE; default: st_next = ST_IDLE; endcase @@ -196,6 +225,11 @@ module mlkem_top #( c_poly <= 3'd0; c_widx <= 8'd0; c_busy <= 1'b0; + n_slot <= 3'd0; + n_ridx <= 9'd0; + n_widx <= 8'd0; + n_valid <= 1'b0; + n_pending <= 1'b0; end else begin st <= st_next; @@ -271,6 +305,52 @@ module mlkem_top #( end end end + + // Arm N stage when C finishes: start NTT on slot S0 + if (st == ST_C && st_next == ST_N) begin + n_slot <= 3'd0; + n_ridx <= 9'd0; + n_widx <= 8'd0; + n_valid <= 1'b1; // begin loading first poly + n_pending <= 1'b0; + end + + // ---- ST_N: forward NTT each of S0,S1,E0,E1 in place ---- + if (st == ST_N) begin + // LOAD phase: stream 256 coeffs into ntt_core + if (n_valid && ntt_ready) begin + if (n_ridx == 9'd255) begin + n_valid <= 1'b0; // last coeff presented this cycle + n_ridx <= 9'd0; + end else begin + n_ridx <= n_ridx + 9'd1; + end + end + + // OUTPUT phase: collect 256 results, write back to same slot + if (ntt_vo) begin + polymem[n_slot_addr*256 + n_widx] <= ntt_coeff; + n_widx <= n_widx + 8'd1; // wraps 255->0 after last + end + + // Slot complete when ntt_core returns to DONE + if (ntt_done) begin + if (n_slot + 3'd1 < 2*K) begin + n_slot <= n_slot + 3'd1; + n_widx <= 8'd0; + n_pending <= 1'b1; // wait one cycle for core IDLE + end else begin + n_slot <= n_slot + 3'd1; // == 2K -> ST_DONE + end + end + + // Kick next slot's load once core is back IDLE + if (n_pending && ntt_ready && !ntt_done) begin + n_valid <= 1'b1; + n_ridx <= 9'd0; + n_pending <= 1'b0; + end + end end end