142 lines
5.3 KiB
Coq
142 lines
5.3 KiB
Coq
|
|
// Continuous health tests for one entropy generator.
|
||
|
|
//
|
||
|
|
// - Repetition count test and adaptive proportion test (NIST SP 800-90B, section 4.4) on the
|
||
|
|
// generator's spatial XOR stream. The default cutoffs in strix_core assume 0.5 bits of
|
||
|
|
// min-entropy per bit and a false-alarm probability of 2^-40, which is far below the
|
||
|
|
// measured entropy, so they only catch real failures.
|
||
|
|
// - Dead-ring monitor: visits the rings one at a time and counts those whose sampled output
|
||
|
|
// does not change during a whole window.
|
||
|
|
//
|
||
|
|
// Failures are sticky until `clear`, which leaves the dead-ring sweep running. Monitoring
|
||
|
|
// pauses while the generator is disabled and for a short settling period after it is enabled.
|
||
|
|
module health_monitor #(
|
||
|
|
parameter RINGS = 90,
|
||
|
|
parameter DEAD_WINDOW_BITS = 16, // 2^16 clk cycles per ring (0.55 ms at 120 MHz)
|
||
|
|
parameter SETTLE_BITS = 10 // 1024 cycles ignored after enable
|
||
|
|
) (
|
||
|
|
input wire clk,
|
||
|
|
input wire active, // generator enabled and system out of reset
|
||
|
|
input wire clear, // clears failures and event counters
|
||
|
|
input wire sample_bit, // spatial XOR stream, one bit per clock
|
||
|
|
input wire [RINGS-1:0] ring_samples, // synchronized ring outputs
|
||
|
|
input wire [7:0] rct_cutoff,
|
||
|
|
input wire [10:0] apt_cutoff,
|
||
|
|
output reg rct_failed,
|
||
|
|
output reg apt_failed,
|
||
|
|
output reg [7:0] rct_events, // saturating
|
||
|
|
output reg [7:0] apt_events, // saturating
|
||
|
|
output reg [7:0] dead_rings, // result of the last complete sweep
|
||
|
|
output reg sweep_done // at least one sweep completed since activation
|
||
|
|
);
|
||
|
|
|
||
|
|
// Settling
|
||
|
|
|
||
|
|
reg [SETTLE_BITS-1:0] settle_count = {SETTLE_BITS{1'b0}};
|
||
|
|
wire settled = &settle_count;
|
||
|
|
|
||
|
|
always @(posedge clk) begin
|
||
|
|
if (!active) settle_count <= {SETTLE_BITS{1'b0}};
|
||
|
|
else if (!settled) settle_count <= settle_count + 1'b1;
|
||
|
|
end
|
||
|
|
|
||
|
|
wire testing = active && settled;
|
||
|
|
|
||
|
|
// Repetition count test: a run of rct_cutoff identical bits is a failure.
|
||
|
|
|
||
|
|
reg previous_bit;
|
||
|
|
reg [7:0] run_length;
|
||
|
|
|
||
|
|
always @(posedge clk) begin
|
||
|
|
previous_bit <= sample_bit;
|
||
|
|
if (!testing || sample_bit != previous_bit)
|
||
|
|
run_length <= 8'd1;
|
||
|
|
else if (run_length != 8'hFF)
|
||
|
|
run_length <= run_length + 8'd1;
|
||
|
|
end
|
||
|
|
|
||
|
|
wire rct_event = testing && sample_bit == previous_bit && run_length + 8'd1 == rct_cutoff;
|
||
|
|
|
||
|
|
// Adaptive proportion test: within each 1024-bit window, the first bit's value occurring
|
||
|
|
// apt_cutoff times or more is a failure.
|
||
|
|
|
||
|
|
reg [9:0] window_position;
|
||
|
|
reg reference_bit;
|
||
|
|
reg [10:0] matches;
|
||
|
|
reg window_flagged;
|
||
|
|
|
||
|
|
always @(posedge clk) begin
|
||
|
|
if (!testing) begin
|
||
|
|
window_position <= 10'd0;
|
||
|
|
end else begin
|
||
|
|
window_position <= window_position + 10'd1;
|
||
|
|
if (window_position == 10'd0) begin
|
||
|
|
reference_bit <= sample_bit;
|
||
|
|
matches <= 11'd1;
|
||
|
|
window_flagged <= 1'b0;
|
||
|
|
end else if (sample_bit == reference_bit) begin
|
||
|
|
matches <= matches + 11'd1;
|
||
|
|
if (matches + 11'd1 == apt_cutoff) window_flagged <= 1'b1;
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
wire apt_event = testing && window_position != 10'd0 && sample_bit == reference_bit
|
||
|
|
&& matches + 11'd1 == apt_cutoff && !window_flagged;
|
||
|
|
|
||
|
|
// Sticky results
|
||
|
|
|
||
|
|
always @(posedge clk) begin
|
||
|
|
if (clear) begin
|
||
|
|
rct_failed <= 1'b0;
|
||
|
|
apt_failed <= 1'b0;
|
||
|
|
rct_events <= 8'd0;
|
||
|
|
apt_events <= 8'd0;
|
||
|
|
end else begin
|
||
|
|
if (rct_event) begin
|
||
|
|
rct_failed <= 1'b1;
|
||
|
|
if (rct_events != 8'hFF) rct_events <= rct_events + 8'd1;
|
||
|
|
end
|
||
|
|
if (apt_event) begin
|
||
|
|
apt_failed <= 1'b1;
|
||
|
|
if (apt_events != 8'hFF) apt_events <= apt_events + 8'd1;
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
// Dead-ring monitor
|
||
|
|
|
||
|
|
reg [7:0] ring_index;
|
||
|
|
reg [DEAD_WINDOW_BITS-1:0] dead_window;
|
||
|
|
reg ring_bit, ring_bit_previous, ring_toggled;
|
||
|
|
reg [7:0] dead_so_far;
|
||
|
|
|
||
|
|
always @(posedge clk) begin
|
||
|
|
ring_bit <= ring_samples[ring_index];
|
||
|
|
ring_bit_previous <= ring_bit;
|
||
|
|
if (!testing) begin
|
||
|
|
ring_index <= 8'd0;
|
||
|
|
dead_window <= {DEAD_WINDOW_BITS{1'b0}};
|
||
|
|
ring_toggled <= 1'b0;
|
||
|
|
dead_so_far <= 8'd0;
|
||
|
|
sweep_done <= 1'b0;
|
||
|
|
end else begin
|
||
|
|
dead_window <= dead_window + 1'b1;
|
||
|
|
// The first two cycles of a window still carry the previous ring's bits.
|
||
|
|
if (dead_window > 2 && ring_bit != ring_bit_previous) ring_toggled <= 1'b1;
|
||
|
|
if (&dead_window) begin
|
||
|
|
ring_toggled <= 1'b0;
|
||
|
|
if (ring_index == RINGS - 1) begin
|
||
|
|
ring_index <= 8'd0;
|
||
|
|
dead_rings <= dead_so_far + !ring_toggled;
|
||
|
|
dead_so_far <= 8'd0;
|
||
|
|
sweep_done <= 1'b1;
|
||
|
|
end else begin
|
||
|
|
ring_index <= ring_index + 8'd1;
|
||
|
|
dead_so_far <= dead_so_far + !ring_toggled;
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
endmodule
|