// 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