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
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
|
||||
Loading…
Add table
Add a link
Reference in a new issue