feat: add per-generator NIST health tests and dead-ring monitor, onod3000 output test (FPGA v4)

This commit is contained in:
randogoth 2026-10-25 13:36:55 +02:00
parent 585e755c66
commit 67bd7eec58
9 changed files with 436 additions and 28 deletions

View file

@ -1,6 +1,6 @@
// Testbench for strix_core: register access, generator XOR logic checked against the sampled
// ring outputs, captures read back across transactions, generator enable, and the frequency
// counter. Rings are replaced by the SIM model in ring_osc.v.
// ring outputs, captures read back across transactions, generator enable, the frequency
// counter, and the health monitors with injected faults. Rings are replaced by the SIM model in ring_osc.v.
`timescale 1ns / 1ps
module tb_strix_core;
@ -14,7 +14,14 @@ module tb_strix_core;
REG_GENERATORS = 7'h06, REG_RINGS = 7'h07, REG_SOURCE_A = 7'h08,
REG_DECIM_LOW = 7'h0A, REG_CONTROL = 7'h0B, REG_STATUS = 7'h0C,
REG_DATA = 7'h10, REG_RING_SELECT = 7'h11, REG_FREQ_COUNT0 = 7'h15,
REG_GEN_ENABLE = 7'h16, REG_TEMPORAL = 7'h17, REG_FREQ_GEN = 7'h18;
REG_GEN_ENABLE = 7'h16, REG_TEMPORAL = 7'h17, REG_FREQ_GEN = 7'h18,
REG_HEALTHY = 7'h20, REG_HEALTH_CTRL = 7'h21;
// Per-generator health registers
function [6:0] gen_reg(input integer generator, input integer index);
gen_reg = 7'h30 + 4 * generator + index;
endfunction
localparam FLAGS = 0, RCT_EVENTS = 1, APT_EVENTS = 2, DEAD_RINGS = 3;
reg clk = 1'b0;
always #4.1667 clk = ~clk; // 120 MHz
@ -23,7 +30,8 @@ module tb_strix_core;
reg sck = 1'b0, mosi = 1'b0, ss_n = 1'b1;
wire miso, done;
strix_core #(.CAPTURE_ADDR_WIDTH(CAPTURE_ADDR_WIDTH), .FREQ_GATE_BITS(14), .RINGS_PER_GEN(RINGS)) dut (
strix_core #(.CAPTURE_ADDR_WIDTH(CAPTURE_ADDR_WIDTH), .FREQ_GATE_BITS(14), .RINGS_PER_GEN(RINGS),
.DEAD_WINDOW_BITS(6)) dut (
.clk(clk), .reset(reset),
.spi_sck(sck), .spi_mosi(mosi), .spi_ss_n(ss_n), .spi_miso(miso),
.capture_done(done)
@ -171,7 +179,7 @@ module tb_strix_core;
// Identification
read_reg(REG_ID0, v); expect(v, "S", "ID0");
read_reg(REG_ID3, v); expect(v, "X", "ID3");
read_reg(REG_VERSION, v); expect(v, 8'h03, "VERSION");
read_reg(REG_VERSION, v); expect(v, 8'h04, "VERSION");
read_reg(REG_GENERATORS, v); expect(v, 8'd4, "GENERATORS");
read_reg(REG_RINGS, v); expect(v, RINGS, "RINGS");
read_reg(REG_TEMPORAL, v); expect(v, 8'd5, "TEMPORAL");
@ -216,6 +224,41 @@ module tb_strix_core;
read_reg(REG_FREQ_COUNT0, v);
if (v == 8'd0) begin $display("FAIL frequency count is zero"); errors = errors + 1; end
// Health: all generators healthy once a dead-ring sweep (90 x 64 cycles) has completed
#100000;
read_reg(REG_HEALTHY, v); expect(v, 8'h0F, "HEALTHY");
read_reg(gen_reg(0, FLAGS), v); expect(v, 8'h38, "FLAGS g0"); // healthy, swept, active
read_reg(gen_reg(0, DEAD_RINGS), v); expect(v, 8'd0, "DEAD g0");
// A dead ring is counted but tolerated
force dut.gen[1].generator.raw_enhanced[7] = 1'b0;
#120000;
read_reg(gen_reg(1, DEAD_RINGS), v); expect(v, 8'd1, "DEAD g1");
read_reg(REG_HEALTHY, v); expect(v, 8'h0F, "HEALTHY dead ring");
release dut.gen[1].generator.raw_enhanced[7];
// A stuck spatial stream fails both NIST tests
force dut.gen[2].generator.level4 = 1'b0;
#20000;
release dut.gen[2].generator.level4;
read_reg(REG_HEALTHY, v); expect(v, 8'h0B, "HEALTHY stuck");
read_reg(gen_reg(2, FLAGS), v); expect(v & 8'h03, 8'h03, "FLAGS g2 failed");
read_reg(gen_reg(2, RCT_EVENTS), v);
if (v == 8'd0) begin $display("FAIL no RCT events"); errors = errors + 1; end
read_reg(gen_reg(2, APT_EVENTS), v);
if (v == 8'd0) begin $display("FAIL no APT events"); errors = errors + 1; end
// Clear restores health without restarting the sweep
write_reg(REG_HEALTH_CTRL, 8'h01);
read_reg(REG_HEALTHY, v); expect(v, 8'h0F, "HEALTHY cleared");
read_reg(gen_reg(2, RCT_EVENTS), v); expect(v, 8'd0, "RCT events cleared");
// A disabled generator is not healthy
write_reg(REG_GEN_ENABLE, 8'h07);
read_reg(REG_HEALTHY, v); expect(v, 8'h07, "HEALTHY disabled");
read_reg(gen_reg(3, FLAGS), v); expect(v, 8'h00, "FLAGS g3 disabled");
write_reg(REG_GEN_ENABLE, 8'h0F);
if (xor_checks < 100) begin
$display("FAIL only %0d temporal XOR checks ran", xor_checks);
errors = errors + 1;