Merge FPGA repository back into UHD repository

The FPGA codebase was removed from the UHD repository in 2014 to reduce
the size of the repository. However, over the last half-decade, the
split between the repositories has proven more burdensome than it has
been helpful. By merging the FPGA code back, it will be possible to
create atomic commits that touch both FPGA and UHD codebases. Continuous
integration testing is also simplified by merging the repositories,
because it was previously difficult to automatically derive the correct
UHD branch when testing a feature branch on the FPGA repository.

This commit also updates the license files and paths therein.

We are therefore merging the repositories again. Future development for
FPGA code will happen in the same repository as the UHD host code and
MPM code.

== Original Codebase and Rebasing ==

The original FPGA repository will be hosted for the foreseeable future
at its original local location: https://github.com/EttusResearch/fpga/

It can be used for bisecting, reference, and a more detailed history.

The final commit from said repository to be merged here is
05003794e2da61cabf64dd278c45685a7abad7ec. This commit is tagged as
v4.0.0.0-pre-uhd-merge.

If you have changes in the FPGA repository that you want to rebase onto
the UHD repository, simply run the following commands:

- Create a directory to store patches (this should be an empty
  directory):

    mkdir ~/patches

- Now make sure that your FPGA codebase is based on the same state as
  the code that was merged:

    cd src/fpga # Or wherever your FPGA code is stored
    git rebase v4.0.0.0-pre-uhd-merge

  Note: The rebase command may look slightly different depending on what
  exactly you're trying to rebase.

- Create a patch set for your changes versus v4.0.0.0-pre-uhd-merge:

    git format-patch v4.0.0.0-pre-uhd-merge -o ~/patches

  Note: Make sure that only patches are stored in your output directory.
  It should otherwise be empty. Make sure that you picked the correct
  range of commits, and only commits you wanted to rebase were exported
  as patch files.

- Go to the UHD repository and apply the patches:

    cd src/uhd # Or wherever your UHD repository is stored
    git am --directory fpga ~/patches/*
    rm -rf ~/patches # This is for cleanup

== Contributors ==

The following people have contributed mainly to these files (this list
is not complete):

Co-authored-by: Alex Williams <alex.williams@ni.com>
Co-authored-by: Andrej Rode <andrej.rode@ettus.com>
Co-authored-by: Ashish Chaudhari <ashish@ettus.com>
Co-authored-by: Ben Hilburn <ben.hilburn@ettus.com>
Co-authored-by: Ciro Nishiguchi <ciro.nishiguchi@ni.com>
Co-authored-by: Daniel Jepson <daniel.jepson@ni.com>
Co-authored-by: Derek Kozel <derek.kozel@ettus.com>
Co-authored-by: EJ Kreinar <ej@he360.com>
Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com>
Co-authored-by: Ian Buckley <ian.buckley@gmail.com>
Co-authored-by: Jörg Hofrichter <joerg.hofrichter@ni.com>
Co-authored-by: Jon Kiser <jon.kiser@ni.com>
Co-authored-by: Josh Blum <josh@joshknows.com>
Co-authored-by: Jonathon Pendlum <jonathan.pendlum@ettus.com>
Co-authored-by: Martin Braun <martin.braun@ettus.com>
Co-authored-by: Matt Ettus <matt@ettus.com>
Co-authored-by: Michael West <michael.west@ettus.com>
Co-authored-by: Moritz Fischer <moritz.fischer@ettus.com>
Co-authored-by: Nick Foster <nick@ettus.com>
Co-authored-by: Nicolas Cuervo <nicolas.cuervo@ettus.com>
Co-authored-by: Paul Butler <paul.butler@ni.com>
Co-authored-by: Paul David <paul.david@ettus.com>
Co-authored-by: Ryan Marlow <ryan.marlow@ettus.com>
Co-authored-by: Sugandha Gupta <sugandha.gupta@ettus.com>
Co-authored-by: Sylvain Munaut <tnt@246tNt.com>
Co-authored-by: Trung Tran <trung.tran@ettus.com>
Co-authored-by: Vidush Vishwanath <vidush.vishwanath@ettus.com>
Co-authored-by: Wade Fife <wade.fife@ettus.com>


Original-commit: bafa9d95453387814ef25e6b6256ba8db2df612f
This commit is contained in:
Martin Braun
2020-01-28 09:35:36 -08:00
co-authored by Alex Williams Andrej Rode Ashish Chaudhari Ben Hilburn Ciro Nishiguchi Daniel Jepson Derek Kozel EJ Kreinar Humberto Jimenez Ian Buckley Jörg Hofrichter Jon Kiser Josh Blum Jonathon Pendlum Matt Ettus Michael West Moritz Fischer Nick Foster Nicolas Cuervo Paul Butler Paul David Ryan Marlow Sugandha Gupta Sylvain Munaut Trung Tran Vidush Vishwanath Wade Fife
parent 74893643ca
commit 6b67702ad7
2157 changed files with 1282567 additions and 0 deletions
+61
View File
@@ -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\
))
+99
View File
@@ -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
+91
View File
@@ -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
+164
View File
@@ -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
+87
View File
@@ -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
+193
View File
@@ -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
+87
View File
@@ -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
+232
View File
@@ -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
+97
View File
@@ -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
+122
View File
@@ -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
+145
View File
@@ -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
+248
View File
@@ -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
+228
View File
@@ -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
+164
View File
@@ -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
+30
View File
@@ -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
+45
View File
@@ -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
+148
View File
@@ -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
+70
View File
@@ -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
+78
View File
@@ -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
+98
View File
@@ -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
+38
View File
@@ -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
+25
View File
@@ -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
+3
View File
@@ -0,0 +1,3 @@
Contributions from:
Ettus Research, A National Instruments Company
Alex Forencich <alex@alexforencich.com>
+206
View File
@@ -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
+103
View File
@@ -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
+259
View File
@@ -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
+94
View File
@@ -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
+223
View File
@@ -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
+253
View File
@@ -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
+772
View File
@@ -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
+28
View File
@@ -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
+55
View File
@@ -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
+34
View File
@@ -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
+79
View File
@@ -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
+79
View File
@@ -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
+70
View File
@@ -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
+120
View File
@@ -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
+129
View File
@@ -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
+46
View File
@@ -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
+63
View File
@@ -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
+113
View File
@@ -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
+37
View File
@@ -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
+144
View File
@@ -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
+126
View File
@@ -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
+86
View File
@@ -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
+38
View File
@@ -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
+44
View File
@@ -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
+170
View File
@@ -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
+107
View File
@@ -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
+229
View File
@@ -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
+50
View File
@@ -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
+47
View File
@@ -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
+66
View File
@@ -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