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