aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/top/x400/x4xx_gpio_atr.v
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/top/x400/x4xx_gpio_atr.v')
-rw-r--r--fpga/usrp3/top/x400/x4xx_gpio_atr.v376
1 files changed, 376 insertions, 0 deletions
diff --git a/fpga/usrp3/top/x400/x4xx_gpio_atr.v b/fpga/usrp3/top/x400/x4xx_gpio_atr.v
new file mode 100644
index 000000000..98ba881f3
--- /dev/null
+++ b/fpga/usrp3/top/x400/x4xx_gpio_atr.v
@@ -0,0 +1,376 @@
+//
+// Copyright 2021 Ettus Research, A National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: x4xx_gpio_atr
+//
+// Description:
+//
+// This module controls the behavior of a GPIO bus of arbitrary width
+// based on the current ATR state. The radio state is determined by
+// combining the TX and RX states of each rf channel in the radio in
+// the following order: {tx_rf1, rx_rf1, tx_rf0, rx_rf0}
+//
+// Parameters:
+//
+// REG_BASE : Base address to use for registers.
+// REG_SIZE : Register space size.
+// WIDTH : Number of GPIO lines controlled by this block.
+//
+module x4xx_gpio_atr #(
+ parameter REG_BASE = 0,
+ parameter REG_SIZE = 'h20,
+ parameter WIDTH = 32
+) (
+ // Slave ctrlport interface
+ input wire ctrlport_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,
+ output reg s_ctrlport_resp_ack = 1'b0,
+ output reg [ 1:0] s_ctrlport_resp_status = 2'b00,
+ output reg [31:0] s_ctrlport_resp_data = {32 {1'bX}},
+ // Run state signals that indicate tx and rx operation
+ input wire [3:0] db_state,
+ // GPIO control signals
+ input wire [WIDTH-1:0] gpio_in, //GPIO input state
+ output reg [WIDTH-1:0] gpio_out = {WIDTH {1'b0}}, //GPIO output state
+ output reg [WIDTH-1:0] gpio_ddr = {WIDTH {1'b0}} //GPIO direction (0=input, 1=output)
+);
+
+ `include "../../lib/rfnoc/core/ctrlport.vh"
+ `include "regmap/gpio_atr_regmap_utils.vh"
+
+ reg [WIDTH-1:0] in_atr_state [15:0];
+
+ initial begin
+ in_atr_state[0] = {WIDTH {1'b0}};
+ in_atr_state[1] = {WIDTH {1'b0}};
+ in_atr_state[2] = {WIDTH {1'b0}};
+ in_atr_state[3] = {WIDTH {1'b0}};
+ in_atr_state[4] = {WIDTH {1'b0}};
+ in_atr_state[5] = {WIDTH {1'b0}};
+ in_atr_state[6] = {WIDTH {1'b0}};
+ in_atr_state[7] = {WIDTH {1'b0}};
+ in_atr_state[8] = {WIDTH {1'b0}};
+ in_atr_state[9] = {WIDTH {1'b0}};
+ in_atr_state[10] = {WIDTH {1'b0}};
+ in_atr_state[11] = {WIDTH {1'b0}};
+ in_atr_state[12] = {WIDTH {1'b0}};
+ in_atr_state[13] = {WIDTH {1'b0}};
+ in_atr_state[14] = {WIDTH {1'b0}};
+ in_atr_state[15] = {WIDTH {1'b0}};
+ end
+
+ reg [WIDTH-1:0] ddr_reg, atr_disable, gpio_sw_rb = {WIDTH {1'b0}};
+ reg [WIDTH-1:0] ogpio, igpio = {WIDTH {1'b0}};
+ reg [WIDTH-1:0] classic_atr_select = {WIDTH {1'b0}};
+
+ // DB state/Classic ATR selector
+ reg atr_mode = 1'b0;
+
+ genvar state;
+ //---------------------------------------------------------------------------
+ // Control interface handling
+ //---------------------------------------------------------------------------
+
+ // Check that address is within this module's range.
+ wire address_in_range = (s_ctrlport_req_addr >= REG_BASE) && (s_ctrlport_req_addr < REG_BASE + REG_SIZE);
+
+ // Check that address is targeting an ATR state.
+ wire address_is_atr = (s_ctrlport_req_addr >= REG_BASE + ATR_STATE(0)) && (s_ctrlport_req_addr <= REG_BASE + ATR_STATE(15));
+ // Decode the ATR state being addressed.
+ wire [3:0]atr_address = s_ctrlport_req_addr[5:2];
+
+ always @ (posedge ctrlport_clk) begin
+ if (ctrlport_rst) begin
+ s_ctrlport_resp_ack <= 1'b0;
+ s_ctrlport_resp_data <= {32 {1'bX}};
+ s_ctrlport_resp_status <= 2'b00;
+
+ ddr_reg <= {WIDTH {1'b0}};
+ atr_disable <= {WIDTH {1'b0}};
+ classic_atr_select <= {WIDTH {1'b0}};
+ atr_mode <= 1'b0;
+
+ in_atr_state[0] <= {WIDTH {1'b0}};
+ in_atr_state[1] <= {WIDTH {1'b0}};
+ in_atr_state[2] <= {WIDTH {1'b0}};
+ in_atr_state[3] <= {WIDTH {1'b0}};
+ in_atr_state[4] <= {WIDTH {1'b0}};
+ in_atr_state[5] <= {WIDTH {1'b0}};
+ in_atr_state[6] <= {WIDTH {1'b0}};
+ in_atr_state[7] <= {WIDTH {1'b0}};
+ in_atr_state[8] <= {WIDTH {1'b0}};
+ in_atr_state[9] <= {WIDTH {1'b0}};
+ in_atr_state[10] <= {WIDTH {1'b0}};
+ in_atr_state[11] <= {WIDTH {1'b0}};
+ in_atr_state[12] <= {WIDTH {1'b0}};
+ in_atr_state[13] <= {WIDTH {1'b0}};
+ in_atr_state[14] <= {WIDTH {1'b0}};
+ in_atr_state[15] <= {WIDTH {1'b0}};
+
+ end else begin
+ // Write registers
+ if (s_ctrlport_req_wr) begin
+ // Acknowledge by default
+ s_ctrlport_resp_ack <= 1'b1;
+ s_ctrlport_resp_data <= {CTRLPORT_DATA_W {1'b0}};
+ s_ctrlport_resp_status <= CTRL_STS_OKAY;
+
+ // Address ATR state writes
+ if(address_is_atr) begin
+ in_atr_state[atr_address][GPIO_STATE_A_MSB:GPIO_STATE_A] <= s_ctrlport_req_data[GPIO_STATE_A_MSB:GPIO_STATE_A];
+ in_atr_state[atr_address][GPIO_STATE_B_MSB:GPIO_STATE_B] <= s_ctrlport_req_data[GPIO_STATE_B_MSB:GPIO_STATE_B];
+ end else begin
+
+ // Address writes to the rest of the register space
+ case (s_ctrlport_req_addr)
+
+ REG_BASE + ATR_OPTION_REGISTRER: begin
+ atr_mode <= s_ctrlport_req_data[ATR_OPTION];
+ end
+
+ REG_BASE + CLASSIC_ATR_CONFIG: begin
+ classic_atr_select[RF_SELECT_A_MSB:RF_SELECT_A] <= s_ctrlport_req_data[RF_SELECT_A_MSB:RF_SELECT_A];
+ classic_atr_select[RF_SELECT_B_MSB:RF_SELECT_B] <= s_ctrlport_req_data[RF_SELECT_B_MSB:RF_SELECT_B];
+ end
+
+ REG_BASE + GPIO_DIR: begin
+ ddr_reg[GPIO_DIR_A_MSB:GPIO_DIR_A] <= s_ctrlport_req_data[GPIO_DIR_A_MSB:GPIO_DIR_A];
+ ddr_reg[GPIO_DIR_B_MSB:GPIO_DIR_B] <= s_ctrlport_req_data[GPIO_DIR_B_MSB:GPIO_DIR_B];
+ end
+
+ REG_BASE + GPIO_DISABLED: begin
+ atr_disable[GPIO_DISABLED_A_MSB:GPIO_DISABLED_A] <= s_ctrlport_req_data[GPIO_DISABLED_A_MSB:GPIO_DISABLED_A];
+ atr_disable[GPIO_DISABLED_B_MSB:GPIO_DISABLED_B] <= s_ctrlport_req_data[GPIO_DISABLED_B_MSB:GPIO_DISABLED_B];
+ end
+
+ // No register implementation for provided address
+ default: begin
+ // Acknowledge and provide error status if address is in range
+ if (address_in_range) begin
+ s_ctrlport_resp_status <= CTRL_STS_CMDERR;
+
+ // No response if out of range
+ end else begin
+ s_ctrlport_resp_ack <= 1'b0;
+ end
+ end
+ endcase
+ end
+
+ // Read registers
+ end else if (s_ctrlport_req_rd) begin
+ // Acknowledge by default
+ s_ctrlport_resp_ack <= 1'b1;
+ s_ctrlport_resp_data <= {CTRLPORT_DATA_W {1'b0}};
+ s_ctrlport_resp_status <= CTRL_STS_OKAY;
+
+ // Address ATR state reads
+ if(address_is_atr) begin
+ s_ctrlport_resp_data[GPIO_STATE_A_MSB:GPIO_STATE_A] <= in_atr_state[atr_address][GPIO_STATE_A_MSB:GPIO_STATE_A];
+ s_ctrlport_resp_data[GPIO_STATE_B_MSB:GPIO_STATE_B] <= in_atr_state[atr_address][GPIO_STATE_B_MSB:GPIO_STATE_B];
+ end else begin
+
+ // Address reads to the rest of the register space
+ case (s_ctrlport_req_addr)
+
+ REG_BASE + ATR_OPTION_REGISTRER: begin
+ s_ctrlport_resp_data[ATR_OPTION] <= atr_mode;
+ end
+
+ REG_BASE + CLASSIC_ATR_CONFIG: begin
+ s_ctrlport_resp_data[RF_SELECT_A_MSB:RF_SELECT_A] <= classic_atr_select[RF_SELECT_A_MSB:RF_SELECT_A];
+ s_ctrlport_resp_data[RF_SELECT_B_MSB:RF_SELECT_B] <= classic_atr_select[RF_SELECT_B_MSB:RF_SELECT_B];
+ end
+
+ REG_BASE + GPIO_DIR: begin
+ s_ctrlport_resp_data[GPIO_DIR_A_MSB:GPIO_DIR_A] <= ddr_reg[GPIO_DIR_A_MSB:GPIO_DIR_A];
+ s_ctrlport_resp_data[GPIO_DIR_B_MSB:GPIO_DIR_B] <= ddr_reg[GPIO_DIR_B_MSB:GPIO_DIR_B];
+ end
+
+ REG_BASE + GPIO_DISABLED: begin
+ s_ctrlport_resp_data[GPIO_DISABLED_A_MSB:GPIO_DISABLED_A] <= atr_disable[GPIO_DISABLED_A_MSB:GPIO_DISABLED_A];
+ s_ctrlport_resp_data[GPIO_DISABLED_B_MSB:GPIO_DISABLED_B] <= atr_disable[GPIO_DISABLED_B_MSB:GPIO_DISABLED_B];
+ end
+
+ REG_BASE + GPIO_IN: begin
+ s_ctrlport_resp_data[GPIO_IN_A_MSB:GPIO_IN_A] <= gpio_sw_rb[GPIO_IN_A_MSB:GPIO_IN_A];
+ s_ctrlport_resp_data[GPIO_IN_B_MSB:GPIO_IN_B] <= gpio_sw_rb[GPIO_IN_B_MSB:GPIO_IN_B];
+ end
+
+ // No register implementation for provided address
+ default: begin
+ // Acknowledge and provide error status if address is in range
+ if (address_in_range) begin
+ s_ctrlport_resp_status <= CTRL_STS_CMDERR;
+
+ // No response if out of range
+ end else begin
+ s_ctrlport_resp_ack <= 1'b0;
+ end
+ end
+ endcase
+ end
+
+ end else begin
+ s_ctrlport_resp_ack <= 1'b0;
+ end
+ end
+ end
+
+ genvar i;
+
+ //Pipeline for easier timing closure
+ reg [ 3:0] db_state_d = 4'b0;
+ reg atr_mode_d = 1'b0;
+ reg [WIDTH-1:0] classic_atr_select_d = {WIDTH {1'b0}};
+ always @(posedge ctrlport_clk) begin
+ db_state_d <= db_state;
+ atr_mode_d <= atr_mode;
+ classic_atr_select_d <= classic_atr_select;
+ end
+
+ generate
+ for (i=0; i<WIDTH; i=i+1) begin: gpio_mux_gen
+ //ATR selection MUX
+ // Classic ATR
+ always @(posedge ctrlport_clk) begin
+ if(atr_mode_d) begin
+ if (atr_disable[i]) begin
+ // ATR state 0 for RF 0 and ATR state 4 for RF1
+ ogpio[i] <= in_atr_state[classic_atr_select_d[i]*4][i];
+ end else begin
+ if (classic_atr_select_d[i]) begin // RF 1
+ ogpio[i] <= in_atr_state[db_state_d[3:2] + 4][i];
+ end else begin // RF 0
+ ogpio[i] <= in_atr_state[db_state_d[1:0]][i];
+ end
+ end
+ end else begin // Use DB state
+ if (atr_disable[i]) begin
+ ogpio[i] <= in_atr_state[0][i];
+ end else begin
+ ogpio[i] <= in_atr_state[db_state_d][i];
+ end
+ end
+ end
+ end
+ endgenerate
+
+ //Pipeline input, output and direction
+ always @(posedge ctrlport_clk)
+ gpio_out <= ogpio;
+
+ always @(posedge ctrlport_clk)
+ igpio <= gpio_in;
+
+ always @(posedge ctrlport_clk)
+ gpio_ddr <= ddr_reg;
+
+ //Generate software readback state
+ generate
+ for (i=0; i<WIDTH; i=i+1) begin: gpio_rb_gen
+ always @(posedge ctrlport_clk)
+ gpio_sw_rb[i] <= gpio_ddr[i] ? gpio_out[i] : igpio[i];
+ end
+ endgenerate
+
+endmodule
+
+//XmlParse xml_on
+//<regmap name="GPIO_ATR_REGMAP" readablestrobes="false" generatevhdl="true" ettusguidelines="true">
+// <group name="GPIO_ATR_REGS">
+// <info>
+// Describes the behavior of GPIO lines when controlled by the ATR state.
+// </info>
+//
+// <regtype name="GPIO_ATR_STATE" size="32">
+// <info>Holds a single bit setting for GPIO lines in both ports for a particular ATR sate</info>
+// <bitfield name="GPIO_STATE_A" range="0..11" initialvalue="0"/>
+// <bitfield name="GPIO_STATE_B" range="16..27" initialvalue="0"/>
+// </regtype>
+//
+// <register name="ATR_STATE" typename="GPIO_ATR_STATE" offset="0x00" count="16" options="--step 4">
+// <info>
+// Describes GPIO behavior for the different ATR states. When @.ATR_OPTION
+// is set to use the DB states, TX and RX states for RF0 and RF1 are
+// combined to create a single vector. This creates 16 different
+// combinations, each with its own register. When @.ATR_OPTION is set to
+// classic ATR, offsets 0x00-0x03 in this register group will be driven
+// in accordance with the state of RF0, and offsets 0x04-0x07 will be
+// driven in accordance with the state of RF1.
+// CLASSIC ATR MAPPING: Idle[RF0:0x00; RF1:0x04], RX[RF0:0x01; RF1:0x05],
+// TX[RF0:0x02; RF1:0x06], FDX[RF0:0x03; RF1:0x07]
+// </info>
+// </register>
+// <register name="ATR_OPTION_REGISTRER" offset="0x44" size="32">
+// <info>
+// Controls whether GPIO lines use the TX and RX state of an RF channel
+// (Classic ATR) or the daughterboard state the selector for the
+// @.GPIO_ATR_STATE.
+// </info>
+// <bitfield name="ATR_OPTION" range="0" initialvalue="0">
+// <info>
+// Sets the scheme in which RF states in the radio will control GPIO
+// lines. 0 = DB state is used. RF states are combined and the
+// GPIO state is driven based on all 16 @.GPIO_ATR_STATE registers.
+// 1 = Each RF channel has its separate ATR state(Classic ATR).
+// Use register @.CLASSIC_ATR_CONFIG to indicate the RF channel
+// to which each GPIO line responds to.
+// </info>
+// </bitfield>
+// </register>
+// <register name="CLASSIC_ATR_CONFIG" offset="0x40" size="32">
+// <info>
+// Controls the RF state mapping of each GPIO line when classic
+// ATR mode is active.
+// </info>
+// <bitfield name="RF_SELECT_A" range="0..11" initialvalue="0">
+// <info>
+// Set which RF channel's state to reflect in the pins for
+// HDMI connector A when @.ATR_OPTION is set to classic ATR.
+// Controlled in a per-pin basis.
+// 0 = RF0 State(GPIO_ATR_STATE 0x00-0x03)
+// 1 = RF1 State(GPIO_ATR_STATE 0x04-0x07)
+// </info>
+// </bitfield>
+// <bitfield name="RF_SELECT_B" range="16..27" initialvalue="0">
+// <info>
+// Set which RF channel's state to reflect in the pins of
+// HDMI connector B when @.ATR_OPTION is set to classic ATR.
+// Controlled in a per-pin basis.
+// 0 = RF0 State(GPIO_ATR_STATE 0x00-0x03)
+// 1 = RF1 State(GPIO_ATR_STATE 0x04-0x07)
+// </info>
+// </bitfield>
+// </register>
+// <register name="GPIO_DIR" offset="0x48" size="32">
+// <info>
+// Controls the direction of each GPIO signal when controlled by the radio state.
+// 0 = GPIO pin set to input. 1 = GPIO pin set to output
+// </info>
+// <bitfield name="GPIO_DIR_A" range="0..11" initialvalue="0"/>
+// <bitfield name="GPIO_DIR_B" range="16..27" initialvalue="0"/>
+// </register>
+// <register name="GPIO_DISABLED" offset="0x4C" size="32">
+// <info>
+// Disable ATR Control. DB state 0 will be reflected regardless of the ATR state.
+// </info>
+// <bitfield name="GPIO_DISABLED_A" range="0..11" initialvalue="0"/>
+// <bitfield name="GPIO_DISABLED_B" range="16..27" initialvalue="0"/>
+// </register>
+// <register name="GPIO_IN" offset="0x50" size="32">
+// <info>
+// Reflects the logic state of each GPIO input.
+// </info>
+// <bitfield name="GPIO_IN_A" range="0..11" initialvalue="0"/>
+// <bitfield name="GPIO_IN_B" range="16..27" initialvalue="0"/>
+// </register>
+// </group>
+//</regmap>
+//XmlParse xml_off