90 lines
3 KiB
Coq
90 lines
3 KiB
Coq
|
|
// 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
|