// // Copyright 2021 Ettus Research, A National Instruments Brand // // SPDX-License-Identifier: LGPL-3.0-or-later // // Module: spi_slave_to_ctrlport_master // // Description: // // SPI slave to ContolPort master conversion in order to tunnel control port // request through an SPI bus. // // The request format on SPI is defined as: // // Write request: // 1'b1 = write, 15 bit address, 32 bit data (MOSI), 8 bit processing gap, // 5 bit padding, 1 bit ack, 2 bit status // // Read request: // 1'b0 = read, 15 bit address, 8 bit processing gap, 32 bit data (MISO), // 5 bit padding, 1 bit ack, 2 bit status // // Parameters: // // CLK_FREQUENCY : Frequency of "clk" // SPI_FREQUENCY : Frequency of "sclk" // `default_nettype none module spi_slave_to_ctrlport_master #( parameter CLK_FREQUENCY = 50000000, parameter SPI_FREQUENCY = 10000000 ) ( //--------------------------------------------------------------- // ControlPort Master //--------------------------------------------------------------- input wire ctrlport_clk, input wire ctrlport_rst, output wire m_ctrlport_req_wr, output wire m_ctrlport_req_rd, output wire [19:0] m_ctrlport_req_addr, output wire [31:0] m_ctrlport_req_data, input wire m_ctrlport_resp_ack, input wire [ 1:0] m_ctrlport_resp_status, input wire [31:0] m_ctrlport_resp_data, //--------------------------------------------------------------- // SPI Slave //--------------------------------------------------------------- input wire sclk, input wire cs_n, input wire mosi, output wire miso ); `include "../../../lib/rfnoc/core/ctrlport.vh" //--------------------------------------------------------------- // SPI Slave //--------------------------------------------------------------- wire [7:0] data_in; wire [7:0] data_out; wire data_in_valid; wire data_out_valid; wire data_in_required; wire spi_slave_active; spi_slave #( .CLK_FREQUENCY (CLK_FREQUENCY), .SPI_FREQUENCY (SPI_FREQUENCY) ) spi_slave_async ( .sclk (sclk), .cs_n (cs_n), .mosi (mosi), .miso (miso), .clk (ctrlport_clk), .rst (ctrlport_rst), .data_in_required (data_in_required), .data_in_valid (data_in_valid), .data_in (data_in), .data_out_valid (data_out_valid), .data_out (data_out), .active (spi_slave_active) ); //--------------------------------------------------------------- // Reset Generation from SPI Slave //--------------------------------------------------------------- reg spi_slave_active_delayed = 1'b0; always @(posedge ctrlport_clk) begin if (ctrlport_rst) begin spi_slave_active_delayed <= 1'b0; end else begin spi_slave_active_delayed <= spi_slave_active; end end // Trigger reset on falling edge of active signal (rising edge of cs_n) wire spi_slave_reset; assign spi_slave_reset = spi_slave_active_delayed & (~spi_slave_active); //--------------------------------------------------------------- // Transfer Constants //--------------------------------------------------------------- localparam NUM_BYTES_TRANSACTION = 8; localparam NUM_BYTES_WRITE_REQUEST_PAYLOAD = 6; localparam NUM_BYTES_READ_REQUEST_PAYLOAD = 2; localparam MAX_BYTES_RESPONSE_PAYLOAD = 5; //--------------------------------------------------------------- // Data Receiver //--------------------------------------------------------------- reg [3:0] num_bytes_received; reg request_received; reg write_request; reg provide_response; reg [NUM_BYTES_WRITE_REQUEST_PAYLOAD*8-1:0] request_reg = {NUM_BYTES_WRITE_REQUEST_PAYLOAD*8 {1'b0}}; always @(posedge ctrlport_clk) begin if (ctrlport_rst || spi_slave_reset) begin num_bytes_received <= 4'b0; request_received <= 1'b0; write_request <= 1'b0; provide_response <= 1'b0; end else begin // Counter number of received bytes if (data_out_valid) begin // Increment counter num_bytes_received <= num_bytes_received + 1'b1; if (num_bytes_received == NUM_BYTES_TRANSACTION-1) begin num_bytes_received <= 4'b0; end end // Check for read / write on first received byte's MSB if (data_out_valid && (num_bytes_received == 0)) begin write_request <= data_out[7]; end // Detect complete request request_received <= 1'b0; if (data_out_valid) begin if (write_request && (num_bytes_received == NUM_BYTES_WRITE_REQUEST_PAYLOAD-1)) begin request_received <= 1'b1; provide_response <= 1'b1; end else if (~write_request && (num_bytes_received == NUM_BYTES_READ_REQUEST_PAYLOAD-1)) begin request_received <= 1'b1; provide_response <= 1'b1; end end // Detect end of response on last received byte if (num_bytes_received == NUM_BYTES_TRANSACTION-1) begin provide_response <= 1'b0; end // Capture data into shift register if (data_out_valid) begin request_reg <= {request_reg[NUM_BYTES_WRITE_REQUEST_PAYLOAD*8-8-1:0], data_out}; end end end // Drive ControlPort localparam SPI_TRANSFER_ADDRESS_WIDTH = 15; assign m_ctrlport_req_wr = request_received && write_request; assign m_ctrlport_req_rd = request_received && ~write_request; assign m_ctrlport_req_data = request_reg[CTRLPORT_DATA_W-1:0]; assign m_ctrlport_req_addr = (write_request) ? {5'b0, request_reg[CTRLPORT_DATA_W+:SPI_TRANSFER_ADDRESS_WIDTH]} : {5'b0, request_reg[0+:SPI_TRANSFER_ADDRESS_WIDTH]}; //--------------------------------------------------------------- // Response Handling //--------------------------------------------------------------- reg [MAX_BYTES_RESPONSE_PAYLOAD*8-1:0] response_reg; reg ready_for_response; // active during processing gap wire write_response_byte; always @(posedge ctrlport_clk) begin if (ctrlport_rst || spi_slave_reset) begin response_reg <= {8*MAX_BYTES_RESPONSE_PAYLOAD {1'b0}}; ready_for_response <= 1'b0; end else begin // Reset response on new request if (request_received) begin ready_for_response <= 1'b1; if (write_request) begin // Just last byte -> padding, ack flag, CMDERR, padding (data length) response_reg <= {5'b0, 1'b1, CTRL_STS_CMDERR, {CTRLPORT_DATA_W{1'b0}}}; end else begin // Last 5 bytes -> data = 0, Padding, ack flag, CMDERR response_reg <= {{CTRLPORT_DATA_W{1'b0}}, 5'b0, 1'b1, CTRL_STS_CMDERR}; end // Capture response within processing gap, leave default response from above otherwise end else if (m_ctrlport_resp_ack && ready_for_response) begin if (write_request) begin response_reg <= {5'b0, m_ctrlport_resp_ack, m_ctrlport_resp_status, {CTRLPORT_DATA_W{1'b0}}}; end else begin response_reg <= {m_ctrlport_resp_data, 5'b0, m_ctrlport_resp_ack, m_ctrlport_resp_status}; end end // Shift data after writing to slave if (write_response_byte) begin response_reg <= {response_reg[0+:(MAX_BYTES_RESPONSE_PAYLOAD-1)*8], 8'b0}; ready_for_response <= 1'b0; end end end // Response is written after request part has been transferred assign write_response_byte = data_in_required && provide_response; // Assign SPI slave inputs assign data_in = response_reg[(MAX_BYTES_RESPONSE_PAYLOAD-1)*8+:8]; assign data_in_valid = write_response_byte; endmodule `default_nettype wire