From 971ce97d5078a9a883cac8d05b422034e0fc76be Mon Sep 17 00:00:00 2001 From: FallenSigh Date: Mon, 29 Jun 2026 22:42:49 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E5=88=A0=E9=99=A4=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=96=87=E4=BB=B6=E4=B8=8E=20ml-kem-r=20?= =?UTF-8?q?=E7=97=95=E8=BF=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除 top/TB 下已被 katK 参数化 TB 取代的增量开发 TB: tb_mlkem_kg_{2a,2c,2d,2e,2f,kat,katN}_xsim.v - 删除依赖外部 ml-kem-r Rust 参考的向量生成 / 交叉校验脚本: gen_vectors.py / gen_encaps_vectors.py / gen_decaps_vectors.py / xcheck_mlkemr.py(KAT/golden 向量已提交,无需运行期依赖) - 清理源码与文档注释中的 ml-kem-r / Rust 字样 保留的 5 个 TB(kg/enc/dec katK + hello_world + two_inst)即 run_tb.sh / xsim_run.tcl 实际引用的全部。KeyGen KAT 与 hello_world 烟囱测试通过。 --- README.md | 4 +- sync_rtl/top/TB/gen_decaps_vectors.py | 80 ---------- sync_rtl/top/TB/gen_encaps_vectors.py | 76 --------- sync_rtl/top/TB/gen_vectors.py | 165 -------------------- sync_rtl/top/TB/tb_mlkem_enc_katK_xsim.v | 14 +- sync_rtl/top/TB/tb_mlkem_hello_world_xsim.v | 2 +- sync_rtl/top/TB/tb_mlkem_kg_2a_xsim.v | 45 ------ sync_rtl/top/TB/tb_mlkem_kg_2c_xsim.v | 60 ------- sync_rtl/top/TB/tb_mlkem_kg_2d_xsim.v | 57 ------- sync_rtl/top/TB/tb_mlkem_kg_2e_xsim.v | 57 ------- sync_rtl/top/TB/tb_mlkem_kg_2f_xsim.v | 66 -------- sync_rtl/top/TB/tb_mlkem_kg_katN_xsim.v | 72 --------- sync_rtl/top/TB/tb_mlkem_kg_kat_xsim.v | 68 -------- sync_rtl/top/TB/xcheck_mlkemr.py | 118 -------------- sync_rtl/top/mlkem_top.v | 2 +- 15 files changed, 11 insertions(+), 875 deletions(-) delete mode 100644 sync_rtl/top/TB/gen_decaps_vectors.py delete mode 100644 sync_rtl/top/TB/gen_encaps_vectors.py delete mode 100644 sync_rtl/top/TB/gen_vectors.py delete mode 100644 sync_rtl/top/TB/tb_mlkem_kg_2a_xsim.v delete mode 100644 sync_rtl/top/TB/tb_mlkem_kg_2c_xsim.v delete mode 100644 sync_rtl/top/TB/tb_mlkem_kg_2d_xsim.v delete mode 100644 sync_rtl/top/TB/tb_mlkem_kg_2e_xsim.v delete mode 100644 sync_rtl/top/TB/tb_mlkem_kg_2f_xsim.v delete mode 100644 sync_rtl/top/TB/tb_mlkem_kg_katN_xsim.v delete mode 100644 sync_rtl/top/TB/tb_mlkem_kg_kat_xsim.v delete mode 100755 sync_rtl/top/TB/xcheck_mlkemr.py diff --git a/README.md b/README.md index 8e4019e..9bf49e7 100644 --- a/README.md +++ b/README.md @@ -203,7 +203,7 @@ c' = K-PKE.Encrypt(ek_pke, m', r') ←── 复用整条 Encaps 流水 | `tb_mlkem_kg_katK_xsim.v` | KeyGen | 逐字节核对 `ek==pk`、`dk==sk` | | `tb_mlkem_enc_katK_xsim.v` | Encaps | 核对 `ss`、`c` | | `tb_mlkem_dec_katK_xsim.v` | Decaps | 核对 D0..D7 各阶段;正常密文 `ss==KAT.ss`、损坏密文 `ss==KAT.ss_n` | -| `tb_mlkem_hello_world_xsim.v` | 全流程(单实例) | 复现 `hello_world.rs`:KeyGen→Encaps→XOR→Decaps→XOR | +| `tb_mlkem_hello_world_xsim.v` | 全流程(单实例) | 端到端演示:KeyGen→Encaps→XOR→Decaps→XOR | | `tb_mlkem_two_inst_xsim.v` | 全流程(双实例) | 实例 A 做 KeyGen+Encaps,实例 B 做 Decaps | 每个参数化 TB 通过 `xelab -generic_top KP=2|3|4` 选安全等级(驱动到运行时 `k_i`),`+CASE=n` 选用例号。 @@ -222,7 +222,7 @@ c' = K-PKE.Encrypt(ek_pke, m', r') ←── 复用整条 Encaps 流水 ./run_tb.sh hello two # hello_world 端到端(双实例:genenc + dec) ``` -`hello_world` 硬件输出与 Rust 参考逐字节一致:`shared_key=ced0c031a4bee34a...`、`encrypted=a6b5ac5dcb9e9425b9e3b8`、`decrypted="hello world"`。 +`hello_world` 硬件端到端输出:`shared_key=ced0c031a4bee34a...`、`encrypted=a6b5ac5dcb9e9425b9e3b8`、`decrypted="hello world"`,密钥匹配、消息正确还原。 ### 验证注意事项 diff --git a/sync_rtl/top/TB/gen_decaps_vectors.py b/sync_rtl/top/TB/gen_decaps_vectors.py deleted file mode 100644 index c3d7a74..0000000 --- a/sync_rtl/top/TB/gen_decaps_vectors.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env python3 -"""gen_decaps_vectors.py - Emit per-byte Decaps KAT vectors for the XSIM TB. - -For ML-KEM-512/768/1024, parse the NIST .rsp and write, per case: - dec_k{K}_c{N}_dk.hex : dk (=sk) bytes, one hex byte per line, byte 0 first - dec_k{K}_c{N}_ct.hex : ct bytes (valid ciphertext), one per line, byte 0 first - dec_k{K}_c{N}_ss.hex : ss bytes (shared secret for valid ct), byte 0 first - dec_k{K}_c{N}_ctn.hex : ct_n bytes (corrupted ct -> implicit reject) - dec_k{K}_c{N}_ssn.hex : ss_n bytes (shared secret K-bar for the reject path) - -The TB streams dk into the design via dk_in_* (routed to dk_pke/ek_pke/h/z by -region) and ct into c_in_*, runs Decaps, and checks ss: - valid ct -> ss == ss (c'==c, K' kept) - ct_n -> ss == ss_n (c'!=c, K-bar via J(z||c_n)) - -Run: python3 gen_decaps_vectors.py [num_cases] -""" - -import os -import re -import sys - -ML_KEM_R = os.environ.get("ML_KEM_R", os.path.expanduser("~/Dev/ml-kem-r")) -OUT_DIR = os.path.join(os.path.dirname(__file__), "vectors") -KATS = {2: "kat_MLKEM_512.rsp", 3: "kat_MLKEM_768.rsp", 4: "kat_MLKEM_1024.rsp"} - - -def parse_kat(path, n): - """Return list of dicts {count, sk, ct, ss, ct_n, ss_n} (hex) for first n.""" - vecs, cur = [], {} - with open(path) as f: - for line in f: - line = line.strip() - m = re.match(r"^count\s*=\s*(\d+)$", line) - if m: - if cur: - vecs.append(cur) - if len(vecs) >= n: - break - cur = {"count": int(m.group(1))} - continue - # exact-key match: 'ct =' vs 'ct_n =', 'ss =' vs 'ss_n =' - m = re.match(r"^(sk|ct|ss|ct_n|ss_n)\s*=\s*([0-9a-fA-F]+)$", line) - if m and cur: - cur[m.group(1)] = m.group(2).lower() - if cur and len(vecs) < n: - vecs.append(cur) - return vecs - - -def write_bytes(path, hexstr): - """Write hex string as one byte per line (byte 0 = first 2 hex chars).""" - with open(path, "w") as f: - for i in range(0, len(hexstr), 2): - f.write(hexstr[i:i + 2] + "\n") - - -def main(): - ncases = int(sys.argv[1]) if len(sys.argv) > 1 else 3 - os.makedirs(OUT_DIR, exist_ok=True) - for k, fname in KATS.items(): - path = os.path.join(ML_KEM_R, "test_data", fname) - if not os.path.exists(path): - print(f"skip K={k}: {path} not found", file=sys.stderr) - continue - vecs = parse_kat(path, ncases) - for v in vecs: - n = v["count"] - base = os.path.join(OUT_DIR, f"dec_k{k}_c{n}") - write_bytes(f"{base}_dk.hex", v["sk"]) - write_bytes(f"{base}_ct.hex", v["ct"]) - write_bytes(f"{base}_ss.hex", v["ss"]) - write_bytes(f"{base}_ctn.hex", v["ct_n"]) - write_bytes(f"{base}_ssn.hex", v["ss_n"]) - print(f"K={k}: wrote {len(vecs)} cases " - f"(dk={len(vecs[0]['sk'])//2}B ct={len(vecs[0]['ct'])//2}B)") - - -if __name__ == "__main__": - main() diff --git a/sync_rtl/top/TB/gen_encaps_vectors.py b/sync_rtl/top/TB/gen_encaps_vectors.py deleted file mode 100644 index f596084..0000000 --- a/sync_rtl/top/TB/gen_encaps_vectors.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python3 -"""gen_encaps_vectors.py - Emit per-byte Encaps KAT vectors for the XSIM TB. - -For ML-KEM-512/768/1024, parse the NIST .rsp and write, per case: - enc_k{K}_c{N}_ek.hex : ek (=pk) bytes, one hex byte per line, byte 0 first - enc_k{K}_c{N}_m.hex : msg bytes, one per line, byte 0 first - enc_k{K}_c{N}_ct.hex : ct bytes, one per line, byte 0 first (golden) - enc_k{K}_c{N}_ss.hex : ss bytes, one per line, byte 0 first (golden) - -Per-byte layout removes all endianness ambiguity: the TB preloads ek_bram with -addr i = line i, builds m_i[8*i +: 8] = m byte i, and compares ct/ss byte-wise. - -Run: python3 gen_encaps_vectors.py [num_cases] -""" - -import os -import re -import sys - -ML_KEM_R = os.environ.get("ML_KEM_R", os.path.expanduser("~/Dev/ml-kem-r")) -OUT_DIR = os.path.join(os.path.dirname(__file__), "vectors") -KATS = {2: "kat_MLKEM_512.rsp", 3: "kat_MLKEM_768.rsp", 4: "kat_MLKEM_1024.rsp"} - - -def parse_kat(path, n): - """Return list of dicts {count, msg, pk, ct, ss} (hex strings) for first n.""" - vecs, cur = [], {} - with open(path) as f: - for line in f: - line = line.strip() - m = re.match(r"^count\s*=\s*(\d+)$", line) - if m: - if cur: - vecs.append(cur) - if len(vecs) >= n: - break - cur = {"count": int(m.group(1))} - continue - # exact-key match so "ct =" doesn't catch "ct_n =" - m = re.match(r"^(msg|pk|ct|ss)\s*=\s*([0-9a-fA-F]+)$", line) - if m and cur: - cur[m.group(1)] = m.group(2).lower() - if cur and len(vecs) < n: - vecs.append(cur) - return vecs - - -def write_bytes(path, hexstr): - """Write hex string as one byte per line (byte 0 = first 2 hex chars).""" - with open(path, "w") as f: - for i in range(0, len(hexstr), 2): - f.write(hexstr[i:i + 2] + "\n") - - -def main(): - ncases = int(sys.argv[1]) if len(sys.argv) > 1 else 3 - os.makedirs(OUT_DIR, exist_ok=True) - for k, fname in KATS.items(): - path = os.path.join(ML_KEM_R, "test_data", fname) - if not os.path.exists(path): - print(f"skip K={k}: {path} not found", file=sys.stderr) - continue - vecs = parse_kat(path, ncases) - for v in vecs: - n = v["count"] - base = os.path.join(OUT_DIR, f"enc_k{k}_c{n}") - write_bytes(f"{base}_ek.hex", v["pk"]) - write_bytes(f"{base}_m.hex", v["msg"]) - write_bytes(f"{base}_ct.hex", v["ct"]) - write_bytes(f"{base}_ss.hex", v["ss"]) - print(f"K={k}: wrote {len(vecs)} cases " - f"(ek={len(vecs[0]['pk'])//2}B ct={len(vecs[0]['ct'])//2}B)") - - -if __name__ == "__main__": - main() diff --git a/sync_rtl/top/TB/gen_vectors.py b/sync_rtl/top/TB/gen_vectors.py deleted file mode 100644 index 41b16e5..0000000 --- a/sync_rtl/top/TB/gen_vectors.py +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/env python3 -"""gen_vectors.py - Parse FIPS 203 KAT test vectors for ML-KEM-512. - -Reads kat_MLKEM_512.rsp and generates hex vector files for the -mlkem_top XSIM testbench. - -Output files (written to vectors/ subdirectory): - mlkem_top_input.hex - d (256b) + msg (256b) + z (256b) per line - mlkem_top_expected.hex - pk (6400b) + sk (13056b) + ct (6144b) + ss (256b) - -KAT file format (per test vector): - count = N - z = <64 hex chars> → 32 bytes, implicit rejection seed - d = <64 hex chars> → 32 bytes, KeyGen randomness - msg = <64 hex chars> → 32 bytes, Encaps message - seed = <128 hex chars> → DRBG seed (ignored) - pk = <1600 hex chars> → 800 bytes, public key - sk = <3264 hex chars> → 1632 bytes, secret key - ct_n = → invalid ciphertext (ignored) - ss_n = → shared secret from invalid ct (ignored) - ct = <1536 hex chars> → 768 bytes, ciphertext - ss = <64 hex chars> → 32 bytes, shared secret -""" - -import os -import re -import sys - -KAT_PATH = "/home/fallensigh/Dev/ml-kem-r/test_data/kat_MLKEM_512.rsp" -OUT_DIR = os.path.join(os.path.dirname(__file__), "vectors") -NUM_VECTORS = 5 - - -def parse_kat(filepath: str, num_vectors: int = NUM_VECTORS) -> list[dict]: - """Parse KAT .rsp file and return list of test vector dicts.""" - vectors: list[dict] = [] - current: dict = {} - - with open(filepath, "r") as f: - for line in f: - line = line.strip() - - # New vector starts with "count = N" - m = re.match(r"^count\s*=\s*(\d+)$", line) - if m: - if current and "count" in current: - vectors.append(current) - current = {} - idx = int(m.group(1)) - if idx >= num_vectors: - break - current = {"count": idx} - continue - - # Parse key = value lines - m = re.match(r"^(\w+)\s*=\s*([0-9a-fA-F]+)$", line) - if m and current: - key = m.group(1) - value = m.group(2).lower() - current[key] = value - continue - - # Add last vector if present - if current and "count" in current: - vectors.append(current) - - # Validate each vector has required fields - required = {"d", "z", "msg", "pk", "sk", "ct", "ss"} - for v in vectors: - missing = required - set(v.keys()) - if missing: - print(f"WARNING: count={v['count']} missing fields: {missing}", - file=sys.stderr) - - return vectors - - -def verify_lengths(vectors: list[dict]) -> bool: - """Verify hex field lengths match FIPS 203 expected sizes.""" - expected = { - "d": 64, # 32 bytes - "z": 64, # 32 bytes - "msg": 64, # 32 bytes - "pk": 1600, # 800 bytes - "sk": 3264, # 1632 bytes - "ct": 1536, # 768 bytes - "ss": 64, # 32 bytes - } - ok = True - for v in vectors: - for field, elen in expected.items(): - if field not in v: - continue - actual = len(v[field]) - if actual != elen: - print(f"WARNING: count={v['count']} {field}: " - f"expected {elen} hex chars, got {actual}", - file=sys.stderr) - ok = False - return ok - - -def write_input_hex(vectors: list[dict], out_dir: str) -> str: - """Write input vectors: d || msg || z (768 bits = 192 hex chars each).""" - os.makedirs(out_dir, exist_ok=True) - out_path = os.path.join(out_dir, "mlkem_top_input.hex") - with open(out_path, "w") as f: - for v in vectors: - d = v.get("d", "0" * 64) - msg = v.get("msg", "0" * 64) - z = v.get("z", "0" * 64) - # Concatenate: [d:255:0][msg:255:0][z:255:0] - f.write(f"{d}{msg}{z}\n") - print(f"Wrote {len(vectors)} input vectors to {out_path}") - return out_path - - -def write_expected_hex(vectors: list[dict], out_dir: str) -> str: - """Write expected output vectors: pk || sk || ct || ss. - - pk: 800 bytes = 1600 hex chars - sk: 1632 bytes = 3264 hex chars - ct: 768 bytes = 1536 hex chars - ss: 32 bytes = 64 hex chars - Total: 3232 bytes = 6464 hex chars per line - """ - os.makedirs(out_dir, exist_ok=True) - out_path = os.path.join(out_dir, "mlkem_top_expected.hex") - with open(out_path, "w") as f: - for v in vectors: - pk = v.get("pk", "0" * 1600) - sk = v.get("sk", "0" * 3264) - ct = v.get("ct", "0" * 1536) - ss = v.get("ss", "0" * 64) - f.write(f"{pk}{sk}{ct}{ss}\n") - print(f"Wrote {len(vectors)} expected vectors to {out_path}") - return out_path - - -def main(): - vectors = parse_kat(KAT_PATH, NUM_VECTORS) - print(f"Parsed {len(vectors)} test vectors from KAT file") - - if len(vectors) == 0: - print("ERROR: No vectors parsed!", file=sys.stderr) - sys.exit(1) - - verify_lengths(vectors) - - # Print summary - for v in vectors: - print(f" count={v['count']}: " - f"d={v.get('d', 'N/A')[:8]}... " - f"msg={v.get('msg', 'N/A')[:8]}... " - f"z={v.get('z', 'N/A')[:8]}... " - f"ss={v.get('ss', 'N/A')[:8]}...") - - write_input_hex(vectors, OUT_DIR) - write_expected_hex(vectors, OUT_DIR) - - print("Done.") - - -if __name__ == "__main__": - main() diff --git a/sync_rtl/top/TB/tb_mlkem_enc_katK_xsim.v b/sync_rtl/top/TB/tb_mlkem_enc_katK_xsim.v index 5c9e5ce..f214c6b 100644 --- a/sync_rtl/top/TB/tb_mlkem_enc_katK_xsim.v +++ b/sync_rtl/top/TB/tb_mlkem_enc_katK_xsim.v @@ -103,7 +103,7 @@ module tb_mlkem_enc_katK_xsim; // ---- 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. + // ---- E2: verify y[i], e1[i] (bank_se), e2 (bank_t slot_t) vs reference. if (KP == 2 && casenum == 0) begin // E1 (verify_e1) dropped: E6's TDEC overwrites bank_a (A_hat slots) // with t_hat, so a post-run A_hat readback is invalid. A_hat is @@ -164,7 +164,7 @@ module tb_mlkem_enc_katK_xsim; ce = ce + 1; end end - if (ce == 0) $display("K=2 CASE 0 PASS (E2): e2 == ml-kem-r golden"); + if (ce == 0) $display("K=2 CASE 0 PASS (E2): e2 == reference golden"); else $display("K=2 CASE 0 FAIL (E2): %0d e2 mismatches", ce); end endtask @@ -184,7 +184,7 @@ module tb_mlkem_enc_katK_xsim; ce = ce + 1; end end - if (ce == 0) $display("K=2 CASE 0 PASS (E3): y_hat[0..1] == ml-kem-r golden"); + if (ce == 0) $display("K=2 CASE 0 PASS (E3): y_hat[0..1] == reference golden"); else $display("K=2 CASE 0 FAIL (E3): %0d coeff mismatches", ce); end endtask @@ -204,7 +204,7 @@ module tb_mlkem_enc_katK_xsim; ce = ce + 1; end end - if (ce == 0) $display("K=2 CASE 0 PASS (E4): u[0..1] == ml-kem-r golden"); + if (ce == 0) $display("K=2 CASE 0 PASS (E4): u[0..1] == reference golden"); else $display("K=2 CASE 0 FAIL (E4): %0d coeff mismatches", ce); end endtask @@ -231,7 +231,7 @@ module tb_mlkem_enc_katK_xsim; endtask // E6: v = INTT(sum_j t_hat[j] o y_hat[j]) + e2 + mu lives in bank_t rel slot - // UPSUM=1 -> dbg slot slot_t_rt+1 = 9 (K=2). Compare to ml-kem-r golden v. + // UPSUM=1 -> dbg slot slot_t_rt+1 = 9 (K=2). Compare to reference golden v. reg [11:0] gv [0:255]; task verify_e6; begin @@ -245,7 +245,7 @@ module tb_mlkem_enc_katK_xsim; ce = ce + 1; end end - if (ce == 0) $display("K=2 CASE 0 PASS (E6): v == ml-kem-r golden"); + if (ce == 0) $display("K=2 CASE 0 PASS (E6): v == reference golden"); else $display("K=2 CASE 0 FAIL (E6): %0d coeff mismatches", ce); end endtask @@ -259,7 +259,7 @@ module tb_mlkem_enc_katK_xsim; be = 0; // Dump the hardware-produced ct (read from ct_bram via dbg_ct tap), // then compare to KAT.ct byte-exact. The $write loop prints the whole - // ciphertext on one line (byte 0 first), same format as ml-kem-r's + // ciphertext on one line (byte 0 first), same format as the reference's // encaps_io example, so the two can be eyeballed / diffed. $write(" ct = "); for (i = 0; i < CTB; i = i + 1) begin diff --git a/sync_rtl/top/TB/tb_mlkem_hello_world_xsim.v b/sync_rtl/top/TB/tb_mlkem_hello_world_xsim.v index 45d978e..6008ff0 100644 --- a/sync_rtl/top/TB/tb_mlkem_hello_world_xsim.v +++ b/sync_rtl/top/TB/tb_mlkem_hello_world_xsim.v @@ -1,4 +1,4 @@ -// tb_mlkem_hello_world_xsim.v - Hardware run of ml-kem-r examples/hello_world.rs. +// tb_mlkem_hello_world_xsim.v - Hardware run of the ML-KEM hello_world demo. // // Mirrors the Rust example end-to-end on the mlkem_top DUT (ML-KEM-512, K=2): // 1. Alice: KeyGen(d=0x42..., z=0x77...) -> (ek 800B, dk 1632B) diff --git a/sync_rtl/top/TB/tb_mlkem_kg_2a_xsim.v b/sync_rtl/top/TB/tb_mlkem_kg_2a_xsim.v deleted file mode 100644 index 1b1a3fb..0000000 --- a/sync_rtl/top/TB/tb_mlkem_kg_2a_xsim.v +++ /dev/null @@ -1,45 +0,0 @@ -// tb_mlkem_kg_2a_xsim.v - Stage 2a: verify G(d||K) -> rho/sigma in mlkem_top. -`timescale 1ns/1ps -module tb_mlkem_kg_2a_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; - localparam [255:0] RHO_EXP = 256'h15f74355ca862c3cdf3dab780c35cf24b88bf144706090a1c17e41205f9f1379; - localparam [255:0] SIG_EXP = 256'h69b042001b5630b1a039116cbfd29f62c0bde5a6b571504a9fcce68bed667fd5; - - integer c; - initial begin - 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<1000) begin @(posedge clk); c=c+1; end - if(!done_o) begin $display("FAIL: timeout"); $finish; end - $display("=== Stage 2a: G(d||K) ==="); - if (dbg_rho_o===RHO_EXP && dbg_sigma_o===SIG_EXP) begin - $display("PASS: rho = %064x", dbg_rho_o); - $display("PASS: sigma = %064x", dbg_sigma_o); - $display("ALL TESTS PASSED"); - end else begin - $display("FAIL:"); - $display(" rho got=%064x", dbg_rho_o); - $display(" rho exp=%064x", RHO_EXP); - $display(" sig got=%064x", dbg_sigma_o); - $display(" sig exp=%064x", SIG_EXP); - end - $finish; - end - initial begin #50000; $display("FAIL: global timeout"); $finish; end -endmodule diff --git a/sync_rtl/top/TB/tb_mlkem_kg_2c_xsim.v b/sync_rtl/top/TB/tb_mlkem_kg_2c_xsim.v deleted file mode 100644 index c1de8b4..0000000 --- a/sync_rtl/top/TB/tb_mlkem_kg_2c_xsim.v +++ /dev/null @@ -1,60 +0,0 @@ -// tb_mlkem_kg_2c_xsim.v - Stage 2c: verify A_hat + s + e stored in mlkem_top. -// Reads golden kg_c000_AsE.hex (8 polys x 256 = 2048 lines, mod-q) and checks -// polymem slots A00,A01,A10,A11,S0,S1,E0,E1 via the debug readback tap. -`timescale 1ns/1ps -module tb_mlkem_kg_2c_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:2047]; - // slot order matches golden file order - reg [3:0] slot_of [0:7]; - integer c, p, idx, errors, gi; - - initial begin - $readmemh("sync_rtl/top/TB/vectors/kg_c000_AsE.hex", gold); - slot_of[0]=4'd0; slot_of[1]=4'd1; slot_of[2]=4'd2; slot_of[3]=4'd3; // A00..A11 - slot_of[4]=4'd4; slot_of[5]=4'd5; // S0,S1 - slot_of[6]=4'd6; slot_of[7]=4'd7; // E0,E1 - - 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<200000) begin @(posedge clk); c=c+1; end - if(!done_o) begin $display("FAIL: timeout after %0d cyc", c); $finish; end - $display("=== Stage 2c: A_hat + s + e (8 polys) === done in %0d cyc", c); - - errors = 0; - for (p = 0; p < 8; 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); // 2 cyc for registered readback - 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 (2048/2048 coeffs)"); - else $display("TESTS FAILED: %0d mismatches", errors); - $finish; - end - initial begin #5000000; $display("FAIL: global timeout"); $finish; end -endmodule diff --git a/sync_rtl/top/TB/tb_mlkem_kg_2d_xsim.v b/sync_rtl/top/TB/tb_mlkem_kg_2d_xsim.v deleted file mode 100644 index 1b00236..0000000 --- a/sync_rtl/top/TB/tb_mlkem_kg_2d_xsim.v +++ /dev/null @@ -1,57 +0,0 @@ -// 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/tb_mlkem_kg_2e_xsim.v b/sync_rtl/top/TB/tb_mlkem_kg_2e_xsim.v deleted file mode 100644 index d9cbe06..0000000 --- a/sync_rtl/top/TB/tb_mlkem_kg_2e_xsim.v +++ /dev/null @@ -1,57 +0,0 @@ -// tb_mlkem_kg_2e_xsim.v - Stage 2e: verify t_hat = e_hat + sum_j A[i][j]*s_hat[j]. -// After ST_M, slots T0,T1 must hold that_0,that_1. -// Golden: kg_c000_that.hex (2 polys x 256 = 512 lines, mod-q). -`timescale 1ns/1ps -module tb_mlkem_kg_2e_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:511]; - reg [3:0] slot_of [0:1]; // T0,T1 - integer c, p, idx, errors, gi; - - initial begin - $readmemh("sync_rtl/top/TB/vectors/kg_c000_that.hex", gold); - slot_of[0]=4'd8; slot_of[1]=4'd9; - - 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<400000) begin @(posedge clk); c=c+1; end - if(!done_o) begin $display("FAIL: timeout after %0d cyc", c); $finish; end - $display("=== Stage 2e: t_hat = e_hat + sum A o s_hat === done in %0d cyc", c); - - errors = 0; - for (p = 0; p < 2; 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 (512/512 t_hat coeffs)"); - else $display("TESTS FAILED: %0d mismatches", errors); - $finish; - end - initial begin #20000000; $display("FAIL: global timeout"); $finish; end -endmodule diff --git a/sync_rtl/top/TB/tb_mlkem_kg_2f_xsim.v b/sync_rtl/top/TB/tb_mlkem_kg_2f_xsim.v deleted file mode 100644 index 29f6b9e..0000000 --- a/sync_rtl/top/TB/tb_mlkem_kg_2f_xsim.v +++ /dev/null @@ -1,66 +0,0 @@ -// tb_mlkem_kg_2f_xsim.v - Stage 2f: verify byteEncode12 -> ek (800B), dk_pke (768B). -// ek = byteEncode12(t_hat[0..1]) || rho ; dk_pke = byteEncode12(s_hat[0..1]). -// Golden: c000_ek.hex (single 1600-hex line), c000_dkpke.hex (1536-hex line). -`timescale 1ns/1ps -module tb_mlkem_kg_2f_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; - reg dbg_byte_sel_i=0; reg [9:0] dbg_byte_idx_i=0; wire [7:0] dbg_byte_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_byte_sel_i(dbg_byte_sel_i), .dbg_byte_idx_i(dbg_byte_idx_i), .dbg_byte_o(dbg_byte_o), - .dbg_rho_o(dbg_rho_o), .dbg_sigma_o(dbg_sigma_o) - ); - always #5 clk = ~clk; - - localparam [255:0] D_LIT = 256'h2426f1941779574d3f1b163bd57f7e173e229e630ec7f7073bdf365137c4bb6d; - - // golden bytes loaded as memories: 1 byte per entry - reg [7:0] ek_gold [0:799]; - reg [7:0] dkp_gold [0:767]; - integer c, i, errors; - - initial begin - $readmemh("sync_rtl/top/TB/vectors/c000_ek_bytes.hex", ek_gold); - $readmemh("sync_rtl/top/TB/vectors/c000_dkpke_bytes.hex", dkp_gold); - - 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<500000) begin @(posedge clk); c=c+1; end - if(!done_o) begin $display("FAIL: timeout after %0d cyc", c); $finish; end - $display("=== Stage 2f: byteEncode12 -> ek/dk_pke === done in %0d cyc", c); - - errors = 0; - // ek: 800 bytes (sel=0) - dbg_byte_sel_i = 1'b0; - for (i = 0; i < 800; i = i + 1) begin - dbg_byte_idx_i = i[9:0]; - @(posedge clk); @(posedge clk); - if (dbg_byte_o !== ek_gold[i]) begin - if (errors < 8) $display(" EK MISMATCH[%0d]: got=%02x exp=%02x", i, dbg_byte_o, ek_gold[i]); - errors = errors + 1; - end - end - // dk_pke: 768 bytes (sel=1) - dbg_byte_sel_i = 1'b1; - for (i = 0; i < 768; i = i + 1) begin - dbg_byte_idx_i = i[9:0]; - @(posedge clk); @(posedge clk); - if (dbg_byte_o !== dkp_gold[i]) begin - if (errors < 8) $display(" DKP MISMATCH[%0d]: got=%02x exp=%02x", i, dbg_byte_o, dkp_gold[i]); - errors = errors + 1; - end - end - if (errors == 0) $display("ALL TESTS PASSED (ek 800B + dk_pke 768B exact)"); - else $display("TESTS FAILED: %0d byte mismatches", errors); - $finish; - end - initial begin #30000000; $display("FAIL: global timeout"); $finish; end -endmodule diff --git a/sync_rtl/top/TB/tb_mlkem_kg_katN_xsim.v b/sync_rtl/top/TB/tb_mlkem_kg_katN_xsim.v deleted file mode 100644 index 9dcf3a8..0000000 --- a/sync_rtl/top/TB/tb_mlkem_kg_katN_xsim.v +++ /dev/null @@ -1,72 +0,0 @@ -// tb_mlkem_kg_katN_xsim.v - ML-KEM-512 KeyGen vs NIST KAT, case selected by +CASE=n. -// Loads d, z, ek, dk from per-case hex files (no literals via text channel). -// d/z: single 256-bit word ($readmemh, byte0 in [7:0]). -// ek : 800 bytes (== KAT pk). dk: 1632 bytes (== KAT sk). -`timescale 1ns/1ps -module tb_mlkem_kg_katN_xsim; - reg clk=0, rst_n=0, start_i=0; - reg [255:0] d_i, z_i; - 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; - reg dbg_byte_sel_i=0; reg [9: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; - - 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_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) - ); - always #5 clk = ~clk; - - reg [255:0] dmem [0:0]; - reg [255:0] zmem [0:0]; - reg [7:0] ek_gold [0:799]; - reg [7:0] dk_gold [0:1631]; - integer c, i, errors, casenum; - reg [8*64-1:0] dfile, zfile, ekfile, dkfile; - - initial begin - if (!$value$plusargs("CASE=%d", casenum)) casenum = 0; - $sformat(dfile, "sync_rtl/top/TB/vectors/kat_c%0d_d.hex", casenum); - $sformat(zfile, "sync_rtl/top/TB/vectors/kat_c%0d_z.hex", casenum); - $sformat(ekfile, "sync_rtl/top/TB/vectors/kat_c%0d_ek.hex", casenum); - $sformat(dkfile, "sync_rtl/top/TB/vectors/kat_c%0d_dk.hex", casenum); - $readmemh(dfile, dmem); - $readmemh(zfile, zmem); - $readmemh(ekfile, ek_gold); - $readmemh(dkfile, dk_gold); - d_i = dmem[0]; - z_i = zmem[0]; - - 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<600000) begin @(posedge clk); c=c+1; end - if(!done_o) begin $display("FAIL case %0d: timeout", casenum); $finish; end - $display("=== KAT case %0d: KeyGen done in %0d cyc ===", casenum, c); - - errors = 0; - dbg_byte_sel_i = 1'b0; - for (i = 0; i < 800; i = i + 1) begin - dbg_byte_idx_i = i[9:0]; @(posedge clk); @(posedge clk); - if (dbg_byte_o !== ek_gold[i]) begin - if (errors < 8) $display(" EK[%0d] got=%02x exp=%02x", i, dbg_byte_o, ek_gold[i]); - errors = errors + 1; - end - end - for (i = 0; i < 1632; i = i + 1) begin - dbg_dk_idx_i = i[11:0]; @(posedge clk); @(posedge clk); - if (dbg_dk_o !== dk_gold[i]) begin - if (errors < 8) $display(" DK[%0d] got=%02x exp=%02x", i, dbg_dk_o, dk_gold[i]); - errors = errors + 1; - end - end - if (errors == 0) $display("CASE %0d PASS: ek==KAT.pk (800B), dk==KAT.sk (1632B)", casenum); - else $display("CASE %0d FAIL: %0d byte mismatches", casenum, errors); - $finish; - end - initial begin #40000000; $display("FAIL: global timeout"); $finish; end -endmodule diff --git a/sync_rtl/top/TB/tb_mlkem_kg_kat_xsim.v b/sync_rtl/top/TB/tb_mlkem_kg_kat_xsim.v deleted file mode 100644 index 5062723..0000000 --- a/sync_rtl/top/TB/tb_mlkem_kg_kat_xsim.v +++ /dev/null @@ -1,68 +0,0 @@ -// tb_mlkem_kg_kat_xsim.v - Stage 4 end-to-end: full ML-KEM-512 KeyGen vs NIST KAT. -// Drives d/z (KAT count=0), runs KeyGen, verifies: -// ek (800B, sel=0) == KAT pk -// dk (1632B, dk tap) == KAT sk (= dk_pke || ek || H(ek) || z) -// No force/release — pure valid/ready via start_i/done_o. -`timescale 1ns/1ps -module tb_mlkem_kg_kat_xsim; - reg clk=0, rst_n=0, start_i=0; - reg [255:0] d_i, z_i; - 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; - reg dbg_byte_sel_i=0; reg [9: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; - - 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_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) - ); - always #5 clk = ~clk; - - // KAT count=0 (byte0-low literals) - localparam [255:0] D_LIT = 256'h2426f1941779574d3f1b163bd57f7e173e229e630ec7f7073bdf365137c4bb6d; - localparam [255:0] Z_LIT = 256'h687acf9406694974d383032f7579378f449c75d0560af56cf921ec48404896f6; - - reg [7:0] ek_gold [0:799]; - reg [7:0] dk_gold [0:1631]; - integer c, i, errors; - - initial begin - $readmemh("sync_rtl/top/TB/vectors/c000_ek_bytes.hex", ek_gold); - $readmemh("sync_rtl/top/TB/vectors/c000_dk_full_bytes.hex", dk_gold); - - d_i = D_LIT; z_i = Z_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<600000) begin @(posedge clk); c=c+1; end - if(!done_o) begin $display("FAIL: timeout after %0d cyc", c); $finish; end - $display("=== Stage 4: ML-KEM-512 KeyGen end-to-end vs NIST KAT === done in %0d cyc", c); - - errors = 0; - // ek == KAT pk (800B) - dbg_byte_sel_i = 1'b0; - for (i = 0; i < 800; i = i + 1) begin - dbg_byte_idx_i = i[9:0]; @(posedge clk); @(posedge clk); - if (dbg_byte_o !== ek_gold[i]) begin - if (errors < 8) $display(" EK[%0d] got=%02x exp=%02x", i, dbg_byte_o, ek_gold[i]); - errors = errors + 1; - end - end - // dk == KAT sk (1632B) - for (i = 0; i < 1632; i = i + 1) begin - dbg_dk_idx_i = i[11:0]; @(posedge clk); @(posedge clk); - if (dbg_dk_o !== dk_gold[i]) begin - if (errors < 8) $display(" DK[%0d] got=%02x exp=%02x", i, dbg_dk_o, dk_gold[i]); - errors = errors + 1; - end - end - if (errors == 0) $display("ALL TESTS PASSED: ek==KAT.pk (800B), dk==KAT.sk (1632B)"); - else $display("TESTS FAILED: %0d byte mismatches", errors); - $finish; - end - initial begin #40000000; $display("FAIL: global timeout"); $finish; end -endmodule diff --git a/sync_rtl/top/TB/xcheck_mlkemr.py b/sync_rtl/top/TB/xcheck_mlkemr.py deleted file mode 100755 index 3e8b76f..0000000 --- a/sync_rtl/top/TB/xcheck_mlkemr.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env python3 -# xcheck_mlkemr.py - Cross-validate the hardware ML-KEM KeyGen KAT vectors -# against the ml-kem-r Rust reference's NIST .rsp files. -# -# WHY THIS SCRIPT EXISTS (byte-order convention): -# The hardware feeds G(reverse(d) || K), i.e. it byte-reverses the d/z seeds -# before hashing. Consequently the hardware vector files -# sync_rtl/top/TB/vectors/kat_k{K}_c{C}_{d,z}.hex -# store d/z in the REVERSE byte order of the NIST .rsp `d`/`z` fields. Both -# conventions hash to the SAME pk/sk (reverse(reverse(d)) == d), so both the -# hardware KAT and ml-kem-r's own KAT pass -- they just disagree on how d/z -# are written down. A naive byte-for-byte compare of d/z therefore "fails" -# even though the implementations are equivalent. This script compares with -# the reversal applied, and compares ek/dk (== pk/sk) directly. -# -# See also: the TB d/z dump in tb_mlkem_kg_katK_xsim.v and the keygen G-hash -# reversal in sync_rtl/top/mlkem_top.v. -# -# Usage: -# python3 sync_rtl/top/TB/xcheck_mlkemr.py -# ML_KEM_R=/path/to/ml-kem-r python3 sync_rtl/top/TB/xcheck_mlkemr.py -# -# Exit code 0 = all consistent, 1 = discrepancy / missing files. - -import os -import sys - -# Hardware vector dir (relative to repo root, where this script's ../../.. is). -SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) -HW_DIR = SCRIPT_DIR # vectors live under SCRIPT_DIR/vectors - -# ml-kem-r location: env override, else common sibling path. -ML_KEM_R = os.environ.get("ML_KEM_R", os.path.expanduser("~/Dev/ml-kem-r")) -RSP = { - 2: "test_data/kat_MLKEM_512.rsp", - 3: "test_data/kat_MLKEM_768.rsp", - 4: "test_data/kat_MLKEM_1024.rsp", -} -# Cases present in the hardware vector set per K (K=2 has 0..4, K=3/4 have 0..2). -CASES = {2: range(5), 3: range(3), 4: range(3)} - - -def die(msg): - print(f"ERROR: {msg}", file=sys.stderr) - sys.exit(1) - - -def rsp_cases(path): - """Parse a NIST .rsp into a list of dicts keyed by count.""" - if not os.path.isfile(path): - die(f"ml-kem-r .rsp not found: {path}\n" - f" set ML_KEM_R=/path/to/ml-kem-r (current: {ML_KEM_R})") - cur, out = None, [] - with open(path) as f: - for ln in f: - ln = ln.strip() - if ln.startswith("count = "): - if cur is not None: - out.append(cur) - cur = {"count": int(ln[8:])} - elif " = " in ln and cur is not None: - k, v = ln.split(" = ", 1) - cur[k] = v.strip().lower() - if cur is not None: - out.append(cur) - return out - - -def hwcat(k, c, which): - """Concatenate a hardware .hex vector file into one lowercase hex string.""" - path = os.path.join(HW_DIR, "vectors", f"kat_k{k}_c{c}_{which}.hex") - if not os.path.isfile(path): - die(f"hardware vector not found: {path}") - with open(path) as f: - return "".join(f.read().split()).lower() - - -def revhex(h): - """Byte-reverse a hex string.""" - return bytes.fromhex(h)[::-1].hex() - - -def main(): - print(f"ml-kem-r: {ML_KEM_R}") - print(f"hardware: {os.path.join(HW_DIR, 'vectors')}") - print("convention: hardware d/z == reverse(NIST d/z); ek==pk, dk==sk\n") - - all_ok = True - total = 0 - for k in (2, 3, 4): - cases = rsp_cases(os.path.join(ML_KEM_R, RSP[k])) - bycount = {c["count"]: c for c in cases} - for c in CASES[k]: - total += 1 - hd, hz = hwcat(k, c, "d"), hwcat(k, c, "z") - hek, hdk = hwcat(k, c, "ek"), hwcat(k, c, "dk") - rc = bycount.get(c) - if rc is None: - die(f"ml-kem-r .rsp for K={k} has no count={c}") - d_ok = revhex(hd) == rc["d"] - z_ok = revhex(hz) == rc["z"] - ek_ok = hek == rc["pk"] - dk_ok = hdk == rc["sk"] - case_ok = d_ok and z_ok and ek_ok and dk_ok - all_ok = all_ok and case_ok - print(f"K={k} c{c}: d(rev)={'Y' if d_ok else 'N'} " - f"z(rev)={'Y' if z_ok else 'N'} " - f"ek={'Y' if ek_ok else 'N'} " - f"dk={'Y' if dk_ok else 'N'} -> " - f"{'OK' if case_ok else 'MISMATCH'}") - - print(f"\n{total} cases checked: " - f"{'ALL CONSISTENT' if all_ok else 'DISCREPANCY FOUND'}") - sys.exit(0 if all_ok else 1) - - -if __name__ == "__main__": - main() diff --git a/sync_rtl/top/mlkem_top.v b/sync_rtl/top/mlkem_top.v index 115bd8c..ac60def 100644 --- a/sync_rtl/top/mlkem_top.v +++ b/sync_rtl/top/mlkem_top.v @@ -13,7 +13,7 @@ // ek = byteEncode12(t_hat[0..K-1]) || rho // dk = byteEncode12(s_hat[0..K-1]) || ek || H(ek) || z // -// Built incrementally and verified stage-by-stage against ml-kem-r golden +// Built incrementally and verified stage-by-stage against reference golden // vectors (test_framework/modules/mlkem_keygen/golden) and NIST KAT. // // Uses independent (verified) leaf modules, each with its own keccak_core: