diff options
author | Wade Fife <wade.fife@ettus.com> | 2021-06-08 19:40:46 -0500 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2021-06-10 11:56:58 -0500 |
commit | 6d3765605262016a80f71e36357f749ea35cbe5a (patch) | |
tree | 7d62d6622befd4132ac1ee085effa1426f7f53e5 /fpga/usrp3/top/x400/dboards | |
parent | f706b89e6974e28ce76aadeeb06169becc86acba (diff) | |
download | uhd-6d3765605262016a80f71e36357f749ea35cbe5a.tar.gz uhd-6d3765605262016a80f71e36357f749ea35cbe5a.tar.bz2 uhd-6d3765605262016a80f71e36357f749ea35cbe5a.zip |
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>
Diffstat (limited to 'fpga/usrp3/top/x400/dboards')
-rw-r--r-- | fpga/usrp3/top/x400/dboards/ctrlport_byte_deserializer.v | 186 | ||||
-rw-r--r-- | fpga/usrp3/top/x400/dboards/ctrlport_byte_serializer.v | 228 | ||||
-rw-r--r-- | fpga/usrp3/top/x400/dboards/db_gpio_interface.v | 323 | ||||
-rw-r--r-- | fpga/usrp3/top/x400/dboards/db_gpio_reordering.v | 108 |
4 files changed, 845 insertions, 0 deletions
diff --git a/fpga/usrp3/top/x400/dboards/ctrlport_byte_deserializer.v b/fpga/usrp3/top/x400/dboards/ctrlport_byte_deserializer.v new file mode 100644 index 000000000..b8fde0025 --- /dev/null +++ b/fpga/usrp3/top/x400/dboards/ctrlport_byte_deserializer.v @@ -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 diff --git a/fpga/usrp3/top/x400/dboards/ctrlport_byte_serializer.v b/fpga/usrp3/top/x400/dboards/ctrlport_byte_serializer.v new file mode 100644 index 000000000..3bb6df3c6 --- /dev/null +++ b/fpga/usrp3/top/x400/dboards/ctrlport_byte_serializer.v @@ -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 diff --git a/fpga/usrp3/top/x400/dboards/db_gpio_interface.v b/fpga/usrp3/top/x400/dboards/db_gpio_interface.v new file mode 100644 index 000000000..1e8cd44ee --- /dev/null +++ b/fpga/usrp3/top/x400/dboards/db_gpio_interface.v @@ -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 diff --git a/fpga/usrp3/top/x400/dboards/db_gpio_reordering.v b/fpga/usrp3/top/x400/dboards/db_gpio_reordering.v new file mode 100644 index 000000000..f24a3f3d9 --- /dev/null +++ b/fpga/usrp3/top/x400/dboards/db_gpio_reordering.v @@ -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 |