feat(mlkem_top): KeyGen stage 2f (byteEncode12 -> ek, dk_pke)

Add ST_E stage: serialize t_hat[0..1] -> ek_mem[0..767], s_hat[0..1] ->
dkp_mem[0..767] via byteEncode12 (2 coeffs -> 3 bytes, LSB-first 12-bit:
b0=c0[7:0], b1={c1[3:0],c0[11:8]}, b2=c1[11:4]), then copy rho into
ek_mem[768..799]. Byte readback tap (dbg_byte_sel/idx -> dbg_byte_o).

Verified vs KAT-derived golden: ek 800B (== KAT pk) + dk_pke 768B
(== KAT sk prefix) byte-exact (20430 cyc). Completes Stage 2 datapath.
This commit is contained in:
2026-06-28 02:03:03 +08:00
parent a9e50ebc0c
commit 17914911c3
4 changed files with 1714 additions and 1 deletions

View File

@@ -0,0 +1,66 @@
// 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

View File

@@ -0,0 +1,768 @@
fa
da
6a
79
a3
79
d2
cb
36
c8
56
45
0c
44
6f
44
84
b8
6a
56
0c
61
70
56
4d
00
2e
4b
74
07
3f
66
a6
fb
6a
57
bc
e1
be
92
a1
a1
b5
31
c6
1f
50
88
2a
00
84
50
85
42
a1
e1
c3
b3
a9
50
f2
07
2e
9a
16
54
4e
46
ca
20
28
4b
60
12
a6
2c
69
7a
c8
58
61
5e
18
1e
1e
a0
b5
5b
00
21
19
09
0b
7b
87
19
20
e9
b8
94
b0
7a
28
12
69
dc
a1
5f
af
4a
09
b0
00
4c
ac
61
a2
ce
a7
3f
28
05
7d
bf
12
19
d0
77
47
b3
81
cf
0d
e5
2a
ba
d3
0c
a0
f5
78
73
b2
bf
a0
12
bd
5b
c2
48
05
f3
65
7a
d0
ba
6b
c4
98
8c
d9
c6
de
49
2c
0d
43
3c
e5
aa
32
8b
d6
c6
cb
50
24
63
34
0d
c5
e0
29
d2
c8
71
3d
78
0f
31
f4
41
c2
4c
40
00
e5
67
99
a6
bc
66
b4
a1
93
c7
3c
ed
56
0f
8a
e4
3f
9b
20
75
ce
b7
78
97
6b
7f
11
1a
cb
38
a3
06
f2
b7
0a
2b
d8
75
2e
82
80
86
ba
0f
3e
81
1a
8c
3b
0d
5f
8b
90
72
15
9e
56
ba
1e
10
08
00
91
95
af
58
a8
5a
3e
27
1f
00
ac
25
cc
f3
23
3c
b8
71
67
68
ad
98
e7
b1
f2
89
0d
b6
17
0f
2b
61
4f
56
c1
93
38
71
ae
1a
41
30
24
ca
9a
21
37
79
5c
8a
2e
9e
15
28
0f
f1
08
ae
e3
28
23
57
51
e5
69
ca
63
a7
82
1f
29
ad
26
70
6b
23
12
24
d9
51
60
bd
75
22
dd
e0
bf
e6
e0
a8
26
19
74
88
5a
2f
df
21
7f
8c
16
c4
f6
7c
0e
9f
b2
a2
f7
03
0c
af
88
1d
2c
8b
2e
6a
68
63
90
61
92
10
8b
00
42
60
b0
ad
ea
07
c0
b3
49
d5
88
11
0a
9c
bb
69
9b
39
52
bb
11
2c
c7
c8
d9
a7
59
b9
55
4c
1f
99
1e
a1
f5
1f
4c
61
78
e4
38
27
c5
14
9b
da
07
7f
bd
76
9a
3b
81
8d
f7
1a
42
86
07
83
fd
23
3d
1b
26
a9
6a
57
3e
99
eb
91
5f
6b
c1
8d
81
3d
ed
14
a3
04
c1
35
8e
47
05
70
d2
54
78
a6
b6
ba
2a
c5
61
28
90
b5
a5
69
e5
26
33
42
45
9c
b9
ca
76
74
f2
28
39
69
54
af
74
8d
9d
50
13
53
42
71
39
04
cc
59
10
6e
0b
b2
19
42
b4
06
6f
86
bf
5d
9c
3c
f1
b6
c0
86
92
c5
b9
88
1b
29
97
10
88
a8
b4
15
e2
6c
4a
25
67
58
8b
2d
df
b6
8d
11
14
6f
cf
f0
b4
cb
79
c5
74
87
01
8c
25
54
1d
32
67
a1
06
40
e4
30
bf
eb
bb
4f
45
a7
99
c9
f5
54
d1
b6
9d
70
c7
9d
17
12
4f
9c
01
cf
3a
d6
56
9e
77
a4
e1
b6
57
04
70
10
06
45
2f
45
a7
2f
32
98
c6
60
82
0f
1c
20
6a
f7
00
c9
a6
21
92
3c
39
11
cd
51
8e
02
24
04
c8
70
c3
d3
b3
b6
83
1a
82
53
6a
31
70
33
4e
31
59
b0
c9
59
03
6c
43
2c
f5
b4
84
b1
2c
75
cc
c3
c9
6c
e3
28
fb
ba
79
e2
f7
24
78
9a
8d
27
f7
af
8b
b0
86
45
d2
c9
a3
e8
7f
80
42
c3
62
27
19
4a
3c
0e
14
d0
91
29
d0
69
d5
37
9d
bd
60
65
65
d9
15
a3
bc
98
82
e8
74
23
a0
62
00
0a
5f
4c
55
23
ec
94
47
ce
a1
64
3a
97
ad
33
c1
98
62
46
ad
91
e2
73
ce
32
ac
af
f4
53
35
99
65
37
07
21

View File

@@ -0,0 +1,800 @@
80
b7
01
00
83
1d
7e
e1
68
f4
73
92
48
31
19
f9
38
63
ec
b0
bc
60
53
12
d3
57
28
87
6b
00
f5
f5
7a
f4
c8
5e
da
97
28
1d
57
43
90
bc
72
56
19
36
68
dc
98
62
53
54
ab
52
b9
bc
e7
81
c5
77
1c
c2
60
99
f6
fb
ac
be
e1
78
4d
19
48
87
73
1f
09
7c
87
e0
82
cb
ea
c5
7d
30
b7
b9
a2
93
87
2c
84
14
66
f2
24
4c
c6
5f
f4
ba
65
3d
67
51
df
a2
20
51
d5
53
36
99
42
c3
1c
29
f9
13
07
af
57
32
92
d6
97
a8
c3
16
12
e0
7b
53
35
cf
08
32
9f
d2
46
bf
2a
32
20
0a
8c
b0
c4
b0
3b
9d
09
c5
ba
1c
39
19
53
9e
92
29
c6
96
92
43
48
28
2f
69
6b
15
25
e5
60
38
06
b4
db
61
8b
3d
c8
63
0c
b2
86
e4
31
17
21
bc
50
31
f2
57
aa
92
5b
f8
09
60
44
5a
a8
d8
e6
97
88
e9
9d
a9
5c
1d
10
cc
3b
e8
58
52
8d
b0
cf
ea
00
3c
74
69
37
80
a6
5a
36
e6
28
43
b3
be
15
40
65
43
d2
2d
83
50
7c
87
b2
78
67
92
c8
0f
a5
62
84
54
a3
10
39
4d
da
4c
2a
b5
94
93
85
02
57
d6
d3
bc
10
71
86
d9
06
27
07
f3
54
e8
1a
1a
10
fc
b7
63
d3
1a
4e
c4
09
aa
24
3e
d2
e5
9b
b1
89
85
5e
b1
65
62
66
bb
37
50
07
85
ea
08
cd
88
c2
22
a8
c5
3a
87
77
ca
b4
2c
9c
23
8a
00
d5
93
69
42
50
26
e9
9d
2a
e6
04
0a
f6
70
90
29
ca
87
89
c8
97
b3
29
fb
a7
48
d6
9c
8a
71
48
c9
5a
bb
0c
71
78
7c
58
f1
2f
21
f2
21
3e
38
92
42
aa
7f
bd
32
0c
12
ac
42
c6
11
60
6c
b8
c1
d1
b4
62
58
86
a1
45
a5
35
bd
65
bd
0d
99
6b
33
0c
16
57
d6
1a
87
e9
04
17
00
32
25
75
98
20
a3
bb
ec
50
5c
da
f1
11
93
22
9a
ae
17
3c
d1
2b
ad
22
a7
a3
0a
76
a9
fa
76
36
c3
d5
aa
48
e1
78
40
12
a0
12
f1
41
68
b7
85
3d
2a
16
f9
0c
a0
2c
c3
3b
20
06
c0
8f
26
c1
5f
ca
29
61
2c
c7
92
e1
cf
02
e1
32
1a
75
a8
10
89
19
86
66
7d
41
48
af
36
e6
5e
1a
59
25
89
02
96
17
73
4c
31
5c
0e
39
b7
51
8c
86
60
cd
c2
65
d0
c8
13
ef
20
a0
43
31
7f
ae
5b
8e
40
54
6e
ec
f7
1d
6c
05
4f
a6
6b
49
40
13
09
be
f3
0f
d5
1a
55
19
75
5d
a9
10
46
89
b2
6f
08
39
15
4c
84
7b
ef
23
65
94
3c
02
49
48
3a
ad
9a
73
67
c9
80
a3
d1
ce
46
d2
00
8a
15
27
5c
a3
bc
a4
83
4e
6f
9a
30
36
c0
cd
80
ba
22
8d
34
b9
78
02
44
d5
c0
cb
e9
07
c6
fb
43
82
4a
9c
07
46
47
16
39
cb
86
18
8c
60
c3
b2
53
21
5c
c9
16
92
bc
d1
49
3d
6a
44
4d
53
64
46
e0
29
68
13
80
01
cc
28
7e
0e
51
8f
3a
a2
5d
8c
3b
95
b3
c3
5c
59
f4
81
9d
48
49
76
02
9a
f0
5a
26
3c
35
33
6b
26
1f
77
b2
cd
c2
93
89
f0
84
c0
4c
bb
37
0c
48
56
2b
5b
50
77
1c
0f
b8
16
90
8e
19
8d
d6
d5
3f
76
e4
8b
f0
42
c2
b1
f8
2a
3a
40
b8
da
f5
1f
46
d7
6f
a4
bc
c9
d3
c8
94
ff
85
0c
0f
aa
32
e0
ac
33
90
1a
4c
79
13
9f
5f
20
41
7e
c1
a1
90
60
70
44
f1
8b
b8
24
cf
35
0c
78
ab
3d
df
3c
2c
86
ca
55
43
f7
15

View File

@@ -36,6 +36,10 @@ module mlkem_top #(
input [3:0] dbg_slot_i, // poly slot (see localparams below)
input [7:0] dbg_idx_i, // coefficient index 0..255
output [11:0] dbg_coeff_o,
// Debug byte readback: ek (sel=0, 0..799) / dk_pke (sel=1, 0..767)
input dbg_byte_sel_i,
input [9:0] dbg_byte_idx_i,
output [7:0] dbg_byte_o,
// Debug taps for hash outputs
output [255:0] dbg_rho_o,
output [255:0] dbg_sigma_o
@@ -65,6 +69,17 @@ module mlkem_top #(
always @(posedge clk) dbg_coeff_r <= polymem[dbg_slot_i*256 + dbg_idx_i];
assign dbg_coeff_o = dbg_coeff_r;
// ek (800B) and dk_pke (768B) byte memories (byteEncode12 output)
localparam EK_BYTES = 384*K + 32; // 800 for K=2
localparam DK_BYTES = 384*K; // 768 for K=2
reg [7:0] ek_mem [0:EK_BYTES-1];
reg [7:0] dkp_mem [0:DK_BYTES-1];
reg [7:0] dbg_byte_r;
always @(posedge clk)
dbg_byte_r <= dbg_byte_sel_i ? dkp_mem[dbg_byte_idx_i] : ek_mem[dbg_byte_idx_i];
assign dbg_byte_o = dbg_byte_r;
// ================================================================
// Top-level FSM (built incrementally). Stage 2a: G only.
// ================================================================
@@ -74,6 +89,7 @@ module mlkem_top #(
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_M = 4'd5; // matrix accumulate t_hat = e_hat + sum A o s_hat
localparam ST_E = 4'd6; // byteEncode12 -> ek_mem, dkp_mem
localparam ST_DONE = 4'd15;
reg [3:0] st, st_next;
@@ -206,6 +222,29 @@ module mlkem_top #(
reg m_loading; // 1 while streaming pairs into poly_mul
reg m_pending; // wait for poly_mul IDLE before next (i,j)
// ---- Stage 2f: byteEncode12 serializer ----
// Pack each poly (2 coeffs -> 3 bytes, LSB-first 12-bit). ek = t_hat[0..K-1]
// bytes || rho; dk_pke = s_hat[0..K-1] bytes. Walk coeff pairs per poly.
reg [2:0] e_poly; // which source poly: 0,1 = t_hat0,t_hat1 -> ek
// 2,3 = s_hat0,s_hat1 -> dk_pke
reg [7:0] e_pair; // 0..127 coeff-pair within poly
reg [9:0] e_rho; // 0..31 rho byte copy index (ek tail)
reg e_done; // serialization complete
// source poly slot for current e_poly
wire [3:0] e_slot = (e_poly == 3'd0) ? SLOT_T0 :
(e_poly == 3'd1) ? SLOT_T1 :
(e_poly == 3'd2) ? SLOT_S0 : SLOT_S1;
// two coeffs of the current pair
wire [11:0] e_c0 = polymem[e_slot*256 + {e_pair, 1'b0}];
wire [11:0] e_c1 = polymem[e_slot*256 + {e_pair, 1'b1}];
// 3 packed bytes
wire [7:0] e_b0 = e_c0[7:0];
wire [7:0] e_b1 = {e_c1[3:0], e_c0[11:8]};
wire [7:0] e_b2 = e_c1[11:4];
// byte base offset within target memory: poly index *384 (= 128 pairs *3)
wire [9:0] e_base = (e_poly[0]) ? 10'd384 : 10'd0; // poly0->0, poly1->384
wire [9:0] e_boff = e_base + {e_pair, 1'b0} + {2'b0, e_pair}; // pair*3
wire [3:0] m_aslot = {2'b0, m_i[0], m_j[0]}; // A_hat[i][j] slot = i*2+j (0..3)
wire [3:0] m_sslot = SLOT_S0 + {3'b0, m_j[0]}; // s_hat[j]
wire [3:0] m_eslot = SLOT_E0 + {3'b0, m_i[0]}; // e_hat[i]
@@ -244,7 +283,8 @@ module mlkem_top #(
ST_A: if (a_pair >= K*K) st_next = ST_C;
ST_C: if (c_poly >= 2*K) st_next = ST_N;
ST_N: if (n_slot >= 2*K) st_next = ST_M;
ST_M: if (m_i >= K) st_next = ST_DONE;
ST_M: if (m_i >= K) st_next = ST_E;
ST_E: if (e_done) st_next = ST_DONE;
ST_DONE: st_next = ST_IDLE;
default: st_next = ST_IDLE;
endcase
@@ -279,6 +319,10 @@ module mlkem_top #(
m_loading <= 1'b0;
m_pending <= 1'b0;
pm_valid <= 1'b0;
e_poly <= 3'd0;
e_pair <= 8'd0;
e_rho <= 10'd0;
e_done <= 1'b0;
end else begin
st <= st_next;
@@ -453,6 +497,41 @@ module mlkem_top #(
m_pending <= 1'b0;
end
end
// Arm E stage when M finishes
if (st == ST_M && st_next == ST_E) begin
e_poly <= 3'd0;
e_pair <= 8'd0;
e_rho <= 10'd0;
e_done <= 1'b0;
end
// ---- ST_E: byteEncode12 t_hat -> ek_mem, s_hat -> dkp_mem, ek tail = rho ----
if (st == ST_E && !e_done) begin
if (e_poly < 3'd4) begin
// pack current coeff-pair (3 bytes)
if (e_poly < 3'd2) begin
ek_mem[e_boff] <= e_b0;
ek_mem[e_boff + 1] <= e_b1;
ek_mem[e_boff + 2] <= e_b2;
end else begin
dkp_mem[e_boff] <= e_b0;
dkp_mem[e_boff + 1] <= e_b1;
dkp_mem[e_boff + 2] <= e_b2;
end
if (e_pair == 8'd127) begin
e_pair <= 8'd0;
e_poly <= e_poly + 3'd1; // next poly (or ->4 = rho phase)
end else begin
e_pair <= e_pair + 8'd1;
end
end else begin
// rho copy: ek_mem[768 + r] = rho byte r (r = 0..31)
ek_mem[10'd768 + e_rho] <= rho_r[e_rho*8 +: 8];
if (e_rho == 10'd31) e_done <= 1'b1;
else e_rho <= e_rho + 10'd1;
end
end
end
end