diff --git a/arduino/libraries/StrixFPGA/src/StrixRegisters.h b/arduino/libraries/StrixFPGA/src/StrixRegisters.h index 845f373..54d6e5b 100644 --- a/arduino/libraries/StrixFPGA/src/StrixRegisters.h +++ b/arduino/libraries/StrixFPGA/src/StrixRegisters.h @@ -1,4 +1,4 @@ -// STRIX FPGA register map (FPGA version 7). Mirrors the definitions in fpga/rtl/strix_core.v, +// STRIX FPGA register map (FPGA version 8). 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 = 7; +const uint8_t FPGA_VERSION = 8; const uint8_t READ = 0x80; // command bit 7: read enum Register : uint8_t { @@ -42,6 +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_GEN_HEALTH = 0x30, // + 4g: flags, RCT events, APT events, dead rings REG_STATS = 0x40, REG_AMP = 0x42, diff --git a/fpga/rtl/strix.v b/fpga/rtl/strix.v index 9b0b0b1..32f6973 100644 --- a/fpga/rtl/strix.v +++ b/fpga/rtl/strix.v @@ -34,6 +34,8 @@ module strix ( // NINA pass-through: the SAMD21 talks to the WiFi module as if wired directly. + wire nina_enable; // from strix_core + wire sam_nina_mosi = bMKR_D[8]; // SERCOM1 wire sam_nina_sck = bMKR_D[9]; wire sam_nina_ss = bMKR_D[5]; @@ -45,7 +47,9 @@ module strix ( assign bWM_PIO29 = sam_nina_sck; assign bWM_PIO28 = sam_nina_ss; assign bWM_PIO27 = sam_nina_gpio0; - assign oWM_RESET = sam_nina_resetn; + // 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; assign oWM_RX = sam_uart_tx; assign bWM_PIO1 = 1'bz; // inputs from NINA assign bWM_PIO7 = 1'bz; @@ -64,7 +68,7 @@ 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) + .spi_miso(strix_miso), .capture_done(capture_done), .nina_enable(nina_enable) ); assign oSAM_INT = capture_done; diff --git a/fpga/rtl/strix_core.v b/fpga/rtl/strix_core.v index 2957977..bb931e1 100644 --- a/fpga/rtl/strix_core.v +++ b/fpga/rtl/strix_core.v @@ -36,10 +36,11 @@ module strix_core #( input wire spi_mosi, input wire spi_ss_n, output wire spi_miso, - output wire capture_done + output wire capture_done, + output reg nina_enable // releases the WiFi module from reset (see REG_NINA_CTRL) ); - localparam [7:0] VERSION = 8'h07; + localparam [7:0] VERSION = 8'h08; localparam GENERATORS = 4; localparam [6:0] REG_ID0 = 7'h00, // "STRX" @@ -84,6 +85,7 @@ 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_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 @@ -327,6 +329,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_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; @@ -403,6 +406,7 @@ module strix_core #( 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; + nina_enable <= 1'b0; stream_von_neumann <= 1'b0; is_read <= 1'b0; reg_addr <= 7'd0; @@ -450,6 +454,7 @@ 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_STREAM_CTRL: begin if (rx_byte[2:0] <= 3'd4) stream_source <= rx_byte[2:0]; stream_von_neumann <= rx_byte[3]; diff --git a/fpga/sim/tb_strix_core.v b/fpga/sim/tb_strix_core.v index 8c97901..9880aa3 100644 --- a/fpga/sim/tb_strix_core.v +++ b/fpga/sim/tb_strix_core.v @@ -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'h07, "VERSION"); + read_reg(REG_VERSION, v); expect(v, 8'h08, "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"); diff --git a/fpga/src/strix.qsf b/fpga/src/strix.qsf index a0aaa9b..6840487 100644 --- a/fpga/src/strix.qsf +++ b/fpga/src/strix.qsf @@ -87,6 +87,11 @@ set_global_assignment -name CONFIGURATION_VCCIO_LEVEL AUTO set_global_assignment -name ON_CHIP_BITSTREAM_DECOMPRESSION ON set_global_assignment -name GENERATE_TTF_FILE ON +# Weak pull-ups keep the NINA module's SS and GPIO0 (boot mode) high while the SAMD21 leaves +# these pass-through pins floating. Its reset is gated in the FPGA (REG_NINA_CTRL). +set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to bMKR_D[5] +set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to bMKR_D[6] + set_location_assignment PIN_E2 -to iCLK set_location_assignment PIN_E1 -to iRESETn set_location_assignment PIN_E10 -to oSDRAM_ADDR[11] diff --git a/tools/jitter.py b/tools/jitter.py index 411e76d..6fb4333 100644 --- a/tools/jitter.py +++ b/tools/jitter.py @@ -82,8 +82,8 @@ class Board: while self.serial.readline(): pass self.serial.timeout = 200 - if not identity.startswith("STRX v7"): - sys.exit(f"unexpected board identity: {identity!r} (needs the v7 FPGA image)") + if not identity.startswith("STRX v8"): + sys.exit(f"unexpected board identity: {identity!r} (needs the v8 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")