Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com> Co-authored-by: Javier Valenzuela <javier.valenzuela@ni.com> Original-commit: 7015f5ed2d495f3908773b7c7d74864d0cc3871a
239 lines
7.7 KiB
Verilog
239 lines
7.7 KiB
Verilog
//
|
|
// 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
|