feat: add FPGA raw jitter capture design with SPI register interface
This commit is contained in:
parent
3ee8a5c3db
commit
0332d8eb33
13 changed files with 936 additions and 2 deletions
130
fpga/rtl/strix_core.v
Normal file
130
fpga/rtl/strix_core.v
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
// Board-independent STRIX logic: entropy generator, raw capture and the SPI register
|
||||
// interface to the SAMD21.
|
||||
//
|
||||
// SPI protocol (mode 0, one transaction per ss_n low period): the first byte is a command,
|
||||
// bit 7 = read (1) or write (0), bits 6:0 = register address. A write stores the following
|
||||
// byte(s) in that register. A read returns the register in every following byte; DATA
|
||||
// advances through the capture buffer with each byte, continuing across transactions.
|
||||
module strix_core #(
|
||||
parameter CAPTURE_ADDR_WIDTH = 15
|
||||
) (
|
||||
input wire clk,
|
||||
input wire reset,
|
||||
input wire spi_sck,
|
||||
input wire spi_mosi,
|
||||
input wire spi_ss_n,
|
||||
output wire spi_miso,
|
||||
output wire capture_done
|
||||
);
|
||||
|
||||
localparam [7:0] VERSION = 8'h01;
|
||||
|
||||
localparam [6:0] REG_ID0 = 7'h00, // "STRX"
|
||||
REG_ID1 = 7'h01,
|
||||
REG_ID2 = 7'h02,
|
||||
REG_ID3 = 7'h03,
|
||||
REG_VERSION = 7'h04,
|
||||
REG_SOURCE = 7'h08, // 0 tap A, 1 enhanced A, 2 enhanced B, 3 pair
|
||||
REG_DECIM_HIGH = 7'h09,
|
||||
REG_DECIM_LOW = 7'h0A,
|
||||
REG_CONTROL = 7'h0B, // write: bit 0 start capture, bit 1 rewind DATA
|
||||
REG_STATUS = 7'h0C, // bit 0 busy, bit 1 done
|
||||
REG_DATA = 7'h10;
|
||||
|
||||
// Entropy and capture
|
||||
|
||||
wire [3:0] samples;
|
||||
|
||||
entropy_gen generator (.clk(clk), .enable(1'b1), .samples(samples));
|
||||
|
||||
reg [1:0] source;
|
||||
reg [15:0] decimation;
|
||||
reg capture_start;
|
||||
wire capture_busy;
|
||||
reg [CAPTURE_ADDR_WIDTH-1:0] prefetch_addr;
|
||||
wire [7:0] capture_byte;
|
||||
|
||||
raw_capture #(.ADDR_WIDTH(CAPTURE_ADDR_WIDTH)) capture (
|
||||
.clk(clk), .reset(reset), .samples(samples), .source(source),
|
||||
.decimation(decimation), .start(capture_start),
|
||||
.busy(capture_busy), .done(capture_done),
|
||||
.read_addr(prefetch_addr), .read_data(capture_byte)
|
||||
);
|
||||
|
||||
// SPI
|
||||
|
||||
wire selected, rx_valid, rx_is_first, tx_loaded;
|
||||
wire [7:0] rx_byte;
|
||||
reg [7:0] tx_byte;
|
||||
|
||||
spi_slave spi (
|
||||
.clk(clk), .reset(reset),
|
||||
.sck(spi_sck), .mosi(spi_mosi), .ss_n(spi_ss_n), .miso(spi_miso),
|
||||
.selected(selected), .rx_valid(rx_valid), .rx_byte(rx_byte),
|
||||
.rx_is_first(rx_is_first), .tx_byte(tx_byte), .tx_loaded(tx_loaded)
|
||||
);
|
||||
|
||||
reg is_read;
|
||||
reg [6:0] reg_addr;
|
||||
|
||||
// DATA bytes are prefetched one ahead: prefetch_addr moves when a byte is loaded for
|
||||
// shifting, bytes_read only when it has been fully clocked out. Between transactions
|
||||
// prefetch_addr rewinds to bytes_read so the trailing prefetch is not lost.
|
||||
reg [CAPTURE_ADDR_WIDTH-1:0] bytes_read;
|
||||
|
||||
wire reading_data = is_read && reg_addr == REG_DATA;
|
||||
|
||||
always @(*) begin
|
||||
case (reg_addr)
|
||||
REG_ID0: tx_byte = "S";
|
||||
REG_ID1: tx_byte = "T";
|
||||
REG_ID2: tx_byte = "R";
|
||||
REG_ID3: tx_byte = "X";
|
||||
REG_VERSION: tx_byte = VERSION;
|
||||
REG_SOURCE: tx_byte = {6'd0, source};
|
||||
REG_DECIM_HIGH: tx_byte = decimation[15:8];
|
||||
REG_DECIM_LOW: tx_byte = decimation[7:0];
|
||||
REG_STATUS: tx_byte = {6'd0, capture_done, capture_busy};
|
||||
REG_DATA: tx_byte = capture_byte;
|
||||
default: tx_byte = 8'h00;
|
||||
endcase
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
capture_start <= 1'b0;
|
||||
if (reset) begin
|
||||
source <= 2'd3;
|
||||
decimation <= 16'd1;
|
||||
is_read <= 1'b0;
|
||||
reg_addr <= 7'd0;
|
||||
bytes_read <= {CAPTURE_ADDR_WIDTH{1'b0}};
|
||||
prefetch_addr <= {CAPTURE_ADDR_WIDTH{1'b0}};
|
||||
end else begin
|
||||
if (!selected)
|
||||
prefetch_addr <= bytes_read;
|
||||
else if (tx_loaded && reading_data)
|
||||
prefetch_addr <= prefetch_addr + 1'b1;
|
||||
|
||||
if (rx_valid) begin
|
||||
if (rx_is_first) begin
|
||||
is_read <= rx_byte[7];
|
||||
reg_addr <= rx_byte[6:0];
|
||||
end else if (reading_data) begin
|
||||
bytes_read <= bytes_read + 1'b1;
|
||||
end else if (!is_read) begin
|
||||
case (reg_addr)
|
||||
REG_SOURCE: source <= rx_byte[1:0];
|
||||
REG_DECIM_HIGH: decimation[15:8] <= rx_byte;
|
||||
REG_DECIM_LOW: decimation[7:0] <= rx_byte;
|
||||
REG_CONTROL: begin
|
||||
if (rx_byte[0]) capture_start <= 1'b1;
|
||||
if (rx_byte[0] || rx_byte[1]) bytes_read <= {CAPTURE_ADDR_WIDTH{1'b0}};
|
||||
end
|
||||
default: ;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
Loading…
Add table
Add a link
Reference in a new issue