fix: hand GPIO0 to the WiFi module, count only healthy generators, shorten amplifier stage path (FPGA v9)
This commit is contained in:
parent
2a96333aef
commit
5c69d826a2
7 changed files with 61 additions and 31 deletions
|
|
@ -1,4 +1,4 @@
|
|||
// STRIX FPGA register map (FPGA version 8). Mirrors the definitions in fpga/rtl/strix_core.v,
|
||||
// STRIX FPGA register map (FPGA version 9). Mirrors the definitions in fpga/rtl/strix_core.v,
|
||||
// which also documents the SPI protocol and snapshot layouts.
|
||||
#pragma once
|
||||
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
namespace strix {
|
||||
|
||||
const uint8_t FPGA_VERSION = 8;
|
||||
const uint8_t FPGA_VERSION = 9;
|
||||
const uint8_t READ = 0x80; // command bit 7: read
|
||||
|
||||
enum Register : uint8_t {
|
||||
|
|
@ -42,7 +42,7 @@ enum Register : uint8_t {
|
|||
REG_LEVEL_LOW = 0x2B,
|
||||
REG_DROP_HIGH = 0x2C,
|
||||
REG_DROP_LOW = 0x2D,
|
||||
REG_NINA_CTRL = 0x2E, // bit 0: WiFi module may run
|
||||
REG_NINA_CTRL = 0x2E, // bit 0: WiFi module may run; bit 1: GPIO0 driven by the module
|
||||
REG_GEN_HEALTH = 0x30, // + 4g: flags, RCT events, APT events, dead rings
|
||||
REG_STATS = 0x40,
|
||||
REG_AMP = 0x42,
|
||||
|
|
@ -61,6 +61,7 @@ const uint8_t STATS_VON_NEUMANN = 0x01, STATS_RESTART = 0x02;
|
|||
const uint8_t STREAM_VON_NEUMANN = 0x08, STREAM_FLUSH = 0x80, STREAM_COMBINED = 4;
|
||||
const uint8_t AMP_VON_NEUMANN = 0x01, AMP_RESTART = 0x02;
|
||||
const uint8_t HEALTH_CLEAR = 0x01;
|
||||
const uint8_t NINA_ENABLE = 0x01, NINA_GPIO0_FROM_MODULE = 0x02;
|
||||
|
||||
// Per-generator health flags (REG_GEN_HEALTH + 4g)
|
||||
const uint8_t FLAG_RCT_FAILED = 0x01, FLAG_APT_FAILED = 0x02, FLAG_TOO_MANY_DEAD = 0x04,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@
|
|||
// Majority vote (mode 1): every param input bits produce one output bit, 1 if d > 0 (use odd
|
||||
// windows to avoid ties, which output 0).
|
||||
//
|
||||
// param = 1 passes bits through unchanged in both modes.
|
||||
// param = 1 passes bits through unchanged in both modes. The end conditions compare the current
|
||||
// position and count against registered limits, keeping the adder off the decision path.
|
||||
module amp_stage (
|
||||
input wire clk,
|
||||
input wire restart,
|
||||
|
|
@ -21,11 +22,21 @@ module amp_stage (
|
|||
reg signed [16:0] position;
|
||||
reg [15:0] seen;
|
||||
|
||||
wire signed [16:0] next_position = position + (in_bit ? 17'sd1 : -17'sd1);
|
||||
wire signed [16:0] bound = {1'b0, param};
|
||||
wire walk_done = next_position == bound || next_position == -bound;
|
||||
wire vote_done = seen == param - 16'd1;
|
||||
// Limits derived from param, registered (param changes only between runs, with a restart).
|
||||
reg signed [16:0] upper_last, lower_last; // +(param - 1) and -(param - 1)
|
||||
reg [15:0] window_last; // param - 1
|
||||
always @(posedge clk) begin
|
||||
upper_last <= {1'b0, param} - 17'sd1;
|
||||
lower_last <= 17'sd1 - {1'b0, param};
|
||||
window_last <= param - 16'd1;
|
||||
end
|
||||
|
||||
// A step reaches the bound when the walk already stands one short of it in that direction.
|
||||
wire walk_done = in_bit ? position == upper_last : position == lower_last;
|
||||
wire vote_done = seen == window_last;
|
||||
wire done = mode ? vote_done : walk_done;
|
||||
// Majority after this bit: d + 1 > 0 for a one, d - 1 > 0 for a zero.
|
||||
wire vote_one = in_bit ? !position[16] : (!position[16] && position > 17'sd1);
|
||||
|
||||
always @(posedge clk) begin
|
||||
out_valid <= 1'b0;
|
||||
|
|
@ -35,11 +46,11 @@ module amp_stage (
|
|||
end else if (in_valid) begin
|
||||
if (done) begin
|
||||
out_valid <= 1'b1;
|
||||
out_bit <= next_position > 0;
|
||||
out_bit <= mode ? vote_one : in_bit;
|
||||
position <= 17'sd0;
|
||||
seen <= 16'd0;
|
||||
end else begin
|
||||
position <= next_position;
|
||||
position <= position + (in_bit ? 17'sd1 : -17'sd1);
|
||||
seen <= seen + 16'd1;
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ module stats_block #(
|
|||
input wire clk,
|
||||
input wire reset,
|
||||
input wire [CHANNELS-1:0] bits,
|
||||
input wire valid, // new bits on all channels this cycle
|
||||
input wire [CHANNELS-1:0] valid, // new bit on each channel this cycle
|
||||
input wire von_neumann,
|
||||
input wire [15:0] trial_ms,
|
||||
input wire restart, // start a new trial now, sequence back to 0
|
||||
|
|
@ -61,17 +61,20 @@ module stats_block #(
|
|||
// 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] vn_valid = valid & have_first & (bits ^ pair_first);
|
||||
wire [CHANNELS-1:0] count_valid = von_neumann ? vn_valid : valid;
|
||||
wire [CHANNELS-1:0] count_bit = von_neumann ? pair_first : bits;
|
||||
|
||||
integer v;
|
||||
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 else begin
|
||||
for (v = 0; v < CHANNELS; v = v + 1)
|
||||
if (valid[v]) begin
|
||||
have_first[v] <= ~have_first[v];
|
||||
pair_first[v] <= bits[v];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ module strix (
|
|||
|
||||
// NINA pass-through: the SAMD21 talks to the WiFi module as if wired directly.
|
||||
|
||||
wire nina_enable; // from strix_core
|
||||
wire nina_enable, nina_gpio0_in; // from strix_core
|
||||
|
||||
wire sam_nina_mosi = bMKR_D[8]; // SERCOM1
|
||||
wire sam_nina_sck = bMKR_D[9];
|
||||
|
|
@ -46,7 +46,9 @@ module strix (
|
|||
assign bWM_PIO36 = sam_nina_mosi;
|
||||
assign bWM_PIO29 = sam_nina_sck;
|
||||
assign bWM_PIO28 = sam_nina_ss;
|
||||
assign bWM_PIO27 = sam_nina_gpio0;
|
||||
// GPIO0 is bidirectional on the module: the SAMD21 drives it to select the boot mode, the
|
||||
// running module drives it as its data-ready signal. Only one side drives at a time.
|
||||
assign bWM_PIO27 = nina_gpio0_in ? 1'bz : sam_nina_gpio0;
|
||||
// The module stays in reset until the firmware enables it: its boot current on top of the
|
||||
// ring oscillators can brown out the board, and it is not needed without WiFi.
|
||||
assign oWM_RESET = nina_enable ? sam_nina_resetn : 1'b0;
|
||||
|
|
@ -57,6 +59,7 @@ module strix (
|
|||
wire nina_miso = bWM_PIO1; // to D10
|
||||
wire nina_ack = bWM_PIO7; // to D4
|
||||
wire nina_uart_tx = iWM_TX; // to D13
|
||||
wire nina_gpio0 = bWM_PIO27; // to D6 while the module drives it
|
||||
|
||||
// STRIX SPI link on SAMD21 SERCOM0 alternate pins: A3 MOSI, A4 SCK, A5 SS, A6 MISO.
|
||||
|
||||
|
|
@ -68,7 +71,8 @@ module strix (
|
|||
strix_core core (
|
||||
.clk(clk), .reset(reset),
|
||||
.spi_sck(strix_sck), .spi_mosi(strix_mosi), .spi_ss_n(strix_ss_n),
|
||||
.spi_miso(strix_miso), .capture_done(capture_done), .nina_enable(nina_enable)
|
||||
.spi_miso(strix_miso), .capture_done(capture_done), .nina_enable(nina_enable),
|
||||
.nina_gpio0_in(nina_gpio0_in)
|
||||
);
|
||||
|
||||
assign oSAM_INT = capture_done;
|
||||
|
|
@ -79,6 +83,7 @@ module strix (
|
|||
generate
|
||||
for (d = 0; d < 15; d = d + 1) begin : header_d
|
||||
assign bMKR_D[d] = (d == 4) ? nina_ack :
|
||||
(d == 6) ? (nina_gpio0_in ? nina_gpio0 : 1'bz) :
|
||||
(d == 10) ? nina_miso :
|
||||
(d == 13) ? nina_uart_tx : 1'bz;
|
||||
end
|
||||
|
|
|
|||
|
|
@ -37,10 +37,11 @@ module strix_core #(
|
|||
input wire spi_ss_n,
|
||||
output wire spi_miso,
|
||||
output wire capture_done,
|
||||
output reg nina_enable // releases the WiFi module from reset (see REG_NINA_CTRL)
|
||||
output reg nina_enable, // releases the WiFi module from reset (see REG_NINA_CTRL)
|
||||
output reg nina_gpio0_in // GPIO0 is driven by the module (to the SAMD21), not to it
|
||||
);
|
||||
|
||||
localparam [7:0] VERSION = 8'h08;
|
||||
localparam [7:0] VERSION = 8'h09;
|
||||
localparam GENERATORS = 4;
|
||||
|
||||
localparam [6:0] REG_ID0 = 7'h00, // "STRX"
|
||||
|
|
@ -85,7 +86,10 @@ module strix_core #(
|
|||
REG_LEVEL_LOW = 7'h2B,
|
||||
REG_DROP_HIGH = 7'h2C, // bytes dropped because the FIFO was full
|
||||
REG_DROP_LOW = 7'h2D,
|
||||
REG_NINA_CTRL = 7'h2E, // bit 0: WiFi module may run (otherwise held in reset)
|
||||
REG_NINA_CTRL = 7'h2E, // bit 0: WiFi module may run (otherwise held in reset);
|
||||
// bit 1: GPIO0 direction, 0 SAMD21 -> module (boot
|
||||
// mode selection), 1 module -> SAMD21 (its data-ready
|
||||
// signal once running)
|
||||
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
|
||||
|
|
@ -208,7 +212,9 @@ module strix_core #(
|
|||
);
|
||||
|
||||
stats_block #(.CHANNELS(GENERATORS + 1), .TICK_CYCLES(TICK_CYCLES)) stats (
|
||||
.clk(clk), .reset(reset), .bits(stats_bits), .valid(stats_valid),
|
||||
// Only healthy generators are counted; the combined channel while any is healthy.
|
||||
.clk(clk), .reset(reset), .bits(stats_bits),
|
||||
.valid({GENERATORS + 1{stats_valid}} & {|stats_healthy, stats_healthy}),
|
||||
.von_neumann(stats_von_neumann), .trial_ms(trial_ms), .restart(stats_restart),
|
||||
.healthy(stats_healthy), .read_index(stats_index), .read_byte(stats_byte),
|
||||
.trial_end(trial_end)
|
||||
|
|
@ -329,7 +335,7 @@ module strix_core #(
|
|||
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_NINA_CTRL: tx_byte_next = {7'd0, nina_enable};
|
||||
REG_NINA_CTRL: tx_byte_next = {6'd0, nina_gpio0_in, nina_enable};
|
||||
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;
|
||||
|
|
@ -407,6 +413,7 @@ module strix_core #(
|
|||
amp_index <= 8'd0;
|
||||
stream_source <= 3'd4;
|
||||
nina_enable <= 1'b0;
|
||||
nina_gpio0_in <= 1'b0;
|
||||
stream_von_neumann <= 1'b0;
|
||||
is_read <= 1'b0;
|
||||
reg_addr <= 7'd0;
|
||||
|
|
@ -454,7 +461,10 @@ 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_NINA_CTRL: nina_enable <= rx_byte[0];
|
||||
REG_NINA_CTRL: begin
|
||||
nina_enable <= rx_byte[0];
|
||||
nina_gpio0_in <= rx_byte[1];
|
||||
end
|
||||
REG_STREAM_CTRL: begin
|
||||
if (rx_byte[2:0] <= 3'd4) stream_source <= rx_byte[2:0];
|
||||
stream_von_neumann <= rx_byte[3];
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ module tb_strix_core;
|
|||
model_latched_ones <= model_ones;
|
||||
model_latched_agree <= model_agree;
|
||||
end
|
||||
if (dut.stats.valid) begin
|
||||
if (dut.stats.valid[0]) begin
|
||||
model_bits <= (dut.trial_end ? 0 : model_bits) + 1;
|
||||
model_ones <= (dut.trial_end ? 0 : model_ones) + dut.stats.bits[0];
|
||||
model_agree <= (dut.trial_end ? 0 : model_agree)
|
||||
|
|
@ -282,7 +282,7 @@ module tb_strix_core;
|
|||
// Identification
|
||||
read_reg(REG_ID0, v); expect(v, "S", "ID0");
|
||||
read_reg(REG_ID3, v); expect(v, "X", "ID3");
|
||||
read_reg(REG_VERSION, v); expect(v, 8'h08, "VERSION");
|
||||
read_reg(REG_VERSION, v); expect(v, 8'h09, "VERSION");
|
||||
read_reg(REG_GENERATORS, v); expect(v, 8'd4, "GENERATORS");
|
||||
read_reg(REG_RINGS, v); expect(v, RINGS, "RINGS");
|
||||
read_reg(REG_TEMPORAL, v); expect(v, 8'd5, "TEMPORAL");
|
||||
|
|
|
|||
|
|
@ -82,8 +82,8 @@ class Board:
|
|||
while self.serial.readline():
|
||||
pass
|
||||
self.serial.timeout = 200
|
||||
if not identity.startswith("STRX v8"):
|
||||
sys.exit(f"unexpected board identity: {identity!r} (needs the v8 FPGA image)")
|
||||
if not identity.startswith("STRX v9"):
|
||||
sys.exit(f"unexpected board identity: {identity!r} (needs the v9 FPGA image)")
|
||||
self.generators = self.read(REG_GENERATORS)
|
||||
self.rings = self.read(REG_RINGS)
|
||||
print(f"board: {identity}, {self.generators} generators x {self.rings} rings")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue