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
148
fpga/rtl/stats_block.v
Normal file
148
fpga/rtl/stats_block.v
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
// Live statistics: back-to-back trials of `trial_ms` milliseconds. During each trial every
|
||||
// channel counts its bits, ones, and agreements (a bit equal to the bit before it, which may
|
||||
// be the last bit of the previous trial, so a trial has as many comparisons as bits). At the
|
||||
// end of a trial the counts are latched into a snapshot and the counters restart in the same
|
||||
// cycle, so no bit is lost between trials. Bias, z-scores, random walk and lag-1
|
||||
// autocorrelation all follow from the three counts; rolling and cumulative windows are built
|
||||
// from consecutive snapshots outside the FPGA.
|
||||
//
|
||||
// With von Neumann enabled, each channel's bits pass through a von Neumann extractor (pairs
|
||||
// 01 -> 0, 10 -> 1, 00 and 11 dropped) before counting.
|
||||
//
|
||||
// Snapshot layout (big-endian), read one byte at a time by index:
|
||||
// 0-3 trial sequence number
|
||||
// 4 healthy generators during the whole trial (AND over the trial)
|
||||
// 5 bit 0: von Neumann was enabled for the whole trial
|
||||
// 6-7 trial length in ms
|
||||
// 8+12c channel c: bits (4), ones (4), agreements (4)
|
||||
// 8+12C trial sequence number again; a mismatch with bytes 0-3 means a torn read
|
||||
module stats_block #(
|
||||
parameter CHANNELS = 5,
|
||||
parameter TICK_CYCLES = 120000 // clk cycles per millisecond
|
||||
) (
|
||||
input wire clk,
|
||||
input wire reset,
|
||||
input wire [CHANNELS-1:0] bits,
|
||||
input wire valid, // new bits on all channels this cycle
|
||||
input wire von_neumann,
|
||||
input wire [15:0] trial_ms,
|
||||
input wire restart, // start a new trial now, sequence back to 0
|
||||
input wire [3:0] healthy,
|
||||
input wire [7:0] read_index,
|
||||
output reg [7:0] read_byte, // one cycle after read_index
|
||||
output reg trial_end // pulse: a new snapshot is available
|
||||
);
|
||||
|
||||
localparam SNAPSHOT_BYTES = 12 + 12 * CHANNELS;
|
||||
|
||||
// Trial timing
|
||||
|
||||
reg [16:0] tick_count;
|
||||
reg [15:0] ms_count;
|
||||
wire tick = (tick_count == TICK_CYCLES - 1);
|
||||
wire trial_due = tick && (ms_count + 16'd1 >= trial_ms);
|
||||
|
||||
always @(posedge clk) begin
|
||||
trial_end <= 1'b0;
|
||||
if (reset || restart) begin
|
||||
tick_count <= 17'd0;
|
||||
ms_count <= 16'd0;
|
||||
end else begin
|
||||
tick_count <= tick ? 17'd0 : tick_count + 17'd1;
|
||||
if (trial_due) begin
|
||||
ms_count <= 16'd0;
|
||||
trial_end <= 1'b1;
|
||||
end else if (tick) begin
|
||||
ms_count <= ms_count + 16'd1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Von Neumann extraction per channel
|
||||
|
||||
reg [CHANNELS-1:0] pair_first, have_first;
|
||||
wire [CHANNELS-1:0] vn_valid = have_first & (bits ^ pair_first);
|
||||
wire [CHANNELS-1:0] count_valid = von_neumann ? (valid ? vn_valid : {CHANNELS{1'b0}})
|
||||
: {CHANNELS{valid}};
|
||||
wire [CHANNELS-1:0] count_bit = von_neumann ? pair_first : bits;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (reset || !von_neumann) begin
|
||||
have_first <= {CHANNELS{1'b0}};
|
||||
end else if (valid) begin
|
||||
have_first <= ~have_first;
|
||||
pair_first <= bits;
|
||||
end
|
||||
end
|
||||
|
||||
// Counters
|
||||
|
||||
reg [31:0] bit_count [0:CHANNELS-1];
|
||||
reg [31:0] one_count [0:CHANNELS-1];
|
||||
reg [31:0] agree_count [0:CHANNELS-1];
|
||||
reg [CHANNELS-1:0] previous_bit, has_previous;
|
||||
|
||||
reg [31:0] sequence_number;
|
||||
reg [3:0] healthy_during;
|
||||
reg von_neumann_during;
|
||||
|
||||
// The snapshot is latched as one flat byte string (byte 0 in the top bits), so reading it
|
||||
// is a plain byte multiplexer.
|
||||
reg [8*SNAPSHOT_BYTES-1:0] snapshot, snapshot_next;
|
||||
|
||||
integer k;
|
||||
always @(*) begin
|
||||
snapshot_next = {SNAPSHOT_BYTES{8'h00}};
|
||||
snapshot_next[8*SNAPSHOT_BYTES-1 -: 64] = {sequence_number, 4'd0, healthy_during & healthy,
|
||||
7'd0, von_neumann_during & von_neumann, trial_ms};
|
||||
for (k = 0; k < CHANNELS; k = k + 1)
|
||||
snapshot_next[8*SNAPSHOT_BYTES-1-64-96*k -: 96] = {bit_count[k], one_count[k], agree_count[k]};
|
||||
snapshot_next[31:0] = sequence_number;
|
||||
end
|
||||
|
||||
integer c;
|
||||
always @(posedge clk) begin
|
||||
if (reset || restart) begin
|
||||
sequence_number <= 32'd0;
|
||||
healthy_during <= 4'hF;
|
||||
von_neumann_during <= von_neumann;
|
||||
for (c = 0; c < CHANNELS; c = c + 1) begin
|
||||
bit_count[c] <= 32'd0;
|
||||
one_count[c] <= 32'd0;
|
||||
agree_count[c] <= 32'd0;
|
||||
end
|
||||
has_previous <= {CHANNELS{1'b0}};
|
||||
end else begin
|
||||
for (c = 0; c < CHANNELS; c = c + 1) begin
|
||||
// The bit arriving in the trial's last cycle belongs to the next trial.
|
||||
if (count_valid[c]) begin
|
||||
bit_count[c] <= (trial_end ? 32'd0 : bit_count[c]) + 32'd1;
|
||||
one_count[c] <= (trial_end ? 32'd0 : one_count[c]) + count_bit[c];
|
||||
agree_count[c] <= (trial_end ? 32'd0 : agree_count[c])
|
||||
+ (has_previous[c] && count_bit[c] == previous_bit[c]);
|
||||
previous_bit[c] <= count_bit[c];
|
||||
has_previous[c] <= 1'b1;
|
||||
end else if (trial_end) begin
|
||||
bit_count[c] <= 32'd0;
|
||||
one_count[c] <= 32'd0;
|
||||
agree_count[c] <= 32'd0;
|
||||
end
|
||||
end
|
||||
if (trial_end) begin
|
||||
snapshot <= snapshot_next;
|
||||
sequence_number <= sequence_number + 32'd1;
|
||||
healthy_during <= healthy;
|
||||
von_neumann_during <= von_neumann;
|
||||
end else begin
|
||||
healthy_during <= healthy_during & healthy;
|
||||
von_neumann_during <= von_neumann_during & von_neumann;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Snapshot read-out, registered: the byte for read_index appears one cycle later.
|
||||
|
||||
always @(posedge clk)
|
||||
read_byte <= (read_index < SNAPSHOT_BYTES) ? snapshot[8*(SNAPSHOT_BYTES-1-read_index) +: 8] : 8'h00;
|
||||
|
||||
endmodule
|
||||
Loading…
Add table
Add a link
Reference in a new issue