219 lines
9.2 KiB
Verilog
219 lines
9.2 KiB
Verilog
// Board-independent STRIX logic: entropy generators, 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
|
|
// byte(s) in that register. A read returns the register in every following byte; DATA
|
|
// advances through the capture buffer with each byte, continuing across transactions.
|
|
//
|
|
// Capture sources, for generator g = 0..3:
|
|
// 0x00+g processed output (spatial XOR, then temporal XOR; capture with decimation = TEMPORAL)
|
|
// 0x04+g spatial XOR of all rings, every clock
|
|
// 0x08+g enhanced output of ring RING_SELECT
|
|
// 0x0C+g single tap of ring RING_SELECT
|
|
module strix_core #(
|
|
parameter CAPTURE_ADDR_WIDTH = 15,
|
|
parameter FREQ_GATE_BITS = 24,
|
|
parameter RINGS_PER_GEN = 90
|
|
) (
|
|
input wire clk,
|
|
input wire reset,
|
|
input wire spi_sck,
|
|
input wire spi_mosi,
|
|
input wire spi_ss_n,
|
|
output wire spi_miso,
|
|
output wire capture_done
|
|
);
|
|
|
|
localparam [7:0] VERSION = 8'h03;
|
|
localparam GENERATORS = 4;
|
|
|
|
localparam [6:0] REG_ID0 = 7'h00, // "STRX"
|
|
REG_ID1 = 7'h01,
|
|
REG_ID2 = 7'h02,
|
|
REG_ID3 = 7'h03,
|
|
REG_VERSION = 7'h04,
|
|
REG_FREQ_GATE = 7'h05, // read-only: gate is 2^value clk cycles
|
|
REG_GENERATORS = 7'h06, // read-only
|
|
REG_RINGS = 7'h07, // read-only: rings per generator
|
|
REG_SOURCE_A = 7'h08,
|
|
REG_DECIM_HIGH = 7'h09,
|
|
REG_DECIM_LOW = 7'h0A,
|
|
REG_CONTROL = 7'h0B, // write: bit 0 start capture, bit 1 rewind DATA,
|
|
// bit 2 start frequency measurement
|
|
REG_STATUS = 7'h0C, // bit 0 capture busy, bit 1 capture done,
|
|
// bit 2 frequency busy, bit 3 frequency valid
|
|
REG_SOURCE_B = 7'h0D,
|
|
REG_MODE = 7'h0E, // bit 0 pair capture (A and B per sample)
|
|
REG_DATA = 7'h10,
|
|
REG_RING_SELECT = 7'h11, // ring for the characterization sources and frequency
|
|
REG_FREQ_COUNT3 = 7'h12, // prescaled edge count, most significant byte first
|
|
REG_FREQ_COUNT2 = 7'h13,
|
|
REG_FREQ_COUNT1 = 7'h14,
|
|
REG_FREQ_COUNT0 = 7'h15,
|
|
REG_GEN_ENABLE = 7'h16, // bit g enables generator g
|
|
REG_TEMPORAL = 7'h17, // consecutive bits XORed per output bit, 1-8
|
|
REG_FREQ_GEN = 7'h18; // generator for the frequency measurement
|
|
|
|
// Generators
|
|
|
|
reg [GENERATORS-1:0] gen_enable;
|
|
reg [3:0] temporal;
|
|
reg [7:0] ring_select;
|
|
wire [GENERATORS-1:0] out_bit, spatial_bit, ring_enhanced, ring_tap, ring_raw_tap;
|
|
|
|
genvar g;
|
|
generate
|
|
for (g = 0; g < GENERATORS; g = g + 1) begin : gen
|
|
entropy_generator #(.RINGS(RINGS_PER_GEN)) generator (
|
|
.clk(clk), .enable(gen_enable[g]), .temporal(temporal), .ring_select(ring_select),
|
|
.out_bit(out_bit[g]), .spatial_bit(spatial_bit[g]),
|
|
.ring_enhanced(ring_enhanced[g]), .ring_tap(ring_tap[g]),
|
|
.ring_raw_tap(ring_raw_tap[g])
|
|
);
|
|
end
|
|
endgenerate
|
|
|
|
// Source selection
|
|
|
|
reg [3:0] source_a, source_b;
|
|
reg bit_a, bit_b;
|
|
wire [15:0] all_sources = {ring_tap, ring_enhanced, spatial_bit, out_bit};
|
|
|
|
always @(posedge clk) begin
|
|
bit_a <= all_sources[source_a];
|
|
bit_b <= all_sources[source_b];
|
|
end
|
|
|
|
// Capture
|
|
|
|
reg pair_mode;
|
|
reg [15:0] decimation;
|
|
reg capture_start;
|
|
wire capture_busy;
|
|
reg [CAPTURE_ADDR_WIDTH-1:0] prefetch_addr;
|
|
wire [7:0] capture_byte;
|
|
|
|
raw_capture #(.ADDR_WIDTH(CAPTURE_ADDR_WIDTH)) capture (
|
|
.clk(clk), .reset(reset), .bit_a(bit_a), .bit_b(bit_b), .pair_mode(pair_mode),
|
|
.decimation(decimation), .start(capture_start),
|
|
.busy(capture_busy), .done(capture_done),
|
|
.read_addr(prefetch_addr), .read_data(capture_byte)
|
|
);
|
|
|
|
// Frequency counter
|
|
|
|
reg [1:0] freq_gen;
|
|
reg freq_start;
|
|
wire freq_busy, freq_valid;
|
|
wire [31:0] freq_count;
|
|
|
|
freq_counter #(.GATE_BITS(FREQ_GATE_BITS)) counter (
|
|
.clk(clk), .reset(reset), .signal(ring_raw_tap[freq_gen]), .start(freq_start),
|
|
.busy(freq_busy), .valid(freq_valid), .count(freq_count)
|
|
);
|
|
|
|
// SPI
|
|
|
|
wire selected, rx_valid, rx_is_first, tx_loaded;
|
|
wire [7:0] rx_byte;
|
|
reg [7:0] tx_byte;
|
|
|
|
spi_slave spi (
|
|
.clk(clk), .reset(reset),
|
|
.sck(spi_sck), .mosi(spi_mosi), .ss_n(spi_ss_n), .miso(spi_miso),
|
|
.selected(selected), .rx_valid(rx_valid), .rx_byte(rx_byte),
|
|
.rx_is_first(rx_is_first), .tx_byte(tx_byte), .tx_loaded(tx_loaded)
|
|
);
|
|
|
|
reg is_read;
|
|
reg [6:0] reg_addr;
|
|
|
|
// DATA bytes are prefetched one ahead: prefetch_addr moves when a byte is loaded for
|
|
// shifting, bytes_read only when it has been fully clocked out. Between transactions
|
|
// prefetch_addr rewinds to bytes_read so the trailing prefetch is not lost.
|
|
reg [CAPTURE_ADDR_WIDTH-1:0] bytes_read;
|
|
|
|
wire reading_data = is_read && reg_addr == REG_DATA;
|
|
|
|
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};
|
|
default: tx_byte = 8'h00;
|
|
endcase
|
|
end
|
|
|
|
always @(posedge clk) begin
|
|
capture_start <= 1'b0;
|
|
freq_start <= 1'b0;
|
|
if (reset) begin
|
|
gen_enable <= {GENERATORS{1'b1}};
|
|
temporal <= 4'd5;
|
|
ring_select <= 8'd0;
|
|
source_a <= 4'h0;
|
|
source_b <= 4'h1;
|
|
pair_mode <= 1'b0;
|
|
decimation <= 16'd5;
|
|
freq_gen <= 2'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)
|
|
prefetch_addr <= bytes_read;
|
|
else if (tx_loaded && reading_data)
|
|
prefetch_addr <= prefetch_addr + 1'b1;
|
|
|
|
if (rx_valid) begin
|
|
if (rx_is_first) begin
|
|
is_read <= rx_byte[7];
|
|
reg_addr <= rx_byte[6:0];
|
|
end else if (reading_data) begin
|
|
bytes_read <= bytes_read + 1'b1;
|
|
end else if (!is_read) begin
|
|
case (reg_addr)
|
|
REG_SOURCE_A: source_a <= rx_byte[3:0];
|
|
REG_SOURCE_B: source_b <= rx_byte[3:0];
|
|
REG_MODE: pair_mode <= rx_byte[0];
|
|
REG_DECIM_HIGH: decimation[15:8] <= rx_byte;
|
|
REG_DECIM_LOW: decimation[7:0] <= rx_byte;
|
|
REG_RING_SELECT: if (rx_byte < RINGS_PER_GEN) ring_select <= rx_byte;
|
|
REG_GEN_ENABLE: gen_enable <= rx_byte[GENERATORS-1:0];
|
|
REG_TEMPORAL: if (rx_byte >= 8'd1 && rx_byte <= 8'd8) temporal <= rx_byte[3:0];
|
|
REG_FREQ_GEN: freq_gen <= rx_byte[1:0];
|
|
REG_CONTROL: begin
|
|
if (rx_byte[0]) capture_start <= 1'b1;
|
|
if (rx_byte[0] || rx_byte[1]) bytes_read <= {CAPTURE_ADDR_WIDTH{1'b0}};
|
|
if (rx_byte[2]) freq_start <= 1'b1;
|
|
end
|
|
default: ;
|
|
endcase
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
endmodule
|