strix/fpga/rtl/entropy_stream.v

90 lines
3 KiB
Coq
Raw Normal View History

// Byte stream of entropy for the SAMD21: bits from the selected channel (optionally through a
// von Neumann extractor) are packed MSB first into bytes and queued in a FIFO. When the FIFO
// is full, new bytes are dropped and counted; bits are never reused.
//
// Reading: `head` is the oldest byte, valid while `available`; `pop` removes it. A popped
// byte's successor appears on `head` two cycles later. `level` and `available` are registered
// and lag by a cycle, which only ever understates what can be read.
module entropy_stream #(
parameter ADDR_WIDTH = 12 // 4096-byte FIFO
) (
input wire clk,
input wire reset,
input wire flush,
input wire bit_in,
input wire valid,
input wire von_neumann,
input wire pop,
output reg [7:0] head,
output reg available,
output reg [ADDR_WIDTH:0] level,
output reg [15:0] dropped // bytes dropped since flush, saturating
);
// Von Neumann (01 -> 0, 10 -> 1)
reg pair_first, have_first;
wire vn_valid = von_neumann ? (valid && have_first && bit_in != pair_first) : valid;
wire vn_bit = von_neumann ? pair_first : bit_in;
always @(posedge clk) begin
if (reset || flush || !von_neumann) begin
have_first <= 1'b0;
end else if (valid) begin
have_first <= ~have_first;
pair_first <= bit_in;
end
end
// Packing
reg [6:0] partial;
reg [2:0] bit_count;
reg byte_ready;
reg [7:0] byte_out;
always @(posedge clk) begin
byte_ready <= 1'b0;
if (reset || flush) begin
bit_count <= 3'd0;
end else if (vn_valid) begin
partial <= {partial[5:0], vn_bit};
bit_count <= bit_count + 3'd1;
if (bit_count == 3'd7) begin
byte_ready <= 1'b1;
byte_out <= {partial, vn_bit};
end
end
end
// FIFO
reg [7:0] buffer [0:(1 << ADDR_WIDTH) - 1];
reg [ADDR_WIDTH:0] write_ptr, read_ptr;
wire [ADDR_WIDTH:0] fill = write_ptr - read_ptr;
wire full = fill[ADDR_WIDTH];
always @(posedge clk) begin
if (reset || flush) begin
write_ptr <= {(ADDR_WIDTH + 1){1'b0}};
read_ptr <= {(ADDR_WIDTH + 1){1'b0}};
dropped <= 16'd0;
end else begin
if (byte_ready) begin
if (!full) begin
buffer[write_ptr[ADDR_WIDTH-1:0]] <= byte_out;
write_ptr <= write_ptr + 1'b1;
end else if (dropped != 16'hFFFF) begin
dropped <= dropped + 16'd1;
end
end
if (pop && fill != 0) read_ptr <= read_ptr + 1'b1;
end
head <= buffer[read_ptr[ADDR_WIDTH-1:0]];
level <= fill;
available <= fill != 0 && !(pop && fill == 1);
end
endmodule