strix/fpga/rtl/bias_amplifier.v

182 lines
7.5 KiB
Verilog

// 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, trial_due;
wire tick = (tick_count == TICK_CYCLES - 1);
always @(posedge clk) begin
sub_end <= 1'b0;
trial_due <= 1'b0;
trial_end <= trial_due;
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 two cycles, once its vote and the vote
// counts are registered.
if (sub_end) begin
if (sub_index + 4'd1 >= subtrials) begin
sub_index <= 4'd0;
trial_due <= 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 [4:0] vote_count [0:1];
wire [4:0] one_votes [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]), .vote_count(vote_count[0]), .one_votes(one_votes[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]), .vote_count(vote_count[1]), .one_votes(one_votes[1]),
.overflow(overflow[1])
);
// Snapshot
function [127:0] path_bytes(input [31:0] bits, input [31:0] ones, input [15:0] vote, input [15:0] cast,
input [4:0] cast_count, input [4:0] ones_cast);
reg valid, result;
begin
valid = cast_count != 0 && {ones_cast, 1'b0} != {1'b0, cast_count};
result = {ones_cast, 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], vote_count[0], one_votes[0]),
path_bytes(trial_bits[1], trial_ones[1], votes[1], voted[1], vote_count[1], one_votes[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