feat: add entropy byte stream FIFO and faster SPI read-back (FPGA v7)
This commit is contained in:
parent
8541cce59c
commit
7777384495
6 changed files with 232 additions and 38 deletions
|
|
@ -1,6 +1,6 @@
|
|||
// Board-independent STRIX logic: entropy generators with health monitors, live statistics,
|
||||
// bias amplifier, source selection, raw capture, frequency counter and the SPI register
|
||||
// interface to the SAMD21.
|
||||
// Board-independent STRIX logic: entropy generators with health monitors, entropy byte
|
||||
// stream, live statistics, 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
|
||||
|
|
@ -20,7 +20,9 @@
|
|||
// 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. AMP does the same for
|
||||
// the bias amplifier's trial snapshot (layout in bias_amplifier.v).
|
||||
// the bias amplifier's trial snapshot (layout in bias_amplifier.v). Reading STREAM returns
|
||||
// entropy bytes from the stream FIFO, removing each byte once it has been clocked out; read
|
||||
// at most STREAM_LEVEL bytes (an empty FIFO returns 0x00).
|
||||
module strix_core #(
|
||||
parameter CAPTURE_ADDR_WIDTH = 15,
|
||||
parameter FREQ_GATE_BITS = 24,
|
||||
|
|
@ -37,7 +39,7 @@ module strix_core #(
|
|||
output wire capture_done
|
||||
);
|
||||
|
||||
localparam [7:0] VERSION = 8'h06;
|
||||
localparam [7:0] VERSION = 8'h07;
|
||||
localparam GENERATORS = 4;
|
||||
|
||||
localparam [6:0] REG_ID0 = 7'h00, // "STRX"
|
||||
|
|
@ -76,7 +78,14 @@ 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_STREAM_CTRL = 7'h29, // bits 2:0 source (0-3 generator, 4 combined),
|
||||
// bit 3 von Neumann; write bit 7: flush
|
||||
REG_LEVEL_HIGH = 7'h2A, // bytes waiting in the stream FIFO
|
||||
REG_LEVEL_LOW = 7'h2B,
|
||||
REG_DROP_HIGH = 7'h2C, // bytes dropped because the FIFO was full
|
||||
REG_DROP_LOW = 7'h2D,
|
||||
REG_STATS = 7'h40, // burst read of the statistics trial snapshot
|
||||
REG_STREAM = 7'h44, // burst read of entropy bytes
|
||||
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
|
||||
|
|
@ -156,6 +165,26 @@ module strix_core #(
|
|||
stats_valid <= out_valid[0];
|
||||
end
|
||||
|
||||
// Entropy byte stream, fed from the same registered outputs. A generator that is not
|
||||
// healthy (or no healthy generator, for the combined channel) feeds nothing.
|
||||
|
||||
reg [2:0] stream_source;
|
||||
reg stream_von_neumann, stream_flush, stream_pop;
|
||||
wire [7:0] stream_head;
|
||||
wire stream_available;
|
||||
wire [12:0] stream_level;
|
||||
wire [15:0] stream_dropped;
|
||||
wire stream_source_healthy = stream_source == 3'd4 ? |stats_healthy
|
||||
: stats_healthy[stream_source[1:0]];
|
||||
|
||||
entropy_stream #(.ADDR_WIDTH(12)) stream (
|
||||
.clk(clk), .reset(reset), .flush(stream_flush),
|
||||
.bit_in(stats_bits[stream_source]), .valid(stats_valid && stream_source_healthy),
|
||||
.von_neumann(stream_von_neumann), .pop(stream_pop),
|
||||
.head(stream_head), .available(stream_available), .level(stream_level),
|
||||
.dropped(stream_dropped)
|
||||
);
|
||||
|
||||
// Bias amplifier, fed from the same registered generator outputs
|
||||
|
||||
reg [GENERATORS-1:0] amp_sources;
|
||||
|
|
@ -277,7 +306,6 @@ module strix_core #(
|
|||
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];
|
||||
|
|
@ -294,8 +322,11 @@ module strix_core #(
|
|||
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_STREAM_CTRL: tx_byte_next = {4'd0, stream_von_neumann, stream_source};
|
||||
REG_LEVEL_HIGH: tx_byte_next = {3'd0, stream_level[12:8]};
|
||||
REG_LEVEL_LOW: tx_byte_next = stream_level[7:0];
|
||||
REG_DROP_HIGH: tx_byte_next = stream_dropped[15:8];
|
||||
REG_DROP_LOW: tx_byte_next = stream_dropped[7:0];
|
||||
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;
|
||||
|
|
@ -313,10 +344,32 @@ module strix_core #(
|
|||
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;
|
||||
// Registered read-back. The next byte is needed only half an SCK period after the previous
|
||||
// one completes. Plain registers are static, so their wide multiplexer gets an extra
|
||||
// pipeline stage; burst registers, whose bytes change while they are read, take a short
|
||||
// path.
|
||||
reg [7:0] register_byte;
|
||||
reg [7:0] burst_byte;
|
||||
reg is_burst;
|
||||
|
||||
always @(*) begin
|
||||
is_burst = 1'b1;
|
||||
case (reg_addr)
|
||||
REG_DATA: burst_byte = capture_byte;
|
||||
REG_STATS: burst_byte = stats_byte;
|
||||
REG_AMP: burst_byte = amp_byte;
|
||||
REG_STREAM: burst_byte = stream_available ? stream_head : 8'h00;
|
||||
default: begin
|
||||
burst_byte = 8'h00;
|
||||
is_burst = 1'b0;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
register_byte <= tx_byte_next;
|
||||
tx_byte <= is_burst ? burst_byte : register_byte;
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
capture_start <= 1'b0;
|
||||
|
|
@ -324,6 +377,8 @@ module strix_core #(
|
|||
health_clear <= 1'b0;
|
||||
stats_restart <= 1'b0;
|
||||
amp_restart <= 1'b0;
|
||||
stream_flush <= 1'b0;
|
||||
stream_pop <= 1'b0;
|
||||
if (reset) begin
|
||||
gen_enable <= {GENERATORS{1'b1}};
|
||||
temporal <= 4'd5;
|
||||
|
|
@ -347,6 +402,8 @@ module strix_core #(
|
|||
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;
|
||||
stream_source <= 3'd4;
|
||||
stream_von_neumann <= 1'b0;
|
||||
is_read <= 1'b0;
|
||||
reg_addr <= 7'd0;
|
||||
bytes_read <= {CAPTURE_ADDR_WIDTH{1'b0}};
|
||||
|
|
@ -373,6 +430,8 @@ module strix_core #(
|
|||
reg_addr <= rx_byte[6:0];
|
||||
end else if (reading_data) begin
|
||||
bytes_read <= bytes_read + 1'b1;
|
||||
end else if (is_read && reg_addr == REG_STREAM) begin
|
||||
stream_pop <= 1'b1; // the byte has been clocked out
|
||||
end else if (!is_read) begin
|
||||
case (reg_addr)
|
||||
REG_SOURCE_A: source_a <= rx_byte[3:0];
|
||||
|
|
@ -391,6 +450,11 @@ 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_STREAM_CTRL: begin
|
||||
if (rx_byte[2:0] <= 3'd4) stream_source <= rx_byte[2:0];
|
||||
stream_von_neumann <= rx_byte[3];
|
||||
stream_flush <= rx_byte[7];
|
||||
end
|
||||
REG_AMP_CTRL: begin
|
||||
amp_von_neumann <= rx_byte[0];
|
||||
amp_restart <= rx_byte[1];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue