fpga: x400: Add support for X410 motherboard FPGA
Co-authored-by: Andrew Moch <Andrew.Moch@ni.com> Co-authored-by: Daniel Jepson <daniel.jepson@ni.com> Co-authored-by: Javier Valenzuela <javier.valenzuela@ni.com> Co-authored-by: Joerg Hofrichter <joerg.hofrichter@ni.com> Co-authored-by: Kumaran Subramoniam <kumaran.subramoniam@ni.com> Co-authored-by: Max Köhler <max.koehler@ni.com> Co-authored-by: Michael Auchter <michael.auchter@ni.com> Co-authored-by: Paul Butler <paul.butler@ni.com> Co-authored-by: Wade Fife <wade.fife@ettus.com> Co-authored-by: Hector Rubio <hrubio@ni.com> Original-commit: 6d3765605262016a80f71e36357f749ea35cbe5a
This commit is contained in:
committed by
Aaron Rossetto
co-authored by
Andrew Moch
Daniel Jepson
Javier Valenzuela
Joerg Hofrichter
Kumaran Subramoniam
Max Köhler
Michael Auchter
Paul Butler
Hector Rubio
parent
bfef20ea45
commit
61782b02d7
@@ -0,0 +1,186 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: ctrlport_byte_deserializer
|
||||
//
|
||||
// Description:
|
||||
// Slave interface of CtrlPort interface serialized as byte stream.
|
||||
// See description in ctrlport_byte_serializer module for more details.
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module ctrlport_byte_deserializer (
|
||||
input wire ctrlport_clk,
|
||||
input wire ctrlport_rst,
|
||||
|
||||
// Request
|
||||
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,
|
||||
|
||||
// Response
|
||||
input wire m_ctrlport_resp_ack,
|
||||
input wire [ 1:0] m_ctrlport_resp_status,
|
||||
input wire [31:0] m_ctrlport_resp_data,
|
||||
|
||||
// byte interface
|
||||
input wire [ 7:0] bytestream_data_in,
|
||||
input wire bytestream_valid_in,
|
||||
input wire bytestream_direction,
|
||||
output reg [ 7:0] bytestream_data_out = 8'b0,
|
||||
output reg bytestream_valid_out = 1'b0,
|
||||
output reg bytestream_output_enable = 1'b0
|
||||
);
|
||||
|
||||
`include "../../../lib/rfnoc/core/ctrlport.vh"
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// transfer constants
|
||||
//---------------------------------------------------------------
|
||||
// derived from transaction specification
|
||||
localparam NUM_BYTES_RX_READ = 2;
|
||||
localparam NUM_BYTES_TX_READ = 5;
|
||||
localparam NUM_BYTES_RX_WRITE = 6;
|
||||
localparam NUM_BYTES_TX_WRITE = 1;
|
||||
|
||||
localparam SPI_TRANSFER_ADDRESS_WIDTH = 15;
|
||||
|
||||
//----------------------------------------------------------
|
||||
// handle transfer
|
||||
//----------------------------------------------------------
|
||||
localparam INIT_RX = 2'd0;
|
||||
localparam RECEIVE = 2'd1;
|
||||
localparam WAIT_RESPONSE = 2'd2;
|
||||
localparam SENDING = 2'd3;
|
||||
|
||||
// internal registers
|
||||
reg [ 1:0] state = INIT_RX;
|
||||
reg [NUM_BYTES_RX_WRITE*8-1:0] request_cache = {NUM_BYTES_RX_WRITE*8 {1'b0}};
|
||||
reg [ NUM_BYTES_TX_READ*8-1:0] response_cache = {NUM_BYTES_TX_READ*8 {1'b0}};
|
||||
reg [ 2:0] byte_counter = 3'b0;
|
||||
reg transfer_complete = 1'b0;
|
||||
reg write_transfer = 1'b0;
|
||||
|
||||
// input registers to relax input timing
|
||||
reg [7:0] bytestream_data_in_reg = 8'b0;
|
||||
reg bytestream_valid_in_reg = 1'b0;
|
||||
reg bytestream_direction_reg = 1'b0;
|
||||
always @ (posedge ctrlport_clk) begin
|
||||
bytestream_data_in_reg <= bytestream_data_in;
|
||||
bytestream_valid_in_reg <= bytestream_valid_in;
|
||||
bytestream_direction_reg <= bytestream_direction;
|
||||
end
|
||||
|
||||
// state machine
|
||||
always @ (posedge ctrlport_clk) begin
|
||||
if (ctrlport_rst) begin
|
||||
state <= INIT_RX;
|
||||
byte_counter <= 3'b0;
|
||||
transfer_complete <= 1'b0;
|
||||
bytestream_output_enable <= 1'b0;
|
||||
|
||||
end else begin
|
||||
// default assignments
|
||||
transfer_complete <= 1'b0;
|
||||
// direction defined by master
|
||||
bytestream_output_enable <= bytestream_direction;
|
||||
bytestream_valid_out <= 1'b0;
|
||||
|
||||
case (state)
|
||||
// additional cycle for switching to make sure valid signal is driven
|
||||
// from master when being in RECEIVE state
|
||||
INIT_RX: begin
|
||||
byte_counter <= 3'b0;
|
||||
if (bytestream_direction_reg == 0) begin
|
||||
state <= RECEIVE;
|
||||
end
|
||||
end
|
||||
|
||||
// wait for reception of request from master
|
||||
RECEIVE: begin
|
||||
if (bytestream_valid_in_reg) begin
|
||||
byte_counter <= byte_counter + 1'b1;
|
||||
request_cache <= {request_cache[NUM_BYTES_RX_WRITE*8-9:0], bytestream_data_in_reg};
|
||||
|
||||
// capture write or read
|
||||
if (byte_counter == 0) begin
|
||||
write_transfer <= bytestream_data_in_reg[7];
|
||||
end
|
||||
|
||||
// wait until request completes
|
||||
if ((write_transfer && byte_counter == NUM_BYTES_RX_WRITE-1) ||
|
||||
(~write_transfer && byte_counter == NUM_BYTES_RX_READ-1)) begin
|
||||
transfer_complete <= 1'b1;
|
||||
state <= WAIT_RESPONSE;
|
||||
end
|
||||
end
|
||||
|
||||
// Workaround for missing pull down resistor:
|
||||
// Use pull up and schmitt trigger to detect FPGA reload by line going high unexpectedly
|
||||
if (bytestream_direction_reg == 1) begin
|
||||
state <= INIT_RX;
|
||||
end
|
||||
end
|
||||
|
||||
WAIT_RESPONSE: begin
|
||||
byte_counter <= 3'b0;
|
||||
if (m_ctrlport_resp_ack) begin
|
||||
state <= SENDING;
|
||||
|
||||
if (write_transfer) begin
|
||||
response_cache <= {5'b0, 1'b1, m_ctrlport_resp_status, 32'b0};
|
||||
end else begin
|
||||
response_cache <= {m_ctrlport_resp_data, 5'b0, 1'b1, m_ctrlport_resp_status};
|
||||
end
|
||||
end
|
||||
|
||||
//abort by host
|
||||
if (bytestream_direction_reg == 0) begin
|
||||
state <= INIT_RX;
|
||||
end
|
||||
end
|
||||
|
||||
SENDING: begin
|
||||
bytestream_valid_out <= 1'b1;
|
||||
bytestream_data_out <= response_cache[NUM_BYTES_TX_READ*8-8+:8];
|
||||
response_cache <= {response_cache[NUM_BYTES_TX_READ*8-9:0], 8'b0};
|
||||
byte_counter <= byte_counter + 1'b1;
|
||||
|
||||
// wait until request completes
|
||||
if ((write_transfer && byte_counter == NUM_BYTES_TX_WRITE-1) ||
|
||||
(~write_transfer && byte_counter == NUM_BYTES_TX_READ-1)) begin
|
||||
state <= INIT_RX;
|
||||
end
|
||||
|
||||
//abort by host
|
||||
if (bytestream_direction_reg == 0) begin
|
||||
state <= INIT_RX;
|
||||
end
|
||||
end
|
||||
|
||||
default: begin
|
||||
state <= INIT_RX;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
//----------------------------------------------------------
|
||||
// assign request to ctrlport
|
||||
//----------------------------------------------------------
|
||||
assign m_ctrlport_req_wr = write_transfer & transfer_complete;
|
||||
assign m_ctrlport_req_rd = ~write_transfer & transfer_complete;
|
||||
assign m_ctrlport_req_data = request_cache[0+:CTRLPORT_DATA_W];
|
||||
assign m_ctrlport_req_addr = (write_transfer) ?
|
||||
// Skipping data in LSBs to get to the address for writes.
|
||||
{5'b0, request_cache[CTRLPORT_DATA_W+:SPI_TRANSFER_ADDRESS_WIDTH]} :
|
||||
// Full request = address of 2 bytes in LSBs.
|
||||
{5'b0, request_cache[0+:SPI_TRANSFER_ADDRESS_WIDTH]};
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
@@ -0,0 +1,228 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: ctrlport_byte_serializer
|
||||
//
|
||||
// Description:
|
||||
// Serializes CtrlPort requests into a byte stream.
|
||||
//
|
||||
// The serialized data is similar to an AXI4-Streaming interface with one byte
|
||||
// per clock cycle and a valid signal. Direction controls the current transmission
|
||||
// direction. 0 = Master to Slave, 1 = Slave to Master, where this module is the
|
||||
// master. Direction is always present in direction master to slave where the
|
||||
// other signals valid and data can be shared on a tri-state bus.
|
||||
//
|
||||
// The transmission is defined as described below. The bytes are transmitted MSB
|
||||
// first.
|
||||
// Write request:
|
||||
// 1'b1 = write, 15 bit address, 32 bit data (MOSI) = 6 bytes request
|
||||
// 5 bit padding, 1 bit ack, 2 bit status = 1 byte response
|
||||
// Read request:
|
||||
// 1'b0 = read, 15 bit address = 2 bytes request
|
||||
// 32 bit data, 5 bit padding, 1 bit ack, 2 bit status = 5 bytes response
|
||||
//
|
||||
// When sharing valid and data signal lines between master and slave the
|
||||
// timing is defined as:
|
||||
//
|
||||
// clk __/--\__/--\__/--\__/--\__/--\__/--\__/--\__/--\__/--\__/--\__/--\__
|
||||
// direction _______________________/-----------------\____________
|
||||
// master output enable _____/-----------------\______________________________
|
||||
// slave output enable _____________________________/-----------------\______
|
||||
// valid & data zzzzz| Master driven | zzz | Slave driven | zzzzz
|
||||
// transaction <--- Request ---><--- Response --->
|
||||
//
|
||||
// The slave should use the direction signal to derive it's own output enable
|
||||
// leaving the master the option to terminate the transaction.
|
||||
// On switch from slave to master the direction has to be changed at least
|
||||
// one clock cycle before enabling the master output enable to avoid driving the
|
||||
// bus from two sources.
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module ctrlport_byte_serializer (
|
||||
// Clock and Reset
|
||||
input wire ctrlport_clk,
|
||||
input wire ctrlport_rst,
|
||||
|
||||
// Request
|
||||
input wire s_ctrlport_req_wr,
|
||||
input wire s_ctrlport_req_rd,
|
||||
input wire [19:0] s_ctrlport_req_addr,
|
||||
input wire [31:0] s_ctrlport_req_data,
|
||||
|
||||
// Response
|
||||
output reg s_ctrlport_resp_ack = 1'b0,
|
||||
output reg [ 1:0] s_ctrlport_resp_status = 2'b0,
|
||||
output reg [31:0] s_ctrlport_resp_data = 32'b0,
|
||||
|
||||
// GPIO interface
|
||||
input wire [ 7:0] bytestream_data_in,
|
||||
input wire bytestream_valid_in,
|
||||
output reg [ 7:0] bytestream_data_out = 8'b0,
|
||||
output reg bytestream_valid_out = 1'b0,
|
||||
output reg bytestream_direction = 1'b0,
|
||||
output reg bytestream_output_enable = 1'b1
|
||||
);
|
||||
|
||||
`include "../../../lib/rfnoc/core/ctrlport.vh"
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// transfer constants
|
||||
//---------------------------------------------------------------
|
||||
// derived from the transaction format (see description above)
|
||||
localparam NUM_BYTES_TX_READ = 2;
|
||||
localparam NUM_BYTES_RX_READ = 5;
|
||||
localparam NUM_BYTES_TX_WRITE = 6;
|
||||
localparam NUM_BYTES_RX_WRITE = 1;
|
||||
|
||||
localparam TIMEOUT_COUNTER_WIDTH = 6;
|
||||
|
||||
//----------------------------------------------------------
|
||||
// FSM to handle transfers
|
||||
//----------------------------------------------------------
|
||||
localparam IDLE = 3'd0;
|
||||
localparam SENDING = 3'd1;
|
||||
localparam INIT_RX = 3'd2;
|
||||
localparam DIR_SWITCH = 3'd3;
|
||||
localparam DIR_SWITCH_DLY = 3'd4;
|
||||
localparam RECEIVING = 3'd5;
|
||||
localparam ACK = 3'd6;
|
||||
localparam TIMEOUT = 3'd7;
|
||||
|
||||
// input registers to relax input timing
|
||||
reg [7:0] bytestream_data_in_reg = 8'b0;
|
||||
reg bytestream_valid_in_reg = 1'b0;
|
||||
always @ (posedge ctrlport_clk) begin
|
||||
bytestream_data_in_reg <= bytestream_data_in;
|
||||
bytestream_valid_in_reg <= bytestream_valid_in;
|
||||
end
|
||||
|
||||
// internal registers
|
||||
reg [ 2:0] state = IDLE;
|
||||
reg [NUM_BYTES_TX_WRITE*8-1:0] request_cache = {NUM_BYTES_TX_WRITE*8{1'b0}};
|
||||
reg [ NUM_BYTES_RX_READ*8-1:0] response_cache = {NUM_BYTES_RX_READ*8{1'b0}};
|
||||
reg [ 2:0] byte_counter = 3'b0;
|
||||
reg write_transfer = 1'b0;
|
||||
reg [TIMEOUT_COUNTER_WIDTH-1:0] timeout_counter = {TIMEOUT_COUNTER_WIDTH {1'b0}};
|
||||
|
||||
always @ (posedge ctrlport_clk) begin
|
||||
if (ctrlport_rst) begin
|
||||
state <= IDLE;
|
||||
|
||||
bytestream_valid_out <= 1'b0;
|
||||
byte_counter <= 3'b0;
|
||||
bytestream_direction <= 1'b0;
|
||||
bytestream_output_enable <= 1'b1;
|
||||
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
end else begin
|
||||
case (state)
|
||||
IDLE: begin
|
||||
// reset values from previous states
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
bytestream_valid_out <= 1'b0;
|
||||
bytestream_output_enable <= 1'b1;
|
||||
byte_counter <= 3'b0;
|
||||
timeout_counter <= {TIMEOUT_COUNTER_WIDTH {1'b0}};
|
||||
|
||||
// start transmission on read or write
|
||||
if (s_ctrlport_req_rd || s_ctrlport_req_wr) begin
|
||||
state <= SENDING;
|
||||
request_cache <= {s_ctrlport_req_wr, s_ctrlport_req_addr[14:0], s_ctrlport_req_data};
|
||||
write_transfer <= s_ctrlport_req_wr;
|
||||
end
|
||||
end
|
||||
|
||||
// send as many bytes as required for read / write
|
||||
SENDING: begin
|
||||
bytestream_data_out <= request_cache[NUM_BYTES_TX_WRITE*8-8+:8];
|
||||
request_cache <= {request_cache[NUM_BYTES_TX_WRITE*8-9:0], 8'b0};
|
||||
bytestream_valid_out <= 1'b1;
|
||||
byte_counter <= byte_counter + 1'b1;
|
||||
|
||||
if ((write_transfer && byte_counter == NUM_BYTES_TX_WRITE-1) ||
|
||||
(~write_transfer && byte_counter == NUM_BYTES_TX_READ-1)) begin
|
||||
state <= INIT_RX;
|
||||
end
|
||||
end
|
||||
|
||||
// first cycle for switching to make sure valid signal is driven
|
||||
// from slave when being in RECEIVING state
|
||||
INIT_RX: begin
|
||||
state <= DIR_SWITCH;
|
||||
|
||||
bytestream_direction <= 1'b1;
|
||||
bytestream_output_enable <= 1'b0;
|
||||
bytestream_valid_out <= 1'b0;
|
||||
|
||||
byte_counter <= 3'b0;
|
||||
end
|
||||
|
||||
// second switching cycle to let CPLD load the lines based on direction
|
||||
DIR_SWITCH: begin
|
||||
state <= DIR_SWITCH_DLY;
|
||||
end
|
||||
|
||||
// third switching cycle to compensate data input register
|
||||
DIR_SWITCH_DLY: begin
|
||||
state <= RECEIVING;
|
||||
end
|
||||
|
||||
// wait for response to be received
|
||||
// immediately change direction after successful reception to have one
|
||||
// clock cycle of pause to avoid double driving the bus
|
||||
RECEIVING: begin
|
||||
timeout_counter <= timeout_counter + 1;
|
||||
|
||||
if (bytestream_valid_in_reg) begin
|
||||
byte_counter <= byte_counter + 1'b1;
|
||||
response_cache = {response_cache[NUM_BYTES_RX_READ*8-9:0], bytestream_data_in_reg};
|
||||
|
||||
if ((write_transfer && byte_counter == NUM_BYTES_RX_WRITE-1) ||
|
||||
(~write_transfer && byte_counter == NUM_BYTES_RX_READ-1)) begin
|
||||
state <= ACK;
|
||||
bytestream_direction <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
if (timeout_counter == {TIMEOUT_COUNTER_WIDTH {1'b1}}) begin
|
||||
state <= TIMEOUT;
|
||||
bytestream_direction <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
// issue ctrlport response
|
||||
ACK: begin
|
||||
state <= IDLE;
|
||||
|
||||
s_ctrlport_resp_ack <= 1'b1;
|
||||
// status based on received ack
|
||||
s_ctrlport_resp_status <= response_cache[2] ? response_cache[1:0] : CTRL_STS_CMDERR;
|
||||
if (write_transfer) begin
|
||||
s_ctrlport_resp_data <= 32'b0;
|
||||
end else begin
|
||||
s_ctrlport_resp_data <= response_cache[39:8];
|
||||
end
|
||||
end
|
||||
|
||||
TIMEOUT: begin
|
||||
state <= IDLE;
|
||||
|
||||
s_ctrlport_resp_ack <= 1'b1;
|
||||
s_ctrlport_resp_status <= CTRL_STS_CMDERR;
|
||||
s_ctrlport_resp_data <= 32'b0;
|
||||
end
|
||||
|
||||
default: begin
|
||||
state <= IDLE;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
@@ -0,0 +1,323 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: db_gpio_interface
|
||||
//
|
||||
// Description:
|
||||
// Interface for GPIO interface towards daughterboards.
|
||||
//
|
||||
// A ControlPort interface is serialized into bytes along with a valid signal.
|
||||
// The ControlPort supports write requests only. Byte enables are not supported.
|
||||
// There is support for timed commands.
|
||||
// Furthermore there are 4 state wires towards the DB. Ensure an appropriate
|
||||
// hold time on the states as the transmission happens in pll_ref_clk, which is
|
||||
// slower than radio_clk. Pulses of e.g. just a single clock cycle may not get
|
||||
// transferred to the DB.
|
||||
//
|
||||
// The 20 available GPIO lines are assigned with
|
||||
// - 5x empty
|
||||
// - bytestream direction
|
||||
// - bytestream valid
|
||||
// - bytestream data (8 bits)
|
||||
// - 1x empty
|
||||
// - db_state (4 bits)
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module db_gpio_interface (
|
||||
// Clocks and reset
|
||||
input wire radio_clk,
|
||||
input wire pll_ref_clk,
|
||||
|
||||
// DB state lines (domain: radio_clk)
|
||||
input wire [ 3:0] db_state,
|
||||
|
||||
// time interfaces (domain: radio_clk)
|
||||
input wire [63:0] radio_time,
|
||||
input wire radio_time_stb,
|
||||
input wire [ 3:0] time_ignore_bits,
|
||||
|
||||
// Request (domain: radio_clk)
|
||||
input wire ctrlport_rst,
|
||||
input wire s_ctrlport_req_wr,
|
||||
input wire s_ctrlport_req_rd,
|
||||
input wire [19:0] s_ctrlport_req_addr,
|
||||
input wire [31:0] s_ctrlport_req_data,
|
||||
input wire [ 3:0] s_ctrlport_req_byte_en,
|
||||
input wire s_ctrlport_req_has_time,
|
||||
input wire [63:0] s_ctrlport_req_time,
|
||||
|
||||
// Response (domain: radio_clk)
|
||||
output wire s_ctrlport_resp_ack,
|
||||
output wire [ 1:0] s_ctrlport_resp_status,
|
||||
output wire [31:0] s_ctrlport_resp_data,
|
||||
|
||||
// GPIO interface (domain: pll_ref_clk)
|
||||
input wire [19:0] gpio_in,
|
||||
output wire [19:0] gpio_out,
|
||||
output wire [19:0] gpio_out_en,
|
||||
|
||||
// Version (Constant)
|
||||
output wire [95:0] version_info
|
||||
);
|
||||
|
||||
`include "../regmap/versioning_regs_regmap_utils.vh"
|
||||
`include "../regmap/versioning_utils.vh"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Timed command processing
|
||||
//----------------------------------------------------------------------------
|
||||
wire [19:0] ctrlport_timed_req_addr;
|
||||
wire [31:0] ctrlport_timed_req_data;
|
||||
wire ctrlport_timed_req_rd;
|
||||
wire ctrlport_timed_req_wr;
|
||||
wire ctrlport_timed_resp_ack;
|
||||
reg [31:0] ctrlport_timed_resp_data = 0;
|
||||
reg [ 1:0] ctrlport_timed_resp_status = 0;
|
||||
|
||||
ctrlport_timer #(
|
||||
.EXEC_LATE_CMDS(1)
|
||||
) ctrlport_timer_i (
|
||||
.clk (radio_clk),
|
||||
.rst (ctrlport_rst),
|
||||
.time_now (radio_time),
|
||||
.time_now_stb (radio_time_stb),
|
||||
.time_ignore_bits (time_ignore_bits),
|
||||
.s_ctrlport_req_wr (s_ctrlport_req_wr),
|
||||
.s_ctrlport_req_rd (s_ctrlport_req_rd),
|
||||
.s_ctrlport_req_addr (s_ctrlport_req_addr),
|
||||
.s_ctrlport_req_data (s_ctrlport_req_data),
|
||||
.s_ctrlport_req_byte_en (s_ctrlport_req_byte_en),
|
||||
.s_ctrlport_req_has_time (s_ctrlport_req_has_time),
|
||||
.s_ctrlport_req_time (s_ctrlport_req_time),
|
||||
.s_ctrlport_resp_ack (s_ctrlport_resp_ack),
|
||||
.s_ctrlport_resp_status (s_ctrlport_resp_status),
|
||||
.s_ctrlport_resp_data (s_ctrlport_resp_data),
|
||||
.m_ctrlport_req_wr (ctrlport_timed_req_wr),
|
||||
.m_ctrlport_req_rd (ctrlport_timed_req_rd),
|
||||
.m_ctrlport_req_addr (ctrlport_timed_req_addr),
|
||||
.m_ctrlport_req_data (ctrlport_timed_req_data),
|
||||
.m_ctrlport_req_byte_en (),
|
||||
.m_ctrlport_resp_ack (ctrlport_timed_resp_ack),
|
||||
.m_ctrlport_resp_status (ctrlport_timed_resp_status),
|
||||
.m_ctrlport_resp_data (ctrlport_timed_resp_data)
|
||||
);
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Clock domain crossing (radio_clk -> pll_ref_clk)
|
||||
//----------------------------------------------------------------------------
|
||||
// Radio_clk is derived from pll_ref_clk by an integer multiplier and
|
||||
// originate from the same PLL.
|
||||
// Therefore the clock crossing can be achieved by using simple registers.
|
||||
// Static timing analysis will be able to meet setup and hold requirements on
|
||||
// them.
|
||||
|
||||
// holding read and write flags for multiple radio_clk cycles
|
||||
reg ctrlport_timed_req_wr_hold = 1'b0;
|
||||
reg ctrlport_timed_req_rd_hold = 1'b0;
|
||||
|
||||
reg [19:0] ctrlport_req_addr_prc = 20'b0;
|
||||
reg [31:0] ctrlport_req_data_prc = 32'b0;
|
||||
reg ctrlport_req_rd_prc = 1'b0;
|
||||
reg ctrlport_req_wr_prc = 1'b0;
|
||||
|
||||
wire ctrlport_resp_ack_prc;
|
||||
wire [31:0] ctrlport_resp_data_prc;
|
||||
wire [ 1:0] ctrlport_resp_status_prc;
|
||||
|
||||
reg ctrlport_req_rd_fall = 1'b0;
|
||||
reg ctrlport_req_wr_fall = 1'b0;
|
||||
reg [31:0] ctrlport_resp_data_fall = 32'b0;
|
||||
reg [ 1:0] ctrlport_resp_status_fall = 2'b0;
|
||||
reg ctrlport_resp_ack_fall = 1'b0;
|
||||
|
||||
// Retime signals to falling edge of radio_clk.
|
||||
// Because radio_clk is more heavily loaded than pll_ref_clk, it arrives at
|
||||
// the FF's later, which leads to hold time violations when moving signals
|
||||
// from pll_ref_clk to radio_clk. By sampling on the falling edge of
|
||||
// radio_clk, we provide (nominally) half a radio_clk period of hold, while
|
||||
// reducing setup time by half. The late arrival of radio_clk adds back some
|
||||
// of the lost setup margin.
|
||||
always @(negedge radio_clk) begin
|
||||
ctrlport_req_rd_fall <= ctrlport_req_rd_prc;
|
||||
ctrlport_req_wr_fall <= ctrlport_req_wr_prc;
|
||||
ctrlport_resp_ack_fall <= ctrlport_resp_ack_prc;
|
||||
ctrlport_resp_status_fall <= ctrlport_resp_status_prc;
|
||||
ctrlport_resp_data_fall <= ctrlport_resp_data_prc;
|
||||
end
|
||||
|
||||
always @(posedge radio_clk) begin
|
||||
if (ctrlport_req_wr_fall) begin
|
||||
ctrlport_timed_req_wr_hold <= 1'b0;
|
||||
end else if (ctrlport_timed_req_wr) begin
|
||||
ctrlport_timed_req_wr_hold <= 1'b1;
|
||||
end
|
||||
if (ctrlport_req_rd_fall) begin
|
||||
ctrlport_timed_req_rd_hold <= 1'b0;
|
||||
end else if (ctrlport_timed_req_rd) begin
|
||||
ctrlport_timed_req_rd_hold <= 1'b1;
|
||||
end
|
||||
|
||||
// capture request address and data
|
||||
if (ctrlport_timed_req_wr || ctrlport_timed_req_rd) begin
|
||||
ctrlport_req_addr_prc <= ctrlport_timed_req_addr;
|
||||
ctrlport_req_data_prc <= ctrlport_timed_req_data;
|
||||
end
|
||||
end
|
||||
|
||||
// capture extended flags in pll_ref_clk domain
|
||||
always @(posedge pll_ref_clk) begin
|
||||
ctrlport_req_wr_prc <= ctrlport_timed_req_wr_hold;
|
||||
ctrlport_req_rd_prc <= ctrlport_timed_req_rd_hold;
|
||||
end
|
||||
|
||||
// search for rising edge in response
|
||||
reg [1:0] ctrlport_timed_ack_reg = 2'b0;
|
||||
always @(posedge radio_clk) begin
|
||||
ctrlport_timed_ack_reg = {ctrlport_timed_ack_reg[0], ctrlport_resp_ack_fall};
|
||||
end
|
||||
assign ctrlport_timed_resp_ack = ctrlport_timed_ack_reg[0] & ~ctrlport_timed_ack_reg[1];
|
||||
|
||||
// capture response data
|
||||
always @(posedge radio_clk) begin
|
||||
if (ctrlport_resp_ack_fall) begin
|
||||
ctrlport_timed_resp_status <= ctrlport_resp_status_fall;
|
||||
ctrlport_timed_resp_data <= ctrlport_resp_data_fall;
|
||||
end
|
||||
end
|
||||
|
||||
// transfer state lines
|
||||
reg [3:0] db_state_prc = 4'b0;
|
||||
reg [3:0] db_state_prc_fe = 4'b0;
|
||||
always @(posedge pll_ref_clk) begin
|
||||
db_state_prc <= db_state;
|
||||
end
|
||||
always @(negedge pll_ref_clk) begin
|
||||
db_state_prc_fe <= db_state_prc;
|
||||
end
|
||||
|
||||
// transfer reset
|
||||
reg ctrlport_rst_hold = 1'b0;
|
||||
reg ctrlport_rst_prc = 1'b0;
|
||||
reg ctrlport_rst_fall = 1'b0;
|
||||
always @(posedge radio_clk) begin
|
||||
if (ctrlport_rst) begin
|
||||
ctrlport_rst_hold <= 1'b1;
|
||||
end else if (ctrlport_rst_fall) begin
|
||||
ctrlport_rst_hold <= 1'b0;
|
||||
end
|
||||
end
|
||||
always @(posedge pll_ref_clk) begin
|
||||
ctrlport_rst_prc <= ctrlport_rst_hold;
|
||||
end
|
||||
always @(negedge radio_clk) begin
|
||||
ctrlport_rst_fall <= ctrlport_rst_prc;
|
||||
end
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Ctrlport serializer
|
||||
//----------------------------------------------------------------------------
|
||||
wire [7:0] bytestream_data_in;
|
||||
wire [7:0] bytestream_data_out;
|
||||
wire bytestream_direction;
|
||||
wire bytestream_output_enable;
|
||||
wire bytestream_valid_in;
|
||||
wire bytestream_valid_out;
|
||||
|
||||
ctrlport_byte_serializer serializer_i (
|
||||
.ctrlport_clk (pll_ref_clk),
|
||||
.ctrlport_rst (ctrlport_rst_prc),
|
||||
.s_ctrlport_req_wr (ctrlport_req_wr_prc),
|
||||
.s_ctrlport_req_rd (ctrlport_req_rd_prc),
|
||||
.s_ctrlport_req_addr (ctrlport_req_addr_prc),
|
||||
.s_ctrlport_req_data (ctrlport_req_data_prc),
|
||||
.s_ctrlport_resp_ack (ctrlport_resp_ack_prc),
|
||||
.s_ctrlport_resp_status (ctrlport_resp_status_prc),
|
||||
.s_ctrlport_resp_data (ctrlport_resp_data_prc),
|
||||
.bytestream_data_in (bytestream_data_in),
|
||||
.bytestream_valid_in (bytestream_valid_in),
|
||||
.bytestream_data_out (bytestream_data_out),
|
||||
.bytestream_valid_out (bytestream_valid_out),
|
||||
.bytestream_direction (bytestream_direction),
|
||||
.bytestream_output_enable (bytestream_output_enable)
|
||||
);
|
||||
|
||||
// IOB registers to drive data on the falling edge
|
||||
reg [7:0] bytestream_data_out_fe;
|
||||
reg bytestream_direction_fe;
|
||||
reg bytestream_output_enable_fe;
|
||||
reg bytestream_valid_out_fe;
|
||||
|
||||
// Signals are shifted into a falling edge domain to help meet
|
||||
// hold requirements at CPLD
|
||||
always @(negedge pll_ref_clk) begin
|
||||
if (ctrlport_rst_prc) begin
|
||||
bytestream_data_out_fe <= 8'b0;
|
||||
bytestream_valid_out_fe <= 1'b0;
|
||||
bytestream_direction_fe <= 1'b0;
|
||||
bytestream_output_enable_fe <= 1'b1;
|
||||
end else begin
|
||||
bytestream_data_out_fe <= bytestream_data_out;
|
||||
bytestream_valid_out_fe <= bytestream_valid_out;
|
||||
bytestream_direction_fe <= bytestream_direction;
|
||||
bytestream_output_enable_fe <= bytestream_output_enable;
|
||||
end
|
||||
end
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// wire assignment
|
||||
//----------------------------------------------------------------------------
|
||||
// 5 unused, 10 used, 1 unused and 4 used signals
|
||||
assign gpio_out = {5'b0, bytestream_direction_fe, bytestream_valid_out_fe, bytestream_data_out_fe, 1'b0, db_state_prc_fe};
|
||||
assign gpio_out_en = {5'b0, 1'b1, {9 {bytestream_output_enable_fe}}, 1'b0, {4 {1'b1}} };
|
||||
|
||||
assign bytestream_valid_in = gpio_in[13];
|
||||
assign bytestream_data_in = gpio_in[12:5];
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// version_info
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
// Version metadata, constants come from auto-generated versioning_regs_regmap_utils.vh
|
||||
assign version_info = build_component_versions(
|
||||
DB_GPIO_IFC_VERSION_LAST_MODIFIED_TIME,
|
||||
build_version(
|
||||
DB_GPIO_IFC_OLDEST_COMPATIBLE_VERSION_MAJOR,
|
||||
DB_GPIO_IFC_OLDEST_COMPATIBLE_VERSION_MINOR,
|
||||
DB_GPIO_IFC_OLDEST_COMPATIBLE_VERSION_BUILD),
|
||||
build_version(
|
||||
DB_GPIO_IFC_CURRENT_VERSION_MAJOR,
|
||||
DB_GPIO_IFC_CURRENT_VERSION_MINOR,
|
||||
DB_GPIO_IFC_CURRENT_VERSION_BUILD));
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
|
||||
//XmlParse xml_on
|
||||
//<regmap name="VERSIONING_REGS_REGMAP">
|
||||
// <group name="VERSIONING_CONSTANTS">
|
||||
// <enumeratedtype name="DB_GPIO_IFC_VERSION" showhex="true">
|
||||
// <info>
|
||||
// Daughterboard GPIO interface.{BR/}
|
||||
// For guidance on when to update these revision numbers,
|
||||
// please refer to the register map documentation accordingly:
|
||||
// <li> Current version: @.VERSIONING_REGS_REGMAP..CURRENT_VERSION
|
||||
// <li> Oldest compatible version: @.VERSIONING_REGS_REGMAP..OLDEST_COMPATIBLE_VERSION
|
||||
// <li> Version last modified: @.VERSIONING_REGS_REGMAP..VERSION_LAST_MODIFIED
|
||||
// </info>
|
||||
// <value name="DB_GPIO_IFC_CURRENT_VERSION_MAJOR" integer="1"/>
|
||||
// <value name="DB_GPIO_IFC_CURRENT_VERSION_MINOR" integer="0"/>
|
||||
// <value name="DB_GPIO_IFC_CURRENT_VERSION_BUILD" integer="0"/>
|
||||
// <value name="DB_GPIO_IFC_OLDEST_COMPATIBLE_VERSION_MAJOR" integer="1"/>
|
||||
// <value name="DB_GPIO_IFC_OLDEST_COMPATIBLE_VERSION_MINOR" integer="0"/>
|
||||
// <value name="DB_GPIO_IFC_OLDEST_COMPATIBLE_VERSION_BUILD" integer="0"/>
|
||||
// <value name="DB_GPIO_IFC_VERSION_LAST_MODIFIED_TIME" integer="0x20110616"/>
|
||||
// </enumeratedtype>
|
||||
// </group>
|
||||
//</regmap>
|
||||
//XmlParse xml_off
|
||||
@@ -0,0 +1,108 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: db_gpio_reordering
|
||||
//
|
||||
// Description:
|
||||
// Reorders the GPIO wires towards the DB CPLDs in a common way for DB 0 and 1.
|
||||
//
|
||||
// The digital daughterboard connector has 120 pins [A-F][1-20].
|
||||
// The numbering on the motherboard traces do not match for daughterboard 0 and 1.
|
||||
// This module orders the FPGA outputs MSB first and connects it to the DB
|
||||
// connection with increasing letter and increasing number.
|
||||
// For DB 0 this results in:
|
||||
// FPGA Bit 19 = A7 (trace: DB0/1_GPIO[19])
|
||||
// FPGA Bit 18 = A8 (trace: DB0/1_GPIO[17])
|
||||
// ...
|
||||
// FPGA Bit 0 = C19 (trace: DB0/1_GPIO[12])
|
||||
// This enables usages of the same daughterboard CPLD image on both connectors.
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module db_gpio_reordering (
|
||||
// 20 bit internal interface
|
||||
output wire [19:0] db0_gpio_in_int,
|
||||
input wire [19:0] db0_gpio_out_int,
|
||||
input wire [19:0] db0_gpio_out_en_int,
|
||||
output wire [19:0] db1_gpio_in_int,
|
||||
input wire [19:0] db1_gpio_out_int,
|
||||
input wire [19:0] db1_gpio_out_en_int,
|
||||
|
||||
// 20 bit external interface
|
||||
input wire [19:0] db0_gpio_in_ext,
|
||||
output wire [19:0] db0_gpio_out_ext,
|
||||
output wire [19:0] db0_gpio_out_en_ext,
|
||||
input wire [19:0] db1_gpio_in_ext,
|
||||
output wire [19:0] db1_gpio_out_ext,
|
||||
output wire [19:0] db1_gpio_out_en_ext
|
||||
);
|
||||
|
||||
//port indexes
|
||||
localparam ENTRY_BITWIDTH = 5;
|
||||
localparam NUM_ENTRIES = 20;
|
||||
localparam [ENTRY_BITWIDTH*NUM_ENTRIES-1:0] PORT0_MAPPING = {
|
||||
5'd 19,
|
||||
5'd 17,
|
||||
5'd 0,
|
||||
5'd 14,
|
||||
5'd 15,
|
||||
5'd 10,
|
||||
5'd 4,
|
||||
5'd 5,
|
||||
5'd 16,
|
||||
5'd 18,
|
||||
5'd 8,
|
||||
5'd 6,
|
||||
5'd 1,
|
||||
5'd 9,
|
||||
5'd 2,
|
||||
5'd 3,
|
||||
5'd 11,
|
||||
5'd 7,
|
||||
5'd 13,
|
||||
5'd 12 };
|
||||
localparam [ENTRY_BITWIDTH*NUM_ENTRIES-1:0] PORT1_MAPPING = {
|
||||
5'd 10,
|
||||
5'd 6,
|
||||
5'd 7,
|
||||
5'd 2,
|
||||
5'd 3,
|
||||
5'd 0,
|
||||
5'd 1,
|
||||
5'd 4,
|
||||
5'd 8,
|
||||
5'd 9,
|
||||
5'd 11,
|
||||
5'd 5,
|
||||
5'd 13,
|
||||
5'd 12,
|
||||
5'd 15,
|
||||
5'd 14,
|
||||
5'd 19,
|
||||
5'd 18,
|
||||
5'd 17,
|
||||
5'd 16 };
|
||||
|
||||
// reordering assignments
|
||||
generate
|
||||
genvar i;
|
||||
for (i=0; i<NUM_ENTRIES; i=i+1) begin : reordering_gen
|
||||
// input data
|
||||
assign db0_gpio_in_int[i] = db0_gpio_in_ext[PORT0_MAPPING[i*ENTRY_BITWIDTH +: ENTRY_BITWIDTH]];
|
||||
assign db1_gpio_in_int[i] = db1_gpio_in_ext[PORT1_MAPPING[i*ENTRY_BITWIDTH +: ENTRY_BITWIDTH]];
|
||||
|
||||
// output data
|
||||
assign db0_gpio_out_ext[PORT0_MAPPING[i*ENTRY_BITWIDTH +: ENTRY_BITWIDTH]] = db0_gpio_out_int[i];
|
||||
assign db1_gpio_out_ext[PORT1_MAPPING[i*ENTRY_BITWIDTH +: ENTRY_BITWIDTH]] = db1_gpio_out_int[i];
|
||||
|
||||
// output enable
|
||||
assign db0_gpio_out_en_ext[PORT0_MAPPING[i*ENTRY_BITWIDTH +: ENTRY_BITWIDTH]] = db0_gpio_out_en_int[i];
|
||||
assign db1_gpio_out_en_ext[PORT1_MAPPING[i*ENTRY_BITWIDTH +: ENTRY_BITWIDTH]] = db1_gpio_out_en_int[i];
|
||||
end
|
||||
endgenerate
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
Reference in New Issue
Block a user