// 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