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
109
fpga/rtl/amp_path.v
Normal file
109
fpga/rtl/amp_path.v
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
// One amplifier path: the generator streams are merged into one stream, which then passes
|
||||
// three stages in series. Merging before amplifying is equivalent to amplifying each stream
|
||||
// with the same first stage and merging after (the random walk is distributive), and needs a
|
||||
// third of the stages. Amplified bits are tallied per sub-trial and per trial.
|
||||
module amp_path #(
|
||||
parameter GENERATORS = 4
|
||||
) (
|
||||
input wire clk,
|
||||
input wire restart,
|
||||
input wire [GENERATORS-1:0] in_valid,
|
||||
input wire [GENERATORS-1:0] in_bit,
|
||||
input wire [2:0] stage_mode,
|
||||
input wire [47:0] stage_param, // stage s at [16*s +: 16]
|
||||
input wire sub_end, // pulse: close the current sub-trial
|
||||
input wire [3:0] sub_index, // sub-trial being closed
|
||||
input wire trial_end, // pulse: start a new trial (after the last sub_end)
|
||||
output reg [31:0] trial_bits, // amplified bits in the current trial
|
||||
output reg [31:0] trial_ones,
|
||||
output reg [15:0] votes, // bit i: sub-trial i voted 1
|
||||
output reg [15:0] voted, // bit i: sub-trial i had a majority
|
||||
output reg overflow // a merge input arrived before the last was taken
|
||||
);
|
||||
|
||||
// Merge: each generator's latest bit waits in its own slot; one slot is drained per clock,
|
||||
// lowest index first. Generators deliver at most one bit per `temporal` (>= 1, default 5)
|
||||
// clocks in lockstep, so with up to 4 generators the slots never overflow at temporal >= 4
|
||||
// (flagged if they do).
|
||||
|
||||
reg [GENERATORS-1:0] pending, pending_bit, take;
|
||||
reg merged_valid, merged_bit;
|
||||
integer i;
|
||||
|
||||
always @(*) begin
|
||||
take = {GENERATORS{1'b0}};
|
||||
for (i = 0; i < GENERATORS; i = i + 1)
|
||||
if (pending[i] && take == {GENERATORS{1'b0}}) take[i] = 1'b1;
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
merged_valid <= |take && !restart;
|
||||
for (i = 0; i < GENERATORS; i = i + 1)
|
||||
if (take[i]) merged_bit <= pending_bit[i];
|
||||
|
||||
if (restart) begin
|
||||
pending <= {GENERATORS{1'b0}};
|
||||
overflow <= 1'b0;
|
||||
end else begin
|
||||
for (i = 0; i < GENERATORS; i = i + 1) begin
|
||||
if (in_valid[i]) begin
|
||||
if (pending[i] && !take[i]) overflow <= 1'b1;
|
||||
pending[i] <= 1'b1;
|
||||
pending_bit[i] <= in_bit[i];
|
||||
end else if (take[i]) begin
|
||||
pending[i] <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Stages
|
||||
|
||||
wire first_valid, first_bit, second_valid, second_bit, out_valid, out_bit;
|
||||
|
||||
amp_stage first (
|
||||
.clk(clk), .restart(restart), .mode(stage_mode[0]), .param(stage_param[15:0]),
|
||||
.in_valid(merged_valid), .in_bit(merged_bit), .out_valid(first_valid), .out_bit(first_bit)
|
||||
);
|
||||
|
||||
amp_stage second (
|
||||
.clk(clk), .restart(restart), .mode(stage_mode[1]), .param(stage_param[31:16]),
|
||||
.in_valid(first_valid), .in_bit(first_bit), .out_valid(second_valid), .out_bit(second_bit)
|
||||
);
|
||||
|
||||
amp_stage third (
|
||||
.clk(clk), .restart(restart), .mode(stage_mode[2]), .param(stage_param[47:32]),
|
||||
.in_valid(second_valid), .in_bit(second_bit), .out_valid(out_valid), .out_bit(out_bit)
|
||||
);
|
||||
|
||||
// Tallies. A bit arriving in a sub_end or trial_end cycle counts towards the next one, so
|
||||
// the trial count and the sum of its sub-trial counts can differ by one bit.
|
||||
|
||||
reg [23:0] sub_bits, sub_ones;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (restart || trial_end) begin
|
||||
trial_bits <= out_valid ? 32'd1 : 32'd0;
|
||||
trial_ones <= (out_valid && out_bit) ? 32'd1 : 32'd0;
|
||||
votes <= 16'd0;
|
||||
voted <= 16'd0;
|
||||
end else if (out_valid) begin
|
||||
trial_bits <= trial_bits + 32'd1;
|
||||
trial_ones <= trial_ones + out_bit;
|
||||
end
|
||||
|
||||
if (restart || sub_end) begin
|
||||
sub_bits <= out_valid ? 24'd1 : 24'd0;
|
||||
sub_ones <= (out_valid && out_bit) ? 24'd1 : 24'd0;
|
||||
end else if (out_valid) begin
|
||||
sub_bits <= sub_bits + 24'd1;
|
||||
sub_ones <= sub_ones + out_bit;
|
||||
end
|
||||
|
||||
if (!restart && sub_end && sub_bits != 0 && {sub_ones, 1'b0} != sub_bits) begin
|
||||
voted[sub_index] <= 1'b1;
|
||||
votes[sub_index] <= {sub_ones, 1'b0} > sub_bits;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
48
fpga/rtl/amp_stage.v
Normal file
48
fpga/rtl/amp_stage.v
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
// One bias amplifier stage. Both modes track the walk position d = ones - zeros.
|
||||
//
|
||||
// Random walk (mode 0): reaching d = +param outputs a 1, d = -param outputs a 0, and the walk
|
||||
// restarts at 0. Stages compose: bounds n1 then n2 amplify like a single bound n1*n2.
|
||||
//
|
||||
// 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.
|
||||
module amp_stage (
|
||||
input wire clk,
|
||||
input wire restart,
|
||||
input wire mode, // 0 random walk, 1 majority vote
|
||||
input wire [15:0] param, // bound or window, at least 1
|
||||
input wire in_valid,
|
||||
input wire in_bit,
|
||||
output reg out_valid,
|
||||
output reg out_bit
|
||||
);
|
||||
|
||||
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;
|
||||
wire done = mode ? vote_done : walk_done;
|
||||
|
||||
always @(posedge clk) begin
|
||||
out_valid <= 1'b0;
|
||||
if (restart) begin
|
||||
position <= 17'sd0;
|
||||
seen <= 16'd0;
|
||||
end else if (in_valid) begin
|
||||
if (done) begin
|
||||
out_valid <= 1'b1;
|
||||
out_bit <= next_position > 0;
|
||||
position <= 17'sd0;
|
||||
seen <= 16'd0;
|
||||
end else begin
|
||||
position <= next_position;
|
||||
seen <= seen + 16'd1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
186
fpga/rtl/bias_amplifier.v
Normal file
186
fpga/rtl/bias_amplifier.v
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
// Bias amplifier with two paths fed by the selected healthy generators:
|
||||
// path 0 (bias): the generator bits themselves;
|
||||
// path 1 (autocorrelation): each bit XNOR the previous bit of the same generator, so
|
||||
// positive lag-1 autocorrelation becomes an excess of ones.
|
||||
// An optional von Neumann extractor per generator runs before both paths.
|
||||
//
|
||||
// Trials consist of `subtrials` sub-trials of `sub_ms` milliseconds. Each sub-trial votes with
|
||||
// the majority of its amplified bits (no vote on a tie or without bits); the trial result is
|
||||
// the majority of the votes.
|
||||
//
|
||||
// Snapshot layout (big-endian), latched at each trial end:
|
||||
// 0-3 trial sequence number
|
||||
// 4 bits 3:0 generators used during the whole trial, bit 4 von Neumann during the
|
||||
// whole trial, bit 5 merge overflow since restart (should never be set)
|
||||
// 5 sub-trials per trial
|
||||
// 6 sub-trial length in ms
|
||||
// 7 0
|
||||
// 8+16p path p: amplified bits (4), ones (4), votes (2), voted (2), vote count (1),
|
||||
// result (1: bit 0 trial bit, bit 1 trial valid), 0 (2)
|
||||
// 40-43 trial sequence number again; a mismatch with bytes 0-3 means a torn read
|
||||
module bias_amplifier #(
|
||||
parameter GENERATORS = 4,
|
||||
parameter TICK_CYCLES = 120000 // clk cycles per millisecond
|
||||
) (
|
||||
input wire clk,
|
||||
input wire reset,
|
||||
input wire [GENERATORS-1:0] gen_bit,
|
||||
input wire gen_valid, // all generators deliver in lockstep
|
||||
input wire [GENERATORS-1:0] sources, // selected and healthy generators
|
||||
input wire von_neumann,
|
||||
input wire [2:0] stage_mode,
|
||||
input wire [47:0] stage_param,
|
||||
input wire [7:0] sub_ms,
|
||||
input wire [3:0] subtrials,
|
||||
input wire restart,
|
||||
input wire [7:0] read_index,
|
||||
output reg [7:0] read_byte, // one cycle after read_index
|
||||
output reg trial_end
|
||||
);
|
||||
|
||||
localparam SNAPSHOT_BYTES = 44;
|
||||
|
||||
wire start_over = reset || restart;
|
||||
|
||||
// Von Neumann (01 -> 0, 10 -> 1) and autocorrelation conversion per generator
|
||||
|
||||
reg [GENERATORS-1:0] pair_first, have_first, previous_bit, has_previous;
|
||||
wire [GENERATORS-1:0] vn_valid = von_neumann ? (gen_valid ? have_first & (gen_bit ^ pair_first) : {GENERATORS{1'b0}})
|
||||
: {GENERATORS{gen_valid}};
|
||||
wire [GENERATORS-1:0] vn_bit = von_neumann ? pair_first : gen_bit;
|
||||
wire [GENERATORS-1:0] bias_valid = vn_valid & sources;
|
||||
wire [GENERATORS-1:0] ac_valid = vn_valid & has_previous & sources;
|
||||
wire [GENERATORS-1:0] ac_bit = ~(vn_bit ^ previous_bit);
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (start_over || !von_neumann) begin
|
||||
have_first <= {GENERATORS{1'b0}};
|
||||
end else if (gen_valid) begin
|
||||
have_first <= ~have_first;
|
||||
pair_first <= gen_bit;
|
||||
end
|
||||
if (start_over) begin
|
||||
has_previous <= {GENERATORS{1'b0}};
|
||||
end else begin
|
||||
has_previous <= has_previous | vn_valid;
|
||||
previous_bit <= (vn_valid & vn_bit) | (~vn_valid & previous_bit);
|
||||
end
|
||||
end
|
||||
|
||||
// Sub-trial and trial timing
|
||||
|
||||
reg [16:0] tick_count;
|
||||
reg [7:0] ms_count;
|
||||
reg [3:0] sub_index;
|
||||
reg sub_end;
|
||||
wire tick = (tick_count == TICK_CYCLES - 1);
|
||||
|
||||
always @(posedge clk) begin
|
||||
sub_end <= 1'b0;
|
||||
trial_end <= 1'b0;
|
||||
if (start_over) begin
|
||||
tick_count <= 17'd0;
|
||||
ms_count <= 8'd0;
|
||||
sub_index <= 4'd0;
|
||||
end else begin
|
||||
tick_count <= tick ? 17'd0 : tick_count + 17'd1;
|
||||
if (tick) begin
|
||||
if (ms_count + 8'd1 >= sub_ms) begin
|
||||
ms_count <= 8'd0;
|
||||
sub_end <= 1'b1;
|
||||
end else begin
|
||||
ms_count <= ms_count + 8'd1;
|
||||
end
|
||||
end
|
||||
// trial_end follows the last sub_end by one cycle, once its vote is registered.
|
||||
if (sub_end) begin
|
||||
if (sub_index + 4'd1 >= subtrials) begin
|
||||
sub_index <= 4'd0;
|
||||
trial_end <= 1'b1;
|
||||
end else begin
|
||||
sub_index <= sub_index + 4'd1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Paths
|
||||
|
||||
wire [31:0] trial_bits [0:1];
|
||||
wire [31:0] trial_ones [0:1];
|
||||
wire [15:0] votes [0:1];
|
||||
wire [15:0] voted [0:1];
|
||||
wire [1:0] overflow;
|
||||
|
||||
amp_path #(.GENERATORS(GENERATORS)) bias_path (
|
||||
.clk(clk), .restart(start_over), .in_valid(bias_valid), .in_bit(vn_bit),
|
||||
.stage_mode(stage_mode), .stage_param(stage_param),
|
||||
.sub_end(sub_end), .sub_index(sub_index), .trial_end(trial_end),
|
||||
.trial_bits(trial_bits[0]), .trial_ones(trial_ones[0]),
|
||||
.votes(votes[0]), .voted(voted[0]), .overflow(overflow[0])
|
||||
);
|
||||
|
||||
amp_path #(.GENERATORS(GENERATORS)) autocorrelation_path (
|
||||
.clk(clk), .restart(start_over), .in_valid(ac_valid), .in_bit(ac_bit),
|
||||
.stage_mode(stage_mode), .stage_param(stage_param),
|
||||
.sub_end(sub_end), .sub_index(sub_index), .trial_end(trial_end),
|
||||
.trial_bits(trial_bits[1]), .trial_ones(trial_ones[1]),
|
||||
.votes(votes[1]), .voted(voted[1]), .overflow(overflow[1])
|
||||
);
|
||||
|
||||
// Snapshot
|
||||
|
||||
function [4:0] count_ones(input [15:0] value);
|
||||
integer b;
|
||||
begin
|
||||
count_ones = 5'd0;
|
||||
for (b = 0; b < 16; b = b + 1) count_ones = count_ones + value[b];
|
||||
end
|
||||
endfunction
|
||||
|
||||
function [127:0] path_bytes(input [31:0] bits, input [31:0] ones, input [15:0] vote, input [15:0] cast);
|
||||
reg [4:0] cast_count, one_votes;
|
||||
reg valid, result;
|
||||
begin
|
||||
cast_count = count_ones(cast);
|
||||
one_votes = count_ones(vote & cast);
|
||||
valid = cast_count != 0 && {one_votes, 1'b0} != {1'b0, cast_count};
|
||||
result = {one_votes, 1'b0} > {1'b0, cast_count};
|
||||
path_bytes = {bits, ones, vote, cast, 3'd0, cast_count, 6'd0, valid, result, 16'd0};
|
||||
end
|
||||
endfunction
|
||||
|
||||
reg [31:0] sequence_number;
|
||||
reg [GENERATORS-1:0] sources_during;
|
||||
reg von_neumann_during;
|
||||
reg [7:0] snapshot [0:SNAPSHOT_BYTES-1]; // byte 0 first, indexed directly
|
||||
|
||||
wire [8*SNAPSHOT_BYTES-1:0] snapshot_next = {
|
||||
sequence_number,
|
||||
2'd0, |overflow, von_neumann_during & von_neumann, sources_during & sources,
|
||||
4'd0, subtrials, sub_ms, 8'd0,
|
||||
path_bytes(trial_bits[0], trial_ones[0], votes[0], voted[0]),
|
||||
path_bytes(trial_bits[1], trial_ones[1], votes[1], voted[1]),
|
||||
sequence_number
|
||||
};
|
||||
|
||||
integer k;
|
||||
always @(posedge clk) begin
|
||||
if (start_over) begin
|
||||
sequence_number <= 32'd0;
|
||||
sources_during <= sources;
|
||||
von_neumann_during <= von_neumann;
|
||||
end else if (trial_end) begin
|
||||
for (k = 0; k < SNAPSHOT_BYTES; k = k + 1)
|
||||
snapshot[k] <= snapshot_next[8*(SNAPSHOT_BYTES-1-k) +: 8];
|
||||
sequence_number <= sequence_number + 32'd1;
|
||||
sources_during <= sources;
|
||||
von_neumann_during <= von_neumann;
|
||||
end else begin
|
||||
sources_during <= sources_during & sources;
|
||||
von_neumann_during <= von_neumann_during & von_neumann;
|
||||
end
|
||||
read_byte <= (read_index < SNAPSHOT_BYTES) ? snapshot[read_index] : 8'h00;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -86,9 +86,10 @@ module stats_block #(
|
|||
reg [3:0] healthy_during;
|
||||
reg von_neumann_during;
|
||||
|
||||
// The snapshot is latched as one flat byte string (byte 0 in the top bits), so reading it
|
||||
// is a plain byte multiplexer.
|
||||
reg [8*SNAPSHOT_BYTES-1:0] snapshot, snapshot_next;
|
||||
// The snapshot is assembled as one flat byte string (byte 0 in the top bits) and latched
|
||||
// into a byte array, so reading it is a plain byte multiplexer on the index.
|
||||
reg [8*SNAPSHOT_BYTES-1:0] snapshot_next;
|
||||
reg [7:0] snapshot [0:SNAPSHOT_BYTES-1]; // byte 0 first, indexed directly
|
||||
|
||||
integer k;
|
||||
always @(*) begin
|
||||
|
|
@ -100,7 +101,7 @@ module stats_block #(
|
|||
snapshot_next[31:0] = sequence_number;
|
||||
end
|
||||
|
||||
integer c;
|
||||
integer c, b;
|
||||
always @(posedge clk) begin
|
||||
if (reset || restart) begin
|
||||
sequence_number <= 32'd0;
|
||||
|
|
@ -129,7 +130,8 @@ module stats_block #(
|
|||
end
|
||||
end
|
||||
if (trial_end) begin
|
||||
snapshot <= snapshot_next;
|
||||
for (b = 0; b < SNAPSHOT_BYTES; b = b + 1)
|
||||
snapshot[b] <= snapshot_next[8*(SNAPSHOT_BYTES-1-b) +: 8];
|
||||
sequence_number <= sequence_number + 32'd1;
|
||||
healthy_during <= healthy;
|
||||
von_neumann_during <= von_neumann;
|
||||
|
|
@ -143,6 +145,6 @@ module stats_block #(
|
|||
// Snapshot read-out, registered: the byte for read_index appears one cycle later.
|
||||
|
||||
always @(posedge clk)
|
||||
read_byte <= (read_index < SNAPSHOT_BYTES) ? snapshot[8*(SNAPSHOT_BYTES-1-read_index) +: 8] : 8'h00;
|
||||
read_byte <= (read_index < SNAPSHOT_BYTES) ? snapshot[read_index] : 8'h00;
|
||||
|
||||
endmodule
|
||||
|
|
|
|||
|
|
@ -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