feat: add live statistics with per-trial snapshots and von Neumann option (FPGA v5)
This commit is contained in:
parent
67bd7eec58
commit
5305d66102
8 changed files with 427 additions and 17 deletions
|
|
@ -1,6 +1,7 @@
|
|||
// Testbench for strix_core: register access, generator XOR logic checked against the sampled
|
||||
// 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.
|
||||
// counter, the health monitors with injected faults, and the statistics snapshots against a
|
||||
// reference model. Rings are replaced by the SIM model in ring_osc.v.
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module tb_strix_core;
|
||||
|
|
@ -15,7 +16,11 @@ module tb_strix_core;
|
|||
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_HEALTHY = 7'h20, REG_HEALTH_CTRL = 7'h21;
|
||||
REG_HEALTHY = 7'h20, REG_HEALTH_CTRL = 7'h21, REG_TRIAL_LOW = 7'h27,
|
||||
REG_STATS_CTRL = 7'h28, REG_STATS = 7'h40;
|
||||
localparam SNAPSHOT_BYTES = 72;
|
||||
localparam TICK_CYCLES = 1500; // 1 "ms" = 12.5 us: a 10 "ms" trial outlasts a 72-byte read
|
||||
localparam TRIAL_BITS = 10 * TICK_CYCLES / 5; // 10 "ms" trials at temporal 5
|
||||
|
||||
// Per-generator health registers
|
||||
function [6:0] gen_reg(input integer generator, input integer index);
|
||||
|
|
@ -31,7 +36,7 @@ module tb_strix_core;
|
|||
wire miso, done;
|
||||
|
||||
strix_core #(.CAPTURE_ADDR_WIDTH(CAPTURE_ADDR_WIDTH), .FREQ_GATE_BITS(14), .RINGS_PER_GEN(RINGS),
|
||||
.DEAD_WINDOW_BITS(6)) dut (
|
||||
.DEAD_WINDOW_BITS(6), .TICK_CYCLES(TICK_CYCLES)) dut (
|
||||
.clk(clk), .reset(reset),
|
||||
.spi_sck(sck), .spi_mosi(mosi), .spi_ss_n(ss_n), .spi_miso(miso),
|
||||
.capture_done(done)
|
||||
|
|
@ -80,6 +85,40 @@ module tb_strix_core;
|
|||
end
|
||||
end
|
||||
|
||||
// Reference model of statistics channel 0, mirroring stats_block without von Neumann:
|
||||
// counts restart at each trial end, and the bit arriving in that cycle opens the new trial.
|
||||
reg [31:0] model_bits = 0, model_ones = 0, model_agree = 0;
|
||||
reg [31:0] model_latched_bits, model_latched_ones, model_latched_agree;
|
||||
reg model_previous, model_has_previous = 1'b0;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (dut.stats_restart) begin
|
||||
model_bits <= 0; model_ones <= 0; model_agree <= 0; model_has_previous <= 1'b0;
|
||||
end else begin
|
||||
if (dut.trial_end) begin
|
||||
model_latched_bits <= model_bits;
|
||||
model_latched_ones <= model_ones;
|
||||
model_latched_agree <= model_agree;
|
||||
end
|
||||
if (dut.stats.valid) begin
|
||||
model_bits <= (dut.trial_end ? 0 : model_bits) + 1;
|
||||
model_ones <= (dut.trial_end ? 0 : model_ones) + dut.stats.bits[0];
|
||||
model_agree <= (dut.trial_end ? 0 : model_agree)
|
||||
+ (model_has_previous && dut.stats.bits[0] == model_previous);
|
||||
model_previous <= dut.stats.bits[0];
|
||||
model_has_previous <= 1'b1;
|
||||
end else if (dut.trial_end) begin
|
||||
model_bits <= 0; model_ones <= 0; model_agree <= 0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
reg [7:0] snapshot [0:SNAPSHOT_BYTES-1];
|
||||
|
||||
function [31:0] snapshot_word(input integer index);
|
||||
snapshot_word = {snapshot[index], snapshot[index+1], snapshot[index+2], snapshot[index+3]};
|
||||
endfunction
|
||||
|
||||
task spi_byte(input [7:0] out, output [7:0] in);
|
||||
integer i;
|
||||
begin
|
||||
|
|
@ -169,8 +208,27 @@ module tb_strix_core;
|
|||
end
|
||||
endtask
|
||||
|
||||
reg [7:0] v;
|
||||
integer changes, polls;
|
||||
task read_snapshot;
|
||||
reg [7:0] dummy;
|
||||
integer i;
|
||||
begin
|
||||
ss_n = 1'b0; #SCK_HALF;
|
||||
spi_byte({1'b1, REG_STATS}, dummy);
|
||||
for (i = 0; i < SNAPSHOT_BYTES; i = i + 1) spi_byte(8'h00, snapshot[i]);
|
||||
#SCK_HALF ss_n = 1'b1; #(4 * SCK_HALF);
|
||||
end
|
||||
endtask
|
||||
|
||||
task wait_trial_end;
|
||||
begin
|
||||
@(posedge dut.trial_end);
|
||||
#200;
|
||||
end
|
||||
endtask
|
||||
|
||||
reg [7:0] v;
|
||||
integer changes, polls;
|
||||
reg [31:0] sequence_seen;
|
||||
|
||||
initial begin
|
||||
#100 reset = 1'b0;
|
||||
|
|
@ -179,7 +237,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'h04, "VERSION");
|
||||
read_reg(REG_VERSION, v); expect(v, 8'h05, "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");
|
||||
|
|
@ -259,6 +317,47 @@ module tb_strix_core;
|
|||
read_reg(gen_reg(3, FLAGS), v); expect(v, 8'h00, "FLAGS g3 disabled");
|
||||
write_reg(REG_GEN_ENABLE, 8'h0F);
|
||||
|
||||
// Statistics: 10 "ms" trials, temporal 5 -> TRIAL_BITS bits per channel per trial
|
||||
write_reg(REG_TEMPORAL, 8'd5);
|
||||
write_reg(REG_TRIAL_LOW, 8'd10);
|
||||
write_reg(REG_STATS_CTRL, 8'h02); // restart
|
||||
wait_trial_end;
|
||||
wait_trial_end;
|
||||
read_snapshot;
|
||||
if (snapshot_word(0) !== snapshot_word(SNAPSHOT_BYTES - 4)) begin
|
||||
$display("FAIL torn snapshot"); errors = errors + 1;
|
||||
end
|
||||
expect(snapshot_word(0), 8'd1, "trial sequence");
|
||||
expect(snapshot[4], 8'h0F, "trial healthy");
|
||||
expect(snapshot[7], 8'd10, "trial ms");
|
||||
if (snapshot_word(8) !== model_latched_bits || snapshot_word(12) !== model_latched_ones
|
||||
|| snapshot_word(16) !== model_latched_agree) begin
|
||||
$display("FAIL channel 0: %0d/%0d/%0d, model %0d/%0d/%0d", snapshot_word(8), snapshot_word(12),
|
||||
snapshot_word(16), model_latched_bits, model_latched_ones, model_latched_agree);
|
||||
errors = errors + 1;
|
||||
end
|
||||
if (snapshot_word(8) < TRIAL_BITS - 1 || snapshot_word(8) > TRIAL_BITS + 1) begin
|
||||
$display("FAIL channel 0 bit count %0d, expected about %0d", snapshot_word(8), TRIAL_BITS);
|
||||
errors = errors + 1;
|
||||
end
|
||||
if (snapshot_word(8 + 48) !== snapshot_word(8)) begin
|
||||
$display("FAIL combined channel bit count differs"); errors = errors + 1;
|
||||
end
|
||||
if (snapshot_word(12) == 0 || snapshot_word(12) == snapshot_word(8)) begin
|
||||
$display("FAIL channel 0 ones count %0d is degenerate", snapshot_word(12)); errors = errors + 1;
|
||||
end
|
||||
|
||||
// Von Neumann keeps about a quarter of the bits
|
||||
write_reg(REG_STATS_CTRL, 8'h03);
|
||||
wait_trial_end;
|
||||
wait_trial_end;
|
||||
read_snapshot;
|
||||
expect(snapshot[5], 8'h01, "trial von Neumann");
|
||||
if (snapshot_word(8) < TRIAL_BITS / 10 || snapshot_word(8) > TRIAL_BITS * 45 / 100) begin
|
||||
$display("FAIL von Neumann bit count %0d", snapshot_word(8)); errors = errors + 1;
|
||||
end
|
||||
write_reg(REG_STATS_CTRL, 8'h00);
|
||||
|
||||
if (xor_checks < 100) begin
|
||||
$display("FAIL only %0d temporal XOR checks ran", xor_checks);
|
||||
errors = errors + 1;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue