feat: init mlkem project with Verilator test framework

- sync_rtl/common/: skid_buffer, pipeline_reg, defines (valid/ready)
- sync_rtl/mod_add/: modular adder example with Verilator C++ TB
- test_framework/: Python-driven Verilator compile/sim/compare pipeline
- test_framework/modules/mod_add/: 50-vector test plan, full鏈路 PASS
- .trellis/spec/: RTL and test_framework conventions documented
This commit is contained in:
2026-06-24 19:43:29 +08:00
commit 8fdf944555
216 changed files with 24140 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
`ifndef DEFINES_VH
`define DEFINES_VH
`define CLK_PERIOD 10.0 // 100MHz
`define Q 3329 // ML-KEM prime modulus
`define N 256 // polynomial degree
`endif

View File

@@ -0,0 +1,53 @@
// pipeline_reg.v - Single pipeline stage with valid/ready handshake
//
// valid_i && ready_o -> register data -> valid_o next cycle.
// ready_o = !valid_o || ready_i.
//
// Parameters:
// DW = data width (default 12)
//
// Interface:
// clk, rst_n - clock, active-low reset
// data_i, valid_i, ready_o - input side
// data_o, valid_o, ready_i - output side
module pipeline_reg #(parameter DW = 12) (
input clk,
input rst_n,
input [DW-1:0] data_i,
input valid_i,
output ready_o,
output [DW-1:0] data_o,
output valid_o,
input ready_i
);
reg valid_r;
reg [DW-1:0] data_r;
// ready_o: accept new data when output slot is free
assign ready_o = !valid_r || ready_i;
// Drive outputs from internal register
assign valid_o = valid_r;
assign data_o = data_r;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
valid_r <= 1'b0;
data_r <= {DW{1'b0}};
end else begin
if (valid_r && ready_i) begin
// Output consumed, clear valid
valid_r <= 1'b0;
end
if (valid_i && ready_o) begin
// Capture new data
data_r <= data_i;
valid_r <= 1'b1;
end
end
end
endmodule

View File

@@ -0,0 +1,73 @@
// skid_buffer.v - 2-entry skid buffer with valid/ready handshake
//
// When valid_i && ready_o, capture data_i. Output via valid_o && ready_i.
// Stores one extra entry in skid register. Pure combinational ready_o path.
//
// Parameters:
// DW = data width (default 12)
//
// Interface:
// clk, rst_n - clock, active-low reset
// data_i, valid_i, ready_o - input side
// data_o, valid_o, ready_i - output side
module skid_buffer #(parameter DW = 12) (
input clk,
input rst_n,
input [DW-1:0] data_i,
input valid_i,
output ready_o,
output [DW-1:0] data_o,
output valid_o,
input ready_i
);
// Internal state
reg skid_valid; // skid register has valid data
reg [DW-1:0] skid_data; // skid register data
// Output register (pipeline stage)
reg out_valid;
reg [DW-1:0] out_data;
// ready_o is combinational: ready when output slot is empty
// (either out_valid is 0, or ready_i is high and we can accept new data)
assign ready_o = !out_valid || ready_i;
// valid_o drives from output register
assign valid_o = out_valid;
assign data_o = out_data;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
skid_valid <= 1'b0;
skid_data <= {DW{1'b0}};
out_valid <= 1'b0;
out_data <= {DW{1'b0}};
end else begin
if (out_valid && ready_i) begin
// Output consumed: load from skid if available
if (skid_valid) begin
out_data <= skid_data;
out_valid <= 1'b1;
skid_valid <= 1'b0;
end else begin
out_valid <= 1'b0;
end
end
if (valid_i && ready_o) begin
if (!out_valid || (out_valid && ready_i && !skid_valid)) begin
// Output slot free: go directly to output
out_data <= data_i;
out_valid <= 1'b1;
end else begin
// Output slot occupied: store in skid
skid_data <= data_i;
skid_valid <= 1'b1;
end
end
end
end
endmodule