aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/top/x400/dboards/db_gpio_interface.v
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/top/x400/dboards/db_gpio_interface.v')
-rw-r--r--fpga/usrp3/top/x400/dboards/db_gpio_interface.v323
1 files changed, 323 insertions, 0 deletions
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