Files
b210-k7-fpga/top/x400/cpld/spi_slave_to_ctrlport_master.v
T
7e59e9516d fpga: x400: cpld: Add support for X410 motherboard CPLD
Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com>
Co-authored-by: Javier Valenzuela <javier.valenzuela@ni.com>


Original-commit: 7015f5ed2d495f3908773b7c7d74864d0cc3871a
2021-06-10 11:56:58 -05:00

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