aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/control
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/control')
-rw-r--r--fpga/usrp3/lib/control/Makefile.srcs61
-rw-r--r--fpga/usrp3/lib/control/ad5662_auto_spi.v99
-rw-r--r--fpga/usrp3/lib/control/arb_qualify_master.v91
-rw-r--r--fpga/usrp3/lib/control/axi_crossbar.v164
-rw-r--r--fpga/usrp3/lib/control/axi_crossbar_intf.sv87
-rw-r--r--fpga/usrp3/lib/control/axi_crossbar_regport.v193
-rw-r--r--fpga/usrp3/lib/control/axi_fifo_header.v87
-rw-r--r--fpga/usrp3/lib/control/axi_forwarding_cam.v232
-rw-r--r--fpga/usrp3/lib/control/axi_setting_reg.v97
-rw-r--r--fpga/usrp3/lib/control/axi_slave_mux.v122
-rw-r--r--fpga/usrp3/lib/control/axi_test_vfifo.v145
-rw-r--r--fpga/usrp3/lib/control/axil_ctrlport_master.v248
-rw-r--r--fpga/usrp3/lib/control/axil_regport_master.v228
-rw-r--r--fpga/usrp3/lib/control/axil_to_ni_regport.v164
-rw-r--r--fpga/usrp3/lib/control/bin2gray.v30
-rw-r--r--fpga/usrp3/lib/control/binary_encoder.v45
-rw-r--r--fpga/usrp3/lib/control/db_control.v148
-rw-r--r--fpga/usrp3/lib/control/fe_control.v70
-rw-r--r--fpga/usrp3/lib/control/filter_bad_sid.v78
-rw-r--r--fpga/usrp3/lib/control/gpio_atr.v98
-rw-r--r--fpga/usrp3/lib/control/gpio_atr_io.v38
-rw-r--r--fpga/usrp3/lib/control/gray2bin.v25
-rw-r--r--fpga/usrp3/lib/control/map/AUTHORS3
-rw-r--r--fpga/usrp3/lib/control/map/axis_muxed_kv_map.v206
-rw-r--r--fpga/usrp3/lib/control/map/cam.v103
-rw-r--r--fpga/usrp3/lib/control/map/cam_bram.v259
-rw-r--r--fpga/usrp3/lib/control/map/cam_priority_encoder.v94
-rw-r--r--fpga/usrp3/lib/control/map/cam_srl.v223
-rw-r--r--fpga/usrp3/lib/control/map/kv_map.v253
-rw-r--r--fpga/usrp3/lib/control/mdio_master.v772
-rw-r--r--fpga/usrp3/lib/control/por_gen.v28
-rw-r--r--fpga/usrp3/lib/control/priority_encoder.v55
-rw-r--r--fpga/usrp3/lib/control/priority_encoder_one_hot.v34
-rw-r--r--fpga/usrp3/lib/control/pulse_stretch.v79
-rw-r--r--fpga/usrp3/lib/control/pulse_stretch_min.v79
-rw-r--r--fpga/usrp3/lib/control/pulse_synchronizer.v70
-rw-r--r--fpga/usrp3/lib/control/ram_2port.v120
-rw-r--r--fpga/usrp3/lib/control/ram_2port_impl.vh129
-rw-r--r--fpga/usrp3/lib/control/regport_resp_mux.v46
-rw-r--r--fpga/usrp3/lib/control/regport_to_settingsbus.v63
-rw-r--r--fpga/usrp3/lib/control/regport_to_xbar_settingsbus.v113
-rw-r--r--fpga/usrp3/lib/control/reset_sync.v37
-rw-r--r--fpga/usrp3/lib/control/s7_icap_wb.v144
-rw-r--r--fpga/usrp3/lib/control/serial_to_settings.v126
-rw-r--r--fpga/usrp3/lib/control/serial_to_settings_tb.v86
-rw-r--r--fpga/usrp3/lib/control/setting_reg.v38
-rw-r--r--fpga/usrp3/lib/control/settings_bus_mux.v44
-rw-r--r--fpga/usrp3/lib/control/settings_bus_timed_2clk.v170
-rw-r--r--fpga/usrp3/lib/control/simple_i2c_core.v107
-rw-r--r--fpga/usrp3/lib/control/simple_spi_core.v229
-rw-r--r--fpga/usrp3/lib/control/synchronizer.v50
-rw-r--r--fpga/usrp3/lib/control/synchronizer_impl.v47
-rw-r--r--fpga/usrp3/lib/control/user_settings.v66
53 files changed, 6423 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/control/Makefile.srcs b/fpga/usrp3/lib/control/Makefile.srcs
new file mode 100644
index 000000000..578f19ea5
--- /dev/null
+++ b/fpga/usrp3/lib/control/Makefile.srcs
@@ -0,0 +1,61 @@
+#
+# Copyright 2013 Ettus Research LLC
+# Copyright 2017 Ettus Research, a National Instruments Company
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+##################################################
+# Control Lib Sources
+##################################################
+CONTROL_LIB_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/control/, \
+ad5662_auto_spi.v \
+arb_qualify_master.v \
+axi_crossbar.v \
+axi_crossbar_regport.v \
+axi_fifo_header.v \
+axi_forwarding_cam.v \
+axi_setting_reg.v \
+axi_slave_mux.v \
+axi_test_vfifo.v \
+bin2gray.v \
+binary_encoder.v \
+db_control.v \
+fe_control.v \
+filter_bad_sid.v \
+gpio_atr_io.v \
+gpio_atr.v \
+gray2bin.v \
+por_gen.v \
+priority_encoder_one_hot.v \
+priority_encoder.v \
+ram_2port_impl.vh \
+ram_2port.v \
+reset_sync.v \
+s7_icap_wb.v \
+serial_to_settings.v \
+setting_reg.v \
+settings_bus_mux.v \
+settings_bus_timed_2clk.v \
+simple_i2c_core.v \
+simple_spi_core.v \
+synchronizer_impl.v \
+synchronizer.v \
+pulse_synchronizer.v \
+user_settings.v \
+axil_regport_master.v \
+axil_to_ni_regport.v \
+regport_resp_mux.v \
+regport_to_xbar_settingsbus.v \
+regport_to_settingsbus.v \
+pulse_stretch.v \
+pulse_stretch_min.v \
+mdio_master.v \
+map/cam_priority_encoder.v \
+map/cam_bram.v \
+map/cam_srl.v \
+map/cam.v \
+map/kv_map.v \
+map/axis_muxed_kv_map.v \
+axil_ctrlport_master.v\
+))
diff --git a/fpga/usrp3/lib/control/ad5662_auto_spi.v b/fpga/usrp3/lib/control/ad5662_auto_spi.v
new file mode 100644
index 000000000..d9f2e53be
--- /dev/null
+++ b/fpga/usrp3/lib/control/ad5662_auto_spi.v
@@ -0,0 +1,99 @@
+//
+// Copyright 2015 Ettus Research
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// The AD5662 DAC serial interface uses 24-bit transfers to encode 16-bits
+// of actual data, two bits for power-down mode, and six pad bits. This
+// module stores a copy of the last-programmed value, and will generate a
+// serial stream if ever the input word (dat) changes. It will ignore
+// changes to (dat) while it is busy with a serial update.
+//
+module ad5662_auto_spi
+(
+ input clk,
+ input [15:0] dat,
+ output reg sclk,
+ output reg mosi,
+ output reg sync_n
+);
+ // initialize ldat to 0, thus forcing
+ // a reload on init.
+ reg [15:0] ldat = 16'd0;
+ wire upd = (dat != ldat); // new data present, need to update hw
+
+ reg [23:0] shft=24'b0;
+ wire [23:0] nxt_shft;
+
+ // clock cycle counter to throttle generated spi cycles
+ // allowing one spi clock cycle every 16 cycles of clk, with clk at 200 MHz
+ // gives a spi clock rate of 12 MHz. This can be made more sophisticated
+ // or parameterized, if more flexibility in clk is needed, of course.
+ reg [3:0] ccnt=4'b0;
+ wire [3:0] nxt_ccnt = ccnt + 1'b1;
+ wire half = ccnt==4'b1000;
+ wire full = ccnt==4'b1111;
+ reg sena, hena;
+ wire cena;
+ always @(posedge clk) if (cena) ccnt <= nxt_ccnt;
+ always @(posedge clk) sena <= full; // state updates and rising sclk
+ always @(posedge clk) hena <= half; // for falling sclk
+
+ // transfer state counter
+ reg [4:0] scnt = 5'b0;
+ reg [4:0] nxt_scnt;
+ always @(posedge clk) begin
+ if (sena) begin
+ scnt <= nxt_scnt;
+ shft <= nxt_shft;
+ mosi <= shft[23];
+ end
+ end
+
+
+ // 32 possible states - more than enough to shift-out 24 bits and manage
+ // the sync_n line
+
+ // particular scnt values of interest
+ localparam READY=5'b00000; // waiting for new data
+ localparam DCAPT=5'b00001; // new data transfers into ldat
+ localparam SYNCL=5'b00010; // assert sync_n low
+ localparam SYNCH=5'b11011; // return sync_n high
+
+ assign cena = upd | scnt != READY;
+
+ always @(scnt or upd)
+ begin
+ case (scnt)
+ READY:
+ nxt_scnt = upd ? DCAPT : READY;
+ SYNCH:
+ nxt_scnt = READY;
+ default:
+ nxt_scnt = scnt + 1'b1;
+ endcase
+ end
+
+ // note: defining the power-down mode bits to 00 for "normal operation"
+ assign nxt_shft = (scnt == SYNCL) ? { 8'b000000_00, ldat } : { shft[22:0], 1'b0 };
+
+ // Update ldat when dat has changed, but only if READY.
+ // Changes to dat arriving faster than can be kept up with here are ignored
+ // until the cycle-in-progress is completed.
+ wire ldat_ena = sena & (scnt == DCAPT);
+ always @(posedge clk) begin
+ if (ldat_ena) ldat <= dat;
+ end
+
+ // keep the sync_n line low when idle to minimize power consumption
+ // it gets brought high just before beginning each transaction
+ wire nxt_sync_n = (scnt==SYNCL) | (scnt==SYNCH);
+ always @(posedge clk) if (sena) sync_n <= nxt_sync_n;
+
+ reg sclk_go;
+ always @(posedge clk) sclk_go <= (scnt > SYNCL);
+ wire nxt_sclk = ~sclk_go ? 1'b1 : ~sclk;
+ always @(posedge clk) if (sena | hena) sclk <= nxt_sclk;
+
+endmodule
diff --git a/fpga/usrp3/lib/control/arb_qualify_master.v b/fpga/usrp3/lib/control/arb_qualify_master.v
new file mode 100644
index 000000000..9a8fdb015
--- /dev/null
+++ b/fpga/usrp3/lib/control/arb_qualify_master.v
@@ -0,0 +1,91 @@
+//
+// Copyright 2012 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+
+//
+// This module forms the qualification engine for a single master as
+// part of a larger arbitration engine for a slave. It would typically
+// be instantiated from arb_select_master.v to form a complete arbitor solution.
+//
+
+module arb_qualify_master
+ #(
+ parameter WIDTH=16 // Bit width of destination field.
+ )
+ (
+ input clk,
+ input reset,
+ input clear,
+ // Header signals
+ input [WIDTH-1:0] header,
+ input header_valid,
+ // Slave Confg Signals
+ input [WIDTH-1:0] slave_addr,
+ input [WIDTH-1:0] slave_mask,
+ input slave_valid,
+ // Arbitration flags
+ output reg master_valid,
+ input master_ack
+ );
+
+ localparam WAIT_HEADER_VALID = 0;
+ localparam MATCH = 1;
+ localparam WAIT_HEADER_NOT_VALID = 2;
+
+
+ reg [1:0] state, next_state;
+
+
+ // Does masked slave address match header field for dest from master?
+ assign header_match = ((header & slave_mask) == (slave_addr & slave_mask)) && slave_valid;
+
+
+ always @(posedge clk)
+ if (reset | clear) begin
+ state <= WAIT_HEADER_VALID;
+ master_valid <= 0;
+ end else
+ begin
+ case(state)
+ //
+ // Wait here until Masters FIFO presents a valid header word.
+ //
+ WAIT_HEADER_VALID: begin
+ if (header_valid)
+ if (header_match) begin
+ state <= MATCH;
+ master_valid <= 1;
+ end else
+ next_state <= WAIT_HEADER_NOT_VALID;
+ end
+ //
+ // There should only ever be one match across various arbitors
+ // if they are configured correctly and since the backing FIFO in the
+ // master should not start to drain until the arbitration is won
+ // by that master, master_ack should always preceed de-assertion of
+ // header_valid so we don't check for the other order of deassertion.
+ //
+ MATCH: begin
+ if (master_ack) begin
+ master_valid <= 0;
+ state <= WAIT_HEADER_NOT_VALID;
+ end
+ end
+ //
+ // Wait here until this master starts to drain this packet from his FIFO.
+ //
+ WAIT_HEADER_NOT_VALID: begin
+ if (!header_valid) begin
+ state <= WAIT_HEADER_VALID;
+ end
+ end
+ endcase // case(state)
+ end // else: !if(reset | clear)
+
+endmodule // arb_qualify_master
+
+ \ No newline at end of file
diff --git a/fpga/usrp3/lib/control/axi_crossbar.v b/fpga/usrp3/lib/control/axi_crossbar.v
new file mode 100644
index 000000000..e34c3f43f
--- /dev/null
+++ b/fpga/usrp3/lib/control/axi_crossbar.v
@@ -0,0 +1,164 @@
+/////////////////////////////////////////////////////////////////////
+//
+// Copyright 2012 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axi_crossbar
+// Description:
+// - Control Registers
+// - CAM to setup routing between RFNoC blocks
+//
+/////////////////////////////////////////////////////////////////////
+
+module axi_crossbar
+ #(
+ parameter BASE = 0, // settings bus base address
+ parameter FIFO_WIDTH = 64, // AXI4-STREAM data bus width
+ parameter DST_WIDTH = 16, // Width of DST field we are routing on.
+ parameter NUM_INPUTS = 2, // number of input AXI4-STREAM buses
+ parameter NUM_OUTPUTS = 2 // number of output AXI4-STREAM buses
+ )
+ (
+ input clk,
+ input reset,
+ input clear,
+ input [7:0] local_addr,
+ // Inputs
+ input [(FIFO_WIDTH*NUM_INPUTS)-1:0] i_tdata,
+ input [NUM_INPUTS-1:0] i_tvalid,
+ input [NUM_INPUTS-1:0] i_tlast,
+ output [NUM_INPUTS-1:0] i_tready,
+ input [NUM_INPUTS-1:0] pkt_present,
+ // Setting Bus
+ input set_stb,
+ input [15:0] set_addr,
+ input [31:0] set_data,
+ // Output
+ output [(FIFO_WIDTH*NUM_OUTPUTS)-1:0] o_tdata,
+ output [NUM_OUTPUTS-1:0] o_tvalid,
+ output [NUM_OUTPUTS-1:0] o_tlast,
+ input [NUM_OUTPUTS-1:0] o_tready,
+ // readback bus
+ input rb_rd_stb,
+ input [$clog2(NUM_OUTPUTS)+$clog2(NUM_INPUTS)-1:0] rb_addr,
+ output reg [31:0] rb_data
+ );
+
+ genvar m,n;
+
+ wire [(NUM_INPUTS*NUM_OUTPUTS)-1:0] forward_valid_in;
+ wire [(NUM_INPUTS*NUM_OUTPUTS)-1:0] forward_ack_in;
+ wire [(NUM_INPUTS*NUM_OUTPUTS)-1:0] forward_valid_out;
+ wire [(NUM_INPUTS*NUM_OUTPUTS)-1:0] forward_ack_out;
+
+ wire [NUM_INPUTS-1:0] i_tready_slave [0:NUM_OUTPUTS-1];
+
+ //
+ // Instantiate an axi_slave_mux for every slave/output of the Crossbar switch.
+ // Each axi_slave_mux contains logic to maux and resolve arbitration
+ // for this particular slave/output.
+ //
+
+ generate
+ for (m = 0; m < NUM_OUTPUTS; m = m + 1) begin: instantiate_slave_mux
+
+ wire [NUM_INPUTS-1:0] i_tready_tmp;
+
+ axi_slave_mux
+ #(
+ .FIFO_WIDTH(FIFO_WIDTH), // AXI4-STREAM data bus width
+ .DST_WIDTH(DST_WIDTH), // Width of DST field we are routing on.
+ .NUM_INPUTS(NUM_INPUTS) // number of input AXI buses
+ ) axi_slave_mux_i
+ (
+ .clk(clk),
+ .reset(reset),
+ .clear(clear),
+ // Inputs
+ .i_tdata(i_tdata),
+ .i_tvalid(i_tvalid),
+ .i_tlast(i_tlast),
+ .i_tready(i_tready_tmp),
+ // Forwarding flags (One from each Input/Master)
+ .forward_valid(forward_valid_in[(m+1)*NUM_INPUTS-1:m*NUM_INPUTS]),
+ .forward_ack(forward_ack_out[(m+1)*NUM_INPUTS-1:m*NUM_INPUTS]),
+ // Output
+ .o_tdata(o_tdata[(m*FIFO_WIDTH)+FIFO_WIDTH-1:m*FIFO_WIDTH]),
+ .o_tvalid(o_tvalid[m]),
+ .o_tlast(o_tlast[m]),
+ .o_tready(o_tready[m])
+ );
+
+ if (m==0)
+ assign i_tready_slave[0] = i_tready_tmp;
+ else
+ assign i_tready_slave[m] = i_tready_tmp | i_tready_slave[m-1] ;
+
+ end // block: instantiate_slave_mux
+ endgenerate
+
+ assign i_tready = i_tready_slave[NUM_OUTPUTS-1];
+
+ //
+ // Permute the forwarding flag buses
+ //
+
+ generate
+ for (m = 0; m < NUM_OUTPUTS; m = m + 1) begin: permute_outer
+ for (n = 0; n < NUM_INPUTS; n = n + 1) begin: permute_inner
+ assign forward_valid_in[n*NUM_OUTPUTS+m] = forward_valid_out[n+m*NUM_INPUTS];
+ assign forward_ack_in[n+m*NUM_INPUTS] = forward_ack_out[n*NUM_OUTPUTS+m];
+ end
+ end
+
+ endgenerate
+
+
+ //
+ // Instantiate an axi_forwarding_cam for every Input/Master of the Crossbar switch.
+ // Each contains a TCAM like lookup that allocates an egress port.
+ //
+
+ wire [31:0] rb_data_mux[0:NUM_INPUTS-1];
+
+ generate
+ for (m = 0; m < NUM_INPUTS; m = m + 1) begin: instantiate_cam
+ axi_forwarding_cam
+ #(
+ .BASE(BASE),
+ .WIDTH(FIFO_WIDTH), // Bit width of FIFO word.
+ .NUM_OUTPUTS(NUM_OUTPUTS)
+ ) axi_forwarding_cam_i
+ (
+ .clk(clk),
+ .reset(reset),
+ .clear(clear),
+ // Monitored FIFO signals
+ .o_tdata(i_tdata[(m*FIFO_WIDTH)+FIFO_WIDTH-1:m*FIFO_WIDTH]),
+ .o_tvalid(i_tvalid[m]),
+ .o_tready(i_tready[m]),
+ .o_tlast(i_tlast[m]),
+ .pkt_present(pkt_present[m]),
+ // Configuration
+ .local_addr(local_addr),
+ // Setting Bus
+ .set_stb(set_stb),
+ .set_addr(set_addr),
+ .set_data(set_data),
+ // Header signals
+ .forward_valid(forward_valid_out[(m+1)*NUM_OUTPUTS-1:m*NUM_OUTPUTS]),
+ .forward_ack(forward_ack_in[(m+1)*NUM_OUTPUTS-1:m*NUM_OUTPUTS]),
+ // Readback bus
+ .rb_rd_stb(rb_rd_stb && (rb_addr[$clog2(NUM_OUTPUTS)+$clog2(NUM_INPUTS)-1:$clog2(NUM_OUTPUTS)] == m)),
+ .rb_addr(rb_addr[$clog2(NUM_OUTPUTS)-1:0]),
+ .rb_data(rb_data_mux[m])
+ );
+ end // block: instantiate_fifo_header
+ endgenerate
+
+ // Pipeline readback data to alleviate timing issues
+ always @(posedge clk) rb_data <= rb_data_mux[rb_addr[$clog2(NUM_OUTPUTS)+$clog2(NUM_INPUTS)-1:$clog2(NUM_OUTPUTS)]];
+
+
+endmodule // axi_crossbar
diff --git a/fpga/usrp3/lib/control/axi_crossbar_intf.sv b/fpga/usrp3/lib/control/axi_crossbar_intf.sv
new file mode 100644
index 000000000..b0c07ba9f
--- /dev/null
+++ b/fpga/usrp3/lib/control/axi_crossbar_intf.sv
@@ -0,0 +1,87 @@
+//
+// Copyright 2015 Ettus Research LLC
+// Copyright 2017 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Wraps AXI crossbar and exposes cvita_stream_t and settings_bus_t interfaces
+
+`include "sim_cvita_lib.svh"
+`include "sim_set_rb_lib.svh"
+
+module axi_crossbar_intf
+#(
+ parameter BASE = 0, // settings bus base address
+ parameter FIFO_WIDTH = 64, // AXI4-STREAM data bus width
+ parameter DST_WIDTH = 16, // Width of DST field we are routing on.
+ parameter NUM_PORTS = 2 // number of cvita busses
+)(
+ input clk,
+ input reset,
+ input clear,
+ input [7:0] local_addr,
+ axis_t.slave s_cvita[0:NUM_PORTS-1],
+ axis_t.master m_cvita[0:NUM_PORTS-1],
+ settings_bus_t.slave set_bus,
+ readback_bus_t.master rb_bus
+);
+
+ wire [NUM_PORTS*64-1:0] flat_i_tdata;
+ wire [NUM_PORTS-1:0] i_tlast, i_tvalid, i_tready;
+ wire [NUM_PORTS*64-1:0] flat_o_tdata;
+ wire [NUM_PORTS-1:0] o_tlast, o_tvalid, o_tready;
+
+ // Flattern CE tdata arrays
+ genvar i;
+ generate
+ for (i = 0; i < NUM_PORTS; i = i + 1) begin
+ assign flat_i_tdata[i*FIFO_WIDTH+FIFO_WIDTH-1:i*FIFO_WIDTH] = s_cvita[i].tdata;
+ assign i_tlast[i] = s_cvita[i].tlast;
+ assign i_tvalid[i] = s_cvita[i].tvalid;
+ assign s_cvita[i].tready = i_tready[i];
+ end
+ for (i = 0; i < NUM_PORTS; i = i + 1) begin
+ assign m_cvita[i].tdata = flat_o_tdata[i*FIFO_WIDTH+FIFO_WIDTH-1:i*FIFO_WIDTH];
+ assign m_cvita[i].tlast = o_tlast[i];
+ assign m_cvita[i].tvalid = o_tvalid[i];
+ assign o_tready[i] = m_cvita[i].tready;
+ end
+ endgenerate
+
+ wire set_stb = set_bus.stb;
+ wire [15:0] set_addr = set_bus.addr;
+ wire [31:0] set_data = set_bus.data;
+ wire rb_rd_stb = rb_bus.stb;
+ wire [2*$clog2(NUM_PORTS):0] rb_addr = rb_bus.addr[2*$clog2(NUM_PORTS):0];
+ wire [31:0] rb_data;
+ assign rb_bus.data = rb_data;
+
+ axi_crossbar #(
+ .BASE(BASE),
+ .FIFO_WIDTH(FIFO_WIDTH),
+ .DST_WIDTH(DST_WIDTH),
+ .NUM_INPUTS(NUM_PORTS),
+ .NUM_OUTPUTS(NUM_PORTS))
+ inst_axi_crossbar (
+ .clk(clk),
+ .reset(reset),
+ .clear(clear),
+ .local_addr(local_addr),
+ .i_tdata(flat_i_tdata),
+ .i_tvalid(i_tvalid),
+ .i_tlast(i_tlast),
+ .i_tready(i_tready),
+ .pkt_present(i_tvalid),
+ .set_stb(set_stb),
+ .set_addr(set_addr),
+ .set_data(set_data),
+ .o_tdata(flat_o_tdata),
+ .o_tvalid(o_tvalid),
+ .o_tlast(o_tlast),
+ .o_tready(o_tready),
+ .rb_rd_stb(rb_rd_stb),
+ .rb_addr(rb_addr),
+ .rb_data(rb_data));
+
+endmodule
+
diff --git a/fpga/usrp3/lib/control/axi_crossbar_regport.v b/fpga/usrp3/lib/control/axi_crossbar_regport.v
new file mode 100644
index 000000000..15b74517d
--- /dev/null
+++ b/fpga/usrp3/lib/control/axi_crossbar_regport.v
@@ -0,0 +1,193 @@
+/////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axi_crossbar_regport
+// Description:
+// - axi_crossbar with regport interface for register/CAM access
+//
+//////////////////////////////////////////////////////////////////////
+
+module axi_crossbar_regport #(
+ parameter REG_BASE = 0, // settings bus base address
+ parameter FIFO_WIDTH = 64, // AXI4-STREAM data bus width
+ parameter DST_WIDTH = 16, // Width of DST field we are routing on.
+ parameter NUM_INPUTS = 2, // number of input AXI4-STREAM buses
+ parameter NUM_OUTPUTS = 2, // number of output AXI4-STREAM buses
+ parameter REG_DWIDTH = 32, // Width of the AXI4-Lite data bus (must be 32 or 64)
+ parameter REG_AWIDTH = 14 // Width of the address bus
+)(
+ input clk,
+ input reset,
+ input clear,
+
+ input reg_wr_req,
+ input [REG_AWIDTH-1:0] reg_wr_addr,
+ input [REG_DWIDTH-1:0] reg_wr_data,
+
+ input reg_rd_req,
+ input [REG_AWIDTH-1:0] reg_rd_addr,
+ output [REG_DWIDTH-1:0] reg_rd_data,
+ output reg_rd_resp,
+
+ // Inputs
+ input [(FIFO_WIDTH*NUM_INPUTS)-1:0] i_tdata,
+ input [NUM_INPUTS-1:0] i_tvalid,
+ input [NUM_INPUTS-1:0] i_tlast,
+ output [NUM_INPUTS-1:0] i_tready,
+ input [NUM_INPUTS-1:0] pkt_present,
+
+ // Output
+ output [(FIFO_WIDTH*NUM_OUTPUTS)-1:0] o_tdata,
+ output [NUM_OUTPUTS-1:0] o_tvalid,
+ output [NUM_OUTPUTS-1:0] o_tlast,
+ input [NUM_OUTPUTS-1:0] o_tready
+);
+
+ localparam XBAR_VERSION = 32'b1;
+ localparam XBAR_NUM_PORTS = NUM_INPUTS; //or NUM_OUTPUTS
+
+ localparam REG_XBAR_VERSION = REG_BASE + 14'h10;
+ localparam REG_XBAR_NUM_PORTS = REG_BASE + 14'h14;
+ localparam REG_XBAR_LOCAL_ADDR = REG_BASE + 14'h18;
+ localparam REG_BASE_XBAR_SETTING_REG = REG_BASE + 14'h20;
+ localparam REG_END_ADDR_XBAR_SETTING_REG = REG_BASE + 14'h1000;
+
+ // Settings bus address width
+ localparam SR_AWIDTH = 12;
+
+ wire xbar_set_stb;
+ wire [REG_DWIDTH-1:0] xbar_set_data;
+ wire [SR_AWIDTH-1:0] xbar_set_addr;
+
+ wire xbar_rb_stb;
+ wire [SR_AWIDTH-1:0] xbar_rb_addr;
+ wire [REG_DWIDTH-1:0] xbar_rb_data;
+
+ reg [31:0] local_addr_reg;
+ reg reg_rd_resp_glob;
+ reg [REG_DWIDTH-1:0] reg_rd_data_glob;
+
+ wire [REG_DWIDTH-1:0] reg_rd_data_xbar;
+ wire reg_rd_resp_xbar;
+
+ regport_resp_mux #(
+ .WIDTH(REG_DWIDTH),
+ .NUM_SLAVES(2)
+ ) inst_regport_resp_mux_xbar (
+ .clk(clk),
+ .reset(reset),
+ .sla_rd_resp({reg_rd_resp_glob, reg_rd_resp_xbar}),
+ .sla_rd_data({reg_rd_data_glob, reg_rd_data_xbar}),
+ .mst_rd_resp(reg_rd_resp),
+ .mst_rd_data(reg_rd_data)
+ );
+
+ // Read Registers
+ always @ (posedge clk) begin
+ if (reset) begin
+ local_addr_reg <= 32'h0;
+ end
+ else begin
+ if (reg_wr_req)
+ case (reg_wr_addr)
+ REG_XBAR_LOCAL_ADDR:
+ local_addr_reg <= reg_wr_data;
+ endcase
+ end
+ end
+
+ // Write Registers
+ always @ (posedge clk) begin
+ if (reset)
+ reg_rd_resp_glob <= 1'b0;
+
+ else begin
+ if (reg_rd_req) begin
+ reg_rd_resp_glob <= 1'b1;
+
+ case (reg_rd_addr)
+ REG_XBAR_VERSION:
+ reg_rd_data_glob <= XBAR_VERSION;
+
+ REG_XBAR_NUM_PORTS:
+ reg_rd_data_glob <= XBAR_NUM_PORTS;
+
+ REG_XBAR_LOCAL_ADDR:
+ reg_rd_data_glob <= local_addr_reg;
+
+ default:
+ reg_rd_resp_glob <= 1'b0;
+ endcase
+ end
+ else if (reg_rd_resp_glob) begin
+ reg_rd_resp_glob <= 1'b0;
+ end
+ end
+ end
+
+ regport_to_xbar_settingsbus #(
+ .BASE(REG_BASE_XBAR_SETTING_REG),
+ .END_ADDR(REG_END_ADDR_XBAR_SETTING_REG),
+ .DWIDTH(REG_DWIDTH),
+ .AWIDTH(REG_AWIDTH),
+ .SR_AWIDTH(SR_AWIDTH),
+ .ADDRESSING("WORD")
+ ) inst_regport_to_xbar_settingsbus (
+ .clk(clk),
+ .reset(reset),
+
+ .reg_wr_req(reg_wr_req),
+ .reg_wr_addr(reg_wr_addr),
+ .reg_wr_data(reg_wr_data),
+ .reg_rd_req(reg_rd_req),
+ .reg_rd_addr(reg_rd_addr),
+ .reg_rd_data(reg_rd_data_xbar),
+ .reg_rd_resp(reg_rd_resp_xbar),
+
+ .set_stb(xbar_set_stb),
+ .set_addr(xbar_set_addr),
+ .set_data(xbar_set_data),
+ .rb_stb(xbar_rb_stb),
+ .rb_addr(xbar_rb_addr),
+ .rb_data(xbar_rb_data)
+ );
+
+ axi_crossbar #(
+ .BASE(0), // Set to 0 as logic for other values has not been tested
+ .FIFO_WIDTH(FIFO_WIDTH),
+ .DST_WIDTH(DST_WIDTH),
+ .NUM_INPUTS(NUM_INPUTS),
+ .NUM_OUTPUTS(NUM_OUTPUTS)
+ ) axi_crossbar (
+ .clk(clk),
+ .reset(reset),
+ .clear(1'b0),
+ .local_addr(local_addr_reg),
+
+ // settings bus for config
+ .set_stb(xbar_set_stb),
+ .set_addr({4'b0000,xbar_set_addr}),
+ .set_data(xbar_set_data),
+ .rb_rd_stb(xbar_rb_stb),
+ .rb_addr(xbar_rb_addr[$clog2(NUM_INPUTS)+$clog2(NUM_OUTPUTS)-1:0]),
+ .rb_data(xbar_rb_data),
+
+ // inputs, real men flatten busses
+ .i_tdata(i_tdata),
+ .i_tlast(i_tlast),
+ .i_tvalid(i_tvalid),
+ .i_tready(i_tready),
+
+ // outputs, real men flatten busses
+ .o_tdata(o_tdata),
+ .o_tlast(o_tlast),
+ .o_tvalid(o_tvalid),
+ .o_tready(o_tready),
+ .pkt_present(pkt_present)
+ );
+
+endmodule // axi_crossbar_regport
+
diff --git a/fpga/usrp3/lib/control/axi_fifo_header.v b/fpga/usrp3/lib/control/axi_fifo_header.v
new file mode 100644
index 000000000..551c2b760
--- /dev/null
+++ b/fpga/usrp3/lib/control/axi_fifo_header.v
@@ -0,0 +1,87 @@
+//
+// Copyright 2012 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+
+//
+// This module is connected to the output port of an AXI4-STREAM FIFO that is used to move packetized data.
+// It extracts and indicates the header (first word) of a packet in the FIFO. The header and flag are pipelined
+// for timing closure.
+//
+
+module axi_fifo_header
+ #(
+ parameter WIDTH=64 // Bit width of FIFO word.
+ )
+ (
+ input clk,
+ input reset,
+ input clear,
+ // Monitored FIFO signals
+ input [WIDTH-1:0] o_tdata,
+ input o_tvalid,
+ input o_tready,
+ input o_tlast,
+ input pkt_present,
+ // Header signals
+ output reg [WIDTH-1:0] header,
+ output reg header_valid
+ );
+
+ localparam WAIT_SOF = 0;
+ localparam WAIT_EOF = 1;
+
+ reg out_state;
+
+
+ //
+ // Monitor packets leaving FIFO
+ //
+ always @(posedge clk)
+ if (reset | clear) begin
+ out_state <= WAIT_SOF;
+ end else
+ case(out_state)
+ //
+ // After RESET or the EOF of previous packet, the first cycle with
+ // output valid asserted is the SOF and presents the Header word.
+ // The cycle following the concurrent presentation of asserted output
+ // valid and output ready presents the word following the header.
+ //
+ WAIT_SOF:
+ if (o_tvalid && o_tready) begin
+ out_state <= WAIT_EOF;
+ end else begin
+ out_state <= WAIT_SOF;
+ end
+ //
+ // EOF is signalled by o_tlast asserted whilst output valid and ready asserted.
+ //
+ WAIT_EOF:
+ if (o_tlast && o_tvalid && o_tready) begin
+ out_state <= WAIT_SOF;
+ end else begin
+ out_state <= WAIT_EOF;
+ end
+ endcase // case(in_state)
+
+ //
+ // Pipeline Header signals
+ //
+ always @(posedge clk)
+ if (reset | clear) begin
+ header <= 0;
+ header_valid <= 0;
+ end else if (o_tvalid && (out_state == WAIT_SOF) && pkt_present) begin
+ // Header will remian valid until o_tready is asserted as this will cause a state transition.
+ header <= o_tdata;
+ header_valid <= 1;
+ end else begin
+ header_valid <= 0;
+ end
+
+
+endmodule // axi_fifo_header
diff --git a/fpga/usrp3/lib/control/axi_forwarding_cam.v b/fpga/usrp3/lib/control/axi_forwarding_cam.v
new file mode 100644
index 000000000..64076bd0a
--- /dev/null
+++ b/fpga/usrp3/lib/control/axi_forwarding_cam.v
@@ -0,0 +1,232 @@
+//
+// Copyright 2013 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// This module implements a highly customized content-addressable memory (CAM)
+// that enables forwarding decisions to be made on a 16 bit field from a stream ID (SID) field.
+// The forwarding is generic in the sense that a SID's host destination can map to any endpoint / crossbar port.
+//
+// The 16 bits are allocated by convention as 8 bits of Network address (addresses USRP's / AXI crossbars) and
+// 8 bits of Host address (addresses endpoints / crossbar ports in a USRP).
+//
+// By definition if the destination field in the SID addresses a different
+// USRP / crossbar than this one then we don't care about the Host field, only the Network field.
+// We only look at the Host field when the Network field addresses us.
+// Thus we need a CAM of 256+256 entries with Log2(N) bits, where N is the number of
+// slave(output) ports on the crossbar switch.
+//
+// SID format:
+//
+// |---------|---------|---------|---------|
+// | SRC | SRC | DST | DST |
+// | NETWORK | HOST | NETWORK | HOST |
+// |---------|---------|---------|---------|
+// 8 8 8 8
+
+module axi_forwarding_cam
+ #(
+ parameter BASE = 0, // BASE address for setting registers in this block. (512 addrs used)
+ parameter WIDTH=64, // Bit width of FIFO word.
+ parameter NUM_OUTPUTS=2 // Number of outputs (destinations) in crossbar.
+ )
+ (
+ input clk,
+ input reset,
+ input clear,
+ // Monitored FIFO signals
+ input [WIDTH-1:0] o_tdata,
+ input o_tvalid,
+ input o_tready,
+ input o_tlast,
+ input pkt_present,
+ // Configuration
+ input [7:0] local_addr,
+ // Setting Bus
+ input set_stb,
+ input [15:0] set_addr,
+ input [31:0] set_data,
+
+ output reg [NUM_OUTPUTS-1:0] forward_valid,
+ input [NUM_OUTPUTS-1:0] forward_ack,
+
+ input rb_rd_stb,
+ input [$clog2(NUM_OUTPUTS)-1:0] rb_addr,
+ output [31:0] rb_data
+ );
+
+
+ localparam WAIT_SOF = 0;
+ localparam WAIT_EOF = 1;
+ reg state;
+
+ localparam IDLE = 0;
+ localparam FORWARD = 1;
+ localparam WAIT = 2;
+
+ reg [1:0] demux_state;
+
+ reg [15:0] dst;
+ reg dst_valid, dst_valid_reg;
+ wire local_dst;
+ wire [8:0] read_addr;
+
+ //
+ // Monitor packets leaving FIFO
+ //
+ always @(posedge clk)
+ if (reset | clear) begin
+ state <= WAIT_SOF;
+ end else
+ case(state)
+ //
+ // After RESET or the EOF of previous packet, the first cycle with
+ // output valid asserted is the SOF and presents the Header word.
+ // The cycle following the concurrent presentation of asserted output
+ // valid and output ready presents the word following the header.
+ //
+ WAIT_SOF:
+ if (o_tvalid && o_tready) begin
+ state <= WAIT_EOF;
+ end else begin
+ state <= WAIT_SOF;
+ end
+ //
+ // EOF is signalled by o_tlast asserted whilst output valid and ready asserted.
+ //
+ WAIT_EOF:
+ if (o_tlast && o_tvalid && o_tready) begin
+ state <= WAIT_SOF;
+ end else begin
+ state <= WAIT_EOF;
+ end
+ endcase // case(in_state)
+
+ //
+ // Extract Destination fields(s) from SID
+ //
+ always @(posedge clk)
+ if (reset | clear) begin
+ dst <= 0;
+ dst_valid <= 0;
+ dst_valid_reg <= 0;
+ end else if (o_tvalid && (state == WAIT_SOF) && pkt_present) begin
+ // SID will remain valid until o_tready is asserted as this will cause a state transition.
+ dst <= o_tdata[15:0];
+ dst_valid <= 1;
+ dst_valid_reg <= dst_valid;
+ end else begin
+ dst_valid <= 0;
+ dst_valid_reg <= dst_valid;
+ end
+
+ //
+ // Is Network field in DST our local address?
+ //
+ assign local_dst = (dst[15:8] == local_addr) && dst_valid;
+
+
+ //
+ // Mux address to RAM so that it searches CAM for Network field or Host field.
+ // Network addresses are stored in the lower 256 locations, host addresses the upper 256.
+ //
+ assign read_addr = {local_dst,(local_dst ? dst[7:0] : dst[15:8])};
+
+ //
+ // Implement CAM as block RAM here, 512xCeil(Log2(NUM_OUTPUTS))
+ //
+ //synthesis attribute ram_style of mem is block
+ reg [$clog2(NUM_OUTPUTS)-1 : 0] mem [0:511];
+
+ // Initialize the CAM's local address forwarding decisions with sensible defaults by
+ // assuming dst[7:4] = crossbar port, dst[3:0] = block port. Setup a one-to-one mapping
+ // for crossbar ports and always map same crossbar port regardless of block port.
+ // i.e.
+ // dst 8'h00 => forward to crossbar port 0
+ // dst 8'h01 => forward to crossbar port 0
+ // dst 8'h10 => forward to crossbar port 1
+ // etc.
+ integer xbar_port;
+ integer block_port;
+ initial begin
+ for (xbar_port = 0; xbar_port < NUM_OUTPUTS; xbar_port = xbar_port + 1) begin
+ for (block_port = 0; block_port < 16; block_port = block_port + 1) begin
+ mem[256+(xbar_port << 4)+block_port] = xbar_port;
+ end
+ end
+ end
+
+ reg [8:0] read_addr_reg;
+ wire write;
+ wire [$clog2(NUM_OUTPUTS)-1:0] read_data;
+
+ assign write = (set_addr[15:9] == (BASE >>9)) && set_stb; // Addr decode.
+
+ always @(posedge clk)
+ begin
+ read_addr_reg <= read_addr;
+
+ if (write) begin
+ mem[set_addr[8:0]] <= set_data[$clog2(NUM_OUTPUTS)-1:0];
+ end
+
+ end
+
+ assign read_data = mem[read_addr_reg];
+
+
+ //
+ // State machine to manage forwarding flags.
+ //
+ always @(posedge clk)
+ if (reset | clear) begin
+ forward_valid <= {NUM_OUTPUTS{1'b0}};
+ demux_state <= IDLE;
+ end else
+ case(demux_state)
+
+ // Wait for Valid DST which indicates a new packet lookup in the CAM.
+ IDLE: begin
+ if (dst_valid_reg == 1) begin
+ forward_valid <= 1'b1 << read_data;
+ demux_state <= FORWARD;
+ end
+ end
+ // When Slave/Output thats forwarding ACK's the forward flag, clear request and wait for packet to be transfered
+ FORWARD: begin
+ if ((forward_ack & forward_valid) != 0) begin
+ forward_valid <= {NUM_OUTPUTS{1'b0}};
+ demux_state <= WAIT;
+ end
+ end
+ // When packet transfered go back to idle.
+ WAIT: begin
+ if (forward_ack == 0)
+ demux_state <= IDLE;
+ end
+
+ endcase // case (demux_state)
+
+ //
+ // Compile forwarding statistics
+ // (This uses a lot of registers!)
+ //
+ genvar m;
+ reg [31:0] statistics [0:NUM_OUTPUTS-1];
+
+ generate
+ for (m = 0; m < NUM_OUTPUTS; m = m + 1) begin: generate_stats
+ always @(posedge clk) begin
+ if (reset | clear) begin
+ statistics[m] <= 0;
+ end else if (forward_ack[m] & forward_valid[m]) begin
+ statistics[m] <= statistics[m] + 1;
+ end
+ end
+ end
+ endgenerate
+
+ assign rb_data = statistics[rb_addr];
+
+endmodule
diff --git a/fpga/usrp3/lib/control/axi_setting_reg.v b/fpga/usrp3/lib/control/axi_setting_reg.v
new file mode 100644
index 000000000..9d419ec32
--- /dev/null
+++ b/fpga/usrp3/lib/control/axi_setting_reg.v
@@ -0,0 +1,97 @@
+//
+// Copyright 2016 Ettus Research
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Settings register with AXI stream output.
+//
+// Parameters / common use cases:
+// USE_ADDR_LAST & ADDR_LAST User wants additional address that when written to asserts tlast.
+// Useful for the last word in a packet.
+// USE_FIFO & FIFO_SIZE Downstream block can throttle and a FIFO is needed to handle that case.
+// STROBE_LAST User always wants to assert tlast on writes. More efficient than USE_ADDR_LAST
+// since only one address is used instead of two.
+// REPEATS Keep tvalid asserted after initial write.
+// STROBE_LAST & REPEATS tlast is asserted on the initial write then deasserted for repeating output.
+// MSB_ALIGN Left justify data versus right justify.
+
+module axi_setting_reg #(
+ parameter ADDR = 0,
+ parameter USE_ADDR_LAST = 0,
+ parameter ADDR_LAST = ADDR+1,
+ parameter AWIDTH = 8,
+ parameter WIDTH = 32,
+ parameter USE_FIFO = 0,
+ parameter FIFO_SIZE = 5,
+ parameter DATA_AT_RESET = 0,
+ parameter VALID_AT_RESET = 0,
+ parameter LAST_AT_RESET = 0,
+ parameter STROBE_LAST = 0,
+ parameter REPEATS = 0,
+ parameter MSB_ALIGN = 0
+)
+(
+ input clk, input reset, output reg error_stb,
+ input set_stb, input [AWIDTH-1:0] set_addr, input [31:0] set_data,
+ output [WIDTH-1:0] o_tdata, output o_tlast, output o_tvalid, input o_tready
+);
+
+ reg init;
+
+ reg [WIDTH-1:0] o_tdata_int;
+ reg o_tlast_int, o_tvalid_int;
+ wire o_tready_int;
+
+ always @(posedge clk) begin
+ if (reset) begin
+ o_tdata_int <= DATA_AT_RESET;
+ o_tvalid_int <= VALID_AT_RESET;
+ o_tlast_int <= LAST_AT_RESET;
+ init <= 1'b0;
+ error_stb <= 1'b0;
+ end else begin
+ error_stb <= 1'b0;
+ if (o_tvalid_int & o_tready_int) begin
+ // Deassert tvalid / tlast only if not repeating the output
+ if (REPEATS == 0) begin
+ o_tvalid_int <= 1'b0;
+ end
+ if ((REPEATS == 0) | (STROBE_LAST == 1)) begin
+ o_tlast_int <= 1'b0;
+ end
+ end
+ if (set_stb & ((ADDR[AWIDTH-1:0] == set_addr) | (USE_ADDR_LAST & (ADDR_LAST[AWIDTH-1:0] == set_addr)))) begin
+ init <= 1'b1;
+ o_tdata_int <= (MSB_ALIGN == 0) ? set_data[WIDTH-1:0] : set_data[31:32-WIDTH];
+ o_tvalid_int <= 1'b1;
+ if (set_stb & (STROBE_LAST | (USE_ADDR_LAST & (ADDR_LAST[AWIDTH-1:0] == set_addr)))) begin
+ o_tlast_int <= 1'b1;
+ end else begin
+ o_tlast_int <= 1'b0;
+ end
+ if (~o_tready_int) begin
+ error_stb <= 1'b1;
+ end
+ end
+ end
+ end
+
+ generate
+ if (USE_FIFO) begin
+ axi_fifo #(
+ .WIDTH(WIDTH+1), .SIZE(FIFO_SIZE))
+ axi_fifo (
+ .clk(clk), .reset(reset), .clear(1'b0),
+ .i_tdata({o_tlast_int,o_tdata_int}), .i_tvalid(o_tvalid_int), .i_tready(o_tready_int),
+ .o_tdata({o_tlast,o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready),
+ .space(), .occupied());
+ end else begin
+ assign o_tdata = o_tdata_int;
+ assign o_tlast = o_tlast_int;
+ assign o_tvalid = o_tvalid_int;
+ assign o_tready_int = o_tready;
+ end
+ endgenerate
+
+endmodule
diff --git a/fpga/usrp3/lib/control/axi_slave_mux.v b/fpga/usrp3/lib/control/axi_slave_mux.v
new file mode 100644
index 000000000..0b217dc92
--- /dev/null
+++ b/fpga/usrp3/lib/control/axi_slave_mux.v
@@ -0,0 +1,122 @@
+//
+// Copyright 2012 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+`ifndef LOG2
+`define LOG2(N) (\
+ N < 2 ? 0 : \
+ N < 4 ? 1 : \
+ N < 8 ? 2 : \
+ N < 16 ? 3 : \
+ N < 32 ? 4 : \
+ N < 64 ? 5 : \
+ N < 128 ? 6 : \
+ N < 256 ? 7 : \
+ N < 512 ? 8 : \
+ N < 1024 ? 9 : 10)
+`endif
+
+module axi_slave_mux
+ #(
+ parameter FIFO_WIDTH = 64, // AXI4-STREAM data bus width
+ parameter DST_WIDTH = 16, // Width of DST field we are routing on.
+ parameter NUM_INPUTS = 2 // number of input AXI buses
+ )
+ (
+ input clk,
+ input reset,
+ input clear,
+ // Inputs
+ input [(FIFO_WIDTH*NUM_INPUTS)-1:0] i_tdata,
+ input [NUM_INPUTS-1:0] i_tvalid,
+ input [NUM_INPUTS-1:0] i_tlast,
+ output [NUM_INPUTS-1:0] i_tready,
+ // Forwarding Flags
+ input [NUM_INPUTS-1:0] forward_valid,
+ output reg [NUM_INPUTS-1:0] forward_ack,
+ // Output
+ output [FIFO_WIDTH-1:0] o_tdata,
+ output o_tvalid,
+ output o_tlast,
+ input o_tready
+ );
+
+ wire [FIFO_WIDTH-1:0] i_tdata_array [0:NUM_INPUTS-1];
+
+ reg [`LOG2(NUM_INPUTS):0] select;
+ reg enable;
+
+
+ reg state;
+
+ localparam CHECK_THIS_INPUT = 0;
+ localparam WAIT_LAST = 1;
+
+
+ always @(posedge clk)
+ if (reset | clear) begin
+ state <= CHECK_THIS_INPUT;
+ select <= 0;
+ enable <= 0;
+ forward_ack <= 0;
+ end else begin
+ case(state)
+ // Is the currently selected input addressing this slave with a ready packet?
+ CHECK_THIS_INPUT: begin
+ if (forward_valid[select]) begin
+ enable <= 1;
+ forward_ack[select] <= 1;
+ state <= WAIT_LAST;
+ end else if (select == NUM_INPUTS - 1 ) begin
+ select <= 0;
+ end else begin
+ select <= select + 1;
+ end
+ end
+ // Assert ACK immediately to forwarding logic and then wait for end of packet.
+ WAIT_LAST: begin
+
+ if (i_tlast[select] && i_tvalid[select] && o_tready) begin
+ if (select == NUM_INPUTS - 1 ) begin
+ select <= 0;
+ end else begin
+ select <= select + 1;
+ end
+ state <= CHECK_THIS_INPUT;
+ forward_ack <= 0;
+ enable <= 0;
+ end else begin
+ forward_ack[select] <= 1;
+ enable <= 1;
+ end
+ end
+ endcase // case(state)
+ end
+
+ //
+ // Combinatorial mux
+ //
+ genvar m;
+
+ generate
+ for (m = 0; m < NUM_INPUTS; m = m + 1) begin: form_buses
+ assign i_tdata_array[m] = i_tdata[(m*FIFO_WIDTH)+FIFO_WIDTH-1:m*FIFO_WIDTH];
+ end
+ endgenerate
+
+ assign o_tdata = i_tdata_array[select];
+ assign o_tvalid = enable && i_tvalid[select];
+ assign o_tlast = enable && i_tlast[select];
+ // assign i_tready = {NUM_INPUTS{o_tready}} & (enable << select);
+
+ generate
+ for (m = 0; m < NUM_INPUTS; m = m + 1) begin: form_ready
+ assign i_tready[m] = o_tready && enable && (select == m);
+ end
+ endgenerate
+
+
+endmodule // axi_slave_mux
diff --git a/fpga/usrp3/lib/control/axi_test_vfifo.v b/fpga/usrp3/lib/control/axi_test_vfifo.v
new file mode 100644
index 000000000..1d37bfd1b
--- /dev/null
+++ b/fpga/usrp3/lib/control/axi_test_vfifo.v
@@ -0,0 +1,145 @@
+//
+// Copyright 2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+//
+// Test Virtual FIFO's by streaming modulo 2^32 counter (replicated in upper
+// and lower 32bits). Test result by tracking count on receive and using
+// sticky flag for error indication.
+// Also provide signal from MSB of 32bit count to blink LED.
+//
+
+module axi_test_vfifo
+ #(parameter PACKET_SIZE = 128)
+ (
+ input aclk,
+ input aresetn,
+ input enable,
+ // AXI Stream Out
+ output reg out_axis_tvalid,
+ input out_axis_tready,
+ output [63 : 0] out_axis_tdata,
+ output reg [7 : 0] out_axis_tstrb,
+ output reg [7 : 0] out_axis_tkeep,
+ output reg out_axis_tlast,
+ output reg [0 : 0] out_axis_tid,
+ output reg [0 : 0] out_axis_tdest,
+ input vfifo_full,
+ // AXI Stream In
+ input in_axis_tvalid,
+ output reg in_axis_tready,
+ input [63 : 0] in_axis_tdata,
+ input [7 : 0] in_axis_tstrb,
+ input [7 : 0] in_axis_tkeep,
+ input in_axis_tlast,
+ input [0 : 0] in_axis_tid,
+ input [0 : 0] in_axis_tdest,
+ // Flags
+ output reg flag_error,
+ output heartbeat_in,
+ output heartbeat_out,
+ output [31:0] expected_count
+ );
+
+
+ reg [31:0] out_count;
+ reg [31:0] in_count;
+ reg [63:0] in_axis_tdata_reg;
+ reg in_data_valid;
+
+
+
+ //
+ // Output
+ //
+ always @(posedge aclk)
+ if (!aresetn) begin
+ out_count <= 0;
+ out_axis_tvalid <= 0;
+ out_axis_tid <= 0; // Don't care.
+ out_axis_tdest <= 0; // Only use port 0 of VFIFO.
+ out_axis_tstrb <= 0; // Unused in VFIFO
+ out_axis_tkeep <= 8'hFF; // Always use every byte of data
+ out_axis_tlast <= 1'b0;
+ end else if (enable) begin
+ if (~vfifo_full) begin
+ // Always ready to output new count value.
+ out_axis_tvalid <= 1;
+ if (out_axis_tready)
+ out_count <= out_count + 1;
+ // Assert TLAST every PACKET_SIZE beats.
+ if (out_count[15:0] == PACKET_SIZE)
+ out_axis_tlast <= 1'b1;
+ else
+ out_axis_tlast <= 1'b0;
+ end else begin
+ out_axis_tvalid <= 0;
+ end
+ end else begin
+ out_axis_tlast <= 1'b0;
+ out_axis_tvalid <= 0;
+ end
+
+ assign out_axis_tdata = {out_count,out_count};
+
+ assign heartbeat_out = out_count[28];
+
+
+ //
+ // Input (Ignore TLAST signal)
+ //
+ always @(posedge aclk)
+ if (!aresetn) begin
+ in_axis_tready <= 0;
+ in_axis_tdata_reg <= 0;
+ in_data_valid <= 0;
+
+ end else if (enable) begin
+ in_axis_tready <= 1;
+ in_axis_tdata_reg <= in_axis_tdata;
+ if (in_axis_tvalid)
+ in_data_valid <= 1;
+ else
+ in_data_valid <= 0;
+ end else begin
+ in_data_valid <= 0;
+ in_axis_tready <= 0;
+ end // else: !if(enable)
+
+
+ assign heartbeat_in = in_count[28];
+
+ //
+ // Input Checker
+ //
+ always @(posedge aclk)
+ if (!aresetn) begin
+ in_count <= 0;
+ flag_error <= 0;
+ end else if (enable) begin
+ if (in_data_valid) begin
+
+ if ((in_axis_tdata_reg[63:32] != in_count) || (in_axis_tdata_reg[31:0] != in_count))
+ begin
+ flag_error <= 1;
+ in_count <= in_axis_tdata_reg[63:32] + 1;
+ end
+ else
+ begin
+ flag_error <= 0;
+ in_count <= in_count + 1;
+ end
+
+ end
+ end
+
+ assign expected_count = in_count;
+
+
+endmodule // axi_test_vfifo
+
+
+
+
diff --git a/fpga/usrp3/lib/control/axil_ctrlport_master.v b/fpga/usrp3/lib/control/axil_ctrlport_master.v
new file mode 100644
index 000000000..b1b5a7d2c
--- /dev/null
+++ b/fpga/usrp3/lib/control/axil_ctrlport_master.v
@@ -0,0 +1,248 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axil_ctrlport_master
+// Description:
+// An AXI4-Lite read/write control port adapter
+//
+// Converts AXI4-Lite transactions into control port requests.
+// Converts all AXI requests to control port by only forwarding the
+// CTRLPORT_AWIDTH LSBs of the address.
+//
+// Limitation:
+// The control port interface will only use address, data, byte enable and
+// wr/rd flags. All other signals are tied to 0.
+
+
+module axil_ctrlport_master #(
+ parameter TIMEOUT = 10, // log2(timeout). Control port will timeout after 2^TIMEOUT AXI clock cycles
+ parameter AXI_AWIDTH = 17, // Width of the AXI bus. Aliasing occurs of AXI_AWIDTH > CTRLPORT_AWIDTH
+ parameter CTRLPORT_AWIDTH = 17 // Number of address LSBs forwarded to m_ctrlport_req_addr
+)(
+ //Clock and reset
+ input wire s_axi_aclk,
+ input wire s_axi_aresetn,
+ // AXI4-Lite: Write address port (domain: s_axi_aclk)
+ input wire [AXI_AWIDTH-1:0] s_axi_awaddr,
+ input wire s_axi_awvalid,
+ output reg s_axi_awready,
+ // AXI4-Lite: Write data port (domain: s_axi_aclk)
+ input wire [31:0] s_axi_wdata,
+ input wire [ 3:0] s_axi_wstrb,
+ input wire s_axi_wvalid,
+ output reg s_axi_wready,
+ // AXI4-Lite: Write response port (domain: s_axi_aclk)
+ output reg [ 1:0] s_axi_bresp = 0,
+ output reg s_axi_bvalid,
+ input wire s_axi_bready,
+ // AXI4-Lite: Read address port (domain: s_axi_aclk)
+ input wire [AXI_AWIDTH-1:0] s_axi_araddr,
+ input wire s_axi_arvalid,
+ output reg s_axi_arready,
+ // AXI4-Lite: Read data port (domain: s_axi_aclk)
+ output reg [31:0] s_axi_rdata = 0,
+ output reg [ 1:0] s_axi_rresp = 0,
+ output reg s_axi_rvalid,
+ input wire s_axi_rready,
+ // Control port master request interface
+ output reg m_ctrlport_req_wr,
+ output reg m_ctrlport_req_rd,
+ output reg [19:0] m_ctrlport_req_addr = 0,
+ output wire [ 9:0] m_ctrlport_req_portid,
+ output wire [15:0] m_ctrlport_req_rem_epid,
+ output wire [ 9:0] m_ctrlport_req_rem_portid,
+ output reg [31:0] m_ctrlport_req_data = 0,
+ output reg [ 3:0] m_ctrlport_req_byte_en = 0,
+ output wire m_ctrlport_req_has_time,
+ output wire [63:0] m_ctrlport_req_time,
+ // Control port master response interface
+ input wire m_ctrlport_resp_ack,
+ input wire [ 1:0] m_ctrlport_resp_status,
+ input wire [31:0] m_ctrlport_resp_data
+);
+
+ `include "../axi/axi_defs.v"
+ `include "../rfnoc/core/ctrlport.vh"
+
+ //----------------------------------------------------------
+ // unused ctrlport outputs
+ //----------------------------------------------------------
+ assign m_ctrlport_req_portid = 10'b0;
+ assign m_ctrlport_req_rem_epid = 16'b0;
+ assign m_ctrlport_req_rem_portid = 10'b0;
+ assign m_ctrlport_req_has_time = 1'b0;
+ assign m_ctrlport_req_time = 64'b0;
+
+ //----------------------------------------------------------
+ // Address calculation
+ //----------------------------------------------------------
+ // define configuration for the address calculation
+ localparam [CTRLPORT_ADDR_W-1:0] ADDRESS_MASK = {CTRLPORT_ADDR_W {1'b0}} | {CTRLPORT_AWIDTH {1'b1}};
+
+ // bits to extract from AXI address
+ localparam AXI_ADDR_BITS_TO_FORWARD = (AXI_AWIDTH < CTRLPORT_ADDR_W) ? AXI_AWIDTH : CTRLPORT_ADDR_W;
+
+ //----------------------------------------------------------
+ // State machine for read and write
+ //----------------------------------------------------------
+ localparam IDLE = 4'd0;
+ localparam READ_INIT = 4'd1;
+ localparam WRITE_INIT = 4'd2;
+ localparam READ_TRANSFER = 4'd3;
+ localparam WRITE_TRANSFER = 4'd4;
+ localparam READ_IN_PROGRESS = 4'd5;
+ localparam WRITE_IN_PROGRESS = 4'd6;
+ localparam WRITE_DONE = 4'd7;
+ localparam READ_DONE = 4'd8;
+
+ reg [3:0] state;
+ reg [TIMEOUT-1:0] timeout_counter;
+
+ always @ (posedge s_axi_aclk) begin
+ if (~s_axi_aresetn) begin
+ state <= IDLE;
+
+ // clear AXI feedback paths and controlport requests
+ s_axi_awready <= 1'b0;
+ s_axi_wready <= 1'b0;
+ s_axi_bvalid <= 1'b0;
+ s_axi_arready <= 1'b0;
+ s_axi_rvalid <= 1'b0;
+ m_ctrlport_req_rd <= 1'b0;
+ m_ctrlport_req_wr <= 1'b0;
+ end else begin
+ case (state)
+ // decide whether a read or write should be handled
+ IDLE: begin
+ timeout_counter <= {TIMEOUT {1'b1}};
+
+ if (s_axi_arvalid) begin
+ state <= READ_INIT;
+ end
+ else if (s_axi_awvalid) begin
+ state <= WRITE_INIT;
+ end
+ end
+
+ // wait for FIFO to get read to assign valid
+ READ_INIT: begin
+ // signal ready to upstream module
+ s_axi_arready <= 1'b1;
+
+ state <= READ_TRANSFER;
+ end
+
+ // transfer data to FIFO
+ READ_TRANSFER: begin
+ // clear ready flag from READ_INIT state
+ s_axi_arready <= 1'b0;
+ // transfer data to controlport
+ m_ctrlport_req_rd <= 1'b1;
+ m_ctrlport_req_addr <= s_axi_araddr[AXI_ADDR_BITS_TO_FORWARD-1:0] & ADDRESS_MASK;
+ m_ctrlport_req_byte_en <= 4'b1111;
+
+ state <= READ_IN_PROGRESS;
+ end
+
+ // wait for controlport response is available
+ READ_IN_PROGRESS: begin
+ // clear read flag from previous state
+ m_ctrlport_req_rd <= 1'b0;
+
+ //decrement timeout
+ timeout_counter <= timeout_counter - 1;
+
+ if (m_ctrlport_resp_ack == 1'b1 || timeout_counter == 0) begin
+ s_axi_rvalid <= 1'b1;
+ s_axi_rdata <= m_ctrlport_resp_data;
+ s_axi_rresp <= `AXI4_RESP_OKAY;
+
+ // use AXI DECERR to inform about failed transaction
+ if (timeout_counter == 0) begin
+ s_axi_rresp <= `AXI4_RESP_DECERR;
+ end else begin
+ // if controlport response is not OKAY use AXI SLVERR to propagate error
+ if (m_ctrlport_resp_status != CTRL_STS_OKAY) begin
+ s_axi_rresp <= `AXI4_RESP_SLVERR;
+ end
+ end
+
+ state <= READ_DONE;
+ end
+ end
+
+ // wait until read response is transferred
+ READ_DONE: begin
+ if (s_axi_rready) begin
+ s_axi_rvalid <= 1'b0;
+ state <= IDLE;
+ end
+ end
+
+ //wait for FIFO and data to process
+ WRITE_INIT: begin
+ if (s_axi_wvalid) begin
+ s_axi_awready <= 1'b1;
+ s_axi_wready <= 1'b1;
+ state <= WRITE_TRANSFER;
+ end
+ end
+
+ // transfer data to FIFO
+ WRITE_TRANSFER: begin
+ // clear ready flags from READ_INIT state
+ s_axi_awready <= 1'b0;
+ s_axi_wready <= 1'b0;
+ // transfer data to controlport
+ m_ctrlport_req_wr <= 1'b1;
+ m_ctrlport_req_addr <= s_axi_awaddr[AXI_ADDR_BITS_TO_FORWARD-1:0] & ADDRESS_MASK;
+ m_ctrlport_req_data <= s_axi_wdata;
+ m_ctrlport_req_byte_en <= s_axi_wstrb;
+
+ state <= WRITE_IN_PROGRESS;
+ end
+
+ // wait for write to complete
+ WRITE_IN_PROGRESS: begin
+ // clear write flag from previous state
+ m_ctrlport_req_wr <= 1'b0;
+
+ //decrement timeout
+ timeout_counter <= timeout_counter - 1;
+
+ if (m_ctrlport_resp_ack == 1'b1 || timeout_counter == 0) begin
+ s_axi_bvalid <= 1'b1;
+ s_axi_rdata <= 32'b0;
+ s_axi_bresp <= `AXI4_RESP_OKAY;
+
+ // use AXI DECERR to inform about failed transaction
+ if (timeout_counter == 0) begin
+ s_axi_bresp <= `AXI4_RESP_DECERR;
+ end else begin
+ // if controlport response is not OKAY use AXI SLVERR to propagate error
+ if (m_ctrlport_resp_status != CTRL_STS_OKAY) begin
+ s_axi_bresp <= `AXI4_RESP_SLVERR;
+ end
+ end
+
+ state <= WRITE_DONE;
+ end
+ end
+
+ WRITE_DONE: begin
+ if (s_axi_bready) begin
+ state <= IDLE;
+ s_axi_bvalid <= 1'b0;
+ end
+ end
+
+ default: begin
+ state <= IDLE;
+ end
+ endcase
+ end
+ end
+
+endmodule
diff --git a/fpga/usrp3/lib/control/axil_regport_master.v b/fpga/usrp3/lib/control/axil_regport_master.v
new file mode 100644
index 000000000..de23e7697
--- /dev/null
+++ b/fpga/usrp3/lib/control/axil_regport_master.v
@@ -0,0 +1,228 @@
+//
+// Copyright 2016-2017 Ettus Research
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+// An AXI4-Lite read/write register port adapter
+//
+// Converts memory mapped flow controlled AXI4-Lite transactions into a much
+// simpler non flow controlled write and read register bus.
+//
+// WRITE Transaction:
+// - Transaction completes in one cycle
+// - Valid, Strobe, Address and Data asserted in same cycle
+// __ __ __ __
+// clk __| |__| |__| |__| |__
+// _____
+// reg_wr_req ________| |___________
+// _____
+// reg_wr_keep XXXXXXXX|_____|XXXXXXXXXXX
+// _____
+// reg_wr_addr XXXXXXXX|_____|XXXXXXXXXXX
+// _____
+// reg_wr_data XXXXXXXX|_____|XXXXXXXXXXX
+//;
+// READ Transaction:
+// - Transaction request completes in one cycle, with valid and address assertion
+// - Transaction response must complete in at least one cycle with resp and data
+// - resp must be asserted between 1 and pow(2, TIMEOUT) cycles otherwise the read will timeout
+// __ __ __ __ __
+// clk __| |__| |__| |__| |__| |__
+// _____
+// reg_rd_req ________| |_________________
+// _____
+// reg_rd_addr XXXXXXXX|_____|XXXXXXXXXXXXXXXXX
+// _____
+// reg_rd_resp ____________________| |_____
+// _____
+// reg_rd_data XXXXXXXXXXXXXXXXXXXX|_____|XXXXX
+
+
+module axil_regport_master #(
+ parameter DWIDTH = 32, // Width of the AXI4-Lite data bus (must be 32 or 64)
+ parameter AWIDTH = 32, // Width of the address bus
+ parameter WRBASE = 32'h0, // Write address base
+ parameter RDBASE = 32'h0, // Read address base
+ parameter TIMEOUT = 10 // log2(timeout). Read will timeout after (2^TIMEOUT - 1) cycles
+)(
+ // Clock and reset
+ input s_axi_aclk,
+ input s_axi_aresetn,
+ input reg_clk,
+ // AXI4-Lite: Write address port (domain: s_axi_aclk)
+ input [AWIDTH-1:0] s_axi_awaddr,
+ input s_axi_awvalid,
+ output reg s_axi_awready,
+ // AXI4-Lite: Write data port (domain: s_axi_aclk)
+ input [DWIDTH-1:0] s_axi_wdata,
+ input [DWIDTH/8-1:0] s_axi_wstrb,
+ input s_axi_wvalid,
+ output reg s_axi_wready,
+ // AXI4-Lite: Write response port (domain: s_axi_aclk)
+ output reg [1:0] s_axi_bresp,
+ output reg s_axi_bvalid,
+ input s_axi_bready,
+ // AXI4-Lite: Read address port (domain: s_axi_aclk)
+ input [AWIDTH-1:0] s_axi_araddr,
+ input s_axi_arvalid,
+ output reg s_axi_arready,
+ // AXI4-Lite: Read data port (domain: s_axi_aclk)
+ output reg [DWIDTH-1:0] s_axi_rdata,
+ output reg [1:0] s_axi_rresp,
+ output reg s_axi_rvalid,
+ input s_axi_rready,
+ // Register port: Write port (domain: reg_clk)
+ output reg_wr_req,
+ output [AWIDTH-1:0] reg_wr_addr,
+ output [DWIDTH-1:0] reg_wr_data,
+ output [DWIDTH/8-1:0] reg_wr_keep,
+ // Register port: Read port (domain: reg_clk)
+ output reg_rd_req,
+ output [AWIDTH-1:0] reg_rd_addr,
+ input reg_rd_resp,
+ input [DWIDTH-1:0] reg_rd_data
+);
+
+ //NOTE: clog2 only works when assigned to a parameter
+ // localparam does not work
+ parameter ADDR_LSB = $clog2(DWIDTH/8); //Do not modify
+
+ //----------------------------------------------------------
+ // Write state machine
+ //----------------------------------------------------------
+ reg [AWIDTH-1:0] wr_addr_cache;
+ wire wr_fifo_valid, wr_fifo_ready;
+ wire [AWIDTH-1:0] wr_addr_rel = (s_axi_awaddr - WRBASE);
+
+ // Generate s_axi_awready and latch write address
+ always @(posedge s_axi_aclk) begin
+ if (!s_axi_aresetn) begin
+ s_axi_awready <= 1'b0;
+ wr_addr_cache <= {AWIDTH{1'b0}};
+ end else begin
+ if (~s_axi_awready && s_axi_awvalid && s_axi_wvalid && wr_fifo_ready) begin
+ s_axi_awready <= 1'b1;
+ wr_addr_cache <= {wr_addr_rel[AWIDTH-1:ADDR_LSB], {ADDR_LSB{1'b0}}};
+ end else begin
+ s_axi_awready <= 1'b0;
+ end
+ end
+ end
+
+ // Generate s_axi_wready
+ always @(posedge s_axi_aclk) begin
+ if (!s_axi_aresetn) begin
+ s_axi_wready <= 1'b0;
+ end else begin
+ if (~s_axi_wready && s_axi_wvalid && s_axi_awvalid)
+ s_axi_wready <= 1'b1;
+ else
+ s_axi_wready <= 1'b0;
+ end
+ end
+
+ // Generate write response
+ assign wr_fifo_valid = s_axi_awready && s_axi_awvalid && s_axi_wready && s_axi_wvalid && ~s_axi_bvalid;
+
+ always @(posedge s_axi_aclk) begin
+ if (!s_axi_aresetn) begin
+ s_axi_bvalid <= 1'b0;
+ s_axi_bresp <= 2'b0;
+ end else begin
+ if (wr_fifo_valid && wr_fifo_ready) begin
+ // indicates a valid write response is available
+ s_axi_bvalid <= 1'b1;
+ s_axi_bresp <= 2'b0; // 'OKAY' response
+ end else begin
+ if (s_axi_bready && s_axi_bvalid)
+ s_axi_bvalid <= 1'b0;
+ end
+ end
+ end
+
+ axi_fifo_2clk #( .WIDTH(DWIDTH/8 + AWIDTH + DWIDTH), .SIZE(0) ) wr_fifo_2clk_i (
+ .reset(~s_axi_aresetn), .i_aclk(s_axi_aclk),
+ .i_tdata({s_axi_wstrb, wr_addr_cache, s_axi_wdata}),
+ .i_tvalid(wr_fifo_valid), .i_tready(wr_fifo_ready),
+ .o_aclk(reg_clk),
+ .o_tdata({reg_wr_keep, reg_wr_addr, reg_wr_data}),
+ .o_tvalid(reg_wr_req), .o_tready(1'b1)
+ );
+
+ //----------------------------------------------------------
+ // Read state machine
+ //----------------------------------------------------------
+ reg [TIMEOUT-1:0] read_pending_ctr = {TIMEOUT{1'b0}};
+ wire read_timed_out = (read_pending_ctr == {{(TIMEOUT-1){1'b0}}, 1'b1});
+ wire read_pending = (read_pending_ctr != {TIMEOUT{1'b0}});
+ wire [AWIDTH-1:0] rd_addr_rel = (s_axi_araddr - RDBASE);
+
+ wire rdreq_fifo_ready, rdresp_fifo_valid;
+ wire [DWIDTH-1:0] rdresp_fifo_data;
+
+ // Generate s_axi_arready and latch read address
+ always @(posedge s_axi_aclk) begin
+ if (!s_axi_aresetn) begin
+ s_axi_arready <= 1'b0;
+ read_pending_ctr <= {TIMEOUT{1'b0}};
+ end else begin
+ if (~s_axi_arready && s_axi_arvalid && rdreq_fifo_ready) begin
+ s_axi_arready <= 1'b1;
+ read_pending_ctr <= {TIMEOUT{1'b1}};
+ end else begin
+ s_axi_arready <= 1'b0;
+ end
+ if (read_pending) begin
+ if (rdresp_fifo_valid && ~s_axi_rvalid)
+ read_pending_ctr <= {TIMEOUT{1'b0}};
+ else
+ read_pending_ctr <= read_pending_ctr - 1'b1;
+ end
+ end
+ end
+
+ // Perform read transaction
+ always @(posedge s_axi_aclk) begin
+ if (!s_axi_aresetn) begin
+ s_axi_rvalid <= 1'b0;
+ s_axi_rresp <= 2'b00;
+ s_axi_rdata <= 0;
+ end else begin
+ if (read_pending && rdresp_fifo_valid && ~s_axi_rvalid) begin
+ // Valid read data is available at the read data bus
+ s_axi_rvalid <= 1'b1;
+ s_axi_rresp <= 2'b00; // 'OKAY' response
+ s_axi_rdata <= rdresp_fifo_data;
+ end else if (read_pending && read_timed_out && ~s_axi_rvalid) begin
+ // Read timed out. Assert error.
+ s_axi_rvalid <= 1'b1;
+ s_axi_rresp <= 2'b10; // 'SLVERR' response
+ s_axi_rdata <= {DWIDTH{1'b1}};
+ end else if (s_axi_rvalid && s_axi_rready) begin
+ // Read data is accepted by the master
+ s_axi_rvalid <= 1'b0;
+ end
+ end
+ end
+
+ axi_fifo_2clk #( .WIDTH(AWIDTH), .SIZE(0) ) readreq_fifo_2clk_i (
+ .reset(~s_axi_aresetn), .i_aclk(s_axi_aclk),
+ .i_tdata({rd_addr_rel[AWIDTH-1:ADDR_LSB], {ADDR_LSB{1'b0}}}),
+ .i_tvalid(s_axi_arready && s_axi_arvalid), .i_tready(rdreq_fifo_ready),
+ .o_aclk(reg_clk),
+ .o_tdata(reg_rd_addr),
+ .o_tvalid(reg_rd_req), .o_tready(1'b1)
+ );
+
+ axi_fifo_2clk #( .WIDTH(DWIDTH), .SIZE(0) ) rdresp_fifo_2clk_i (
+ .reset(~s_axi_aresetn), .i_aclk(reg_clk),
+ .i_tdata(reg_rd_data),
+ .i_tvalid(reg_rd_resp), .i_tready(/* lossy */),
+ .o_aclk(s_axi_aclk),
+ .o_tdata(rdresp_fifo_data),
+ .o_tvalid(rdresp_fifo_valid), .o_tready(~read_pending || (s_axi_rvalid && (s_axi_rresp == 2'b00)))
+ );
+
+endmodule
diff --git a/fpga/usrp3/lib/control/axil_to_ni_regport.v b/fpga/usrp3/lib/control/axil_to_ni_regport.v
new file mode 100644
index 000000000..876991ed9
--- /dev/null
+++ b/fpga/usrp3/lib/control/axil_to_ni_regport.v
@@ -0,0 +1,164 @@
+//
+// Copyright 2016 Ettus Research
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// AXI4lite to NI Register Port interface
+//
+
+module axil_to_ni_regport #(
+ parameter RP_AWIDTH = 16,
+ parameter RP_DWIDTH = 32,
+ parameter TIMEOUT = 512
+)(
+ input s_axi_aclk,
+ input s_axi_areset,
+
+ // AXI4lite interface
+ input [31:0] s_axi_awaddr,
+ input s_axi_awvalid,
+ output s_axi_awready,
+ input [31:0] s_axi_wdata,
+ input [3:0] s_axi_wstrb,
+ input s_axi_wvalid,
+ output s_axi_wready,
+ output [1:0] s_axi_bresp,
+ output s_axi_bvalid,
+ input s_axi_bready,
+ input [31:0] s_axi_araddr,
+ input s_axi_arvalid,
+ output s_axi_arready,
+ output [31:0] s_axi_rdata,
+ output [1:0] s_axi_rresp,
+ output s_axi_rvalid,
+ input s_axi_rready,
+
+ // RegPort interface, the out vs in
+ // is seen from the slave device
+ // hooked up to the regport
+ output reg_port_in_rd,
+ output reg_port_in_wt,
+ output [RP_AWIDTH-1:0] reg_port_in_addr,
+ output [RP_DWIDTH-1:0] reg_port_in_data,
+ input [RP_DWIDTH-1:0] reg_port_out_data,
+ input reg_port_out_ready
+);
+
+ localparam IDLE = 3'd0;
+ localparam READ_INIT = 3'd1;
+ localparam WRITE_INIT = 3'd2;
+ localparam READ_IN_PROGRESS = 3'd3;
+ localparam WRITE_IN_PROGRESS = 3'd4;
+ localparam WRITE_DONE = 3'd5;
+ localparam READ_DONE = 3'd6;
+
+ reg [RP_AWIDTH-1:0] addr;
+ reg [RP_DWIDTH-1:0] rb_data;
+ reg [RP_DWIDTH-1:0] wr_data;
+ reg [2:0] state;
+ reg [9:0] count;
+ reg [1:0] rresp;
+ reg [1:0] bresp;
+
+ always @ (posedge s_axi_aclk) begin
+ if (s_axi_areset) begin
+ state <= IDLE;
+ addr <= 'd0;
+ rb_data <= 'd0;
+ wr_data <= 'd0;
+
+ count <= 10'd0;
+ rresp <= 2'd0;
+ bresp <= 2'd0;
+ end
+ else case (state)
+
+ IDLE: begin
+ if (s_axi_arvalid) begin
+ state <= READ_INIT;
+ addr <= s_axi_araddr[RP_AWIDTH-1:0];
+ end
+ else if (s_axi_awvalid) begin
+ state <= WRITE_INIT;
+ addr <= s_axi_awaddr[RP_AWIDTH-1:0];
+ end
+ end
+
+ READ_INIT: begin
+ state <= READ_IN_PROGRESS;
+ count <= 10'd0;
+ rresp <= 2'b00;
+ end
+
+ READ_IN_PROGRESS: begin
+ if (reg_port_out_ready) begin
+ rb_data <= reg_port_out_data;
+ state <= READ_DONE;
+ end
+ else if (count >= TIMEOUT) begin
+ state <= READ_DONE;
+ rresp <= 2'b10;
+ end
+ else begin
+ count <= count + 1'b1;
+ end
+ end
+
+ READ_DONE: begin
+ if (s_axi_rready) begin
+ state <= IDLE;
+ end
+ end
+
+ WRITE_INIT: begin
+ if (s_axi_wvalid) begin
+ wr_data <= s_axi_wdata[RP_DWIDTH-1:0];
+ state <= WRITE_IN_PROGRESS;
+ count <= 10'd0;
+ bresp <= 2'b00;
+ end
+ end
+
+ WRITE_IN_PROGRESS: begin
+ if (reg_port_out_ready) begin
+ state <= WRITE_DONE;
+ end
+ else if (count >= TIMEOUT) begin
+ state <= READ_DONE;
+ bresp <= 2'b10;
+ end
+ else begin
+ count <= count + 1'b1;
+ end
+ end
+
+ WRITE_DONE: begin
+ if (s_axi_bready)
+ state <= IDLE;
+ end
+
+ default: begin
+ state <= IDLE;
+ end
+
+ endcase
+ end
+
+ assign s_axi_awready = (state == IDLE);
+ assign s_axi_wready = (state == WRITE_INIT);
+ assign s_axi_bvalid = (state == WRITE_DONE);
+ assign s_axi_bresp = bresp;
+
+ assign s_axi_arready = (state == IDLE);
+ assign s_axi_rdata = rb_data;
+ assign s_axi_rvalid = (state == READ_DONE);
+ assign s_axi_rresp = rresp;
+
+ assign reg_port_in_wt = (state == WRITE_INIT) & s_axi_wvalid;
+ assign reg_port_in_data = (state == WRITE_INIT) ? s_axi_wdata : wr_data;
+ assign reg_port_in_addr = addr;
+
+ assign reg_port_in_rd = (state == READ_INIT);
+
+endmodule
diff --git a/fpga/usrp3/lib/control/bin2gray.v b/fpga/usrp3/lib/control/bin2gray.v
new file mode 100644
index 000000000..bd68cdb80
--- /dev/null
+++ b/fpga/usrp3/lib/control/bin2gray.v
@@ -0,0 +1,30 @@
+//
+// Copyright 2011 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+
+
+module bin2gray
+ #(parameter WIDTH=8)
+ (input [WIDTH-1:0] bin,
+ output [WIDTH-1:0] gray);
+
+ assign gray = (bin >> 1) ^ bin;
+
+endmodule // bin2gray
diff --git a/fpga/usrp3/lib/control/binary_encoder.v b/fpga/usrp3/lib/control/binary_encoder.v
new file mode 100644
index 000000000..6262b3df0
--- /dev/null
+++ b/fpga/usrp3/lib/control/binary_encoder.v
@@ -0,0 +1,45 @@
+//
+// Copyright 2013 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+`define log2(N) ( N < 2 ? 0 : \
+ N < 4 ? 1 : \
+ N < 8 ? 2 : \
+ N < 16 ? 3 : \
+ N < 32 ? 4 : \
+ N < 64 ? 5 : \
+ N < 128 ? 6 : \
+ N < 256 ? 7 : \
+ N < 512 ? 8 : \
+ N < 1024 ? 9 : \
+ 10 \
+ )
+
+module binary_encoder
+#(
+ parameter SIZE = 16
+)
+(
+ input [SIZE-1:0] in,
+ output [`log2(SIZE)-1:0] out
+);
+
+ genvar m,n;
+
+ generate
+ // Loop enough times to represent the total number of input bits as an encoded value
+ for (m = 0; m <= `log2(SIZE-1); m = m + 1) begin: expand_or_tree
+ wire [SIZE-1:0] encoding;
+ // Build enable mask by iterating through every input bit.
+ for (n = 0; n < SIZE ; n = n + 1) begin: encode_this_bit
+ assign encoding[n] = n[m];
+ end
+ // OR tree for this output bit with appropriate bits enabled.
+ assign out[m] = |(encoding & in);
+ end
+ endgenerate
+
+endmodule // binary_encoder
diff --git a/fpga/usrp3/lib/control/db_control.v b/fpga/usrp3/lib/control/db_control.v
new file mode 100644
index 000000000..757657a22
--- /dev/null
+++ b/fpga/usrp3/lib/control/db_control.v
@@ -0,0 +1,148 @@
+//
+// Copyright 2016 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+module db_control #(
+ // Drive SPI core with input spi_clk instead of ce_clk. This is useful if ce_clk is very slow which
+ // would cause spi transactions to take a long time. WARNING: This adds a clock crossing FIFO!
+ parameter USE_SPI_CLK = 0,
+ parameter SR_BASE = 160,
+ parameter RB_BASE = 16,
+ parameter NUM_SPI_SEN = 8
+)(
+ // Commands from Radio Core
+ input clk, input reset,
+ input set_stb, input [7:0] set_addr, input [31:0] set_data,
+ output reg rb_stb, input [7:0] rb_addr, output reg [63:0] rb_data,
+ input run_rx, input run_tx,
+ // Frontend / Daughterboard I/O
+ input [31:0] misc_ins, output [31:0] misc_outs,
+ input [31:0] fp_gpio_in, output [31:0] fp_gpio_out, output [31:0] fp_gpio_ddr, input [31:0] fp_gpio_fab,
+ input [31:0] db_gpio_in, output [31:0] db_gpio_out, output [31:0] db_gpio_ddr, input [31:0] db_gpio_fab,
+ output [31:0] leds,
+ input spi_clk, input spi_rst, output [NUM_SPI_SEN-1:0] sen, output sclk, output mosi, input miso
+);
+
+ localparam [7:0] SR_MISC_OUTS = SR_BASE + 8'd0;
+ localparam [7:0] SR_SPI = SR_BASE + 8'd8;
+ localparam [7:0] SR_LEDS = SR_BASE + 8'd16;
+ localparam [7:0] SR_FP_GPIO = SR_BASE + 8'd24;
+ localparam [7:0] SR_DB_GPIO = SR_BASE + 8'd32;
+
+ localparam [7:0] RB_MISC_IO = RB_BASE + 0;
+ localparam [7:0] RB_SPI = RB_BASE + 1;
+ localparam [7:0] RB_LEDS = RB_BASE + 2;
+ localparam [7:0] RB_DB_GPIO = RB_BASE + 3;
+ localparam [7:0] RB_FP_GPIO = RB_BASE + 4;
+
+ /********************************************************
+ ** Settings registers
+ ********************************************************/
+ setting_reg #(.my_addr(SR_MISC_OUTS), .width(32)) sr_misc_outs (
+ .clk(clk), .rst(reset),
+ .strobe(set_stb), .addr(set_addr), .in(set_data),
+ .out(misc_outs), .changed());
+
+ // Readback
+ reg spi_readback_stb_hold;
+ reg [31:0] spi_readback_hold;
+ wire [31:0] spi_readback_sync;
+ wire [31:0] fp_gpio_readback, db_gpio_readback;
+ always @* begin
+ case(rb_addr)
+ // Use a latched spi readback stobe so additional readbacks after a SPI transaction will work
+ RB_MISC_IO : {rb_stb, rb_data} <= {spi_readback_stb_hold, {misc_ins, misc_outs}};
+ RB_SPI : {rb_stb, rb_data} <= {spi_readback_stb_hold, {32'd0, spi_readback_hold}};
+ RB_LEDS : {rb_stb, rb_data} <= {spi_readback_stb_hold, {32'd0, leds}};
+ RB_DB_GPIO : {rb_stb, rb_data} <= {spi_readback_stb_hold, {32'd0, db_gpio_readback}};
+ RB_FP_GPIO : {rb_stb, rb_data} <= {spi_readback_stb_hold, {32'd0, fp_gpio_readback}};
+ default : {rb_stb, rb_data} <= {spi_readback_stb_hold, {64'h0BADC0DE0BADC0DE}};
+ endcase
+ end
+
+ /********************************************************
+ ** GPIO
+ ********************************************************/
+ gpio_atr #(.BASE(SR_LEDS), .WIDTH(32), .FAB_CTRL_EN(0), .DEFAULT_DDR(32'hFFFF_FFFF), .DEFAULT_IDLE(32'd0)) leds_gpio_atr (
+ .clk(clk), .reset(reset),
+ .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
+ .rx(run_rx), .tx(run_tx),
+ .gpio_in(32'd0), .gpio_out(leds), .gpio_ddr(/*unused, assumed output only*/),
+ .gpio_out_fab(32'h00000000 /*LEDs don't have fabric control*/), .gpio_sw_rb());
+
+ gpio_atr #(.BASE(SR_FP_GPIO), .WIDTH(32), .FAB_CTRL_EN(1), .DEFAULT_DDR(32'hFFFF_FFFF), .DEFAULT_IDLE(32'd0)) fp_gpio_atr (
+ .clk(clk), .reset(reset),
+ .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
+ .rx(run_rx), .tx(run_tx),
+ .gpio_in(fp_gpio_in), .gpio_out(fp_gpio_out), .gpio_ddr(fp_gpio_ddr),
+ .gpio_out_fab(fp_gpio_fab), .gpio_sw_rb(fp_gpio_readback));
+
+ gpio_atr #(.BASE(SR_DB_GPIO), .WIDTH(32), .FAB_CTRL_EN(1), .DEFAULT_DDR(32'hFFFF_FFFF), .DEFAULT_IDLE(32'd0)) db_gpio_atr (
+ .clk(clk), .reset(reset),
+ .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
+ .rx(run_rx), .tx(run_tx),
+ .gpio_in(db_gpio_in), .gpio_out(db_gpio_out), .gpio_ddr(db_gpio_ddr),
+ .gpio_out_fab(db_gpio_fab), .gpio_sw_rb(db_gpio_readback));
+
+ /********************************************************
+ ** SPI
+ ********************************************************/
+ wire spi_set_stb;
+ wire [7:0] spi_set_addr;
+ wire [31:0] spi_set_data;
+ wire spi_readback_stb, spi_readback_stb_sync;
+ wire [31:0] spi_readback;
+ wire spi_clk_int, spi_rst_int;
+ generate
+ if (USE_SPI_CLK) begin
+ axi_fifo_2clk #(.WIDTH(8 + 32), .SIZE(0)) set_2clk_i (
+ .reset(reset),
+ .i_aclk(clk), .i_tdata({set_addr, set_data}), .i_tvalid(set_stb), .i_tready(),
+ .o_aclk(spi_clk), .o_tdata({spi_set_addr, spi_set_data}), .o_tvalid(spi_set_stb), .o_tready(spi_set_stb));
+
+ axi_fifo_2clk #(.WIDTH(32), .SIZE(0)) rb_2clk_i (
+ .reset(reset),
+ .i_aclk(spi_clk), .i_tdata(spi_readback), .i_tvalid(spi_readback_stb), .i_tready(),
+ .o_aclk(clk), .o_tdata(spi_readback_sync), .o_tvalid(spi_readback_stb_sync), .o_tready(spi_readback_stb_sync));
+
+ assign spi_clk_int = spi_clk;
+ assign spi_rst_int = spi_rst;
+ end else begin
+ assign spi_set_stb = set_stb;
+ assign spi_set_addr = set_addr;
+ assign spi_set_data = set_data;
+ assign spi_readback_stb_sync = spi_readback_stb;
+ assign spi_readback_sync = spi_readback;
+ assign spi_clk_int = clk;
+ assign spi_rst_int = reset;
+ end
+ endgenerate
+
+ // Need to latch spi_readback_stb in case of additional readbacks
+ // after the initial spi transaction.
+ always @(posedge clk) begin
+ if (reset) begin
+ spi_readback_stb_hold <= 1'b1;
+ end else begin
+ if (set_stb & (set_addr == SR_SPI+2 /* Trigger address */)) begin
+ spi_readback_stb_hold <= 1'b0;
+ end else if (spi_readback_stb_sync) begin
+ spi_readback_hold <= spi_readback_sync;
+ spi_readback_stb_hold <= 1'b1;
+ end
+ end
+ end
+
+ // SPI Core instantiation
+ // Note: We don't use "ready" because we use readback_stb to backpressure the settings bus
+ simple_spi_core #(.BASE(SR_SPI), .WIDTH(NUM_SPI_SEN), .CLK_IDLE(0), .SEN_IDLE(8'hFF)) simple_spi_core (
+ .clock(spi_clk_int), .reset(spi_rst_int),
+ .set_stb(spi_set_stb), .set_addr(spi_set_addr), .set_data(spi_set_data),
+ .readback(spi_readback), .readback_stb(spi_readback_stb), .ready(/* Unused */),
+ .sen(sen), .sclk(sclk), .mosi(mosi), .miso(miso),
+ .debug());
+
+endmodule
diff --git a/fpga/usrp3/lib/control/fe_control.v b/fpga/usrp3/lib/control/fe_control.v
new file mode 100644
index 000000000..9e0c6cea5
--- /dev/null
+++ b/fpga/usrp3/lib/control/fe_control.v
@@ -0,0 +1,70 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: fe_control
+//
+// Description: Handle the front end control from the radio settings bus.
+// The module gets generated NUM_CHANNELS times to give independent control to
+// the individual channels.
+//
+
+module fe_control #(
+ parameter NUM_CHANNELS = 2,
+ parameter [7:0] SR_FE_CHAN_OFFSET = 16,
+ parameter [7:0] SR_TX_FE_BASE = 192,
+ parameter [7:0] SR_RX_FE_BASE = 200
+)(
+ input clk, input reset,
+ // Commands from Radio Core
+ input set_stb, input [7:0] set_addr, input [31:0] set_data,
+ input time_sync,
+ // Radio datapath
+ input [NUM_CHANNELS-1:0] tx_stb, input [32*NUM_CHANNELS-1:0] tx_data_in, output [32*NUM_CHANNELS-1:0] tx_data_out,
+ output [NUM_CHANNELS-1:0] rx_stb, input [32*NUM_CHANNELS-1:0] rx_data_in, output [32*NUM_CHANNELS-1:0] rx_data_out
+);
+
+ genvar i;
+ generate for (i = 0; i < NUM_CHANNELS; i = i + 1)
+ begin
+ localparam SR_TX_OFFSET_I = SR_TX_FE_BASE + SR_FE_CHAN_OFFSET*i + 0;
+ localparam SR_TX_OFFSET_Q = SR_TX_FE_BASE + SR_FE_CHAN_OFFSET*i + 1;
+ localparam SR_TX_MAG_CORRECTION = SR_TX_FE_BASE + SR_FE_CHAN_OFFSET*i + 2;
+ localparam SR_TX_PHASE_CORRECTION = SR_TX_FE_BASE + SR_FE_CHAN_OFFSET*i + 3;
+ localparam SR_TX_MUX = SR_TX_FE_BASE + SR_FE_CHAN_OFFSET*i + 4;
+
+ tx_frontend_gen3 #(
+ .SR_OFFSET_I(SR_TX_OFFSET_I), .SR_OFFSET_Q(SR_TX_OFFSET_Q),.SR_MAG_CORRECTION(SR_TX_MAG_CORRECTION),
+ .SR_PHASE_CORRECTION(SR_TX_PHASE_CORRECTION), .SR_MUX(SR_TX_MUX),
+ .BYPASS_DC_OFFSET_CORR(0), .BYPASS_IQ_COMP(0),
+ .DEVICE("7SERIES")
+ ) tx_fe_corr_i (
+ .clk(clk), .reset(reset),
+ .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
+ .tx_stb(tx_stb[i]), .tx_i(tx_data_in[32+(32*i)-1:16+(32*i)]), .tx_q(tx_data_in[16+(32*i)-1:(32*i)]),
+ .dac_stb(), .dac_i(tx_data_out[32+(32*i)-1:16+(32*i)]), .dac_q(tx_data_out[16+(32*i)-1:(32*i)])
+ );
+
+ localparam SR_RX_MAG_CORRECTION = SR_RX_FE_BASE + SR_FE_CHAN_OFFSET*i + 0;
+ localparam SR_RX_PHASE_CORRECTION = SR_RX_FE_BASE + SR_FE_CHAN_OFFSET*i + 1;
+ localparam SR_RX_OFFSET_I = SR_RX_FE_BASE + SR_FE_CHAN_OFFSET*i + 2;
+ localparam SR_RX_OFFSET_Q = SR_RX_FE_BASE + SR_FE_CHAN_OFFSET*i + 3;
+ localparam SR_RX_IQ_MAPPING = SR_RX_FE_BASE + SR_FE_CHAN_OFFSET*i + 4;
+ localparam SR_RX_HET_PHASE_INCR = SR_RX_FE_BASE + SR_FE_CHAN_OFFSET*i + 5;
+
+ rx_frontend_gen3 #(
+ .SR_MAG_CORRECTION(SR_RX_MAG_CORRECTION), .SR_PHASE_CORRECTION(SR_RX_PHASE_CORRECTION), .SR_OFFSET_I(SR_RX_OFFSET_I),
+ .SR_OFFSET_Q(SR_RX_OFFSET_Q), .SR_IQ_MAPPING(SR_RX_IQ_MAPPING), .SR_HET_PHASE_INCR(SR_RX_HET_PHASE_INCR),
+ .BYPASS_DC_OFFSET_CORR(0), .BYPASS_IQ_COMP(0), .BYPASS_REALMODE_DSP(0),
+ .DEVICE("7SERIES")
+ ) rx_fe_corr_i (
+ .clk(clk), .reset(reset), .sync_in(time_sync),
+ .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
+ .adc_stb(1'b1), .adc_i(rx_data_in[32+(32*i)-1:16+(32*i)]), .adc_q(rx_data_in[16+(32*i)-1:(32*i)]),
+ .rx_stb(rx_stb[i]), .rx_i(rx_data_out[32+(32*i)-1:16+(32*i)]), .rx_q(rx_data_out[16+(32*i)-1:(32*i)])
+ );
+ end
+ endgenerate
+
+endmodule
diff --git a/fpga/usrp3/lib/control/filter_bad_sid.v b/fpga/usrp3/lib/control/filter_bad_sid.v
new file mode 100644
index 000000000..41e42c516
--- /dev/null
+++ b/fpga/usrp3/lib/control/filter_bad_sid.v
@@ -0,0 +1,78 @@
+//
+// Copyright 2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Discard silently packets which don't match this SID
+
+module filter_bad_sid
+ (
+ input clk,
+ input reset,
+ input clear,
+ //
+ input [64:0] i_tdata,
+ input i_tvalid,
+ output i_tready,
+ //
+ output [64:0] o_tdata,
+ output o_tvalid,
+ input o_tready,
+ //
+ output reg [15:0] count
+ );
+
+ reg [1:0] state;
+ wire good_sid;
+ wire qualify_i_tvalid;
+
+ localparam IDLE = 0;
+ localparam ACCEPT = 1;
+ localparam DISCARD = 2;
+
+
+ always @(posedge clk)
+ if (reset | clear) begin
+ state <= IDLE;
+ count <= 0;
+ end else
+ case(state)
+ //
+ IDLE: begin
+ if (i_tvalid && i_tready)
+ if (good_sid)
+ state <= ACCEPT;
+ else begin
+ count <= count + 1;
+ state <= DISCARD;
+ end
+ end
+ //
+ ACCEPT: begin
+ if (i_tvalid && i_tready && i_tdata[64])
+ state <= IDLE;
+ end
+ //
+ DISCARD: begin
+ if (i_tvalid && i_tready && i_tdata[64])
+ state <= IDLE;
+ end
+ endcase // case(state)
+
+ assign good_sid = ((i_tdata[15:0] == 16'h00A0) || (i_tdata[15:0] == 16'h00B0));
+
+ assign qualify_i_tvalid = (state == IDLE) ? good_sid : ((state == DISCARD) ? 1'b0 : 1'b1);
+
+ //
+ // Buffer output, break combinatorial timing paths
+ //
+ axi_fifo_short #(.WIDTH(65)) fifo_short
+ (
+ .clk(clk), .reset(reset), .clear(clear),
+ .i_tdata(i_tdata), .i_tvalid(i_tvalid && qualify_i_tvalid), .i_tready(i_tready),
+ .o_tdata(o_tdata), .o_tvalid(o_tvalid), .o_tready(o_tready),
+ .space(), .occupied()
+ );
+
+endmodule // filter_bad_sid
diff --git a/fpga/usrp3/lib/control/gpio_atr.v b/fpga/usrp3/lib/control/gpio_atr.v
new file mode 100644
index 000000000..f99ac55b1
--- /dev/null
+++ b/fpga/usrp3/lib/control/gpio_atr.v
@@ -0,0 +1,98 @@
+
+//
+// Copyright 2011 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+module gpio_atr #(
+ parameter BASE = 0,
+ parameter WIDTH = 32,
+ parameter FAB_CTRL_EN = 0,
+ parameter DEFAULT_DDR = 0,
+ parameter DEFAULT_IDLE = 0
+) (
+ input clk, input reset, //Clock and reset
+ input set_stb, input [7:0] set_addr, input [31:0] set_data, //Settings control interface
+ input rx, input tx, //Run signals that indicate tx and rx operation
+ input [WIDTH-1:0] gpio_in, //GPIO input state
+ output reg [WIDTH-1:0] gpio_out, //GPIO output state
+ output reg [WIDTH-1:0] gpio_ddr, //GPIO direction (0=input, 1=output)
+ input [WIDTH-1:0] gpio_out_fab, //GPIO driver bus from fabric
+ output reg [WIDTH-1:0] gpio_sw_rb //Readback value for software
+);
+ genvar i;
+
+ wire [WIDTH-1:0] in_idle, in_tx, in_rx, in_fdx, ddr_reg, atr_disable, fabric_ctrl;
+ reg [WIDTH-1:0] ogpio, igpio;
+
+ setting_reg #(.my_addr(BASE+0), .width(WIDTH), .at_reset(DEFAULT_IDLE)) reg_idle (
+ .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
+ .out(in_idle),.changed());
+
+ setting_reg #(.my_addr(BASE+1), .width(WIDTH)) reg_rx (
+ .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
+ .out(in_rx),.changed());
+
+ setting_reg #(.my_addr(BASE+2), .width(WIDTH)) reg_tx (
+ .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
+ .out(in_tx),.changed());
+
+ setting_reg #(.my_addr(BASE+3), .width(WIDTH)) reg_fdx (
+ .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
+ .out(in_fdx),.changed());
+
+ setting_reg #(.my_addr(BASE+4), .width(WIDTH), .at_reset(DEFAULT_DDR)) reg_ddr (
+ .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
+ .out(ddr_reg),.changed());
+
+ setting_reg #(.my_addr(BASE+5), .width(WIDTH)) reg_atr_disable (
+ .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
+ .out(atr_disable),.changed());
+
+ generate if (FAB_CTRL_EN == 1) begin
+ setting_reg #(.my_addr(BASE+6), .width(WIDTH)) reg_fabric_ctrl (
+ .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
+ .out(fabric_ctrl),.changed());
+ end else begin
+ assign fabric_ctrl = {WIDTH{1'b0}};
+ end endgenerate
+
+ //Pipeline rx and tx signals for easier timing closure
+ reg rx_d, tx_d;
+ always @(posedge clk)
+ {rx_d, tx_d} <= {rx, tx};
+
+ generate for (i=0; i<WIDTH; i=i+1) begin: gpio_mux_gen
+ //ATR selection MUX
+ always @(posedge clk) begin
+ case({atr_disable[i], tx_d, rx_d})
+ 3'b000: ogpio[i] <= in_idle[i];
+ 3'b001: ogpio[i] <= in_rx[i];
+ 3'b010: ogpio[i] <= in_tx[i];
+ 3'b011: ogpio[i] <= in_fdx[i];
+ default: ogpio[i] <= in_idle[i]; //If ATR mode is disabled, always use IDLE value
+ endcase
+ end
+
+ //Pipeline input, output and direction
+ //For fabric access, insert MUX as close to the IO as possible
+ always @(posedge clk) begin
+ gpio_out[i] <= fabric_ctrl[i] ? gpio_out_fab[i] : ogpio[i];
+ end
+ end endgenerate
+
+ always @(posedge clk)
+ igpio <= gpio_in;
+
+ always @(posedge clk)
+ gpio_ddr <= ddr_reg;
+
+ //Generate software readback state
+ generate for (i=0; i<WIDTH; i=i+1) begin: gpio_rb_gen
+ always @(posedge clk)
+ gpio_sw_rb[i] <= gpio_ddr[i] ? gpio_out[i] : igpio[i];
+ end endgenerate
+
+endmodule // gpio_atr
diff --git a/fpga/usrp3/lib/control/gpio_atr_io.v b/fpga/usrp3/lib/control/gpio_atr_io.v
new file mode 100644
index 000000000..4c427177b
--- /dev/null
+++ b/fpga/usrp3/lib/control/gpio_atr_io.v
@@ -0,0 +1,38 @@
+
+//
+// Copyright 2015 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+module gpio_atr_io #(
+ parameter WIDTH = 32
+) (
+ input clk,
+ input [WIDTH-1:0] gpio_ddr,
+ input [WIDTH-1:0] gpio_out,
+ output [WIDTH-1:0] gpio_in,
+ inout [WIDTH-1:0] gpio_pins
+);
+
+ //Instantiate registers in the IOB
+ (* IOB = "true" *) reg [WIDTH-1:0] gpio_in_iob, gpio_out_iob;
+ always @(posedge clk) begin
+ gpio_in_iob <= gpio_pins;
+ gpio_out_iob <= gpio_out;
+ end
+ assign gpio_in = gpio_in_iob;
+
+ //Pipeline the data direction bus
+ reg [WIDTH-1:0] gpio_ddr_reg;
+ always @(posedge clk)
+ gpio_ddr_reg <= gpio_ddr;
+
+ //Tristate buffers
+ genvar i;
+ generate for (i=0; i<WIDTH; i=i+1) begin: io_tristate_gen
+ assign gpio_pins[i] = gpio_ddr_reg[i] ? gpio_out_iob[i] : 1'bz;
+ end endgenerate
+
+endmodule // gpio_atr_io
diff --git a/fpga/usrp3/lib/control/gray2bin.v b/fpga/usrp3/lib/control/gray2bin.v
new file mode 100644
index 000000000..c75ad80c6
--- /dev/null
+++ b/fpga/usrp3/lib/control/gray2bin.v
@@ -0,0 +1,25 @@
+//
+// Copyright 2016 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+//
+// Gray: {a,b,c,d}
+// Bits: {a,a^b,a^b^c,a^b^c^d}
+//
+module gray2bin #(
+ parameter WIDTH = 8)
+(
+ input [WIDTH-1:0] gray,
+ output reg [WIDTH-1:0] bin
+);
+
+ integer i;
+ always @(*) begin
+ bin[WIDTH-1] = gray[WIDTH-1];
+ for (i = WIDTH-2; i >= 0; i = i - 1) begin
+ bin[i] = bin[i+1] ^ gray[i];
+ end
+ end
+
+endmodule
diff --git a/fpga/usrp3/lib/control/map/AUTHORS b/fpga/usrp3/lib/control/map/AUTHORS
new file mode 100644
index 000000000..c727d8d3c
--- /dev/null
+++ b/fpga/usrp3/lib/control/map/AUTHORS
@@ -0,0 +1,3 @@
+Contributions from:
+Ettus Research, A National Instruments Company
+Alex Forencich <alex@alexforencich.com> \ No newline at end of file
diff --git a/fpga/usrp3/lib/control/map/axis_muxed_kv_map.v b/fpga/usrp3/lib/control/map/axis_muxed_kv_map.v
new file mode 100644
index 000000000..152cee8eb
--- /dev/null
+++ b/fpga/usrp3/lib/control/map/axis_muxed_kv_map.v
@@ -0,0 +1,206 @@
+//
+// Copyright 2018 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axis_muxed_kv_map
+//
+// Description:
+//
+// This module implements a memory that stores key and value (KV) pairs such
+// that the value can be looked up using the key (e.g., for a routing table).
+// This implementation uses AXI stream for both inserting key-value pairs and
+// for looking up a value by its key. It also supports multiple find/result
+// AXI streams, which share the same KV map internally.
+//
+// Values are inserted into the KV map using the axis_insert_* AXI stream. A
+// value can be looked up by its key using the axis_find_* AXI stream, in
+// which case the resulting value is output on the axis_result_* AXI stream.
+//
+// Ports:
+//
+// axis_insert_tdest : Key to insert into the KV map
+// axis_insert_tdata : Value to associate with the key in TDEST
+// axis_insert_tvalid : Standard AXI stream TVALID
+// axis_insert_tready : Standard AXI stream TREADY
+//
+// axis_find_tdata : Key to look up in the KV map
+// axis_find_tvalid : Standard AXI stream TVALID
+// axis_find_tready : Standard AXI stream TREADY
+//
+// axis_result_tdata : Value associated with key that was input on axis_find
+// axis_result_tkeep : Indicates if TDATA contains a valid value (i.e.,
+// TKEEP is 0 if the lookup fails to find a match)
+// axis_result_tvalid : Standard AXI stream TVALID
+// axis_result_tready : Standard AXI stream TREADY
+//
+// Parameters:
+//
+// KEY_WIDTH : Width of the key (axis_insert_tdest, axis_find_tdata)
+// VAL_WIDTH : Width of the value (axis_insert_tdata, axis_result_tdata)
+// SIZE : Size of the KV map (i.e., 2**SIZE key-value pairs)
+// NUM_PORTS : Number of AXI-Stream ports for the find and result interfaces
+//
+
+module axis_muxed_kv_map #(
+ parameter KEY_WIDTH = 16,
+ parameter VAL_WIDTH = 32,
+ parameter SIZE = 6,
+ parameter NUM_PORTS = 4
+) (
+ input wire clk,
+ input wire reset,
+
+ input wire [KEY_WIDTH-1:0] axis_insert_tdest,
+ input wire [VAL_WIDTH-1:0] axis_insert_tdata,
+ input wire axis_insert_tvalid,
+ output wire axis_insert_tready,
+
+ input wire [(KEY_WIDTH*NUM_PORTS)-1:0] axis_find_tdata,
+ input wire [NUM_PORTS-1:0] axis_find_tvalid,
+ output wire [NUM_PORTS-1:0] axis_find_tready,
+
+ output wire [(VAL_WIDTH*NUM_PORTS)-1:0] axis_result_tdata,
+ output wire [NUM_PORTS-1:0] axis_result_tkeep,
+ output wire [NUM_PORTS-1:0] axis_result_tvalid,
+ input wire [NUM_PORTS-1:0] axis_result_tready
+);
+
+ localparam MUX_W = $clog2(NUM_PORTS) + KEY_WIDTH;
+ localparam DEMUX_W = $clog2(NUM_PORTS) + VAL_WIDTH + 1;
+ genvar i;
+
+ localparam [1:0] ST_IDLE = 2'd0;
+ localparam [1:0] ST_REQUEST = 2'd1;
+ localparam [1:0] ST_PENDING = 2'd2;
+
+ //---------------------------------------------------------
+ // Demux find ports
+ //---------------------------------------------------------
+ wire [KEY_WIDTH-1:0] find_key, find_key_reg;
+ wire find_key_stb;
+ wire [$clog2(NUM_PORTS)-1:0] find_dest, find_dest_reg;
+ wire find_key_valid, find_key_valid_reg;
+ wire find_ready;
+ reg find_in_progress = 1'b0;
+ wire insert_stb;
+ wire insert_busy;
+ wire find_res_stb;
+ wire [VAL_WIDTH-1:0] find_res_val;
+ wire find_res_match, find_res_ready;
+
+ wire [(MUX_W*NUM_PORTS)-1:0] mux_tdata;
+ generate for (i = 0; i < NUM_PORTS; i = i + 1) begin : gen_mux_input
+ assign mux_tdata[(MUX_W*i)+KEY_WIDTH-1:MUX_W*i] = axis_find_tdata[(KEY_WIDTH*i)+:KEY_WIDTH];
+ assign mux_tdata[(MUX_W*(i+1))-1:(MUX_W*i)+KEY_WIDTH] = i;
+ end endgenerate
+
+ axi_mux #(
+ .WIDTH(KEY_WIDTH+$clog2(NUM_PORTS)), .SIZE(NUM_PORTS),
+ .PRE_FIFO_SIZE(0), .POST_FIFO_SIZE($clog2(NUM_PORTS))
+ ) mux_i (
+ .clk(clk), .reset(reset), .clear(1'b0),
+ .i_tdata(mux_tdata), .i_tlast({NUM_PORTS{1'b1}}),
+ .i_tvalid(axis_find_tvalid), .i_tready(axis_find_tready),
+ .o_tdata({find_dest_reg, find_key_reg}), .o_tlast(),
+ .o_tvalid(find_key_valid_reg), .o_tready(find_ready)
+ );
+
+ axi_fifo #(
+ .WIDTH(KEY_WIDTH+$clog2(NUM_PORTS)), .SIZE(1)
+ ) mux_reg_i (
+ .clk(clk), .reset(reset), .clear(1'b0),
+ .i_tdata({find_dest_reg, find_key_reg}),
+ .i_tvalid(find_key_valid_reg), .i_tready(find_ready),
+ .o_tdata({find_dest, find_key}),
+ .o_tvalid(find_key_valid), .o_tready(find_res_stb),
+ .space(), .occupied()
+ );
+
+ always @(posedge clk) begin
+ if (reset) begin
+ find_in_progress <= 1'b0;
+ end else begin
+ if (find_key_stb) begin
+ find_in_progress <= 1'b1;
+ end else if (find_res_stb) begin
+ find_in_progress <= 1'b0;
+ end
+ end
+ end
+
+ // find_key_stb indicates when to begin a new KV map lookup. We must wait
+ // until the output mux is ready before starting a lookup.
+ assign find_key_stb = find_key_valid & find_res_ready & ~find_in_progress;
+
+ //---------------------------------------------------------
+ // Insert logic
+ //---------------------------------------------------------
+ reg [1:0] ins_state = ST_IDLE;
+ always @(posedge clk) begin
+ if (reset) begin
+ ins_state <= ST_IDLE;
+ end else begin
+ case (ins_state)
+ ST_IDLE:
+ if (axis_insert_tvalid & ~insert_busy)
+ ins_state <= ST_REQUEST;
+ ST_REQUEST:
+ ins_state <= ST_PENDING;
+ ST_PENDING:
+ if (~insert_busy)
+ ins_state <= ST_IDLE;
+ default:
+ ins_state <= ST_IDLE;
+ endcase
+ end
+ end
+
+ assign axis_insert_tready = axis_insert_tvalid & (ins_state == ST_PENDING) & ~insert_busy;
+ assign insert_stb = axis_insert_tvalid & (ins_state == ST_REQUEST);
+
+ //---------------------------------------------------------
+ // KV map instantiation
+ //---------------------------------------------------------
+ kv_map #(
+ .KEY_WIDTH (KEY_WIDTH),
+ .VAL_WIDTH (VAL_WIDTH),
+ .SIZE (SIZE)
+ ) map_i (
+ .clk (clk),
+ .reset (reset),
+ .insert_stb (insert_stb),
+ .insert_key (axis_insert_tdest),
+ .insert_val (axis_insert_tdata),
+ .insert_busy (insert_busy),
+ .find_key_stb (find_key_stb),
+ .find_key (find_key),
+ .find_res_stb (find_res_stb),
+ .find_res_match (find_res_match),
+ .find_res_val (find_res_val),
+ .count (/* unused */)
+ );
+
+ //---------------------------------------------------------
+ // Mux results port
+ //---------------------------------------------------------
+ wire [(DEMUX_W*NUM_PORTS)-1:0] demux_tdata;
+ wire [DEMUX_W-1:0] hdr;
+ axi_demux #(
+ .WIDTH(DEMUX_W), .SIZE(NUM_PORTS),
+ .PRE_FIFO_SIZE(1), .POST_FIFO_SIZE(0)
+ ) demux_i (
+ .clk(clk), .reset(reset), .clear(1'b0),
+ .header(hdr), .dest(hdr[DEMUX_W-1:VAL_WIDTH+1]),
+ .i_tdata({find_dest, find_res_match, find_res_val}), .i_tlast(1'b1),
+ .i_tvalid(find_res_stb), .i_tready(find_res_ready),
+ .o_tdata(demux_tdata), .o_tlast(),
+ .o_tvalid(axis_result_tvalid), .o_tready(axis_result_tready)
+ );
+
+ generate for (i = 0; i < NUM_PORTS; i = i + 1) begin : gen_result_output
+ assign axis_result_tdata[(VAL_WIDTH*i)+:VAL_WIDTH] = demux_tdata[(DEMUX_W*i)+VAL_WIDTH-1:DEMUX_W*i];
+ assign axis_result_tkeep[i] = demux_tdata[(DEMUX_W*i)+VAL_WIDTH];
+ end endgenerate
+
+endmodule
diff --git a/fpga/usrp3/lib/control/map/cam.v b/fpga/usrp3/lib/control/map/cam.v
new file mode 100644
index 000000000..6b3237b6f
--- /dev/null
+++ b/fpga/usrp3/lib/control/map/cam.v
@@ -0,0 +1,103 @@
+/*
+
+Copyright (c) 2015-2016 Alex Forencich
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+*/
+
+// Language: Verilog 2001
+
+`timescale 1ns / 1ps
+
+/*
+ * Content Addressable Memory
+ */
+module cam #(
+ // search data bus width
+ parameter DATA_WIDTH = 64,
+ // memory size in log2(words)
+ parameter ADDR_WIDTH = 5,
+ // CAM style (SRL, BRAM)
+ parameter CAM_STYLE = "SRL",
+ // width of data bus slices
+ parameter SLICE_WIDTH = 4
+)
+(
+ input wire clk,
+ input wire rst,
+
+ input wire [ADDR_WIDTH-1:0] write_addr,
+ input wire [DATA_WIDTH-1:0] write_data,
+ input wire write_delete,
+ input wire write_enable,
+ output wire write_busy,
+
+ input wire [DATA_WIDTH-1:0] compare_data,
+ output wire [2**ADDR_WIDTH-1:0] match_many,
+ output wire [2**ADDR_WIDTH-1:0] match_single,
+ output wire [ADDR_WIDTH-1:0] match_addr,
+ output wire match
+);
+
+generate
+ if (CAM_STYLE == "SRL") begin
+ cam_srl #(
+ .DATA_WIDTH(DATA_WIDTH),
+ .ADDR_WIDTH(ADDR_WIDTH),
+ .SLICE_WIDTH(SLICE_WIDTH)
+ )
+ cam_inst (
+ .clk(clk),
+ .rst(rst),
+ .write_addr(write_addr),
+ .write_data(write_data),
+ .write_delete(write_delete),
+ .write_enable(write_enable),
+ .write_busy(write_busy),
+ .compare_data(compare_data),
+ .match_many(match_many),
+ .match_single(match_single),
+ .match_addr(match_addr),
+ .match(match)
+ );
+ end else if (CAM_STYLE == "BRAM") begin
+ cam_bram #(
+ .DATA_WIDTH(DATA_WIDTH),
+ .ADDR_WIDTH(ADDR_WIDTH),
+ .SLICE_WIDTH(SLICE_WIDTH)
+ )
+ cam_inst (
+ .clk(clk),
+ .rst(rst),
+ .write_addr(write_addr),
+ .write_data(write_data),
+ .write_delete(write_delete),
+ .write_enable(write_enable),
+ .write_busy(write_busy),
+ .compare_data(compare_data),
+ .match_many(match_many),
+ .match_single(match_single),
+ .match_addr(match_addr),
+ .match(match)
+ );
+ end
+endgenerate
+
+endmodule
diff --git a/fpga/usrp3/lib/control/map/cam_bram.v b/fpga/usrp3/lib/control/map/cam_bram.v
new file mode 100644
index 000000000..c18df1084
--- /dev/null
+++ b/fpga/usrp3/lib/control/map/cam_bram.v
@@ -0,0 +1,259 @@
+/*
+
+Copyright (c) 2015-2016 Alex Forencich
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+*/
+
+// Language: Verilog 2001
+
+`timescale 1ns / 1ps
+
+/*
+ * Content Addressable Memory (block RAM based)
+ */
+module cam_bram #(
+ // search data bus width
+ parameter DATA_WIDTH = 64,
+ // memory size in log2(words)
+ parameter ADDR_WIDTH = 5,
+ // width of data bus slices
+ parameter SLICE_WIDTH = 9
+)
+(
+ input wire clk,
+ input wire rst,
+
+ input wire [ADDR_WIDTH-1:0] write_addr,
+ input wire [DATA_WIDTH-1:0] write_data,
+ input wire write_delete,
+ input wire write_enable,
+ output wire write_busy,
+
+ input wire [DATA_WIDTH-1:0] compare_data,
+ output wire [2**ADDR_WIDTH-1:0] match_many,
+ output wire [2**ADDR_WIDTH-1:0] match_single,
+ output wire [ADDR_WIDTH-1:0] match_addr,
+ output wire match
+);
+
+// total number of slices (enough to cover DATA_WIDTH with address inputs)
+localparam SLICE_COUNT = (DATA_WIDTH + SLICE_WIDTH - 1) / SLICE_WIDTH;
+// depth of RAMs
+localparam RAM_DEPTH = 2**ADDR_WIDTH;
+
+localparam [2:0]
+ STATE_INIT = 3'd0,
+ STATE_IDLE = 3'd1,
+ STATE_DELETE_1 = 3'd2,
+ STATE_DELETE_2 = 3'd3,
+ STATE_WRITE_1 = 3'd4,
+ STATE_WRITE_2 = 3'd5;
+
+reg [2:0] state_reg = STATE_INIT, state_next;
+
+wire [SLICE_COUNT*SLICE_WIDTH-1:0] compare_data_padded = {{SLICE_COUNT*SLICE_WIDTH-DATA_WIDTH{1'b0}}, compare_data};
+wire [SLICE_COUNT*SLICE_WIDTH-1:0] write_data_padded = {{SLICE_COUNT*SLICE_WIDTH-DATA_WIDTH{1'b0}}, write_data};
+
+reg [SLICE_WIDTH-1:0] count_reg = {SLICE_WIDTH{1'b1}}, count_next;
+
+reg [SLICE_COUNT*SLICE_WIDTH-1:0] ram_addr = {SLICE_COUNT*SLICE_WIDTH{1'b0}};
+reg [RAM_DEPTH-1:0] set_bit;
+reg [RAM_DEPTH-1:0] clear_bit;
+reg wr_en;
+
+reg [ADDR_WIDTH-1:0] write_addr_reg = {ADDR_WIDTH{1'b0}}, write_addr_next;
+reg [SLICE_COUNT*SLICE_WIDTH-1:0] write_data_padded_reg = {SLICE_COUNT*SLICE_WIDTH{1'b0}}, write_data_padded_next;
+reg write_delete_reg = 1'b0, write_delete_next;
+
+reg write_busy_reg = 1'b1;
+
+assign write_busy = write_busy_reg;
+
+reg [RAM_DEPTH-1:0] match_raw_out[SLICE_COUNT-1:0];
+reg [RAM_DEPTH-1:0] match_many_raw;
+
+assign match_many = match_many_raw;
+
+reg [DATA_WIDTH-1:0] erase_ram [RAM_DEPTH-1:0];
+reg [DATA_WIDTH-1:0] erase_data = {DATA_WIDTH{1'b0}};
+reg erase_ram_wr_en;
+
+integer i;
+
+initial begin
+ for (i = 0; i < RAM_DEPTH; i = i + 1) begin
+ erase_ram[i] = {SLICE_COUNT*SLICE_WIDTH{1'b0}};
+ end
+end
+
+integer k;
+
+always @* begin
+ match_many_raw = {RAM_DEPTH{1'b1}};
+ for (k = 0; k < SLICE_COUNT; k = k + 1) begin
+ match_many_raw = match_many_raw & match_raw_out[k];
+ end
+end
+
+cam_priority_encoder #(
+ .WIDTH(RAM_DEPTH),
+ .LSB_PRIORITY("HIGH")
+)
+priority_encoder_inst (
+ .input_unencoded(match_many_raw),
+ .output_valid(match),
+ .output_encoded(match_addr),
+ .output_unencoded(match_single)
+);
+
+// BRAMs
+genvar slice_ind;
+generate
+ for (slice_ind = 0; slice_ind < SLICE_COUNT; slice_ind = slice_ind + 1) begin : slice
+ localparam W = slice_ind == SLICE_COUNT-1 ? DATA_WIDTH-SLICE_WIDTH*slice_ind : SLICE_WIDTH;
+
+ wire [RAM_DEPTH-1:0] match_data;
+ wire [RAM_DEPTH-1:0] ram_data;
+
+ ram_2port #(
+ .DWIDTH(RAM_DEPTH),
+ .AWIDTH(W)
+ )
+ ram_inst
+ (
+ .clka(clk),
+ .ena(1'b1),
+ .wea(1'b0),
+ .addra(compare_data[SLICE_WIDTH * slice_ind +: W]),
+ .dia({RAM_DEPTH{1'b0}}),
+ .doa(match_data),
+ .clkb(clk),
+ .enb(1'b1),
+ .web(wr_en),
+ .addrb(ram_addr[SLICE_WIDTH * slice_ind +: W]),
+ .dib((ram_data & ~clear_bit) | set_bit),
+ .dob(ram_data)
+ );
+
+ always @* begin
+ match_raw_out[slice_ind] <= match_data;
+ end
+ end
+endgenerate
+
+// erase
+always @(posedge clk) begin
+ erase_data <= erase_ram[write_addr_next];
+ if (erase_ram_wr_en) begin
+ erase_data <= write_data_padded_reg;
+ erase_ram[write_addr_next] <= write_data_padded_reg;
+ end
+end
+
+// write
+always @* begin
+ state_next = STATE_IDLE;
+
+ count_next = count_reg;
+ ram_addr = erase_data;
+ set_bit = {RAM_DEPTH{1'b0}};
+ clear_bit = {RAM_DEPTH{1'b0}};
+ wr_en = 1'b0;
+
+ erase_ram_wr_en = 1'b0;
+
+ write_addr_next = write_addr_reg;
+ write_data_padded_next = write_data_padded_reg;
+ write_delete_next = write_delete_reg;
+
+ case (state_reg)
+ STATE_INIT: begin
+ // zero out RAMs
+ ram_addr = {SLICE_COUNT{count_reg}} & {{SLICE_COUNT*SLICE_WIDTH-DATA_WIDTH{1'b0}}, {DATA_WIDTH{1'b1}}};
+ set_bit = {RAM_DEPTH{1'b0}};
+ clear_bit = {RAM_DEPTH{1'b1}};
+ wr_en = 1'b1;
+
+ if (count_reg == 0) begin
+ state_next = STATE_IDLE;
+ end else begin
+ count_next = count_reg - 1;
+ state_next = STATE_INIT;
+ end
+ end
+ STATE_IDLE: begin
+ // idle state
+ write_addr_next = write_addr;
+ write_data_padded_next = write_data_padded;
+ write_delete_next = write_delete;
+
+ if (write_enable) begin
+ // wait for read from erase_ram
+ state_next = STATE_DELETE_1;
+ end else begin
+ state_next = STATE_IDLE;
+ end
+ end
+ STATE_DELETE_1: begin
+ // wait for read
+ state_next = STATE_DELETE_2;
+ end
+ STATE_DELETE_2: begin
+ // clear bit and write back
+ clear_bit = 1'b1 << write_addr;
+ wr_en = 1'b1;
+ if (write_delete_reg) begin
+ state_next = STATE_IDLE;
+ end else begin
+ erase_ram_wr_en = 1'b1;
+ state_next = STATE_WRITE_1;
+ end
+ end
+ STATE_WRITE_1: begin
+ // wait for read
+ state_next = STATE_WRITE_2;
+ end
+ STATE_WRITE_2: begin
+ // set bit and write back
+ set_bit = 1'b1 << write_addr;
+ wr_en = 1'b1;
+ state_next = STATE_IDLE;
+ end
+ endcase
+end
+
+always @(posedge clk) begin
+ if (rst) begin
+ state_reg <= STATE_INIT;
+ count_reg <= {SLICE_WIDTH{1'b1}};
+ write_busy_reg <= 1'b1;
+ end else begin
+ state_reg <= state_next;
+ count_reg <= count_next;
+ write_busy_reg <= state_next != STATE_IDLE;
+ end
+
+ write_addr_reg <= write_addr_next;
+ write_data_padded_reg <= write_data_padded_next;
+ write_delete_reg <= write_delete_next;
+end
+
+endmodule
diff --git a/fpga/usrp3/lib/control/map/cam_priority_encoder.v b/fpga/usrp3/lib/control/map/cam_priority_encoder.v
new file mode 100644
index 000000000..5125134f6
--- /dev/null
+++ b/fpga/usrp3/lib/control/map/cam_priority_encoder.v
@@ -0,0 +1,94 @@
+/*
+
+Copyright (c) 2014-2016 Alex Forencich
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+*/
+
+// Language: Verilog 2001
+
+`timescale 1ns / 1ps
+
+/*
+ * Priority encoder module
+ */
+module cam_priority_encoder #
+(
+ parameter WIDTH = 4,
+ // LSB priority: "LOW", "HIGH"
+ parameter LSB_PRIORITY = "LOW"
+)
+(
+ input wire [WIDTH-1:0] input_unencoded,
+ output wire output_valid,
+ output wire [$clog2(WIDTH)-1:0] output_encoded,
+ output wire [WIDTH-1:0] output_unencoded
+);
+
+// power-of-two width
+parameter W1 = 2**$clog2(WIDTH);
+parameter W2 = W1/2;
+
+generate
+ if (WIDTH == 2) begin
+ // two inputs - just an OR gate
+ assign output_valid = |input_unencoded;
+ if (LSB_PRIORITY == "LOW") begin
+ assign output_encoded = input_unencoded[1];
+ end else begin
+ assign output_encoded = ~input_unencoded[0];
+ end
+ end else begin
+ // more than two inputs - split into two parts and recurse
+ // also pad input to correct power-of-two width
+ wire [$clog2(W2)-1:0] out1, out2;
+ wire valid1, valid2;
+ cam_priority_encoder #(
+ .WIDTH(W2),
+ .LSB_PRIORITY(LSB_PRIORITY)
+ )
+ priority_encoder_inst1 (
+ .input_unencoded(input_unencoded[W2-1:0]),
+ .output_valid(valid1),
+ .output_encoded(out1)
+ );
+ cam_priority_encoder #(
+ .WIDTH(W2),
+ .LSB_PRIORITY(LSB_PRIORITY)
+ )
+ priority_encoder_inst2 (
+ .input_unencoded({{W1-WIDTH{1'b0}}, input_unencoded[WIDTH-1:W2]}),
+ .output_valid(valid2),
+ .output_encoded(out2)
+ );
+ // multiplexer to select part
+ assign output_valid = valid1 | valid2;
+ if (LSB_PRIORITY == "LOW") begin
+ assign output_encoded = valid2 ? {1'b1, out2} : {1'b0, out1};
+ end else begin
+ assign output_encoded = valid1 ? {1'b0, out1} : {1'b1, out2};
+ end
+ end
+endgenerate
+
+// unencoded output
+assign output_unencoded = 1 << output_encoded;
+
+endmodule
diff --git a/fpga/usrp3/lib/control/map/cam_srl.v b/fpga/usrp3/lib/control/map/cam_srl.v
new file mode 100644
index 000000000..6bc4146b0
--- /dev/null
+++ b/fpga/usrp3/lib/control/map/cam_srl.v
@@ -0,0 +1,223 @@
+/*
+
+Copyright (c) 2015-2016 Alex Forencich
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+*/
+
+// Language: Verilog 2001
+
+`timescale 1ns / 1ps
+
+/*
+ * Content Addressable Memory (shift register based)
+ */
+module cam_srl #(
+ // search data bus width
+ parameter DATA_WIDTH = 64,
+ // memory size in log2(words)
+ parameter ADDR_WIDTH = 5,
+ // width of data bus slices (4 for SRL16, 5 for SRL32)
+ parameter SLICE_WIDTH = 4
+)
+(
+ input wire clk,
+ input wire rst,
+
+ input wire [ADDR_WIDTH-1:0] write_addr,
+ input wire [DATA_WIDTH-1:0] write_data,
+ input wire write_delete,
+ input wire write_enable,
+ output wire write_busy,
+
+ input wire [DATA_WIDTH-1:0] compare_data,
+ output wire [2**ADDR_WIDTH-1:0] match_many,
+ output wire [2**ADDR_WIDTH-1:0] match_single,
+ output wire [ADDR_WIDTH-1:0] match_addr,
+ output wire match
+);
+
+// total number of slices (enough to cover DATA_WIDTH with address inputs)
+localparam SLICE_COUNT = (DATA_WIDTH + SLICE_WIDTH - 1) / SLICE_WIDTH;
+// depth of RAMs
+localparam RAM_DEPTH = 2**ADDR_WIDTH;
+
+localparam [1:0]
+ STATE_INIT = 2'd0,
+ STATE_IDLE = 2'd1,
+ STATE_WRITE = 2'd2,
+ STATE_DELETE = 2'd3;
+
+reg [1:0] state_reg = STATE_INIT, state_next;
+
+wire [SLICE_COUNT*SLICE_WIDTH-1:0] compare_data_padded = {{SLICE_COUNT*SLICE_WIDTH-DATA_WIDTH{1'b0}}, compare_data};
+wire [SLICE_COUNT*SLICE_WIDTH-1:0] write_data_padded = {{SLICE_COUNT*SLICE_WIDTH-DATA_WIDTH{1'b0}}, write_data};
+
+reg [SLICE_WIDTH-1:0] count_reg = {SLICE_WIDTH{1'b1}}, count_next;
+
+reg [SLICE_COUNT-1:0] shift_data;
+reg [RAM_DEPTH-1:0] shift_en;
+
+reg [ADDR_WIDTH-1:0] write_addr_reg = {ADDR_WIDTH{1'b0}}, write_addr_next;
+reg [SLICE_COUNT*SLICE_WIDTH-1:0] write_data_padded_reg = {SLICE_COUNT*SLICE_WIDTH{1'b0}}, write_data_padded_next;
+
+reg write_busy_reg = 1'b1;
+
+assign write_busy = write_busy_reg;
+
+reg [RAM_DEPTH-1:0] match_raw_out[SLICE_COUNT-1:0];
+reg [RAM_DEPTH-1:0] match_many_raw;
+reg [RAM_DEPTH-1:0] match_many_reg = {RAM_DEPTH{1'b0}};
+
+assign match_many = match_many_reg;
+
+integer k;
+
+always @* begin
+ match_many_raw = ~shift_en;
+ for (k = 0; k < SLICE_COUNT; k = k + 1) begin
+ match_many_raw = match_many_raw & match_raw_out[k];
+ end
+end
+
+cam_priority_encoder #(
+ .WIDTH(RAM_DEPTH),
+ .LSB_PRIORITY("HIGH")
+)
+priority_encoder_inst (
+ .input_unencoded(match_many_reg),
+ .output_valid(match),
+ .output_encoded(match_addr),
+ .output_unencoded(match_single)
+);
+
+integer i;
+
+// SRLs
+genvar row_ind, slice_ind;
+generate
+ for (row_ind = 0; row_ind < RAM_DEPTH; row_ind = row_ind + 1) begin : row
+ for (slice_ind = 0; slice_ind < SLICE_COUNT; slice_ind = slice_ind + 1) begin : slice
+ reg [2**SLICE_WIDTH-1:0] srl_mem = {2**SLICE_WIDTH{1'b0}};
+
+ // match
+ always @* begin
+ match_raw_out[slice_ind][row_ind] = srl_mem[compare_data_padded[SLICE_WIDTH * slice_ind +: SLICE_WIDTH]];
+ end
+
+ // write
+ always @(posedge clk) begin
+ if (shift_en[row_ind]) begin
+ srl_mem <= {srl_mem[2**SLICE_WIDTH-2:0], shift_data[slice_ind]};
+ end
+ end
+ end
+ end
+endgenerate
+
+// match
+always @(posedge clk) begin
+ match_many_reg <= match_many_raw;
+end
+
+// write
+always @* begin
+ state_next = STATE_IDLE;
+
+ count_next = count_reg;
+ shift_data = {SLICE_COUNT{1'b0}};
+ shift_en = {RAM_DEPTH{1'b0}};
+
+ write_addr_next = write_addr_reg;
+ write_data_padded_next = write_data_padded_reg;
+
+ case (state_reg)
+ STATE_INIT: begin
+ // zero out shift registers
+ shift_en = {RAM_DEPTH{1'b1}};
+ shift_data = {SLICE_COUNT{1'b0}};
+
+ if (count_reg == 0) begin
+ state_next = STATE_IDLE;
+ end else begin
+ count_next = count_reg - 1;
+ state_next = STATE_INIT;
+ end
+ end
+ STATE_IDLE: begin
+ if (write_enable) begin
+ write_addr_next = write_addr;
+ write_data_padded_next = write_data_padded;
+ count_next = {SLICE_WIDTH{1'b1}};
+ if (write_delete) begin
+ state_next = STATE_DELETE;
+ end else begin
+ state_next = STATE_WRITE;
+ end
+ end else begin
+ state_next = STATE_IDLE;
+ end
+ end
+ STATE_WRITE: begin
+ // write entry
+ shift_en = 1'b1 << write_addr;
+
+ for (i = 0; i < SLICE_COUNT; i = i + 1) begin
+ shift_data[i] = count_reg == write_data_padded_reg[SLICE_WIDTH * i +: SLICE_WIDTH];
+ end
+
+ if (count_reg == 0) begin
+ state_next = STATE_IDLE;
+ end else begin
+ count_next = count_reg - 1;
+ state_next = STATE_WRITE;
+ end
+ end
+ STATE_DELETE: begin
+ // delete entry
+ shift_en = 1'b1 << write_addr;
+ shift_data = {SLICE_COUNT{1'b0}};
+
+ if (count_reg == 0) begin
+ state_next = STATE_IDLE;
+ end else begin
+ count_next = count_reg - 1;
+ state_next = STATE_DELETE;
+ end
+ end
+ endcase
+end
+
+always @(posedge clk) begin
+ if (rst) begin
+ state_reg <= STATE_INIT;
+ count_reg <= {SLICE_WIDTH{1'b1}};
+ write_busy_reg <= 1'b1;
+ end else begin
+ state_reg <= state_next;
+ count_reg <= count_next;
+ write_busy_reg <= state_next != STATE_IDLE;
+ end
+
+ write_addr_reg <= write_addr_next;
+ write_data_padded_reg <= write_data_padded_next;
+end
+
+endmodule
diff --git a/fpga/usrp3/lib/control/map/kv_map.v b/fpga/usrp3/lib/control/map/kv_map.v
new file mode 100644
index 000000000..dfb0bca43
--- /dev/null
+++ b/fpga/usrp3/lib/control/map/kv_map.v
@@ -0,0 +1,253 @@
+//
+// Copyright 2018 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: kv_map
+
+module kv_map #(
+ parameter KEY_WIDTH = 16,
+ parameter VAL_WIDTH = 32,
+ parameter SIZE = 6
+) (
+ // Clock and reset
+ input wire clk,
+ input wire reset,
+ // Insert port
+ input wire insert_stb,
+ input wire [KEY_WIDTH-1:0] insert_key,
+ input wire [VAL_WIDTH-1:0] insert_val,
+ output wire insert_busy,
+ // Find port
+ input wire find_key_stb,
+ input wire [KEY_WIDTH-1:0] find_key,
+ output wire find_res_stb,
+ output wire find_res_match,
+ output wire [VAL_WIDTH-1:0] find_res_val,
+ // Count
+ output reg [SIZE-1:0] count = {SIZE{1'b0}}
+);
+
+ //-------------------------------------------------
+ // Instantiate a CAM and a RAM
+ //-------------------------------------------------
+ // The CAM serves as a "set" and the RAM serves as a
+ // random addressable "array". Using thse two data structures
+ // we can build a map. The role of the CAM is to compress
+ // the key to an address that can be used to lookup data
+ // stored in the RAM
+
+ wire cam_wr_en, cam_wr_busy, cam_rd_match;
+ wire [SIZE-1:0] cam_wr_addr, cam_rd_addr;
+ wire [KEY_WIDTH-1:0] cam_wr_data, cam_rd_key;
+
+ wire ram_wr_en;
+ wire [SIZE-1:0] ram_wr_addr;
+ reg [SIZE-1:0] ram_rd_addr;
+ wire [VAL_WIDTH-1:0] ram_wr_data, ram_rd_data;
+
+ cam #(
+ .DATA_WIDTH (KEY_WIDTH),
+ .ADDR_WIDTH (SIZE),
+ .CAM_STYLE (SIZE > 8 ? "BRAM" : "SRL"),
+ .SLICE_WIDTH (SIZE > 8 ? 9 : 5)
+ ) cam_i (
+ .clk (clk),
+ .rst (reset),
+ .write_addr (cam_wr_addr),
+ .write_data (cam_wr_data),
+ .write_delete(1'b0),
+ .write_enable(cam_wr_en),
+ .write_busy (cam_wr_busy),
+ .compare_data(cam_rd_key),
+ .match_addr (cam_rd_addr),
+ .match (cam_rd_match),
+ .match_many (),
+ .match_single()
+ );
+
+ ram_2port #(
+ .DWIDTH(VAL_WIDTH),
+ .AWIDTH(SIZE)
+ ) mem_i (
+ .clka (clk),
+ .ena (ram_wr_en),
+ .wea (1'b1),
+ .addra (ram_wr_addr),
+ .dia (ram_wr_data),
+ .doa (/* Write port only */),
+ .clkb (clk),
+ .enb (1'b1),
+ .web (1'b0),
+ .addrb (ram_rd_addr),
+ .dib (/* Read port only */),
+ .dob (ram_rd_data)
+ );
+
+ // Pipeline read address into RAM
+ always @(posedge clk)
+ ram_rd_addr <= cam_rd_addr;
+
+ //-------------------------------------------------
+ // Find state machine
+ //-------------------------------------------------
+ // The lookup process has three cycles of latency
+ // - CAM lookup has a 1 cycle latency
+ // - The lookup address into the RAM is delayed by 1 cycle for timing
+ // - The RAM takes 1 cycle to produce data
+
+ localparam FIND_CYC = 3;
+
+ reg [FIND_CYC-1:0] find_key_stb_shreg = {FIND_CYC{1'b0}};
+ reg [FIND_CYC-2:0] find_match_shreg = {(FIND_CYC-1){1'b0}};
+ reg find_pending = 1'b0;
+
+ wire find_busy = find_pending | find_key_stb;
+
+ // Delay the find valid signal to account for the latency
+ // of the CAM and RAM
+ always @(posedge clk) begin
+ find_key_stb_shreg <= reset ? {FIND_CYC{1'b0}} :
+ {find_key_stb_shreg[FIND_CYC-2:0], find_key_stb};
+ end
+ assign find_res_stb = find_key_stb_shreg[FIND_CYC-1];
+
+ // Latch the find signal to compute pending
+ always @(posedge clk) begin
+ if (find_key_stb)
+ find_pending <= 1'b1;
+ else if (find_pending)
+ find_pending <= ~find_res_stb;
+ end
+
+ // Delay the match signal to account for the latency of the RAM
+ always @(posedge clk) begin
+ find_match_shreg <= reset ? {(FIND_CYC-1){1'b0}} :
+ {find_match_shreg[FIND_CYC-3:0], cam_rd_match};
+ end
+ assign find_res_match = find_match_shreg[FIND_CYC-2];
+
+
+ //-------------------------------------------------
+ // Insert state machine
+ //-------------------------------------------------
+
+ localparam [2:0] ST_IDLE = 3'd0;
+ localparam [2:0] ST_WAIT_FIND = 3'd1;
+ localparam [2:0] ST_CAM_READ = 3'd2;
+ localparam [2:0] ST_CAM_CHECK_MATCH = 3'd3;
+ localparam [2:0] ST_CAM_RAM_WRITE = 3'd4;
+ localparam [2:0] ST_CAM_WRITE_WAIT = 3'd5;
+ localparam [2:0] ST_RAM_WRITE = 3'd6;
+
+ reg [2:0] ins_state = ST_IDLE;
+
+ reg [KEY_WIDTH-1:0] ins_key_cached;
+ reg [VAL_WIDTH-1:0] ins_val_cached;
+ reg [SIZE-1:0] write_addr = {SIZE{1'b0}};
+ reg [SIZE-1:0] next_addr = {SIZE{1'b0}};
+
+
+ always @(posedge clk) begin
+ if (reset) begin
+ ins_state <= ST_IDLE;
+ next_addr <= {SIZE{1'b0}};
+ end else begin
+ case (ins_state)
+
+ // Idle and waiting for an insert transaction
+ //
+ ST_IDLE: begin
+ // Cache insertion parameters
+ if (insert_stb) begin
+ ins_key_cached <= insert_key;
+ ins_val_cached <= insert_val;
+ // Wait for find to finish
+ ins_state <= find_busy ? ST_WAIT_FIND : ST_CAM_READ;
+ end
+ end
+
+ // Wait for a find transaction to finish
+ //
+ ST_WAIT_FIND: begin
+ // Wait for find to finish
+ if (~find_busy)
+ ins_state <= ST_CAM_READ;
+ end
+
+ // Read the CAM to check if the key to insert already exists
+ //
+ ST_CAM_READ: begin
+ // Ensure that find always has priority
+ if (~find_key_stb)
+ ins_state <= ST_CAM_CHECK_MATCH;
+ end
+
+ // Look at the CAM match signal to evaluate if we skip writing the CAM
+ //
+ ST_CAM_CHECK_MATCH: begin
+ // If the CAM already has this key, then overwrite it
+ if (cam_rd_match) begin
+ ins_state <= ST_RAM_WRITE;
+ write_addr <= cam_rd_addr;
+ end else if (~cam_wr_busy) begin
+ ins_state <= ST_CAM_RAM_WRITE;
+ write_addr <= next_addr;
+ next_addr <= next_addr + 1'b1;
+ end
+ end
+
+ // Write the specified key to the CAM and value to the RAM
+ //
+ ST_CAM_RAM_WRITE: begin
+ ins_state <= ST_CAM_WRITE_WAIT;
+ end
+
+ // Wait for CAM write to finish
+ //
+ ST_CAM_WRITE_WAIT: begin
+ if (~cam_wr_busy) begin
+ ins_state <= ST_IDLE;
+ count <= next_addr;
+ end
+ end
+
+ // Write the specified value to the RAM
+ //
+ ST_RAM_WRITE: begin
+ ins_state <= ST_IDLE;
+ count <= next_addr;
+ end
+
+ default: begin
+ // We should not get here
+ ins_state <= ST_IDLE;
+ end
+ endcase
+ end
+ end
+
+ // CAM Read Port:
+ // - Find has priority so it can interrupt an insert
+ assign cam_rd_key =
+ (ins_state != ST_CAM_READ || find_key_stb) ? find_key : ins_key_cached;
+
+ // RAM Write Port:
+ // - The RAM write enable is held high for 1 cycle
+ // - The address may come from a CAM lookup or could generated
+ assign ram_wr_en = (ins_state == ST_RAM_WRITE || ins_state == ST_CAM_RAM_WRITE);
+ assign ram_wr_addr = write_addr;
+ assign ram_wr_data = ins_val_cached;
+
+ // CAM Write Port:
+ // - The CAM write enable is held high for 1 cycle
+ // - The address may come from a CAM lookup or could generated (same as RAM)
+ assign cam_wr_en = (ins_state == ST_CAM_RAM_WRITE);
+ assign cam_wr_addr = write_addr;
+ assign cam_wr_data = ins_key_cached;
+
+ // Outputs
+ assign insert_busy = (ins_state != ST_IDLE);
+ assign find_res_val = ram_rd_data;
+
+endmodule
diff --git a/fpga/usrp3/lib/control/mdio_master.v b/fpga/usrp3/lib/control/mdio_master.v
new file mode 100644
index 000000000..755fb94ce
--- /dev/null
+++ b/fpga/usrp3/lib/control/mdio_master.v
@@ -0,0 +1,772 @@
+//
+// Copyright 2016 Ettus Research
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+module mdio_master #(
+ parameter REG_AWIDTH = 32,
+ parameter REG_BASE = 'h0,
+ parameter [7:0] MDC_DIVIDER = 8'd200
+) (
+ // Clock and reset
+ input clk,
+ input rst,
+ // MDIO ports
+ output reg mdc,
+ output reg mdio_out,
+ output reg mdio_tri, // Assert to tristate driver.
+ input mdio_in,
+ // Register ports
+ input reg_wr_req,
+ input [REG_AWIDTH-1:0] reg_wr_addr,
+ input [31:0] reg_wr_data,
+ input reg_rd_req,
+ input [REG_AWIDTH-1:0] reg_rd_addr,
+ output reg reg_rd_resp,
+ output reg [31:0] reg_rd_data
+);
+
+ localparam [7:0]
+ IDLE = 0,
+ PREAMBLE1 = 1,
+ PREAMBLE2 = 2,
+ PREAMBLE3 = 3,
+ PREAMBLE4 = 4,
+ PREAMBLE5 = 5,
+ PREAMBLE6 = 6,
+ PREAMBLE7 = 7,
+ PREAMBLE8 = 8,
+ PREAMBLE9 = 9,
+ PREAMBLE10 = 10,
+ PREAMBLE11 = 11,
+ PREAMBLE12 = 12,
+ PREAMBLE13 = 13,
+ PREAMBLE14 = 14,
+ PREAMBLE15 = 15,
+ PREAMBLE16 = 16,
+ PREAMBLE17 = 17,
+ PREAMBLE18 = 18,
+ PREAMBLE19 = 19,
+ PREAMBLE20 = 20,
+ PREAMBLE21 = 21,
+ PREAMBLE22 = 22,
+ PREAMBLE23 = 23,
+ PREAMBLE24 = 24,
+ PREAMBLE25 = 25,
+ PREAMBLE26 = 26,
+ PREAMBLE27 = 27,
+ PREAMBLE28 = 28,
+ PREAMBLE29 = 29,
+ PREAMBLE30 = 30,
+ PREAMBLE31 = 31,
+ PREAMBLE32 = 32,
+ START1 = 33,
+ C22_START2 = 34,
+ C45_START2 = 35,
+ OP1 = 36,
+ OP2 = 37,
+ PRTAD1 = 38,
+ PRTAD2 = 39,
+ PRTAD3 = 40,
+ PRTAD4 = 41,
+ PRTAD5 = 42,
+ DEVAD1 = 43,
+ DEVAD2 = 44,
+ DEVAD3 = 45,
+ DEVAD4 = 46,
+ DEVAD5 = 47,
+ TA1 = 48,
+ TA2 = 49,
+ TA3 = 50,
+ READ1 = 51,
+ READ2 = 52,
+ READ3 = 53,
+ READ4 = 54,
+ READ5 = 55,
+ READ6 = 56,
+ READ7 = 57,
+ READ8 = 58,
+ READ9 = 59,
+ READ10 = 60,
+ READ11 = 61,
+ READ12 = 62,
+ READ13 = 63,
+ READ14 = 64,
+ READ15 = 65,
+ READ16 = 66,
+ WRITE1 = 67,
+ WRITE2 = 68,
+ WRITE3 = 69,
+ WRITE4 = 70,
+ WRITE5 = 71,
+ WRITE6 = 72,
+ WRITE7 = 73,
+ WRITE8 = 74,
+ WRITE9 = 75,
+ WRITE10 = 76,
+ WRITE11 = 77,
+ WRITE12 = 78,
+ WRITE13 = 79,
+ WRITE14 = 80,
+ WRITE15 = 81,
+ WRITE16 = 82,
+ C45_ADDR1 = 83,
+ C45_ADDR2 = 84,
+ C45_ADDR3 = 85,
+ C45_ADDR4 = 86,
+ C45_ADDR5 = 87,
+ C45_ADDR6 = 88,
+ C45_ADDR7 = 89,
+ C45_ADDR8 = 90,
+ C45_ADDR9 = 91,
+ C45_ADDR10 = 92,
+ C45_ADDR11 = 93,
+ C45_ADDR12 = 94,
+ C45_ADDR13 = 95,
+ C45_ADDR14 = 96,
+ C45_ADDR15 = 97,
+ C45_ADDR16 = 98,
+ PREIDLE = 99
+ ;
+
+ localparam REG_MDIO_DATA = REG_BASE + 'h0;
+ localparam REG_MDIO_ADDR = REG_BASE + 'h4;
+ localparam REG_MDIO_OP = REG_BASE + 'h8;
+ localparam REG_MDIO_CTRL_STATUS = REG_BASE + 'hC;
+
+ reg [15:0] mdio_read_data, mdio_write_data;
+ reg [15:0] mdio_address;
+ reg [12:0] mdio_operation;
+ reg [7:0] mdc_clk_count;
+ reg mdc_falling_edge;
+ reg mdio_running;
+ reg mdio_done;
+ reg [7:0] state;
+
+ always @(posedge clk) begin
+ if (rst) begin
+ mdio_write_data <= 16'h0;
+ mdio_address <= 16'h0;
+ mdio_operation <= 13'h0;
+ mdio_running <= 1'b0;
+ end else begin
+ // Handshake to MDIO state machine to reset running flag in status.
+ // Wait for falling MDC edge to prevent S/W race condition occuring
+ // where done flag still asserted but running flag now cleared (repeatedly).
+ if (mdio_done && mdc_falling_edge)
+ mdio_running <= 1'b0;
+
+ // Readable registers
+ if (reg_rd_req) begin
+ reg_rd_resp <= 1'b1;
+ case (reg_rd_addr)
+ REG_MDIO_DATA:
+ reg_rd_data <= {16'h0, mdio_read_data};
+ REG_MDIO_ADDR:
+ reg_rd_data <= {16'h0, mdio_address};
+ REG_MDIO_OP:
+ reg_rd_data <= {16'h0, mdio_operation};
+ REG_MDIO_CTRL_STATUS:
+ reg_rd_data <= {31'b0, mdio_running};
+ default:
+ reg_rd_resp <= 1'b0;
+ endcase
+ end else if (reg_rd_resp) begin
+ reg_rd_resp <= 1'b0;
+ end
+
+ // Writable registers
+ if (reg_wr_req) begin
+ case(reg_wr_addr)
+ REG_MDIO_DATA:
+ mdio_write_data <= reg_wr_data[15:0];
+ REG_MDIO_ADDR:
+ mdio_address <= reg_wr_data[15:0];
+ REG_MDIO_OP:
+ mdio_operation <= reg_wr_data[12:0];
+ REG_MDIO_CTRL_STATUS:
+ if (reg_wr_data[0])
+ mdio_running <= 1'b1; // Trigger mdio operation here. Cleared by state machine at end of bus transaction.
+ endcase
+ end
+ end
+ end
+
+ //
+ // Produce mdc clock as a signal synchronously from Wishbone clock.
+ //
+ always @(posedge clk) begin
+ if (rst) begin
+ mdc_clk_count <= 8'd1;
+ mdc <= 1'b0;
+ mdc_falling_edge <= 1'b0;
+ end else if (mdc_clk_count == (MDC_DIVIDER/2)) begin
+ mdc_clk_count <= 8'd1;
+ mdc <= ~mdc;
+ mdc_falling_edge <= mdc;
+ end else begin
+ mdc_clk_count <= mdc_clk_count + 8'd1;
+ mdc_falling_edge <= 1'b0;
+ end
+ end
+
+ //
+ // MDIO state machine
+ //
+ always @(posedge clk) begin
+ if (rst) begin
+ mdio_tri <= 1'b1;
+ mdio_out <= 1'b0;
+ mdio_done <= 1'b0;
+ mdio_read_data <= 16'b0;
+ state <= IDLE;
+ end else if (mdc_falling_edge) begin
+ // This is the MDIO bus controller. Use falling edge of MDC.
+ mdio_tri <= 1'b1;
+ mdio_out <= 1'b0;
+ mdio_done <= 1'b0;
+ case(state)
+ // IDLE.
+ // In Clause 22 & 45 the master of the MDIO bus is tristate during idle.
+ IDLE: begin
+ mdio_tri <= 1'b1;
+ mdio_out <= 1'b0;
+ if (mdio_running)
+ state <= PREAMBLE1;
+ end
+ // Preamble. All MDIO transactions begin witrh 32bits of 1 bits as a preamble.
+ PREAMBLE1: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE2;
+ end
+ PREAMBLE2: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE3;
+ end
+ PREAMBLE3: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE4;
+ end
+ PREAMBLE4: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE5;
+ end
+ PREAMBLE5: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE6;
+ end
+ PREAMBLE6: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE7;
+ end
+ PREAMBLE7: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE8;
+ end
+ PREAMBLE8: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE9;
+ end
+ PREAMBLE9: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE10;
+ end
+ PREAMBLE10: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE11;
+ end
+ PREAMBLE11: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE12;
+ end
+ PREAMBLE12: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE13;
+ end
+ PREAMBLE13: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE14;
+ end
+ PREAMBLE14: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE15;
+ end
+ PREAMBLE15: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE16;
+ end
+ PREAMBLE16: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE17;
+ end
+ PREAMBLE17: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE18;
+ end
+ PREAMBLE18: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE19;
+ end
+ PREAMBLE19: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE20;
+ end
+ PREAMBLE20: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE21;
+ end
+ PREAMBLE21: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE22;
+ end
+ PREAMBLE22: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE23;
+ end
+ PREAMBLE23: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE24;
+ end
+ PREAMBLE24: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE25;
+ end
+ PREAMBLE25: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE26;
+ end
+ PREAMBLE26: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE27;
+ end
+ PREAMBLE27: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE28;
+ end
+ PREAMBLE28: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE29;
+ end
+ PREAMBLE29: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE30;
+ end
+ PREAMBLE30: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE31;
+ end
+ PREAMBLE31: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= PREAMBLE32;
+ end
+ PREAMBLE32: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= START1;
+ end
+ // Start code for Clause 22 is 01 and Clause 45 is 00
+ START1: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b0;
+ if (mdio_operation[12])
+ // Clause 45 bit set.
+ state <= C45_START2;
+ else
+ state <= C22_START2;
+ end
+ // 2nd Clause 22 start bit is a 1
+ C22_START2: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= OP1;
+ end
+ // 2nd Clause 45 start bit is a 0
+ C45_START2: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b0;
+ state <= OP1;
+ end
+ // Both Clause 22 & 45 use 2 bits for operation and are compatable.
+ // Note we don't screen here for illegal Clause 22 ops.
+ OP1: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_operation[11];
+ state <= OP2;
+ end
+ OP2: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_operation[10];
+ state <= PRTAD1;
+ end
+ // Both Clause 22 & 45 use 2 sucsessive 5 bit fields to form a hierarchical address
+ // though it's used slightly different between the 2 standards.
+ PRTAD1: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_operation[9];
+ state <= PRTAD2;
+ end
+ PRTAD2: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_operation[8];
+ state <= PRTAD3;
+ end
+ PRTAD3: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_operation[7];
+ state <= PRTAD4;
+ end
+ PRTAD4: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_operation[6];
+ state <= PRTAD5;
+ end
+ PRTAD5: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_operation[5];
+ state <= DEVAD1;
+ end
+ DEVAD1: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_operation[4];
+ state <= DEVAD2;
+ end
+ DEVAD2: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_operation[3];
+ state <= DEVAD3;
+ end
+ DEVAD3: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_operation[2];
+ state <= DEVAD4;
+ end
+ DEVAD4: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_operation[1];
+ state <= DEVAD5;
+ end
+ DEVAD5: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_operation[0];
+ state <= TA1;
+ end
+ // Both Clause 22 & Clause 45 use the same turn around on the bus.
+ // Reads have Z as the first bit and 0 driven by the slave for the 2nd bit.
+ // Note that slaves drive the bus on the rising edge of MDC.
+ // Writes and Address cycles have 10 driven by the master.
+ TA1: begin
+ if (mdio_operation[11] == 1'b0) // Write/Address
+ begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b1;
+ state <= TA2;
+ end
+ else // Read
+ begin
+ mdio_tri <= 1'b1;
+ state <= TA3;
+ end
+ end
+ TA2: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= 1'b0;
+ if ( !mdio_operation[12]) // Clause 22 Write
+ state <= WRITE1;
+ else if (mdio_operation[10]) // Clause 45 Write
+ state <= WRITE1;
+ else // Clause 45 ADDRESS
+ state <= C45_ADDR1;
+ end
+ TA3: begin
+ mdio_tri <= 1'b1;
+ state <= READ1;
+ end
+ // Clause 22 Reads and both forms of clause 45 Reads have the same bus transaction from here out.
+ READ1: begin
+ mdio_tri <= 1'b1;
+ mdio_read_data[15] <= mdio_in;
+ state <= READ2;
+ end
+ READ2: begin
+ mdio_tri <= 1'b1;
+ mdio_read_data[14] <= mdio_in;
+ state <= READ3;
+ end
+ READ3: begin
+ mdio_tri <= 1'b1;
+ mdio_read_data[13] <= mdio_in;
+ state <= READ4;
+ end
+ READ4: begin
+ mdio_tri <= 1'b1;
+ mdio_read_data[12] <= mdio_in;
+ state <= READ5;
+ end
+ READ5: begin
+ mdio_tri <= 1'b1;
+ mdio_read_data[11] <= mdio_in;
+ state <= READ6;
+ end
+ READ6: begin
+ mdio_tri <= 1'b1;
+ mdio_read_data[10] <= mdio_in;
+ state <= READ7;
+ end
+ READ7: begin
+ mdio_tri <= 1'b1;
+ mdio_read_data[9] <= mdio_in;
+ state <= READ8;
+ end
+ READ8: begin
+ mdio_tri <= 1'b1;
+ mdio_read_data[8] <= mdio_in;
+ state <= READ9;
+ end
+ READ9: begin
+ mdio_tri <= 1'b1;
+ mdio_read_data[7] <= mdio_in;
+ state <= READ10;
+ end
+ READ10: begin
+ mdio_tri <= 1'b1;
+ mdio_read_data[6] <= mdio_in;
+ state <= READ11;
+ end
+ READ11: begin
+ mdio_tri <= 1'b1;
+ mdio_read_data[5] <= mdio_in;
+ state <= READ12;
+ end
+ READ12: begin
+ mdio_tri <= 1'b1;
+ mdio_read_data[4] <= mdio_in;
+ state <= READ13;
+ end
+ READ13: begin
+ mdio_tri <= 1'b1;
+ mdio_read_data[3] <= mdio_in;
+ state <= READ14;
+ end
+ READ14: begin
+ mdio_tri <= 1'b1;
+ mdio_read_data[2] <= mdio_in;
+ state <= READ15;
+ end
+ READ15: begin
+ mdio_tri <= 1'b1;
+ mdio_read_data[1] <= mdio_in;
+ state <= READ16;
+ end
+ READ16: begin
+ mdio_tri <= 1'b1;
+ mdio_read_data[0] <= mdio_in;
+ state <= PREIDLE;
+ mdio_done <= 1'b1;
+ end
+ // Write 16bits of data for all types of Write.
+ WRITE1: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_write_data[15];
+ state <= WRITE2;
+ end
+ WRITE2: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_write_data[14];
+ state <= WRITE3;
+ end
+ WRITE3: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_write_data[13];
+ state <= WRITE4;
+ end
+ WRITE4: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_write_data[12];
+ state <= WRITE5;
+ end
+ WRITE5: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_write_data[11];
+ state <= WRITE6;
+ end
+ WRITE6: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_write_data[10];
+ state <= WRITE7;
+ end
+ WRITE7: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_write_data[9];
+ state <= WRITE8;
+ end
+ WRITE8: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_write_data[8];
+ state <= WRITE9;
+ end
+ WRITE9: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_write_data[7];
+ state <= WRITE10;
+ end
+ WRITE10: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_write_data[6];
+ state <= WRITE11;
+ end
+ WRITE11: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_write_data[5];
+ state <= WRITE12;
+ end
+ WRITE12: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_write_data[4];
+ state <= WRITE13;
+ end
+ WRITE13: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_write_data[3];
+ state <= WRITE14;
+ end
+ WRITE14: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_write_data[2];
+ state <= WRITE15;
+ end
+ WRITE15: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_write_data[1];
+ state <= WRITE16;
+ end
+ WRITE16: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_write_data[0];
+ state <= PREIDLE;
+ mdio_done <= 1'b1;
+ end
+ // Write 16bits of address for a Clause 45 Address transaction
+ C45_ADDR1: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_address[15];
+ state <= C45_ADDR2;
+ end
+ C45_ADDR2: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_address[14];
+ state <= C45_ADDR3;
+ end
+ C45_ADDR3: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_address[13];
+ state <= C45_ADDR4;
+ end
+ C45_ADDR4: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_address[12];
+ state <= C45_ADDR5;
+ end
+ C45_ADDR5: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_address[11];
+ state <= C45_ADDR6;
+ end
+ C45_ADDR6: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_address[10];
+ state <= C45_ADDR7;
+ end
+ C45_ADDR7: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_address[9];
+ state <= C45_ADDR8;
+ end
+ C45_ADDR8: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_address[8];
+ state <= C45_ADDR9;
+ end
+ C45_ADDR9: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_address[7];
+ state <= C45_ADDR10;
+ end
+ C45_ADDR10: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_address[6];
+ state <= C45_ADDR11;
+ end
+ C45_ADDR11: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_address[5];
+ state <= C45_ADDR12;
+ end
+ C45_ADDR12: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_address[4];
+ state <= C45_ADDR13;
+ end
+ C45_ADDR13: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_address[3];
+ state <= C45_ADDR14;
+ end
+ C45_ADDR14: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_address[2];
+ state <= C45_ADDR15;
+ end
+ C45_ADDR15: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_address[1];
+ state <= C45_ADDR16;
+ end
+ C45_ADDR16: begin
+ mdio_tri <= 1'b0;
+ mdio_out <= mdio_address[0];
+ state <= PREIDLE;
+ mdio_done <= 1'b1;
+ end
+ // PREIDLE allows the mdio_running bit to reset.
+ PREIDLE: begin
+ state <= IDLE;
+ end
+ endcase // case(state)
+ end // if (mdc_falling_edge)
+ end
+
+endmodule
+
diff --git a/fpga/usrp3/lib/control/por_gen.v b/fpga/usrp3/lib/control/por_gen.v
new file mode 100644
index 000000000..b16ceb5bf
--- /dev/null
+++ b/fpga/usrp3/lib/control/por_gen.v
@@ -0,0 +1,28 @@
+//
+// Copyright 2013 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+
+
+module por_gen
+ (input clk,
+ output reset_out);
+
+ reg por_rst;
+ reg [7:0] por_counter = 8'h0;
+
+ always @(posedge clk)
+ if (por_counter != 8'h55)
+ begin
+ por_counter <= por_counter + 8'h1;
+ por_rst <= 1'b1;
+ end
+ else
+ por_rst <= 1'b0;
+
+ assign reset_out = por_rst;
+
+endmodule // por_gen
diff --git a/fpga/usrp3/lib/control/priority_encoder.v b/fpga/usrp3/lib/control/priority_encoder.v
new file mode 100644
index 000000000..8e84fb57a
--- /dev/null
+++ b/fpga/usrp3/lib/control/priority_encoder.v
@@ -0,0 +1,55 @@
+//
+// Copyright 2013 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+`define log2(N) ( N < 2 ? 0 : \
+ N < 4 ? 1 : \
+ N < 8 ? 2 : \
+ N < 16 ? 3 : \
+ N < 32 ? 4 : \
+ N < 64 ? 5 : \
+ N < 128 ? 6 : \
+ N < 256 ? 7 : \
+ N < 512 ? 8 : \
+ N < 1024 ? 9 : \
+ 10 \
+ )
+
+module priority_encoder
+#(
+ parameter WIDTH = 16
+)
+(
+ input [WIDTH-1:0] in,
+ output [`log2(WIDTH)-1:0] out
+);
+
+ wire [WIDTH-1:0] one_hot;
+
+ // the priority encoder spits out the position
+ // of the leading bit as one hot coding
+ priority_encoder_one_hot #
+ (
+ .WIDTH(WIDTH)
+ )
+ prio_one_hot0
+ (
+ .in(in),
+ .out(one_hot)
+ );
+
+ // binary encoder turns the one hot coding
+ // into binary encoding
+ binary_encoder #
+ (
+ .WIDTH(WIDTH)
+ )
+ binary_enc0
+ (
+ .in(one_hot),
+ .out(out)
+ );
+endmodule
diff --git a/fpga/usrp3/lib/control/priority_encoder_one_hot.v b/fpga/usrp3/lib/control/priority_encoder_one_hot.v
new file mode 100644
index 000000000..d2839ddde
--- /dev/null
+++ b/fpga/usrp3/lib/control/priority_encoder_one_hot.v
@@ -0,0 +1,34 @@
+//
+// Copyright 2013 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+module priority_encoder_one_hot
+#(
+ parameter WIDTH = 16
+)
+(
+ input [WIDTH-1:0] in,
+ output [WIDTH-1:0] out
+);
+
+ wire [WIDTH-1:0] in_rev;
+ wire [WIDTH-1:0] in_rev_inv_po = ~in_rev + 1;
+
+ wire [WIDTH-1:0] mask;
+
+ generate
+ genvar i,j;
+
+ for (i=0; i<WIDTH; i=i+1)
+ assign in_rev[i] = in[WIDTH-1-i];
+
+ for (j=0; j<WIDTH; j=j+1)
+ assign mask[j] = in_rev_inv_po[WIDTH-1-j];
+ endgenerate
+
+ assign out = in & mask;
+
+endmodule
diff --git a/fpga/usrp3/lib/control/pulse_stretch.v b/fpga/usrp3/lib/control/pulse_stretch.v
new file mode 100644
index 000000000..2235286e1
--- /dev/null
+++ b/fpga/usrp3/lib/control/pulse_stretch.v
@@ -0,0 +1,79 @@
+//
+// Copyright 2017 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: pulse_stretch
+//
+// Description:
+//
+// Pulse stretcher. Takes any input pulse that is SCALE+2 clock cycles or
+// less and outputs a pulse that is SCALE+1 clock cycles. However, if an
+// input pulse is longer than SCALE+2 clock cycles then the output pulse
+// repeats. If more than one pulse is input within SCALE+2 clock cycles then
+// additional pulses will be ignored.
+//
+// Examples (SCALE = 2):
+//
+// Clock _/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_
+//
+//
+// pulse _/‾‾‾\_______________/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\___________________
+//
+// pulse_stretched _____/‾‾‾‾‾‾‾‾‾‾‾\_______/‾‾‾‾‾‾‾‾‾‾‾\___________________
+//
+//
+// pulse _/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\___________________________________
+//
+// pulse_stretched _____/‾‾‾‾‾‾‾‾‾‾‾\___/‾‾‾‾‾‾‾‾‾‾‾\_______________________
+//
+//
+// pulse _/‾‾‾\_______/‾‾‾\___/‾‾‾\_______/‾‾‾‾‾‾‾\_______________
+//
+// pulse_stretched _____/‾‾‾‾‾‾‾‾‾‾‾\_______/‾‾‾‾‾‾‾‾‾‾‾\___/‾‾‾‾‾‾‾‾‾‾‾\___
+
+// Parameters:
+//
+// SCALE : The number of clock cycles to add to a single cycle pulse. Or, the
+// number of clock cycles, minus 1, for the output pulse.
+//
+
+module pulse_stretch #(
+ parameter SCALE = 64'd12_500_000
+)(
+ input clk,
+ input rst,
+ input pulse,
+ output pulse_stretched
+);
+
+ reg [$clog2(SCALE+1)-1:0] count = 'd0;
+ reg state = 1'b0;
+
+ always @ (posedge clk)
+ if (rst) begin
+ state <= 1'b0;
+ count <= 'd0;
+ end
+ else begin
+ case (state)
+
+ 1'b0: begin
+ if (pulse) begin
+ state <= 1'b1;
+ count <= 'd0;
+ end
+ end
+
+ 1'b1: begin
+ if (count == SCALE)
+ state <= 1'b0;
+ else
+ count <= count + 1'b1;
+ end
+ endcase
+ end
+
+ assign pulse_stretched = (state == 1'b1);
+
+endmodule
diff --git a/fpga/usrp3/lib/control/pulse_stretch_min.v b/fpga/usrp3/lib/control/pulse_stretch_min.v
new file mode 100644
index 000000000..84bf3ecd9
--- /dev/null
+++ b/fpga/usrp3/lib/control/pulse_stretch_min.v
@@ -0,0 +1,79 @@
+//
+// Copyright 2017 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: pulse_stretch_min
+//
+// Description:
+//
+// Pulse stretcher, to guarantee a minimum pulse width. Takes a short input
+// pulse and outputs a pulse that is LENGTH clock cycles long. If the input
+// pulse is longer than LENGTH then the output pulse will be the same length
+// as the input pulse. The output is registered so the output is delayed by
+// one clock cycle relative to the input. If more than one pulse is input
+// within LENGTH+1 clock cycles, then the extra input pulses will not
+// generate output pulses.
+//
+// Examples: (LENGTH = 3)
+//
+// Clock _/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_
+//
+//
+// pulse_in _/‾‾‾\_______________/‾‾‾‾‾‾‾\___________/‾‾‾‾‾‾‾‾‾‾‾\_______
+//
+// pulse_out _____/‾‾‾‾‾‾‾‾‾‾‾\_______/‾‾‾‾‾‾‾‾‾‾‾\_______/‾‾‾‾‾‾‾‾‾‾‾\___
+//
+//
+// pulse_in _/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\______________/‾‾‾\_______/‾‾‾\______
+//
+// pulse_out ______/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\______________/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\__
+//
+//
+// Parameters:
+//
+// LENGTH : Length of the minimum pulse to output, in clock cycles.
+//
+
+module pulse_stretch_min #(
+ parameter LENGTH = 4
+) (
+ input wire clk,
+ input wire rst,
+ input wire pulse_in,
+ output reg pulse_out = 0
+);
+
+ reg [$clog2(LENGTH)-1:0] count = 0;
+ reg state = 0;
+
+ always @ (posedge clk)
+ if (rst) begin
+ state <= 0;
+ count <= 0;
+ pulse_out <= 0;
+ end
+ else begin
+ case (state)
+
+ 1'b0: begin
+ if (pulse_in) begin
+ state <= 1;
+ pulse_out <= 1;
+ count <= 0;
+ end
+ end
+
+ 1'b1: begin
+ if (count == LENGTH-1) begin
+ if (!pulse_in) begin
+ state <= 0;
+ pulse_out <= 0;
+ end
+ end else
+ count <= count + 1;
+ end
+ endcase
+ end
+
+endmodule
diff --git a/fpga/usrp3/lib/control/pulse_synchronizer.v b/fpga/usrp3/lib/control/pulse_synchronizer.v
new file mode 100644
index 000000000..af3460878
--- /dev/null
+++ b/fpga/usrp3/lib/control/pulse_synchronizer.v
@@ -0,0 +1,70 @@
+//
+// Copyright 2018 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: pulse_synchronizer
+// Description:
+// - Synchronizes a single-cycle pulse or an edge from one
+// clock domain to another
+// - Clocks A and B can be asynchronous
+//
+
+module pulse_synchronizer #(
+ parameter MODE = "PULSE", // Capture mode {PULSE, POSEDGE, NEGEDGE}
+ parameter STAGES = 2 // Number of synchronizer stages
+) (
+ input clk_a, // Clock A
+ input rst_a, // Reset in clock domain A
+ input pulse_a, // Pulse in clock domain A to synchronize
+ output busy_a, // Synchronizer is busy (pulse_a ignored when asserted)
+ input clk_b, // Clock B
+ output pulse_b // Pulse in clock domain B
+);
+ // Trigger logic based on the capture mode
+ wire trigger;
+ generate if (MODE == "POSEDGE") begin
+ reg pulse_a_del_pe = 1'b0;
+ always @ (posedge clk_a)
+ pulse_a_del_pe <= rst_a ? 1'b0 : pulse_a;
+ assign trigger = pulse_a & ~pulse_a_del_pe;
+ end else if (MODE == "NEGEDGE") begin
+ reg pulse_a_del_ne = 1'b1;
+ always @ (posedge clk_a)
+ pulse_a_del_ne <= rst_a ? 1'b1 : pulse_a;
+ assign trigger = ~pulse_a & pulse_a_del_ne;
+ end else begin
+ assign trigger = pulse_a;
+ end endgenerate
+
+ // Translate pulse/edge to a level and synchronize that into the B domain
+ reg pulse_toggle_a = 1'b0;
+ always @(posedge clk_a) begin
+ pulse_toggle_a <= rst_a ? 1'b0 : (pulse_toggle_a ^ (trigger & ~busy_a));
+ end
+
+ wire pulse_toggle_b;
+ reg pulse_toggle_b_del = 1'b0;
+ wire handshake_toggle_a;
+
+ synchronizer #(
+ .STAGES(STAGES), .INITIAL_VAL(0)
+ ) toggle_sync_i (
+ .clk(clk_b), .rst(1'b0), .in(pulse_toggle_a), .out(pulse_toggle_b)
+ );
+
+ // Handshake toggle signal back into the A domain to deassert busy
+ synchronizer #(
+ .STAGES(STAGES), .INITIAL_VAL(0)
+ ) handshake_sync_i (
+ .clk(clk_a), .rst(1'b0), .in(pulse_toggle_b_del), .out(handshake_toggle_a)
+ );
+
+ always @(posedge clk_b) begin
+ pulse_toggle_b_del <= pulse_toggle_b;
+ end
+
+ assign pulse_b = pulse_toggle_b_del ^ pulse_toggle_b;
+ assign busy_a = pulse_toggle_a ^ handshake_toggle_a;
+
+endmodule \ No newline at end of file
diff --git a/fpga/usrp3/lib/control/ram_2port.v b/fpga/usrp3/lib/control/ram_2port.v
new file mode 100644
index 000000000..96db90061
--- /dev/null
+++ b/fpga/usrp3/lib/control/ram_2port.v
@@ -0,0 +1,120 @@
+//
+// Copyright 2011 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Description
+// This code implements a parameterizable true dual port memory
+// (both ports can read and write). If an enable is not necessary
+// it may be tied off.
+//
+// Note
+// This module requires the ram_2port_impl.vh header file. The
+// header is included multiple times with different values of
+// the RAM_DIRECTIVE macro to create different implementations of the
+// RAM. An implementation is chosen in ram_2port based on the
+// user parameter for RAM_TYPE.
+
+// Mode: AUTOMATIC
+`define RAM_DIRECTIVE
+`define RAM_MOD_NAME ram_2port_impl_auto
+`include "ram_2port_impl.vh"
+`undef RAM_MOD_NAME
+`undef RAM_DIRECTIVE
+
+// Mode: REG
+`define RAM_DIRECTIVE (* ram_style = "registers" *)
+`define RAM_MOD_NAME ram_2port_impl_reg
+`include "ram_2port_impl.vh"
+`undef RAM_MOD_NAME
+`undef RAM_DIRECTIVE
+
+// Mode: LUTRAM
+`define RAM_DIRECTIVE (* ram_style = "distributed" *)
+`define RAM_MOD_NAME ram_2port_impl_lutram
+`include "ram_2port_impl.vh"
+`undef RAM_MOD_NAME
+`undef RAM_DIRECTIVE
+
+// Mode: BRAM
+`define RAM_DIRECTIVE (* ram_style = "block" *)
+`define RAM_MOD_NAME ram_2port_impl_bram
+`include "ram_2port_impl.vh"
+`undef RAM_MOD_NAME
+`undef RAM_DIRECTIVE
+
+// Mode: URAM
+`define RAM_DIRECTIVE (* ram_style = "ultra" *)
+`define RAM_MOD_NAME ram_2port_impl_uram
+`include "ram_2port_impl.vh"
+`undef RAM_MOD_NAME
+`undef RAM_DIRECTIVE
+
+module ram_2port #(
+ parameter DWIDTH = 32, // Width of the memory block
+ parameter AWIDTH = 9, // log2 of the depth of the memory block
+ parameter RW_MODE = "READ-FIRST", // Read-write mode {READ-FIRST, WRITE-FIRST, NO-CHANGE}
+ parameter RAM_TYPE = "AUTOMATIC", // Type of RAM to infer {AUTOMATIC, REG, LUTRAM, BRAM, URAM}
+ parameter OUT_REG = 0, // Instantiate an output register? (+1 cycle of read latency)
+ parameter INIT_FILE = "" // Optionally initialize memory with this file
+) (
+ input wire clka,
+ input wire ena,
+ input wire wea,
+ input wire [AWIDTH-1:0] addra,
+ input wire [DWIDTH-1:0] dia,
+ output wire [DWIDTH-1:0] doa,
+
+ input wire clkb,
+ input wire enb,
+ input wire web,
+ input wire [AWIDTH-1:0] addrb,
+ input wire [DWIDTH-1:0] dib,
+ output wire [DWIDTH-1:0] dob
+);
+
+ generate
+ if (RAM_TYPE == "URAM")
+ ram_2port_impl_uram #(
+ .DWIDTH(DWIDTH), .AWIDTH(AWIDTH), .RW_MODE(RW_MODE),
+ .OUT_REG(OUT_REG), .INIT_FILE(INIT_FILE)
+ ) impl (
+ .clka(clka), .ena(ena), .wea(wea), .addra(addra), .dia(dia), .doa(doa),
+ .clkb(clkb), .enb(enb), .web(web), .addrb(addrb), .dib(dib), .dob(dob)
+ );
+ else if (RAM_TYPE == "BRAM")
+ ram_2port_impl_bram #(
+ .DWIDTH(DWIDTH), .AWIDTH(AWIDTH), .RW_MODE(RW_MODE),
+ .OUT_REG(OUT_REG), .INIT_FILE(INIT_FILE)
+ ) impl (
+ .clka(clka), .ena(ena), .wea(wea), .addra(addra), .dia(dia), .doa(doa),
+ .clkb(clkb), .enb(enb), .web(web), .addrb(addrb), .dib(dib), .dob(dob)
+ );
+ else if (RAM_TYPE == "LUTRAM")
+ ram_2port_impl_lutram #(
+ .DWIDTH(DWIDTH), .AWIDTH(AWIDTH), .RW_MODE(RW_MODE),
+ .OUT_REG(OUT_REG), .INIT_FILE(INIT_FILE)
+ ) impl (
+ .clka(clka), .ena(ena), .wea(wea), .addra(addra), .dia(dia), .doa(doa),
+ .clkb(clkb), .enb(enb), .web(web), .addrb(addrb), .dib(dib), .dob(dob)
+ );
+ else if (RAM_TYPE == "REG")
+ ram_2port_impl_reg #(
+ .DWIDTH(DWIDTH), .AWIDTH(AWIDTH), .RW_MODE(RW_MODE),
+ .OUT_REG(OUT_REG), .INIT_FILE(INIT_FILE)
+ ) impl (
+ .clka(clka), .ena(ena), .wea(wea), .addra(addra), .dia(dia), .doa(doa),
+ .clkb(clkb), .enb(enb), .web(web), .addrb(addrb), .dib(dib), .dob(dob)
+ );
+ else
+ ram_2port_impl_auto #(
+ .DWIDTH(DWIDTH), .AWIDTH(AWIDTH), .RW_MODE(RW_MODE),
+ .OUT_REG(OUT_REG), .INIT_FILE(INIT_FILE)
+ ) impl (
+ .clka(clka), .ena(ena), .wea(wea), .addra(addra), .dia(dia), .doa(doa),
+ .clkb(clkb), .enb(enb), .web(web), .addrb(addrb), .dib(dib), .dob(dob)
+ );
+ endgenerate
+
+endmodule
diff --git a/fpga/usrp3/lib/control/ram_2port_impl.vh b/fpga/usrp3/lib/control/ram_2port_impl.vh
new file mode 100644
index 000000000..8f8f3bab3
--- /dev/null
+++ b/fpga/usrp3/lib/control/ram_2port_impl.vh
@@ -0,0 +1,129 @@
+//
+// Copyright 2011 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Used by ram_2port.v
+// Requires `RAM_MOD_NAME and `RAM_DIRECTIVE to be defined
+
+module `RAM_MOD_NAME #(
+ parameter DWIDTH = 32, // Width of the memory block
+ parameter AWIDTH = 9, // log2 of the depth of the memory block
+ parameter RW_MODE = "READ-FIRST", // Read-write mode {READ-FIRST, WRITE-FIRST, NO-CHANGE}
+ parameter OUT_REG = 0, // Instantiate an output register? (+1 cycle of read latency)
+ parameter INIT_FILE = "" // Optionally initialize memory with this file
+) (
+ input wire clka,
+ input wire ena,
+ input wire wea,
+ input wire [AWIDTH-1:0] addra,
+ input wire [DWIDTH-1:0] dia,
+ output wire [DWIDTH-1:0] doa,
+
+ input wire clkb,
+ input wire enb,
+ input wire web,
+ input wire [AWIDTH-1:0] addrb,
+ input wire [DWIDTH-1:0] dib,
+ output wire [DWIDTH-1:0] dob
+);
+
+ `RAM_DIRECTIVE reg [DWIDTH-1:0] ram [(1<<AWIDTH)-1:0];
+
+ // Initialize ram to a specified file or to all zeros to match hardware
+ generate if (INIT_FILE != "") begin
+ initial
+ $readmemh(INIT_FILE, ram, 0, (1<<AWIDTH)-1);
+ end else begin
+ integer i;
+ initial
+ for (i = 0; i < (1<<AWIDTH); i = i + 1)
+ ram[i] = {DWIDTH{1'b0}};
+ end endgenerate
+
+ reg [DWIDTH-1:0] doa_r = 'h0, dob_r = 'h0;
+ generate if (OUT_REG == 1) begin
+ // A 2 clock cycle read latency with improve clock-to-out timing
+ reg [DWIDTH-1:0] doa_rr = 'h0, dob_rr = 'h0;
+
+ always @(posedge clka)
+ if (ena)
+ doa_rr <= doa_r;
+
+ always @(posedge clkb)
+ if (enb)
+ dob_rr <= dob_r;
+
+ assign doa = doa_rr;
+ assign dob = dob_rr;
+ end else begin
+ // A 1 clock cycle read latency at the cost of a longer clock-to-out timing
+ assign doa = doa_r;
+ assign dob = dob_r;
+ end endgenerate
+
+ generate if (RW_MODE == "READ-FIRST") begin
+ // When data is written, the prior memory contents at the write
+ // address are presented on the output port.
+ always @(posedge clka) begin
+ if (ena) begin
+ if (wea)
+ ram[addra] <= dia;
+ doa_r <= ram[addra];
+ end
+ end
+ always @(posedge clkb) begin
+ if (enb) begin
+ if (web)
+ ram[addrb] <= dib;
+ dob_r <= ram[addrb];
+ end
+ end
+
+ end else if (RW_MODE == "WRITE-FIRST") begin
+ // The data being written to the RAM also resides on the output port.
+ always @(posedge clka) begin
+ if (ena) begin
+ if (wea) begin
+ ram[addra] <= dia;
+ doa_r <= dia;
+ end else begin
+ doa_r <= ram[addra];
+ end
+ end
+ end
+ always @(posedge clkb) begin
+ if (enb) begin
+ if (web) begin
+ ram[addrb] <= dib;
+ dob_r <= dib;
+ end else begin
+ dob_r <= ram[addrb];
+ end
+ end
+ end
+
+ end else begin
+ // This is a no change RAM which retains the last read value on the output during writes
+ // which is the most power efficient mode.
+ always @(posedge clka) begin
+ if (ena) begin
+ if (wea)
+ ram[addra] <= dia;
+ else
+ doa_r <= ram[addra];
+ end
+ end
+ always @(posedge clkb) begin
+ if (enb) begin
+ if (web)
+ ram[addrb] <= dib;
+ else
+ dob_r <= ram[addrb];
+ end
+ end
+
+ end endgenerate
+
+endmodule
diff --git a/fpga/usrp3/lib/control/regport_resp_mux.v b/fpga/usrp3/lib/control/regport_resp_mux.v
new file mode 100644
index 000000000..93048bec8
--- /dev/null
+++ b/fpga/usrp3/lib/control/regport_resp_mux.v
@@ -0,0 +1,46 @@
+//
+// Copyright 2016 Ettus Research
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+module regport_resp_mux #(
+ parameter WIDTH = 32,
+ parameter NUM_SLAVES = 2
+)(
+ input clk,
+ input reset,
+
+ input [NUM_SLAVES-1:0] sla_rd_resp,
+ input [(NUM_SLAVES*WIDTH)-1:0] sla_rd_data,
+
+ output reg mst_rd_resp,
+ output reg [WIDTH-1:0] mst_rd_data
+);
+ // Treat sla_rd_resp as a one-hot bus.
+ // If multiple resp lines are asserted at the same time, then
+ // it is a violation of the register port protocol
+
+ wire [NUM_SLAVES-1:0] bit_options[0:WIDTH-1];
+ wire [WIDTH-1:0] data_out;
+
+ genvar i, b;
+ generate
+ for (b = 0; b < WIDTH; b = b + 1) begin
+ for (i = 0; i < NUM_SLAVES; i = i + 1) begin
+ assign bit_options[b][i] = sla_rd_data[(i*WIDTH)+b];
+ end
+ assign data_out[b] = |(bit_options[b] & sla_rd_resp);
+ end
+ endgenerate
+
+ always @(posedge clk) begin
+ mst_rd_data <= data_out;
+ if (reset)
+ mst_rd_resp <= 1'b0;
+ else
+ mst_rd_resp <= |(sla_rd_resp);
+ end
+
+endmodule
diff --git a/fpga/usrp3/lib/control/regport_to_settingsbus.v b/fpga/usrp3/lib/control/regport_to_settingsbus.v
new file mode 100644
index 000000000..532380ef3
--- /dev/null
+++ b/fpga/usrp3/lib/control/regport_to_settingsbus.v
@@ -0,0 +1,63 @@
+/////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: regport_to_settingsbus
+// Description:
+// Converts regport write bus to the a setting bus
+// ADDRESSING: Set to "WORD" in case of settings bus. The settings bus
+// uses word addressing and hence the address needs to be shifted by
+// to convert to set_addr.
+//
+/////////////////////////////////////////////////////////////////////
+
+module regport_to_settingsbus #(
+ parameter BASE = 14'h0,
+ parameter END_ADDR = 14'h3FFF,
+ parameter DWIDTH = 32,
+ parameter AWIDTH = 14,
+ parameter SR_AWIDTH = 12,
+ // Dealign for settings bus by shifting by 2
+ parameter ADDRESSING = "WORD",
+ parameter SHIFT = $clog2(DWIDTH/8)
+)(
+ input reset,
+ input clk,
+ input reg_wr_req,
+ input [AWIDTH-1:0] reg_wr_addr,
+ input [DWIDTH-1:0] reg_wr_data,
+
+ output reg set_stb,
+ output reg [SR_AWIDTH-1:0] set_addr,
+ output reg [DWIDTH-1:0] set_data
+);
+
+ wire set_stb_int;
+ wire [DWIDTH-1:0] set_data_int;
+ wire [SR_AWIDTH-1:0] set_addr_base;
+ wire [SR_AWIDTH-1:0] set_addr_int;
+
+ // Strobe asserted only when address is between BASE and END ADDR
+ assign set_stb_int = reg_wr_req && (reg_wr_addr >= BASE) && (reg_wr_addr <= END_ADDR);
+ assign set_addr_base = reg_wr_addr - BASE;
+ // Shift by 2 in case of setting bus
+ assign set_addr_int = (ADDRESSING == "WORD") ? {{SHIFT{1'b0}}, set_addr_base[SR_AWIDTH-1:SHIFT]}
+ : set_addr_base[SR_AWIDTH-1:0];
+ assign set_data_int = reg_wr_data;
+
+ // Adding a pipeline stage
+ always @(posedge clk) begin
+ if (reset) begin
+ set_stb <= 'b0;
+ set_addr <= 'h0;
+ set_data <= 'h0;
+ end else begin
+ set_stb <= set_stb_int;
+ set_addr <= set_addr_int;
+ set_data <= set_data_int;
+ end
+ end
+
+endmodule // regport_to_settingsbus
diff --git a/fpga/usrp3/lib/control/regport_to_xbar_settingsbus.v b/fpga/usrp3/lib/control/regport_to_xbar_settingsbus.v
new file mode 100644
index 000000000..be050dd20
--- /dev/null
+++ b/fpga/usrp3/lib/control/regport_to_xbar_settingsbus.v
@@ -0,0 +1,113 @@
+/////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: regport_to_xbar_settingsbus
+// Description:
+// Converts regport to xbar setting bus.
+// The module is designed only for the crossbar. The readback bus for the
+// rfnoc crossbar reads from the same address as it writes to. Also
+// there is an extra cycle delay in read data in the crossbar, which is
+// why the rb_stb needs to be delayed by a cycle.
+//
+// ADDRESSING: Set to "WORD" in case of settings bus. The settings bus
+// uses word addressing and hence the address needs to be shifted by
+// to convert to set_addr.
+//
+/////////////////////////////////////////////////////////////////////
+
+module regport_to_xbar_settingsbus #(
+ parameter BASE = 14'h0,
+ parameter END_ADDR = 14'h3FFF,
+ parameter DWIDTH = 32,
+ parameter AWIDTH = 14,
+ parameter SR_AWIDTH = 12,
+ // Dealign for settings bus by shifting by 2
+ parameter ADDRESSING = "WORD",
+ parameter SHIFT = $clog2(DWIDTH/8)
+ )(
+ input clk,
+ input reset,
+
+ input reg_wr_req,
+ input [AWIDTH-1:0] reg_wr_addr,
+ input [DWIDTH-1:0] reg_wr_data,
+
+ input reg_rd_req,
+ input [AWIDTH-1:0] reg_rd_addr,
+ output [DWIDTH-1:0] reg_rd_data,
+ output reg_rd_resp,
+
+ output set_stb,
+ output [SR_AWIDTH-1:0] set_addr,
+ output [DWIDTH-1:0] set_data,
+
+ output rb_stb,
+ output [SR_AWIDTH-1:0] rb_addr,
+ input [DWIDTH-1:0] rb_data
+);
+
+ reg reg_rd_req_delay;
+ reg reg_rd_req_delay2;
+ wire [AWIDTH-1:0] set_addr_int;
+ reg [AWIDTH-1:0] rb_addr_int;
+
+ always @(posedge clk) begin
+ if (reset) begin
+ reg_rd_req_delay <= 1'b0;
+ reg_rd_req_delay2 <= 1'b0;
+ rb_addr_int <= 'd0;
+ end
+ else if (reg_rd_req) begin
+ rb_addr_int <= reg_rd_addr - BASE;
+ reg_rd_req_delay <= 1'b1;
+ end
+ else if (reg_rd_req_delay) begin
+ reg_rd_req_delay2 <= 1'b1;
+ reg_rd_req_delay <= 1'b0;
+ end
+ // Deassert after two clock cycles
+ else if (reg_rd_req_delay2) begin
+ reg_rd_req_delay <= 1'b0;
+ reg_rd_req_delay2 <= 1'b0;
+ rb_addr_int <= 'd0;
+ end
+ else begin
+ reg_rd_req_delay <= 1'b0;
+ reg_rd_req_delay2 <= 1'b0;
+ rb_addr_int <= 'd0;
+ end
+ end
+
+ // Write mode of settings bus
+ regport_to_settingsbus #(
+ .BASE(BASE),
+ .END_ADDR(END_ADDR),
+ .DWIDTH(DWIDTH),
+ .AWIDTH(AWIDTH),
+ .SR_AWIDTH(SR_AWIDTH),
+ .ADDRESSING(ADDRESSING)
+ ) xbar_write_settings_bus (
+ .clk(clk),
+ .reset(reset),
+ .reg_wr_req(reg_wr_req),
+ .reg_wr_addr(reg_wr_addr),
+ .reg_wr_data(reg_wr_data),
+
+ .set_stb(set_stb),
+ .set_addr(set_addr),
+ .set_data(set_data)
+ );
+
+ assign rb_addr = (ADDRESSING == "WORD") ? {{SHIFT{1'b0}}, rb_addr_int[SR_AWIDTH-1:SHIFT]}
+ : rb_addr_int[SR_AWIDTH-1:0];
+ // Strobe asserted two cycle after read request only when address is between BASE and END ADDR
+ // This is specific to the xbar as the xbar delays read data by an extra clock
+ // cycle to relax timing.
+ assign rb_stb = reg_rd_req_delay2 && (reg_rd_addr >= BASE) && (reg_rd_addr <= END_ADDR);
+ assign reg_rd_resp = rb_stb;
+ assign reg_rd_data = rb_data;
+
+endmodule
diff --git a/fpga/usrp3/lib/control/reset_sync.v b/fpga/usrp3/lib/control/reset_sync.v
new file mode 100644
index 000000000..2e13abfd4
--- /dev/null
+++ b/fpga/usrp3/lib/control/reset_sync.v
@@ -0,0 +1,37 @@
+//
+// Copyright 2011 Ettus Research LLC
+// Copyright 2018-2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// The purpose of this module is to synchronize a reset from one clock domain
+// to another. The reset_in signal must be driven by a glitch-free source.
+//
+
+module reset_sync (
+ // clock for the output reset
+ input clk,
+ // glitch-free input reset
+ input reset_in,
+ // output reset in the clk domain
+ output reg reset_out);
+
+ wire reset_c;
+
+ synchronizer #(
+ // The input reset is async to the output clk domain... so timing should not be
+ // analyzed here!
+ .FALSE_PATH_TO_IN(1),
+ // Assert reset_out by default. When clk starts toggling the downstream logic will
+ // be in reset for at least 10 clk cycles. This allows the clock to settle (if needed)
+ // and the reset to propagate fully to all logic.
+ .INITIAL_VAL(1),
+ .STAGES(10)
+ ) reset_double_sync (
+ .clk(clk), .rst(1'b0), .in(reset_in), .out(reset_c)
+ );
+
+ always @(posedge clk)
+ reset_out <= reset_c;
+
+endmodule // reset_sync
diff --git a/fpga/usrp3/lib/control/s7_icap_wb.v b/fpga/usrp3/lib/control/s7_icap_wb.v
new file mode 100644
index 000000000..114d370eb
--- /dev/null
+++ b/fpga/usrp3/lib/control/s7_icap_wb.v
@@ -0,0 +1,144 @@
+//
+// Copyright 2011-2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+//
+// Refer to SelectMAP and ICAP docs in UG470
+//
+
+module s7_icap_wb
+ (
+ input clk,
+ input reset,
+ input cyc_i,
+ input stb_i,
+ input we_i,
+ output ack_o,
+ input [31:0] dat_i,
+ output [31:0] dat_o
+ );
+
+ reg rdwrb, csib;
+
+
+ reg [2:0] icap_state;
+ localparam ICAP_IDLE = 0;
+ localparam ICAP_WR0 = 1;
+ localparam ICAP_WR1 = 2;
+ localparam ICAP_RD0 = 3;
+ localparam ICAP_RD1 = 4;
+
+ localparam IDLE = 1'b1;
+ localparam ACTIVE = 1'b0;
+ localparam READ = 1'b1;
+ localparam WRITE = 1'b0;
+
+
+ always @(posedge clk)
+ if(reset) begin
+ rdwrb <= READ;
+ csib <= IDLE;
+ icap_state <= ICAP_IDLE;
+ end
+ else
+ case(icap_state)
+ //
+ // In IDLE state waiting for a READ or WRITE to be signalled from the WB bus.
+ // (In this state rdwrb can flip state without effect because ICAP is not selected)
+ //
+ ICAP_IDLE :
+ begin
+ if(stb_i & cyc_i) begin
+ if(we_i) begin
+ // Start WRITE, assert RDWR_B LOW whilst CSI_B remains HIGH.
+ rdwrb <= WRITE;
+ csib <= IDLE;
+ icap_state <= ICAP_WR0;
+ end else begin
+ // Start READ
+ rdwrb <= READ;
+ csib <= IDLE;
+ icap_state <= ICAP_RD0;
+ end
+ end else begin
+ // Stay IDLE
+ rdwrb <= READ;
+ csib <= IDLE;
+ icap_state <= ICAP_IDLE;
+ end
+ end // case: ICAP_IDLE
+ //
+ // First cycle of WRITE.
+ // Next cycle assert RDWR_B LOW and assert CSI_B LOW.
+ //
+ ICAP_WR0 : begin
+ rdwrb <= WRITE;
+ csib <= ACTIVE;
+ icap_state <= ICAP_WR1;
+ end
+ //
+ // Second cycle of WRITE.
+ // Next cycle assert RDWR_B LOW and assert CSI_B HIGH whilst transitioning to IDLE state
+ //
+ ICAP_WR1 : begin
+ rdwrb <= WRITE;
+ csib <= IDLE;
+ icap_state <= ICAP_IDLE;
+ end
+ //
+ // First cycle of READ.
+ // Next cycle assert RDWR_B HIGH and assert CSI_B LOW.
+ //
+ ICAP_RD0 : begin
+ rdwrb <= READ;
+ csib <= ACTIVE;
+ icap_state <= ICAP_WR1;
+ end
+ //
+ // Second cycle of READ.
+ // Next cycle assert RDWR_B HIGH and assert CSI_B HIGH whilst transitioning to IDLE state
+ //
+ ICAP_RD1 : begin
+ rdwrb <= READ;
+ csib <= IDLE;
+ icap_state <= ICAP_IDLE;
+ end
+
+ endcase // case (icap_state)
+
+ assign ack_o = (icap_state == ICAP_WR1) | (icap_state == ICAP_RD1);
+ //assign debug_out = {17'd0, BUSY, dat_i[7:0], ~CE, ICAPCLK, ~WRITE, icap_state};
+
+
+ ICAPE2 #(
+ .DEVICE_ID(32'h03651093),
+ .ICAP_WIDTH("X32"),
+ .SIM_CFG_FILE_NAME("NONE")
+ )
+ ICAPE2_inst (
+ .O(/*dat_o[31:0]*/),
+ .CLK(clk), // Rising edge referenced for both reads and writes.
+ .CSIB(csib), // CSIB = 0 to select ICAP
+ .I(dat_i[31:0]), // Bitswaped as per SELECTMAP (See UG470 page 40)
+ .RDWRB(rdwrb) // RDWB = 0 for WRITE, = 1 for READ
+ );
+
+ assign dat_0 = 32'h0;
+
+endmodule // s3a_icap_wb
diff --git a/fpga/usrp3/lib/control/serial_to_settings.v b/fpga/usrp3/lib/control/serial_to_settings.v
new file mode 100644
index 000000000..180191fa5
--- /dev/null
+++ b/fpga/usrp3/lib/control/serial_to_settings.v
@@ -0,0 +1,126 @@
+//
+// Copyright 2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+module serial_to_settings
+ (
+ input clk,
+ input reset,
+ // Serial signals (async)
+ input scl,
+ input sda,
+ // Settngs bus out
+ output reg set_stb,
+ output reg [7:0] set_addr,
+ output reg [31:0] set_data,
+ // Debug
+ output [31:0] debug
+ );
+
+ reg [2:0] state;
+
+ localparam SEARCH = 3'h0;
+ localparam ADDRESS = 3'h1;
+ localparam DATA = 3'h2;
+ localparam STOP1 = 3'h3;
+ localparam STOP2 = 3'h4;
+
+ reg scl_pre_reg, scl_reg, scl_reg2;
+ reg sda_pre_reg, sda_reg, sda_reg2;
+ reg [4:0] counter;
+
+
+ always @(posedge clk) begin
+ scl_reg2 <= scl_reg;
+ scl_reg <= scl_pre_reg;
+ scl_pre_reg <= scl;
+ sda_reg2 <= sda_reg;
+ sda_reg <= sda_pre_reg;
+ sda_pre_reg <= sda;
+ end
+
+
+ always @(posedge clk)
+ if (reset) begin
+ state <= SEARCH;
+ counter <= 0;
+ set_addr <= 0;
+ set_data <= 0;
+ set_stb <= 0;
+ end else begin
+ case(state)
+ //
+ // Search for I2C like start indication: SDA goes low whilst clock is high.
+ //
+ SEARCH: begin
+ set_stb <= 0;
+ // Look for START.
+ if (scl_reg && scl_reg2 && !sda_reg && sda_reg2) begin
+ state <= ADDRESS;
+ counter <= 0;
+ end
+ end
+ //
+ // Count 8 Address bits.
+ // Master changes SDA on falling edge of SCL, we sample on the rising edge.
+ //
+ ADDRESS: begin
+ if (scl_reg && !scl_reg2) begin
+ set_addr[7:0] <= {set_addr[6:0],sda_reg};
+ if (counter == 7) begin
+ state <= DATA;
+ counter <= 0;
+ end else
+ counter <= counter + 1;
+ end
+ end
+ //
+ // Count 32 data bits.
+ // Master changes SDA on falling edge of SCL, we sample on the rising edge.
+ //
+ DATA: begin
+ if (scl_reg && !scl_reg2) begin
+ set_data[31:0] <= {set_data[30:0],sda_reg};
+ if (counter == 31) begin
+ state <= STOP1;
+ counter <= 0;
+ end else
+ counter <= counter + 1;
+ end
+ end
+ //
+ // Looks for rising SCL edge before STOP bit.
+ //
+ STOP1: begin
+ if (scl_reg && !scl_reg2) begin
+ state <= STOP2;
+ end
+ end
+ //
+ // Looks for STOP bit
+ //
+ STOP2: begin
+ if (scl_reg && scl_reg2 && sda_reg && !sda_reg2) begin
+ state <= SEARCH;
+ counter <= 0;
+ set_stb <= 1;
+ end
+ end
+
+ endcase // case(state)
+ end // else: !if(reset)
+
+ assign debug =
+ {
+ counter[4:0],
+ state[2:0],
+ scl_reg,
+ sda_reg
+ };
+
+
+
+endmodule // serial_to_settings
diff --git a/fpga/usrp3/lib/control/serial_to_settings_tb.v b/fpga/usrp3/lib/control/serial_to_settings_tb.v
new file mode 100644
index 000000000..d3688f216
--- /dev/null
+++ b/fpga/usrp3/lib/control/serial_to_settings_tb.v
@@ -0,0 +1,86 @@
+//
+// Copyright 2014 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+module serial_to_settings_tb();
+
+
+
+ reg clk;
+ reg reset;
+
+ wire scl;
+ wire sda;
+ wire set_stb;
+ wire [7:0] set_addr;
+ wire [31:0] set_data;
+
+ //
+ // These registers optionaly used
+ // to drive nets through procedural assignments in test bench.
+ // These drivers default to tri-stated.
+ //
+ reg scl_r;
+ reg sda_r;
+
+ assign scl = scl_r;
+ assign sda = sda_r;
+
+ initial
+ begin
+ scl_r <= 1'bz;
+ sda_r <= 1'bz;
+ end
+
+
+
+ serial_to_settings serial_to_settings_i
+ (
+ .clk(clk),
+ .reset(reset),
+ // Serial signals (async)
+ .scl(scl),
+ .sda(sda),
+ // Settngs bus out
+ .set_stb(set_stb),
+ .set_addr(set_addr),
+ .set_data(set_data)
+ );
+
+ // Nasty HAck to convert settings to wishbone crudely.
+ reg wb_stb;
+ wire wb_ack_o;
+
+
+ always @(posedge clk)
+ if (reset)
+ wb_stb <= 0;
+ else
+ wb_stb <= set_stb ? 1 : ((wb_ack_o) ? 0 : wb_stb);
+
+ simple_uart debug_uart
+ (
+ .clk_i(clk),
+ .rst_i(reset),
+ .we_i(wb_stb),
+ .stb_i(wb_stb),
+ .cyc_i(wb_stb),
+ .ack_o(wb_ack_o),
+ .adr_i(set_addr[2:0]),
+ .dat_i(set_data[31:0]),
+ .dat_o(),
+ .rx_int_o(),
+ .tx_int_o(),
+ .tx_o(txd),
+ .rx_i(rxd),
+ .baud_o()
+ );
+
+ //
+ // Bring in a simulation script here
+ //
+ `include "simulation_script.v"
+
+endmodule
diff --git a/fpga/usrp3/lib/control/setting_reg.v b/fpga/usrp3/lib/control/setting_reg.v
new file mode 100644
index 000000000..09bf66286
--- /dev/null
+++ b/fpga/usrp3/lib/control/setting_reg.v
@@ -0,0 +1,38 @@
+//
+// Copyright 2011-2012 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+
+//----------------------------------------------------------------------
+//-- A settings register is a peripheral for the settings register bus.
+//-- When the settings register sees strobe abd a matching address,
+//-- the outputs will be become registered to the given input bus.
+//----------------------------------------------------------------------
+
+module setting_reg
+ #(parameter my_addr = 0,
+ parameter awidth = 8,
+ parameter width = 32,
+ parameter at_reset=0)
+ (input clk, input rst, input strobe, input wire [awidth-1:0] addr,
+ input wire [31:0] in, output reg [width-1:0] out, output reg changed);
+
+ always @(posedge clk)
+ if(rst)
+ begin
+ out <= at_reset;
+ changed <= 1'b0;
+ end
+ else
+ if(strobe & (my_addr==addr))
+ begin
+ out <= in[width-1:0];
+ changed <= 1'b1;
+ end
+ else
+ changed <= 1'b0;
+
+endmodule // setting_reg
diff --git a/fpga/usrp3/lib/control/settings_bus_mux.v b/fpga/usrp3/lib/control/settings_bus_mux.v
new file mode 100644
index 000000000..8d120c9c7
--- /dev/null
+++ b/fpga/usrp3/lib/control/settings_bus_mux.v
@@ -0,0 +1,44 @@
+//
+// Copyright 2016 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Mux multiple settings buses
+
+module settings_bus_mux #(
+ parameter PRIO=0, // 0 = Round robin, 1 = Lower ports get priority (see axi_mux)
+ parameter AWIDTH=8,
+ parameter DWIDTH=32,
+ parameter FIFO_SIZE=1,
+ parameter NUM_BUSES=2)
+(
+ input clk, input reset, input clear,
+ input [NUM_BUSES-1:0] in_set_stb, input [NUM_BUSES*AWIDTH-1:0] in_set_addr, input [NUM_BUSES*DWIDTH-1:0] in_set_data,
+ output out_set_stb, output [AWIDTH-1:0] out_set_addr, output [DWIDTH-1:0] out_set_data, input ready
+);
+
+ wire [NUM_BUSES*(AWIDTH+DWIDTH)-1:0] i_tdata;
+ generate
+ if(NUM_BUSES <= 1) begin
+ assign out_set_stb = in_set_stb;
+ assign out_set_addr = in_set_addr;
+ assign out_set_data = in_set_data;
+ end else begin
+ genvar i;
+ for (i = 0; i < NUM_BUSES; i = i + 1) begin
+ assign i_tdata[(i+1)*(AWIDTH+DWIDTH)-1:i*(AWIDTH+DWIDTH)] = {in_set_addr[(i+1)*AWIDTH-1:i*AWIDTH],in_set_data[(i+1)*DWIDTH-1:i*DWIDTH]};
+ end
+ axi_mux #(
+ .PRIO(PRIO),
+ .WIDTH(AWIDTH+DWIDTH),
+ .PRE_FIFO_SIZE($clog2(NUM_BUSES)),
+ .POST_FIFO_SIZE(FIFO_SIZE),
+ .SIZE(NUM_BUSES))
+ axi_mux (
+ .clk(clk), .reset(reset), .clear(clear),
+ .i_tdata(i_tdata), .i_tlast({NUM_BUSES{1'b1}}), .i_tvalid(in_set_stb), .i_tready(),
+ .o_tdata({out_set_addr,out_set_data}), .o_tlast(), .o_tvalid(out_set_stb), .o_tready(ready));
+ end
+ endgenerate
+endmodule \ No newline at end of file
diff --git a/fpga/usrp3/lib/control/settings_bus_timed_2clk.v b/fpga/usrp3/lib/control/settings_bus_timed_2clk.v
new file mode 100644
index 000000000..2b62ffd26
--- /dev/null
+++ b/fpga/usrp3/lib/control/settings_bus_timed_2clk.v
@@ -0,0 +1,170 @@
+//
+// Copyright 2018 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: settings_bus_timed_2clk
+// Description:
+// - Stores settings bus transaction in a FIFO and
+// releases them based on VITA time input
+// - Also moves the settings bus to the timebase
+// clock domain
+//
+
+module settings_bus_timed_2clk #(
+ parameter SR_AWIDTH = 8,
+ parameter SR_DWIDTH = 32,
+ parameter RB_AWIDTH = 8,
+ parameter RB_DWIDTH = 64,
+ parameter TIMED_CMDS_EN = 0
+) (
+ input sb_clk, // Settings bus clock
+ input sb_rst, // Reset (sb_clk)
+ input tb_clk, // Timebase clock
+ input tb_rst, // Reset (tb_clk)
+
+ input [63:0] vita_time, // Current timebase time
+
+ input s_set_stb, // Settings bus strobe
+ input [SR_AWIDTH-1:0] s_set_addr, // Settings address
+ input [SR_DWIDTH-1:0] s_set_data, // Settings data
+ input s_set_has_time, // Is this a timed command?
+ input [63:0] s_set_time, // Command time
+ output s_set_pending, // Is settings transaction pending?
+ input [RB_AWIDTH-1:0] s_rb_addr, // Readback address
+ output s_rb_stb, // Readback data strobe
+ output [RB_DWIDTH-1:0] s_rb_data, // Readback data value
+
+ output m_set_stb, // Settings bus strobe
+ output [SR_AWIDTH-1:0] m_set_addr, // Settings address
+ output [SR_DWIDTH-1:0] m_set_data, // Settings data
+ output m_set_has_time, // Is this a timed command?
+ output [63:0] m_set_time, // Command time
+ input m_set_pending, // Is settings transaction pending?
+ output [RB_AWIDTH-1:0] m_rb_addr, // Readback address
+ input m_rb_stb, // Readback data strobe
+ input [RB_DWIDTH-1:0] m_rb_data // Readback data value
+);
+
+ // States for input and output state machines
+ localparam [2:0] ST_IDLE = 3'd0; // Nothing is happening on the bus
+ localparam [2:0] ST_SET_ISSUED = 3'd1; // A settings transaction has been issued
+ localparam [2:0] ST_SET_PENDING = 3'd2; // A settings transaction is pending
+ localparam [2:0] ST_RB_PENDING = 3'd3; // Waiting for readback data
+ localparam [2:0] ST_RB_DONE = 3'd4; // Readback data is valid
+
+ wire rb_valid;
+
+ // Input state machine
+ reg [2:0] in_state = ST_IDLE;
+ always @(posedge sb_clk) begin
+ if (sb_rst) begin
+ in_state <= ST_IDLE;
+ end else begin
+ case (in_state)
+ ST_IDLE: begin
+ if (s_set_stb) begin
+ in_state <= ST_SET_PENDING;
+ end
+ end
+ ST_SET_PENDING: begin
+ if (rb_valid) begin
+ in_state <= ST_RB_DONE;
+ end
+ end
+ ST_RB_DONE: begin
+ in_state <= ST_IDLE;
+ end
+ default: begin
+ in_state <= ST_IDLE;
+ end
+ endcase
+ end
+ end
+ assign s_set_pending = (in_state == ST_SET_PENDING);
+ assign s_rb_stb = (in_state == ST_RB_DONE);
+
+ // Clock crossing FIFO (settings)
+ // TODO: Look into a more efficient implementation for a single element
+ // clock crossing FIFO.
+ wire set_pending, set_finished;
+ axi_fifo_2clk #(
+ .WIDTH(SR_AWIDTH+SR_DWIDTH+1+64+RB_AWIDTH), .SIZE(0)
+ ) sb_2clk_fifo_i (
+ .i_aclk(sb_clk), .reset(sb_rst),
+ .i_tdata({s_set_addr, s_set_data, s_set_has_time, s_set_time, s_rb_addr}),
+ .i_tvalid(s_set_stb), .i_tready(/* Ignored: FIFO may not have an exact size*/),
+ .o_aclk(tb_clk),
+ .o_tdata({m_set_addr, m_set_data, m_set_has_time, m_set_time, m_rb_addr}),
+ .o_tvalid(set_pending), .o_tready(set_finished)
+ );
+
+ // Time compare logic
+ // If ~has_time then pass the transaction through, otherwise wait for time
+ // to tick up to command time
+ wire now, late;
+ wire go = ((TIMED_CMDS_EN == 1) && m_set_has_time) ? (now | late) : 1'b1;
+
+ // If this is a timed command then vita_time == m_set_time one cycle before
+ // strobe is asserted i.e. timed strobe assertion has a one cycle latency
+ time_compare time_compare (
+ .clk(tb_clk), .reset(tb_rst),
+ .time_now(vita_time), .trigger_time(m_set_time),
+ .now(now), .early(), .late(late), .too_early()
+ );
+
+ // Clock crossing FIFO (readback)
+ reg [RB_DWIDTH-1:0] cached_rb_data;
+ axi_fifo_2clk #(
+ .WIDTH(RB_DWIDTH), .SIZE(0)
+ ) rbdata_2clk_fifo_i (
+ .reset(tb_rst),
+ .i_aclk(tb_clk), .i_tdata(cached_rb_data), .i_tvalid(set_finished), .i_tready(),
+ .o_aclk(sb_clk), .o_tdata(s_rb_data), .o_tvalid(rb_valid), .o_tready(s_rb_stb)
+ );
+
+ // Output state machine
+ reg [2:0] out_state = ST_IDLE;
+ always @(posedge tb_clk) begin
+ if (tb_rst) begin
+ out_state <= ST_IDLE;
+ end else begin
+ case (out_state)
+ ST_IDLE: begin
+ if (go & set_pending) begin
+ out_state <= ST_SET_ISSUED;
+ end
+ end
+ ST_SET_ISSUED: begin
+ out_state <= ST_SET_PENDING;
+ end
+ ST_SET_PENDING: begin
+ if (~m_set_pending) begin
+ if (m_rb_stb) begin
+ out_state <= ST_RB_DONE;
+ cached_rb_data <= m_rb_data;
+ end else begin
+ out_state <= ST_RB_PENDING;
+ end
+ end
+ end
+ ST_RB_PENDING: begin
+ if (m_rb_stb) begin
+ out_state <= ST_RB_DONE;
+ cached_rb_data <= m_rb_data;
+ end
+ end
+ ST_RB_DONE: begin
+ out_state <= ST_IDLE;
+ end
+ default: begin
+ out_state <= ST_IDLE;
+ end
+ endcase
+ end
+ end
+
+ assign m_set_stb = (out_state == ST_SET_ISSUED);
+ assign set_finished = (out_state == ST_RB_DONE);
+
+endmodule
diff --git a/fpga/usrp3/lib/control/simple_i2c_core.v b/fpga/usrp3/lib/control/simple_i2c_core.v
new file mode 100644
index 000000000..fbbd2e5a1
--- /dev/null
+++ b/fpga/usrp3/lib/control/simple_i2c_core.v
@@ -0,0 +1,107 @@
+//
+// Copyright 2012 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+
+// Simple I2C core
+
+// Settings reg map:
+//
+// BASE+0 control register
+// byte0 - control bits, data byte, or command bits, prescaler
+// byte1 - what to do? (documented in cpp file)
+// write prescaler lo
+// write prescaler hi
+// write control
+// write data
+// write command
+// read data
+// read status
+//
+
+// Readback:
+//
+// byte0 has readback value based on the last read command
+//
+
+module simple_i2c_core
+ #(
+ //settings register base address
+ parameter BASE = 0,
+
+ //i2c line level at reset
+ parameter ARST_LVL = 1
+ )
+ (
+ //clock and synchronous reset
+ input clock, input reset,
+
+ //32-bit settings bus inputs
+ input set_stb, input [7:0] set_addr, input [31:0] set_data,
+
+ //32-bit data readback
+ output reg [31:0] readback,
+
+ //read is high when i2c core can begin another transaction
+ output reg ready,
+
+ // I2C signals
+ // i2c clock line
+ input scl_pad_i, // SCL-line input
+ output scl_pad_o, // SCL-line output (always 1'b0)
+ output scl_padoen_o, // SCL-line output enable (active low)
+
+ // i2c data line
+ input sda_pad_i, // SDA-line input
+ output sda_pad_o, // SDA-line output (always 1'b0)
+ output sda_padoen_o, // SDA-line output enable (active low)
+
+ //optional debug output
+ output [31:0] debug
+ );
+
+ //declare command settings register
+ wire [7:0] sr_what, sr_data;
+ wire sr_changed;
+ setting_reg #(.my_addr(BASE+0),.width(16)) i2c_cmd_sr(
+ .clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
+ .out({sr_what, sr_data}),.changed(sr_changed));
+
+ //declare wb interface signals
+ wire [2:0] wb_addr;
+ wire [7:0] wb_data_mosi;
+ wire [7:0] wb_data_miso;
+ wire wb_we, wb_stb, wb_cyc;
+ wire wb_ack;
+
+ //create wishbone-based i2c core
+ i2c_master_top #(.ARST_LVL(ARST_LVL)) i2c
+ (.wb_clk_i(clock),.wb_rst_i(reset),.arst_i(1'b0),
+ .wb_adr_i(wb_addr),.wb_dat_i(wb_data_mosi),.wb_dat_o(wb_data_miso),
+ .wb_we_i(wb_we),.wb_stb_i(wb_stb),.wb_cyc_i(wb_cyc),
+ .wb_ack_o(wb_ack),.wb_inta_o(),
+ .scl_pad_i(scl_pad_i),.scl_pad_o(scl_pad_o),.scl_padoen_o(scl_padoen_o),
+ .sda_pad_i(sda_pad_i),.sda_pad_o(sda_pad_o),.sda_padoen_o(sda_padoen_o) );
+
+ //not ready between setting register and wishbone ack
+ always @(posedge clock) begin
+ if (reset || wb_ack) ready <= 1;
+ else if (sr_changed) ready <= 0;
+ end
+
+ //register wishbone data on every ack
+ always @(posedge clock) begin
+ if (wb_ack) readback <= {24'b0, wb_data_miso};
+ end
+
+ //assign wishbone signals
+ assign wb_addr = sr_what[2:0];
+ assign wb_stb = sr_changed;
+ assign wb_we = wb_stb && sr_what[3];
+ assign wb_cyc = wb_stb;
+ assign wb_data_mosi = sr_data;
+
+endmodule //simple_i2c_core
diff --git a/fpga/usrp3/lib/control/simple_spi_core.v b/fpga/usrp3/lib/control/simple_spi_core.v
new file mode 100644
index 000000000..dd94b1b2d
--- /dev/null
+++ b/fpga/usrp3/lib/control/simple_spi_core.v
@@ -0,0 +1,229 @@
+//
+// Copyright 2012 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+
+// Simple SPI core, the simplest, yet complete spi core I can think of
+
+// Settings register controlled.
+// 2 settings regs, control and data
+// 1 32-bit readback and status signal
+
+// Settings reg map:
+//
+// BASE+0 divider setting
+// bits [15:0] spi clock divider
+//
+// BASE+1 configuration input
+// bits [23:0] slave select, bit0 = slave0 enabled
+// bits [29:24] num bits (1 through 32)
+// bit [30] data input edge = in data bit latched on rising edge of clock
+// bit [31] data output edge = out data bit latched on rising edge of clock
+//
+// BASE+2 input data
+// Writing this register begins a spi transaction.
+// Bits are latched out from bit 0.
+// Therefore, load this register in reverse.
+//
+// Readback
+// Bits are latched into bit 0.
+// Therefore, data will be in-order.
+
+module simple_spi_core
+ #(
+ //settings register base address
+ parameter BASE = 0,
+
+ //width of serial enables (up to 24 is possible)
+ parameter WIDTH = 8,
+
+ //idle state of the spi clock
+ parameter CLK_IDLE = 0,
+
+ //idle state of the serial enables
+ parameter SEN_IDLE = 24'hffffff
+ )
+ (
+ //clock and synchronous reset
+ input clock, input reset,
+
+ //32-bit settings bus inputs
+ input set_stb, input [7:0] set_addr, input [31:0] set_data,
+
+ //32-bit data readback
+ output [31:0] readback,
+ output reg readback_stb,
+
+ //read is high when spi core can begin another transaction
+ output ready,
+
+ //spi interface, slave selects, clock, data in, data out
+ output [WIDTH-1:0] sen,
+ output sclk,
+ output reg mosi,
+ input miso,
+
+ //optional debug output
+ output [31:0] debug
+ );
+
+ wire [15:0] sclk_divider;
+ setting_reg #(.my_addr(BASE+0),.width(16)) divider_sr(
+ .clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
+ .out(sclk_divider),.changed());
+
+ wire [23:0] slave_select;
+ wire [5:0] num_bits;
+ wire datain_edge, dataout_edge;
+ setting_reg #(.my_addr(BASE+1),.width(32)) ctrl_sr(
+ .clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
+ .out({dataout_edge, datain_edge, num_bits, slave_select}),.changed());
+
+ wire [31:0] mosi_data;
+ wire trigger_spi;
+ setting_reg #(.my_addr(BASE+2),.width(32)) data_sr(
+ .clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
+ .out(mosi_data),.changed(trigger_spi));
+
+ localparam WAIT_TRIG = 0;
+ localparam PRE_IDLE = 1;
+ localparam CLK_REG = 2;
+ localparam CLK_INV = 3;
+ localparam POST_IDLE = 4;
+ localparam IDLE_SEN = 5;
+
+ reg [2:0] state;
+
+ reg ready_reg;
+ assign ready = ready_reg && ~trigger_spi;
+
+ //serial clock either idles or is in one of two clock states
+ reg sclk_reg;
+ assign sclk = sclk_reg;
+
+ //serial enables either idle or enabled based on state
+ // IJB. One pipeline stage to break critical path from register in I/O pads.
+ wire sen_is_idle = (state == WAIT_TRIG) || (state == IDLE_SEN);
+ wire [23:0] sen24 = (sen_is_idle)? SEN_IDLE : (SEN_IDLE ^ slave_select);
+ reg [WIDTH-1:0] sen_reg = SEN_IDLE;
+ always @(posedge clock) begin
+ if (reset) begin
+ sen_reg <= SEN_IDLE;
+ end else begin
+ sen_reg <= sen24[WIDTH-1:0];
+ end
+ end
+ assign sen = sen_reg;
+
+ //data output shift register
+ // IJB. One pipeline stage to break critical path from register in I/O pads.
+ reg [31:0] dataout_reg;
+ wire [31:0] dataout_next = {dataout_reg[30:0], 1'b0};
+
+ always @(posedge clock)
+ mosi <= dataout_reg[31];
+
+ //data input shift register
+ // IJB. Two pipeline stages to break critical path from register in I/O pads.
+ reg miso_pipe, miso_pipe2;
+ always @(posedge clock) begin
+ miso_pipe2 <= miso;
+ miso_pipe <= miso_pipe2;
+ end
+
+ reg [31:0] datain_reg;
+ wire [31:0] datain_next = {datain_reg[30:0], miso_pipe};
+ assign readback = datain_reg;
+
+ //counter for spi clock
+ reg [15:0] sclk_counter;
+ wire sclk_counter_done = (sclk_counter == sclk_divider);
+ wire [15:0] sclk_counter_next = (sclk_counter_done)? 0 : sclk_counter + 1;
+
+ //counter for latching bits miso/mosi
+ reg [6:0] bit_counter;
+ wire [6:0] bit_counter_next = bit_counter + 1;
+ wire bit_counter_done = (bit_counter_next == num_bits);
+
+ always @(posedge clock) begin
+ if (reset) begin
+ state <= WAIT_TRIG;
+ sclk_reg <= CLK_IDLE;
+ ready_reg <= 0;
+ readback_stb <= 1'b0;
+ end
+ else begin
+ case (state)
+
+ WAIT_TRIG: begin
+ if (trigger_spi) state <= PRE_IDLE;
+ readback_stb <= 1'b0;
+ ready_reg <= ~trigger_spi;
+ dataout_reg <= mosi_data;
+ sclk_counter <= 0;
+ bit_counter <= 0;
+ sclk_reg <= CLK_IDLE;
+ end
+
+ PRE_IDLE: begin
+ if (sclk_counter_done) state <= CLK_REG;
+ sclk_counter <= sclk_counter_next;
+ sclk_reg <= CLK_IDLE;
+ end
+
+ CLK_REG: begin
+ if (sclk_counter_done) begin
+ state <= CLK_INV;
+ if (datain_edge != CLK_IDLE) datain_reg <= datain_next;
+ if (dataout_edge != CLK_IDLE && bit_counter != 0) dataout_reg <= dataout_next;
+ sclk_reg <= ~CLK_IDLE; //transition to rising when CLK_IDLE == 0
+ end
+ sclk_counter <= sclk_counter_next;
+ end
+
+ CLK_INV: begin
+ if (sclk_counter_done) begin
+ state <= (bit_counter_done)? POST_IDLE : CLK_REG;
+ bit_counter <= bit_counter_next;
+ if (datain_edge == CLK_IDLE) datain_reg <= datain_next;
+ if (dataout_edge == CLK_IDLE && ~bit_counter_done) dataout_reg <= dataout_next;
+ sclk_reg <= CLK_IDLE; //transition to falling when CLK_IDLE == 0
+ end
+ sclk_counter <= sclk_counter_next;
+ end
+
+ POST_IDLE: begin
+ if (sclk_counter_done) state <= IDLE_SEN;
+ sclk_counter <= sclk_counter_next;
+ sclk_reg <= CLK_IDLE;
+ end
+
+ IDLE_SEN: begin
+ if (sclk_counter_done) begin
+ ready_reg <= 1'b1;
+ readback_stb <= 1'b1;
+ state <= WAIT_TRIG;
+ end
+ sclk_counter <= sclk_counter_next;
+ sclk_reg <= CLK_IDLE;
+ end
+
+ default: state <= WAIT_TRIG;
+
+ endcase //state
+ end
+ end
+
+ assign debug = {
+ trigger_spi, state, //4
+ sclk, mosi, miso, ready, //4
+ //sen[7:0], //8
+ 1'b0, bit_counter[6:0], //8
+ sclk_counter_done, bit_counter_done, //2
+ sclk_counter[5:0] //6
+ };
+
+endmodule //simple_spi_core
diff --git a/fpga/usrp3/lib/control/synchronizer.v b/fpga/usrp3/lib/control/synchronizer.v
new file mode 100644
index 000000000..177b1219c
--- /dev/null
+++ b/fpga/usrp3/lib/control/synchronizer.v
@@ -0,0 +1,50 @@
+//
+// Copyright 2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+module synchronizer #(
+ parameter WIDTH = 1,
+ parameter STAGES = 2,
+ parameter INITIAL_VAL = 0,
+ parameter FALSE_PATH_TO_IN = 1
+)(
+ input clk,
+ input rst,
+ input [WIDTH-1:0] in,
+ output [WIDTH-1:0] out
+);
+
+ //Q: Why do we have a separate impl and instantiate
+ //it with a different instance name based on this
+ //arbitrary parameter FALSE_PATH_TO_IN?
+ //A: To make constraining these synchronizers easier.
+ //We would like to write a single false path constraint
+ //for all synchronizers when the input is truly async.
+ //However other cases might require constraining the input
+ //of this module.
+ //To enable this, all clients that hook up async signals to
+ //the "in" port can set FALSE_PATH_TO_IN=1 (or use the default)
+ //and all clients that want the "in" delay to be constrained can
+ //set FALSE_PATH_TO_IN=0.
+ //In the XDC we can write the following async constraint:
+ //set_false_path -to [get_pins */synchronizer_false_path/stages[0].value_reg[0]/D]
+ //and this will take care of all instances of this module with FALSE_PATH_TO_IN==1
+
+ generate if (FALSE_PATH_TO_IN == 1) begin
+ synchronizer_impl #(
+ .WIDTH(WIDTH), .STAGES(STAGES), .INITIAL_VAL(INITIAL_VAL)
+ ) synchronizer_false_path (
+ .clk(clk), .rst(rst), .in(in), .out(out)
+ );
+ end else begin
+ synchronizer_impl #(
+ .WIDTH(WIDTH), .STAGES(STAGES), .INITIAL_VAL(INITIAL_VAL)
+ ) synchronizer_constrained (
+ .clk(clk), .rst(rst), .in(in), .out(out)
+ );
+ end endgenerate
+
+endmodule //synchronizer
diff --git a/fpga/usrp3/lib/control/synchronizer_impl.v b/fpga/usrp3/lib/control/synchronizer_impl.v
new file mode 100644
index 000000000..1c4f77707
--- /dev/null
+++ b/fpga/usrp3/lib/control/synchronizer_impl.v
@@ -0,0 +1,47 @@
+//
+// Copyright 2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+module synchronizer_impl #(
+ parameter WIDTH = 1,
+ parameter STAGES = 2,
+ parameter INITIAL_VAL = 0
+)(
+ input clk,
+ input rst,
+ input [WIDTH-1:0] in,
+ output [WIDTH-1:0] out
+);
+
+ (* ASYNC_REG = "TRUE" *) reg [WIDTH-1:0] value[0:STAGES-1];
+
+ integer k;
+ initial begin
+ for (k = 0; k < STAGES; k = k + 1) begin
+ value[k] = INITIAL_VAL;
+ end
+ end
+
+ genvar i;
+ generate
+ for (i=0; i<STAGES; i=i+1) begin: stages
+ always @(posedge clk) begin
+ if (rst) begin
+ value[i] <= INITIAL_VAL;
+ end else begin
+ if (i == 0) begin
+ value[i] <= in;
+ end else begin
+ value[i] <= value[i-1];
+ end
+ end
+ end
+ end
+ endgenerate
+
+ assign out = value[STAGES-1];
+
+endmodule //synchronizer_impl
diff --git a/fpga/usrp3/lib/control/user_settings.v b/fpga/usrp3/lib/control/user_settings.v
new file mode 100644
index 000000000..6f4641b33
--- /dev/null
+++ b/fpga/usrp3/lib/control/user_settings.v
@@ -0,0 +1,66 @@
+//
+// Copyright 2012 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+// User settings bus
+//
+// Provides 8-bit address, 32-bit data write only bus for user settings, consumes to addresses in
+// normal settings bus.
+//
+// Write user address to BASE
+// Write user data to BASE+1
+//
+// The user_set_stb will strobe after data write, must write new address even if same as previous one.
+
+module user_settings
+ #(parameter BASE=0)
+ (input clk,
+ input rst,
+
+ input set_stb,
+ input [7:0] set_addr,
+ input [31:0] set_data,
+
+ output set_stb_user,
+ output [7:0] set_addr_user,
+ output [31:0] set_data_user
+ );
+
+ wire addr_changed, data_changed;
+ reg stb_int;
+
+ setting_reg #(.my_addr(BASE+0),.width(8)) sr_0
+ (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),
+ .in(set_data),.out(set_addr_user),.changed(addr_changed) );
+
+ setting_reg #(.my_addr(BASE+1)) sr_1
+ (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),
+ .in(set_data),.out(set_data_user),.changed(data_changed) );
+
+ always @(posedge clk)
+ if (rst|set_stb_user)
+ stb_int <= 0;
+ else
+ if (addr_changed)
+ stb_int <= 1;
+
+ assign set_stb_user = stb_int & data_changed;
+
+endmodule // user_settings
+