148 lines
6.1 KiB
Verilog
148 lines
6.1 KiB
Verilog
// 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
|