feat: add two-path bias amplifier with random walk and majority vote stages (FPGA v6)
This commit is contained in:
parent
5305d66102
commit
8541cce59c
11 changed files with 741 additions and 58 deletions
|
|
@ -1,5 +1,6 @@
|
|||
// Board-independent STRIX logic: entropy generators with health monitors, live statistics,
|
||||
// source selection, raw capture, frequency counter and the SPI register interface to the SAMD21.
|
||||
// bias amplifier, source selection, raw capture, frequency counter and the SPI register
|
||||
// interface to the SAMD21.
|
||||
//
|
||||
// SPI protocol (mode 0, one transaction per ss_n low period): the first byte is a command,
|
||||
// bit 7 = read (1) or write (0), bits 6:0 = register address. A write stores the following
|
||||
|
|
@ -18,7 +19,8 @@
|
|||
//
|
||||
// Statistics channels 0-3 are the generators' processed outputs, channel 4 is the XOR of the
|
||||
// healthy generators' outputs. Reading STATS returns the latest trial snapshot (layout in
|
||||
// stats_block.v) byte by byte, starting at byte 0 in every transaction.
|
||||
// stats_block.v) byte by byte, starting at byte 0 in every transaction. AMP does the same for
|
||||
// the bias amplifier's trial snapshot (layout in bias_amplifier.v).
|
||||
module strix_core #(
|
||||
parameter CAPTURE_ADDR_WIDTH = 15,
|
||||
parameter FREQ_GATE_BITS = 24,
|
||||
|
|
@ -35,7 +37,7 @@ module strix_core #(
|
|||
output wire capture_done
|
||||
);
|
||||
|
||||
localparam [7:0] VERSION = 8'h05;
|
||||
localparam [7:0] VERSION = 8'h06;
|
||||
localparam GENERATORS = 4;
|
||||
|
||||
localparam [6:0] REG_ID0 = 7'h00, // "STRX"
|
||||
|
|
@ -74,7 +76,15 @@ module strix_core #(
|
|||
REG_TRIAL_LOW = 7'h27,
|
||||
REG_STATS_CTRL = 7'h28, // bit 0 von Neumann on the statistics path;
|
||||
// write bit 1: restart trials, sequence 0
|
||||
REG_STATS = 7'h40; // burst read of the trial snapshot
|
||||
REG_STATS = 7'h40, // burst read of the statistics trial snapshot
|
||||
REG_AMP = 7'h42, // burst read of the amplifier trial snapshot
|
||||
REG_AMP_CTRL = 7'h50, // bit 0 von Neumann on the amplifier path;
|
||||
// write bit 1: restart the amplifier, sequence 0
|
||||
REG_AMP_SOURCES = 7'h51, // generators feeding the amplifier (healthy only)
|
||||
REG_AMP_SUB_MS = 7'h52, // sub-trial length in ms
|
||||
REG_AMP_SUBS = 7'h53, // sub-trials per trial, 1-15
|
||||
REG_AMP_STAGE = 7'h54; // stage s at 0x54 + 3s: mode (0 random walk,
|
||||
// 1 majority vote), parameter high, parameter low
|
||||
|
||||
// Per-generator health registers at 0x30 + 4g + index
|
||||
localparam [1:0] GEN_FLAGS = 2'd0, // bit 0 RCT failed, 1 APT failed, 2 too many dead rings,
|
||||
|
|
@ -146,6 +156,26 @@ module strix_core #(
|
|||
stats_valid <= out_valid[0];
|
||||
end
|
||||
|
||||
// Bias amplifier, fed from the same registered generator outputs
|
||||
|
||||
reg [GENERATORS-1:0] amp_sources;
|
||||
reg amp_von_neumann, amp_restart;
|
||||
reg [7:0] amp_sub_ms;
|
||||
reg [3:0] amp_subtrials;
|
||||
reg [2:0] amp_stage_mode;
|
||||
reg [47:0] amp_stage_param;
|
||||
reg [7:0] amp_index;
|
||||
wire [7:0] amp_byte;
|
||||
wire amp_trial_end;
|
||||
|
||||
bias_amplifier #(.GENERATORS(GENERATORS), .TICK_CYCLES(TICK_CYCLES)) amplifier (
|
||||
.clk(clk), .reset(reset), .gen_bit(stats_bits[GENERATORS-1:0]), .gen_valid(stats_valid),
|
||||
.sources(amp_sources & stats_healthy), .von_neumann(amp_von_neumann),
|
||||
.stage_mode(amp_stage_mode), .stage_param(amp_stage_param),
|
||||
.sub_ms(amp_sub_ms), .subtrials(amp_subtrials), .restart(amp_restart),
|
||||
.read_index(amp_index), .read_byte(amp_byte), .trial_end(amp_trial_end)
|
||||
);
|
||||
|
||||
stats_block #(.CHANNELS(GENERATORS + 1), .TICK_CYCLES(TICK_CYCLES)) stats (
|
||||
.clk(clk), .reset(reset), .bits(stats_bits), .valid(stats_valid),
|
||||
.von_neumann(stats_von_neumann), .trial_ms(trial_ms), .restart(stats_restart),
|
||||
|
|
@ -196,7 +226,7 @@ module strix_core #(
|
|||
|
||||
wire selected, rx_valid, rx_is_first, tx_loaded;
|
||||
wire [7:0] rx_byte;
|
||||
reg [7:0] tx_byte;
|
||||
reg [7:0] tx_byte, tx_byte_next;
|
||||
|
||||
spi_slave spi (
|
||||
.clk(clk), .reset(reset),
|
||||
|
|
@ -233,47 +263,67 @@ module strix_core #(
|
|||
|
||||
always @(*) begin
|
||||
case (reg_addr)
|
||||
REG_ID0: tx_byte = "S";
|
||||
REG_ID1: tx_byte = "T";
|
||||
REG_ID2: tx_byte = "R";
|
||||
REG_ID3: tx_byte = "X";
|
||||
REG_VERSION: tx_byte = VERSION;
|
||||
REG_FREQ_GATE: tx_byte = FREQ_GATE_BITS;
|
||||
REG_GENERATORS: tx_byte = GENERATORS;
|
||||
REG_RINGS: tx_byte = RINGS_PER_GEN;
|
||||
REG_SOURCE_A: tx_byte = {4'd0, source_a};
|
||||
REG_DECIM_HIGH: tx_byte = decimation[15:8];
|
||||
REG_DECIM_LOW: tx_byte = decimation[7:0];
|
||||
REG_STATUS: tx_byte = {4'd0, freq_valid, freq_busy, capture_done, capture_busy};
|
||||
REG_SOURCE_B: tx_byte = {4'd0, source_b};
|
||||
REG_MODE: tx_byte = {7'd0, pair_mode};
|
||||
REG_DATA: tx_byte = capture_byte;
|
||||
REG_RING_SELECT: tx_byte = ring_select;
|
||||
REG_FREQ_COUNT3: tx_byte = freq_count[31:24];
|
||||
REG_FREQ_COUNT2: tx_byte = freq_count[23:16];
|
||||
REG_FREQ_COUNT1: tx_byte = freq_count[15:8];
|
||||
REG_FREQ_COUNT0: tx_byte = freq_count[7:0];
|
||||
REG_GEN_ENABLE: tx_byte = {4'd0, gen_enable};
|
||||
REG_TEMPORAL: tx_byte = {4'd0, temporal};
|
||||
REG_FREQ_GEN: tx_byte = {6'd0, freq_gen};
|
||||
REG_HEALTHY: tx_byte = {4'd0, healthy};
|
||||
REG_RCT_CUTOFF: tx_byte = rct_cutoff;
|
||||
REG_APT_HIGH: tx_byte = {5'd0, apt_cutoff[10:8]};
|
||||
REG_APT_LOW: tx_byte = apt_cutoff[7:0];
|
||||
REG_MAX_DEAD: tx_byte = max_dead;
|
||||
REG_TRIAL_HIGH: tx_byte = trial_ms[15:8];
|
||||
REG_TRIAL_LOW: tx_byte = trial_ms[7:0];
|
||||
REG_STATS_CTRL: tx_byte = {7'd0, stats_von_neumann};
|
||||
REG_STATS: tx_byte = stats_byte;
|
||||
default: tx_byte = gen_register(reg_addr);
|
||||
REG_ID0: tx_byte_next = "S";
|
||||
REG_ID1: tx_byte_next = "T";
|
||||
REG_ID2: tx_byte_next = "R";
|
||||
REG_ID3: tx_byte_next = "X";
|
||||
REG_VERSION: tx_byte_next = VERSION;
|
||||
REG_FREQ_GATE: tx_byte_next = FREQ_GATE_BITS;
|
||||
REG_GENERATORS: tx_byte_next = GENERATORS;
|
||||
REG_RINGS: tx_byte_next = RINGS_PER_GEN;
|
||||
REG_SOURCE_A: tx_byte_next = {4'd0, source_a};
|
||||
REG_DECIM_HIGH: tx_byte_next = decimation[15:8];
|
||||
REG_DECIM_LOW: tx_byte_next = decimation[7:0];
|
||||
REG_STATUS: tx_byte_next = {4'd0, freq_valid, freq_busy, capture_done, capture_busy};
|
||||
REG_SOURCE_B: tx_byte_next = {4'd0, source_b};
|
||||
REG_MODE: tx_byte_next = {7'd0, pair_mode};
|
||||
REG_DATA: tx_byte_next = capture_byte;
|
||||
REG_RING_SELECT: tx_byte_next = ring_select;
|
||||
REG_FREQ_COUNT3: tx_byte_next = freq_count[31:24];
|
||||
REG_FREQ_COUNT2: tx_byte_next = freq_count[23:16];
|
||||
REG_FREQ_COUNT1: tx_byte_next = freq_count[15:8];
|
||||
REG_FREQ_COUNT0: tx_byte_next = freq_count[7:0];
|
||||
REG_GEN_ENABLE: tx_byte_next = {4'd0, gen_enable};
|
||||
REG_TEMPORAL: tx_byte_next = {4'd0, temporal};
|
||||
REG_FREQ_GEN: tx_byte_next = {6'd0, freq_gen};
|
||||
REG_HEALTHY: tx_byte_next = {4'd0, healthy};
|
||||
REG_RCT_CUTOFF: tx_byte_next = rct_cutoff;
|
||||
REG_APT_HIGH: tx_byte_next = {5'd0, apt_cutoff[10:8]};
|
||||
REG_APT_LOW: tx_byte_next = apt_cutoff[7:0];
|
||||
REG_MAX_DEAD: tx_byte_next = max_dead;
|
||||
REG_TRIAL_HIGH: tx_byte_next = trial_ms[15:8];
|
||||
REG_TRIAL_LOW: tx_byte_next = trial_ms[7:0];
|
||||
REG_STATS_CTRL: tx_byte_next = {7'd0, stats_von_neumann};
|
||||
REG_STATS: tx_byte_next = stats_byte;
|
||||
REG_AMP: tx_byte_next = amp_byte;
|
||||
REG_AMP_CTRL: tx_byte_next = {7'd0, amp_von_neumann};
|
||||
REG_AMP_SOURCES: tx_byte_next = {4'd0, amp_sources};
|
||||
REG_AMP_SUB_MS: tx_byte_next = amp_sub_ms;
|
||||
REG_AMP_SUBS: tx_byte_next = {4'd0, amp_subtrials};
|
||||
7'h54: tx_byte_next = {7'd0, amp_stage_mode[0]};
|
||||
7'h55: tx_byte_next = amp_stage_param[15:8];
|
||||
7'h56: tx_byte_next = amp_stage_param[7:0];
|
||||
7'h57: tx_byte_next = {7'd0, amp_stage_mode[1]};
|
||||
7'h58: tx_byte_next = amp_stage_param[31:24];
|
||||
7'h59: tx_byte_next = amp_stage_param[23:16];
|
||||
7'h5A: tx_byte_next = {7'd0, amp_stage_mode[2]};
|
||||
7'h5B: tx_byte_next = amp_stage_param[47:40];
|
||||
7'h5C: tx_byte_next = amp_stage_param[39:32];
|
||||
default: tx_byte_next = gen_register(reg_addr);
|
||||
endcase
|
||||
end
|
||||
|
||||
// Registered read-back: the next byte is needed only after half an SCK period, long after
|
||||
// the command byte has set reg_addr, so a cycle of latency keeps this wide multiplexer off
|
||||
// the SPI timing path.
|
||||
always @(posedge clk) tx_byte <= tx_byte_next;
|
||||
|
||||
always @(posedge clk) begin
|
||||
capture_start <= 1'b0;
|
||||
freq_start <= 1'b0;
|
||||
health_clear <= 1'b0;
|
||||
stats_restart <= 1'b0;
|
||||
amp_restart <= 1'b0;
|
||||
if (reset) begin
|
||||
gen_enable <= {GENERATORS{1'b1}};
|
||||
temporal <= 4'd5;
|
||||
|
|
@ -290,11 +340,23 @@ module strix_core #(
|
|||
trial_ms <= 16'd200;
|
||||
stats_von_neumann <= 1'b0;
|
||||
stats_index <= 8'd0;
|
||||
amp_sources <= {GENERATORS{1'b1}};
|
||||
amp_von_neumann <= 1'b0;
|
||||
amp_sub_ms <= 8'd40;
|
||||
amp_subtrials <= 4'd5;
|
||||
amp_stage_mode <= 3'b000;
|
||||
amp_stage_param <= {16'd4, 16'd8, 16'd8}; // random walk 8 x 8 x 4 = bound 256
|
||||
amp_index <= 8'd0;
|
||||
is_read <= 1'b0;
|
||||
reg_addr <= 7'd0;
|
||||
bytes_read <= {CAPTURE_ADDR_WIDTH{1'b0}};
|
||||
prefetch_addr <= {CAPTURE_ADDR_WIDTH{1'b0}};
|
||||
end else begin
|
||||
if (!selected)
|
||||
amp_index <= 8'd0;
|
||||
else if (tx_loaded && is_read && reg_addr == REG_AMP)
|
||||
amp_index <= amp_index + 8'd1;
|
||||
|
||||
if (!selected)
|
||||
stats_index <= 8'd0;
|
||||
else if (tx_loaded && is_read && reg_addr == REG_STATS)
|
||||
|
|
@ -329,6 +391,22 @@ module strix_core #(
|
|||
REG_MAX_DEAD: max_dead <= rx_byte;
|
||||
REG_TRIAL_HIGH: trial_ms[15:8] <= rx_byte;
|
||||
REG_TRIAL_LOW: trial_ms[7:0] <= rx_byte;
|
||||
REG_AMP_CTRL: begin
|
||||
amp_von_neumann <= rx_byte[0];
|
||||
amp_restart <= rx_byte[1];
|
||||
end
|
||||
REG_AMP_SOURCES: amp_sources <= rx_byte[GENERATORS-1:0];
|
||||
REG_AMP_SUB_MS: if (rx_byte != 8'd0) amp_sub_ms <= rx_byte;
|
||||
REG_AMP_SUBS: if (rx_byte >= 8'd1 && rx_byte <= 8'd15) amp_subtrials <= rx_byte[3:0];
|
||||
7'h54: amp_stage_mode[0] <= rx_byte[0];
|
||||
7'h55: amp_stage_param[15:8] <= rx_byte;
|
||||
7'h56: amp_stage_param[7:0] <= rx_byte;
|
||||
7'h57: amp_stage_mode[1] <= rx_byte[0];
|
||||
7'h58: amp_stage_param[31:24] <= rx_byte;
|
||||
7'h59: amp_stage_param[23:16] <= rx_byte;
|
||||
7'h5A: amp_stage_mode[2] <= rx_byte[0];
|
||||
7'h5B: amp_stage_param[47:40] <= rx_byte;
|
||||
7'h5C: amp_stage_param[39:32] <= rx_byte;
|
||||
REG_STATS_CTRL: begin
|
||||
stats_von_neumann <= rx_byte[0];
|
||||
stats_restart <= rx_byte[1];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue