diff options
author | Ben Hilburn <ben.hilburn@ettus.com> | 2013-10-10 10:17:27 -0700 |
---|---|---|
committer | Ben Hilburn <ben.hilburn@ettus.com> | 2013-10-10 10:17:27 -0700 |
commit | 0df4b801a34697f2058b4a7b95e08d2a0576c9db (patch) | |
tree | be10e78d1a97c037a9e7492360a178d1873b9c09 /fpga/usrp3/lib/control | |
parent | 6e7bc850b66e8188718248b76b729c7cf9c89700 (diff) | |
download | uhd-0df4b801a34697f2058b4a7b95e08d2a0576c9db.tar.gz uhd-0df4b801a34697f2058b4a7b95e08d2a0576c9db.tar.bz2 uhd-0df4b801a34697f2058b4a7b95e08d2a0576c9db.zip |
Squashed B200 FPGA Source. Code from Josh Blum, Ian Buckley, and Matt Ettus.
Diffstat (limited to 'fpga/usrp3/lib/control')
21 files changed, 2048 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/control/Makefile.srcs b/fpga/usrp3/lib/control/Makefile.srcs new file mode 100644 index 000000000..e0ad8a942 --- /dev/null +++ b/fpga/usrp3/lib/control/Makefile.srcs @@ -0,0 +1,26 @@ +# +# Copyright 2013 Ettus Research LLC +# + +################################################## +# Control Lib Sources +################################################## +CONTROL_LIB_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/control/, \ +reset_sync.v \ +por_gen.v \ +gpio_atr.v \ +simple_spi_core.v \ +simple_i2c_core.v \ +setting_reg.v \ +settings_bus_crossclock.v \ +radio_ctrl_proc.v \ +ram_2port.v \ +axi_crossbar.v \ +axi_slave_mux.v \ +axi_fifo_header.v \ +arb_qualify_master.v \ +axi_forwarding_cam.v \ +axi_test_vfifo.v \ +dram_2port.v \ +cvita_uart.v \ +)) diff --git a/fpga/usrp3/lib/control/README.txt b/fpga/usrp3/lib/control/README.txt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/fpga/usrp3/lib/control/README.txt diff --git a/fpga/usrp3/lib/control/arb_qualify_master.v b/fpga/usrp3/lib/control/arb_qualify_master.v new file mode 100644 index 000000000..df17fac57 --- /dev/null +++ b/fpga/usrp3/lib/control/arb_qualify_master.v @@ -0,0 +1,88 @@ +// +// Copyright 2012 Ettus Research LLC +// + + +// +// This module forms the qualification engine for a single master as +// part of a larger arbitration engine for a slave. It would typically +// be instantiated from arb_select_master.v to form a complete arbitor solution. +// + +module arb_qualify_master + #( + parameter WIDTH=16 // Bit width of destination field. + ) + ( + input clk, + input reset, + input clear, + // Header signals + input [WIDTH-1:0] header, + input header_valid, + // Slave Confg Signals + input [WIDTH-1:0] slave_addr, + input [WIDTH-1:0] slave_mask, + input slave_valid, + // Arbitration flags + output reg master_valid, + input master_ack + ); + + localparam WAIT_HEADER_VALID = 0; + localparam MATCH = 1; + localparam WAIT_HEADER_NOT_VALID = 2; + + + reg [1:0] state, next_state; + + + // Does masked slave address match header field for dest from master? + assign header_match = ((header & slave_mask) == (slave_addr & slave_mask)) && slave_valid; + + + always @(posedge clk) + if (reset | clear) begin + state <= WAIT_HEADER_VALID; + master_valid <= 0; + end else + begin + case(state) + // + // Wait here until Masters FIFO presents a valid header word. + // + WAIT_HEADER_VALID: begin + if (header_valid) + if (header_match) begin + state <= MATCH; + master_valid <= 1; + end else + next_state <= WAIT_HEADER_NOT_VALID; + end + // + // There should only ever be one match across various arbitors + // if they are configured correctly and since the backing FIFO in the + // master should not start to drain until the arbitration is won + // by that master, master_ack should always preceed de-assertion of + // header_valid so we don't check for the other order of deassertion. + // + MATCH: begin + if (master_ack) begin + master_valid <= 0; + state <= WAIT_HEADER_NOT_VALID; + end + end + // + // Wait here until this master starts to drain this packet from his FIFO. + // + WAIT_HEADER_NOT_VALID: begin + if (!header_valid) begin + state <= WAIT_HEADER_VALID; + end + end + endcase // case(state) + end // else: !if(reset | clear) + +endmodule // arb_qualify_master + +
\ No newline at end of file diff --git a/fpga/usrp3/lib/control/axi_crossbar.v b/fpga/usrp3/lib/control/axi_crossbar.v new file mode 100644 index 000000000..a408f69f0 --- /dev/null +++ b/fpga/usrp3/lib/control/axi_crossbar.v @@ -0,0 +1,167 @@ +// +// Copyright 2012 Ettus Research LLC +// + + +`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 axi_crossbar + #( + 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 [`LOG2(NUM_OUTPUTS)+`LOG2(NUM_INPUTS)-1:0] rb_addr, + output [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(0), + .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_strobe && (rb_addr[`LOG2(NUM_OUTPUTS)+`LOG2(NUM_INPUTS)-1:`LOG2(NUM_OUTPUTS)] == m)), + .rb_addr(rb_addr[`LOG2(NUM_OUTPUTS)-1:0]), + .rb_data(rb_data_mux[m]) + ); + end // block: instantiate_fifo_header + endgenerate + + assign rb_data = rb_data_mux[rb_addr[`LOG2(NUM_OUTPUTS)+`LOG2(NUM_INPUTS)-1:`LOG2(NUM_OUTPUTS)]]; + + +endmodule // axi_crossbar diff --git a/fpga/usrp3/lib/control/axi_crossbar_tb.v b/fpga/usrp3/lib/control/axi_crossbar_tb.v new file mode 100644 index 000000000..1994cb352 --- /dev/null +++ b/fpga/usrp3/lib/control/axi_crossbar_tb.v @@ -0,0 +1,214 @@ +// +// Copyright 2012 Ettus Research LLC +// + +`timescale 1 ps / 1 ps + +module axi_crossbar_tb; + + + localparam STREAM_WIDTH = 64; + + // Currently support simulations upto 8x8 configurations + localparam MAX_NUM_INPUTS = 8; + localparam MAX_NUM_OUTPUTS = 8; + + wire [(MAX_NUM_INPUTS*STREAM_WIDTH)-1:0] i_tdata; + wire [STREAM_WIDTH-1:0] i_tdata_array [0:MAX_NUM_INPUTS-1]; + wire [MAX_NUM_INPUTS-1:0] i_tvalid; + wire [MAX_NUM_INPUTS-1:0] i_tready; + wire [MAX_NUM_INPUTS-1:0] i_tlast; + wire [MAX_NUM_INPUTS-1:0] pkt_present; + + reg [STREAM_WIDTH-1:0] data_in [0:MAX_NUM_INPUTS-1]; + reg [MAX_NUM_INPUTS-1:0] valid_in; + wire [MAX_NUM_INPUTS-1:0] ready_in; + reg [MAX_NUM_INPUTS-1:0] last_in; + + wire [(MAX_NUM_OUTPUTS*STREAM_WIDTH)-1:0] o_tdata; + wire [STREAM_WIDTH-1:0] o_tdata_array [0:MAX_NUM_OUTPUTS-1]; + wire [MAX_NUM_OUTPUTS-1:0] o_tvalid; + wire [MAX_NUM_OUTPUTS-1:0] o_tready; + wire [MAX_NUM_OUTPUTS-1:0] o_tlast; + + + wire [STREAM_WIDTH-1:0] data_out [0:MAX_NUM_OUTPUTS-1]; + wire [MAX_NUM_OUTPUTS-1:0] valid_out; + reg [MAX_NUM_OUTPUTS-1:0] ready_out; + wire [MAX_NUM_OUTPUTS-1:0] last_out; + + + genvar m; + + reg clk; + reg reset; + reg clear; + reg set_stb; + reg [15:0] set_addr; + reg [31:0] set_data; + + // reg reset; + + // + // Simulation specific testbench is included here + // +`include "task_library.v" +`include "simulation_script.v" + + + // + // Define Clocks + // + initial begin + clk = 1'b1; + end + + // 125MHz clock + always #4000 clk = ~clk; + + // + // Good starting state + // + initial begin + reset <= 0; + clear <= 0; + set_stb <= 0; + set_addr <= 0; + set_data <= 0; +/* -----\/----- EXCLUDED -----\/----- + data_in[0] <= 0; + valid_in[0] <= 0; + last_in[0] <= 0; + + data_in[1] <= 0; + valid_in[1] <= 0; + last_in[1] <= 0; + -----/\----- EXCLUDED -----/\----- */ + + + end + + + + // + // AXI Crossbar instance + // + localparam SR_AWIDTH = 16; + localparam SR_XB_LOCAL = 512; + + wire [7:0] local_addr; + + setting_reg #(.my_addr(SR_XB_LOCAL), .awidth(SR_AWIDTH), .width(8)) sr_local_addr + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(local_addr),.changed()); + + axi_crossbar + #( + .FIFO_WIDTH(STREAM_WIDTH), // AXI4-STREAM data bus width + .DST_WIDTH(16), // Width of DST field we are routing on. + .NUM_INPUTS(NUM_INPUTS), // number of input AXI4-STREAM buses + .NUM_OUTPUTS(NUM_OUTPUTS) // number of output AXI4-STREAM buses + ) axi_crossbar_i + ( + .clk(clk), + .reset(reset), + .clear(clear), + .local_addr(local_addr), + // Inputs + .i_tdata(i_tdata[(NUM_INPUTS*STREAM_WIDTH)-1:0]), + .i_tvalid(i_tvalid[NUM_INPUTS-1:0]), + .i_tlast(i_tlast[NUM_INPUTS-1:0]), + .i_tready(i_tready[NUM_INPUTS-1:0]), + .pkt_present(pkt_present[NUM_INPUTS-1:0]), + // Settings bus + .set_stb(set_stb), + .set_addr(set_addr), + .set_data(set_data), + // Output + .o_tdata(o_tdata[(NUM_OUTPUTS*STREAM_WIDTH)-1:0]), + .o_tvalid(o_tvalid[NUM_OUTPUTS-1:0]), + .o_tlast(o_tlast[NUM_OUTPUTS-1:0]), + .o_tready(o_tready[NUM_OUTPUTS-1:0]), + // Readback Bus + .rb_rd_stb(1'b0), + .rb_addr(0), + .rb_data() + ); + + // + // Input FIFOs + // + generate + for (m=0;m<NUM_INPUTS;m=m+1) + begin: input_fifos + + assign i_tdata[(STREAM_WIDTH*m)+STREAM_WIDTH-1:STREAM_WIDTH*m] = i_tdata_array[m]; + + axi_fifo_short + #(.WIDTH(STREAM_WIDTH+1)) axi_fifo_short_in + ( + .clk(clk), + .reset(reset), + .clear(clear), + .o_tdata({i_tlast[m],i_tdata_array[m]}), + .o_tvalid(i_tvalid[m]), + .o_tready(i_tready[m]), + .i_tdata({last_in[m],data_in[m]}), + .i_tvalid(valid_in[m]), + .i_tready(ready_in[m]), + .space(), + .occupied() + ); + + monitor_axi_fifo + #( + .COUNT_BITS(8) + ) monitor_axi_fifo_in + ( + .clk(clk), + .reset(reset), + .clear(clear), + // Monitored FIFO signals + .i_tvalid(valid_in[m]), + .i_tready(ready_in[m]), + .i_tlast(last_in[m]), + .o_tvalid(i_tvalid[m]), + .o_tready(i_tready[m]), + .o_tlast(i_tlast[m]), + // FIFO status output + .pkt_present(pkt_present[m]), // Flags any whole packets present + .pkt_count() + ); + + end + endgenerate + + + // + // Output FIFO's + // + generate + for (m=0;m<NUM_OUTPUTS;m=m+1) + begin: output_fifos + + assign o_tdata_array[m] = o_tdata[(STREAM_WIDTH*m)+STREAM_WIDTH-1:STREAM_WIDTH*m]; + + axi_fifo_short + #(.WIDTH(STREAM_WIDTH+1)) axi_fifo_short_out + ( + .clk(clk), + .reset(reset), + .clear(clear), + .i_tdata({o_tlast[m],o_tdata_array[m]}), + .i_tvalid(o_tvalid[m]), + .i_tready(o_tready[m]), + .o_tdata({last_out[m],data_out[m]}), + .o_tvalid(valid_out[m]), + .o_tready(ready_out[m]), + .space(), + .occupied() + ); + end + endgenerate // block: output_fifos + +endmodule // axi_crossbar_tb diff --git a/fpga/usrp3/lib/control/axi_fifo_header.v b/fpga/usrp3/lib/control/axi_fifo_header.v new file mode 100644 index 000000000..ceac8e324 --- /dev/null +++ b/fpga/usrp3/lib/control/axi_fifo_header.v @@ -0,0 +1,84 @@ +// +// Copyright 2012 Ettus Research LLC +// + + +// +// This module is connected to the output port of an AXI4-STREAM FIFO that is used to move packetized data. +// It extracts and indicates the header (first word) of a packet in the FIFO. The header and flag are pipelined +// for timing closure. +// + +module axi_fifo_header + #( + parameter WIDTH=64 // Bit width of FIFO word. + ) + ( + input clk, + input reset, + input clear, + // Monitored FIFO signals + input [WIDTH-1:0] o_tdata, + input o_tvalid, + input o_tready, + input o_tlast, + input pkt_present, + // Header signals + output reg [WIDTH-1:0] header, + output reg header_valid + ); + + localparam WAIT_SOF = 0; + localparam WAIT_EOF = 1; + + reg out_state; + + + // + // Monitor packets leaving FIFO + // + always @(posedge clk) + if (reset | clear) begin + out_state <= WAIT_SOF; + end else + case(out_state) + // + // After RESET or the EOF of previous packet, the first cycle with + // output valid asserted is the SOF and presents the Header word. + // The cycle following the concurrent presentation of asserted output + // valid and output ready presents the word following the header. + // + WAIT_SOF: + if (o_tvalid && o_tready) begin + out_state <= WAIT_EOF; + end else begin + out_state <= WAIT_SOF; + end + // + // EOF is signalled by o_tlast asserted whilst output valid and ready asserted. + // + WAIT_EOF: + if (o_tlast && o_tvalid && o_tready) begin + out_state <= WAIT_SOF; + end else begin + out_state <= WAIT_EOF; + end + endcase // case(in_state) + + // + // Pipeline Header signals + // + always @(posedge clk) + if (reset | clear) begin + header <= 0; + header_valid <= 0; + end else if (o_tvalid && (out_state == WAIT_SOF) && pkt_present) begin + // Header will remian valid until o_tready is asserted as this will cause a state transition. + header <= o_tdata; + header_valid <= 1; + end else begin + header_valid <= 0; + end + + +endmodule // axi_fifo_header diff --git a/fpga/usrp3/lib/control/axi_forwarding_cam.v b/fpga/usrp3/lib/control/axi_forwarding_cam.v new file mode 100644 index 000000000..2f28b5640 --- /dev/null +++ b/fpga/usrp3/lib/control/axi_forwarding_cam.v @@ -0,0 +1,232 @@ +// +// Copyright 2013 Ettus Research LLC +// + + +// +// This module implements a highly customized TCAM that enbales forwarding +// decisions to be made on a 16bit field from a VITA SID field. +// The 16bits are allocated by convention as 8 bits of Network address +// (Addresses USRP's etc) and 8 bits of Host address (adresses endpoints in +// a USRP). By definition if the DEST field in the SID addresses a different +// USRP 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 Need TCAM of 256+256 entries with Log2(N) bits, where N is the number of +// slave(output) ports on the crossbar switch. +// +// +// +// SID format: +// +// |--------|---------|--------|---------| +// | SOURCE | DEST | DEST | +// | ADDRESS | NETWORK| HOST | +// |--------|---------|--------|---------| +// 8 8 8 8 +// + + +`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 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, + // Forwarding Flags + output reg [NUM_OUTPUTS-1:0] forward_valid, + input [NUM_OUTPUTS-1:0] forward_ack, + // readback bus + input rb_rd_stb, + input [`LOG2(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])}; + + // + // Imply a block RAM here, 512xCeil(Log2(NUM_OUTPUTS)) + // + //synthesis attribute ram_style of mem is block + reg [(`LOG2(NUM_OUTPUTS))-1 : 0] mem [0:511]; + reg [8:0] read_addr_reg; + wire write; + wire [`LOG2(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[`LOG2(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 + 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 << 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 <= 0; + 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) + if (reset | clear) + statistics[m] <= 0; + else if ((rb_addr == m) && rb_rd_stb) + statistics[m] <= 0; + else if (forward_ack[m] & forward_valid[m]) + statistics[m] <= statistics[m] + 1; + end + endgenerate + + assign rb_data = statistics[rb_addr]; + + +endmodule + + + + + diff --git a/fpga/usrp3/lib/control/axi_slave_mux.v b/fpga/usrp3/lib/control/axi_slave_mux.v new file mode 100644 index 000000000..1a307aba5 --- /dev/null +++ b/fpga/usrp3/lib/control/axi_slave_mux.v @@ -0,0 +1,122 @@ +// +// Copyright 2012 Ettus Research LLC +// + + + + +`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 axi_slave_mux + #( + parameter FIFO_WIDTH = 64, // AXI4-STREAM data bus width + parameter DST_WIDTH = 16, // Width of DST field we are routing on. + parameter NUM_INPUTS = 2 // number of input AXI buses + ) + ( + input clk, + input reset, + input clear, + // Inputs + input [(FIFO_WIDTH*NUM_INPUTS)-1:0] i_tdata, + input [NUM_INPUTS-1:0] i_tvalid, + input [NUM_INPUTS-1:0] i_tlast, + output [NUM_INPUTS-1:0] i_tready, + // Forwarding Flags + input [NUM_INPUTS-1:0] forward_valid, + output reg [NUM_INPUTS-1:0] forward_ack, + // Output + output [FIFO_WIDTH-1:0] o_tdata, + output o_tvalid, + output o_tlast, + input o_tready + ); + + wire [FIFO_WIDTH-1:0] i_tdata_array [0:NUM_INPUTS-1]; + + reg [`LOG2(NUM_INPUTS):0] select; + reg enable; + + + reg state; + + localparam CHECK_THIS_INPUT = 0; + localparam WAIT_LAST = 1; + + + always @(posedge clk) + if (reset | clear) begin + state <= CHECK_THIS_INPUT; + select <= 0; + enable <= 0; + forward_ack <= 0; + end else begin + case(state) + // Is the currently selected input addressing this slave with a ready packet? + CHECK_THIS_INPUT: begin + if (forward_valid[select]) begin + enable <= 1; + forward_ack[select] <= 1; + state <= WAIT_LAST; + end else if (select == NUM_INPUTS - 1 ) begin + select <= 0; + end else begin + select <= select + 1; + end + end + // Assert ACK immediately to forwarding logic and then wait for end of packet. + WAIT_LAST: begin + + if (i_tlast[select] && i_tvalid[select] && o_tready) begin + if (select == NUM_INPUTS - 1 ) begin + select <= 0; + end else begin + select <= select + 1; + end + state <= CHECK_THIS_INPUT; + forward_ack <= 0; + enable <= 0; + end else begin + forward_ack[select] <= 1; + enable <= 1; + end + end + endcase // case(state) + end + + // + // Combinatorial mux + // + genvar m; + + generate + for (m = 0; m < NUM_INPUTS; m = m + 1) begin: form_buses + assign i_tdata_array[m] = i_tdata[(m*FIFO_WIDTH)+FIFO_WIDTH-1:m*FIFO_WIDTH]; + end + endgenerate + + assign o_tdata = i_tdata_array[select]; + assign o_tvalid = enable && i_tvalid[select]; + assign o_tlast = enable && i_tlast[select]; + // assign i_tready = {NUM_INPUTS{o_tready}} & (enable << select); + + generate + for (m = 0; m < NUM_INPUTS; m = m + 1) begin: form_ready + assign i_tready[m] = o_tready && enable && (select == m); + end + endgenerate + + +endmodule // axi_slave_mux diff --git a/fpga/usrp3/lib/control/axi_test_vfifo.v b/fpga/usrp3/lib/control/axi_test_vfifo.v new file mode 100644 index 000000000..a436e9c55 --- /dev/null +++ b/fpga/usrp3/lib/control/axi_test_vfifo.v @@ -0,0 +1,139 @@ +// +// Test Virtual FIFO's by streaming modulo 2^32 counter (replicated in upper +// and lower 32bits). Test result by tracking count on receive and using +// sticky flag for error indication. +// Also provide signal from MSB of 32bit count to blink LED. +// + +module axi_test_vfifo + #(parameter PACKET_SIZE = 128) + ( + input aclk, + input aresetn, + input enable, + // AXI Stream Out + output reg out_axis_tvalid, + input out_axis_tready, + output [63 : 0] out_axis_tdata, + output reg [7 : 0] out_axis_tstrb, + output reg [7 : 0] out_axis_tkeep, + output reg out_axis_tlast, + output reg [0 : 0] out_axis_tid, + output reg [0 : 0] out_axis_tdest, + input vfifo_full, + // AXI Stream In + input in_axis_tvalid, + output reg in_axis_tready, + input [63 : 0] in_axis_tdata, + input [7 : 0] in_axis_tstrb, + input [7 : 0] in_axis_tkeep, + input in_axis_tlast, + input [0 : 0] in_axis_tid, + input [0 : 0] in_axis_tdest, + // Flags + output reg flag_error, + output heartbeat_in, + output heartbeat_out, + output [31:0] expected_count + ); + + + reg [31:0] out_count; + reg [31:0] in_count; + reg [63:0] in_axis_tdata_reg; + reg in_data_valid; + + + + // + // Output + // + always @(posedge aclk) + if (!aresetn) begin + out_count <= 0; + out_axis_tvalid <= 0; + out_axis_tid <= 0; // Don't care. + out_axis_tdest <= 0; // Only use port 0 of VFIFO. + out_axis_tstrb <= 0; // Unused in VFIFO + out_axis_tkeep <= 8'hFF; // Always use every byte of data + out_axis_tlast <= 1'b0; + end else if (enable) begin + if (~vfifo_full) begin + // Always ready to output new count value. + out_axis_tvalid <= 1; + if (out_axis_tready) + out_count <= out_count + 1; + // Assert TLAST every PACKET_SIZE beats. + if (out_count[15:0] == PACKET_SIZE) + out_axis_tlast <= 1'b1; + else + out_axis_tlast <= 1'b0; + end else begin + out_axis_tvalid <= 0; + end + end else begin + out_axis_tlast <= 1'b0; + out_axis_tvalid <= 0; + end + + assign out_axis_tdata = {out_count,out_count}; + + assign heartbeat_out = out_count[28]; + + + // + // Input (Ignore TLAST signal) + // + always @(posedge aclk) + if (!aresetn) begin + in_axis_tready <= 0; + in_axis_tdata_reg <= 0; + in_data_valid <= 0; + + end else if (enable) begin + in_axis_tready <= 1; + in_axis_tdata_reg <= in_axis_tdata; + if (in_axis_tvalid) + in_data_valid <= 1; + else + in_data_valid <= 0; + end else begin + in_data_valid <= 0; + in_axis_tready <= 0; + end // else: !if(enable) + + + assign heartbeat_in = in_count[28]; + + // + // Input Checker + // + always @(posedge aclk) + if (!aresetn) begin + in_count <= 0; + flag_error <= 0; + end else if (enable) begin + if (in_data_valid) begin + + if ((in_axis_tdata_reg[63:32] != in_count) || (in_axis_tdata_reg[31:0] != in_count)) + begin + flag_error <= 1; + in_count <= in_axis_tdata_reg[63:32] + 1; + end + else + begin + flag_error <= 0; + in_count <= in_count + 1; + end + + end + end + + assign expected_count = in_count; + + +endmodule // axi_test_vfifo + + + + diff --git a/fpga/usrp3/lib/control/cvita_uart.v b/fpga/usrp3/lib/control/cvita_uart.v new file mode 100644 index 000000000..cbb272fc2 --- /dev/null +++ b/fpga/usrp3/lib/control/cvita_uart.v @@ -0,0 +1,164 @@ + +// +// Copyright 2013 Ettus Research LLC +// + + +//create a compressed vita based uart data interface + +module cvita_uart +#( + parameter SIZE = 0 +) +( + //clocking interface + input clk, input rst, + + //uart interface + input rxd, output txd, + + //chdr fifo input + input [63:0] i_tdata, + input i_tlast, + input i_tvalid, + output i_tready, + + //chdr fifo output + output [63:0] o_tdata, + output o_tlast, + output o_tvalid, + input o_tready +); + + reg [31:0] sid; + + //baud clock divider + reg [15:0] clkdiv; + + //hold rx in disable until a tx event + reg rxd_enable; + + //================================================================== + //== RXD capture and packet generation interface + //================================================================== + wire [7:0] rx_char; + wire fifo_empty; + wire fifo_read; + reg [11:0] seqnum; + wire pgen_trigger; + wire pgen_done; + + //rx uart capture + simple_uart_rx #(.SIZE(SIZE)) simple_uart_rx + ( + .clk(clk), .rst(rst), + .fifo_out(rx_char), .fifo_read(fifo_read), .fifo_level(), .fifo_empty(fifo_empty), + .clkdiv(clkdiv), .rx(rxd) + ); + + //packet generation - holds rx character + context_packet_gen context_packet_gen + ( + .clk(clk), .reset(rst), .clear(1'b0), + .trigger(pgen_trigger), + .seqnum(seqnum), + .sid({sid[15:0], sid[31:16]}), + .body({56'b0, rx_char}), + .vita_time(64'b0), + + .done(pgen_done), + .o_tdata(o_tdata), .o_tlast(o_tlast), .o_tvalid(o_tvalid), .o_tready(o_tready) + ); + + //state machine to manage pgen and rx uart + reg [1:0] rxd_state; + localparam RXD_STATE_RECV_CHAR = 0; + localparam RXD_STATE_PGEN_TRIG = 1; + localparam RXD_STATE_WAIT_DONE = 2; + localparam RXD_STATE_READ_FIFO = 3; + + always @(posedge clk) begin + if (rst) begin + seqnum <= 12'b0; + rxd_state <= RXD_STATE_RECV_CHAR; + end + else case (rxd_state) + + RXD_STATE_RECV_CHAR: begin + if (!fifo_empty && rxd_enable) rxd_state <= RXD_STATE_PGEN_TRIG; + end + + RXD_STATE_PGEN_TRIG: begin + rxd_state <= RXD_STATE_WAIT_DONE; + end + + RXD_STATE_WAIT_DONE: begin + if (pgen_done) rxd_state <= RXD_STATE_READ_FIFO; + end + + RXD_STATE_READ_FIFO: begin + rxd_state <= RXD_STATE_RECV_CHAR; + seqnum <= seqnum + 1'b1; + end + + endcase //rxd_state + end + + assign fifo_read = (rxd_state == RXD_STATE_READ_FIFO) || (!rxd_enable); + assign pgen_trigger = (rxd_state == RXD_STATE_PGEN_TRIG); + + //================================================================== + //== TXD generation and packet control interface + //================================================================== + wire [7:0] tx_char; + wire fifo_write; + wire fifo_full; + + simple_uart_tx #(.SIZE(SIZE)) simple_uart_tx + ( + .clk(clk), .rst(rst), + .fifo_in(tx_char), .fifo_write(fifo_write), .fifo_level(), .fifo_full(fifo_full), + .clkdiv(clkdiv), .baudclk(), .tx(txd) + ); + + //state machine to manage control and tx uart + reg [1:0] txd_state; + localparam TXD_STATE_RECV_CHDR = 0; + localparam TXD_STATE_RECV_TIME = 1; + localparam TXD_STATE_RECV_BODY = 2; + localparam TXD_STATE_DROP_FIFO = 3; + + always @(posedge clk) begin + if (rst) begin; + txd_state <= TXD_STATE_RECV_CHDR; + rxd_enable <= 1'b0; + end + if (i_tvalid && i_tready) case (txd_state) + + TXD_STATE_RECV_CHDR: begin + txd_state <= (i_tdata[61])? TXD_STATE_RECV_TIME : TXD_STATE_RECV_BODY; + sid <= i_tdata[31:0]; + end + + TXD_STATE_RECV_TIME: begin + txd_state <= TXD_STATE_RECV_BODY; + end + + TXD_STATE_RECV_BODY: begin + txd_state <= (i_tlast)? TXD_STATE_RECV_CHDR : TXD_STATE_DROP_FIFO; + clkdiv <= i_tdata[47:32]; + rxd_enable <= 1'b1; + end + + TXD_STATE_DROP_FIFO: begin + if (i_tlast) txd_state <= TXD_STATE_RECV_CHDR; + end + + endcase //txd_state + end + + assign tx_char = i_tdata[7:0]; + assign fifo_write = (txd_state == TXD_STATE_RECV_BODY) && i_tvalid && i_tready; + assign i_tready = !fifo_full; + +endmodule // cvita_uart diff --git a/fpga/usrp3/lib/control/dram_2port.v b/fpga/usrp3/lib/control/dram_2port.v new file mode 100644 index 000000000..186af44e7 --- /dev/null +++ b/fpga/usrp3/lib/control/dram_2port.v @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////// +// Copyright Ettus Research LLC +//////////////////////////////////////////////////////////////////////// + +module dram_2port + #(parameter DWIDTH=32, + parameter AWIDTH=9) + (input clk, + input write, + input [AWIDTH-1:0] raddr, + input [AWIDTH-1:0] waddr, + input [DWIDTH-1:0] wdata, + output [DWIDTH-1:0] rdata); + + reg [DWIDTH-1:0] ram [(1<<AWIDTH)-1:0]; + integer i; + initial + for(i=0;i<(1<<AWIDTH);i=i+1) + ram[i] <= {DWIDTH{1'b0}}; + + assign rdata = ram[raddr]; + + always @(posedge clk) begin + if (write) ram[waddr] <= wdata; + end + +endmodule //dram_2port diff --git a/fpga/usrp3/lib/control/gpio_atr.v b/fpga/usrp3/lib/control/gpio_atr.v new file mode 100644 index 000000000..9c707e52a --- /dev/null +++ b/fpga/usrp3/lib/control/gpio_atr.v @@ -0,0 +1,64 @@ + +// +// Copyright 2011 Ettus Research LLC +// + + + +module gpio_atr + #(parameter BASE = 0, + parameter WIDTH = 32) + (input clk, input reset, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + input rx, input tx, + inout [WIDTH-1:0] gpio, + output reg [31:0] gpio_readback + ); + + wire [WIDTH-1:0] ddr, in_idle, in_tx, in_rx, in_fdx; + reg [WIDTH-1:0] rgpio, igpio; + reg [WIDTH-1:0] gpio_pipe; + + + setting_reg #(.my_addr(BASE+0), .width(WIDTH)) 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)) reg_ddr + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data), + .out(ddr),.changed()); + + always @(posedge clk) + case({tx,rx}) + 2'b00: rgpio <= in_idle; + 2'b01: rgpio <= in_rx; + 2'b10: rgpio <= in_tx; + 2'b11: rgpio <= in_fdx; + endcase // case ({tx,rx}) + + integer n; + always @* + for(n=0;n<WIDTH;n=n+1) + igpio[n] <= ddr[n] ? rgpio[n] : 1'bz; + + assign gpio = igpio; + + // Double pipeline stage for timing, first flop is in IOB, second in core logic. + always @(posedge clk) begin + gpio_pipe <= gpio; + gpio_readback <= gpio_pipe; + end + +endmodule // gpio_atr diff --git a/fpga/usrp3/lib/control/por_gen.v b/fpga/usrp3/lib/control/por_gen.v new file mode 100644 index 000000000..0e4fcd88a --- /dev/null +++ b/fpga/usrp3/lib/control/por_gen.v @@ -0,0 +1,25 @@ +// +// Copyright 2013 Ettus Research LLC +// + + + +module por_gen + (input clk, + output reset_out); + + reg por_rst; + reg [7:0] por_counter = 8'h0; + + always @(posedge clk) + if (por_counter != 8'h55) + begin + por_counter <= por_counter + 8'h1; + por_rst <= 1'b1; + end + else + por_rst <= 1'b0; + + assign reset_out = por_rst; + +endmodule // por_gen diff --git a/fpga/usrp3/lib/control/radio_ctrl_proc.v b/fpga/usrp3/lib/control/radio_ctrl_proc.v new file mode 100644 index 000000000..723ba69ff --- /dev/null +++ b/fpga/usrp3/lib/control/radio_ctrl_proc.v @@ -0,0 +1,143 @@ + + +// Radio Control Processor +// Accepts compressed vita extension context packets of the following form: +// { VITA Compressed Header, Stream ID } +// { Optional 64 bit time } +// { 16'h0, setting bus address [15:0], setting [31:0] } +// +// If there is a timestamp, packet is held until that time comes. +// Goes immediately if there is no timestamp or if time has passed. +// Sends out setting to setting bus, and then generates a response packet +// with the same sequence number, the src/dest swapped streamid, and the actual time +// the setting was sent. +// +// Note -- if t0 is the requested time, the actual send time on the setting bus is t0 + 1 cycle. +// Note 2 -- if t1 is the actual time the setting bus, t1+2 is the reported time. + +module radio_ctrl_proc + (input clk, input reset, input clear, + + input [63:0] ctrl_tdata, input ctrl_tlast, input ctrl_tvalid, output reg ctrl_tready, + output reg [63:0] resp_tdata, output reg resp_tlast, output resp_tvalid, input resp_tready, + + input [63:0] vita_time, + + output set_stb, output [7:0] set_addr, output [31:0] set_data, + input ready, + + input [63:0] readback, + + output [31:0] debug); + + localparam RC_HEAD = 4'd0; + localparam RC_TIME = 4'd1; + localparam RC_DATA = 4'd2; + localparam RC_DUMP = 4'd3; + localparam RC_RESP_HEAD = 4'd4; + localparam RC_RESP_TIME = 4'd5; + localparam RC_RESP_DATA = 4'd6; + + wire IS_EC = ctrl_tdata[63]; + wire HAS_TIME = ctrl_tdata[61]; + reg HAS_TIME_reg; + + reg [3:0] rc_state; + reg [63:0] cmd_time; + + wire now, late, go; + reg [11:0] seqnum; + reg [31:0] sid; + + always @(posedge clk) + if(reset) + begin + rc_state <= RC_HEAD; + HAS_TIME_reg <= 1'b0; + sid <= 32'd0; + seqnum <= 12'd0; + end + else + case(rc_state) + RC_HEAD : + if(ctrl_tvalid) + begin + sid <= ctrl_tdata[31:0]; + seqnum <= ctrl_tdata[59:48]; + HAS_TIME_reg <= HAS_TIME; + if(IS_EC) + if(HAS_TIME) + rc_state <= RC_TIME; + else + rc_state <= RC_DATA; + else + if(~ctrl_tlast) + rc_state <= RC_DUMP; + end + + RC_TIME : + if(ctrl_tvalid) + if(ctrl_tlast) + rc_state <= RC_RESP_HEAD; + else if(go) + rc_state <= RC_DATA; + + RC_DATA : + if(ctrl_tvalid) + if(ready) + if(ctrl_tlast) + rc_state <= RC_RESP_HEAD; + else + rc_state <= RC_DUMP; + + RC_DUMP : + if(ctrl_tvalid) + if(ctrl_tlast) + rc_state <= RC_RESP_HEAD; + + RC_RESP_HEAD : + if(resp_tready) + rc_state <= RC_RESP_TIME; + + RC_RESP_TIME : + if(resp_tready) + rc_state <= RC_RESP_DATA; + + RC_RESP_DATA: + if(resp_tready) + rc_state <= RC_HEAD; + + default : + rc_state <= RC_HEAD; + endcase // case (rc_state) + + always @* + case (rc_state) + RC_HEAD : ctrl_tready <= 1'b1; + RC_TIME : ctrl_tready <= ctrl_tlast | go; + RC_DATA : ctrl_tready <= ready; + RC_DUMP : ctrl_tready <= 1'b1; + default : ctrl_tready <= 1'b0; + endcase // case (rc_state) + + time_compare time_compare + (.clk(clk), .reset(reset), .time_now(vita_time), .trigger_time(ctrl_tdata), .now(now), .early(), .late(late), .too_early()); + + assign go = now | late; + + assign set_stb = (rc_state == RC_DATA) & ready & ctrl_tvalid; + assign set_addr = ctrl_tdata[39:32]; + assign set_data = ctrl_tdata[31:0]; + + always @* + case (rc_state) + RC_RESP_HEAD : { resp_tlast, resp_tdata } <= {1'b0, 4'hA, seqnum, 16'd24, sid[15:0], sid[31:16] }; + RC_RESP_TIME : { resp_tlast, resp_tdata } <= {1'b0, vita_time}; + RC_RESP_DATA : { resp_tlast, resp_tdata } <= {1'b1, readback}; + default : { resp_tlast, resp_tdata } <= 65'h0; + endcase // case (rc_state) + + assign resp_tvalid = (rc_state == RC_RESP_HEAD) | (rc_state == RC_RESP_TIME) | (rc_state == RC_RESP_DATA); + +endmodule // radio_ctrl_proc + diff --git a/fpga/usrp3/lib/control/radio_ctrl_proc_tb.v b/fpga/usrp3/lib/control/radio_ctrl_proc_tb.v new file mode 100644 index 000000000..da863f4ea --- /dev/null +++ b/fpga/usrp3/lib/control/radio_ctrl_proc_tb.v @@ -0,0 +1,104 @@ +`timescale 1ns/1ps + +module radio_ctrl_proc_tb(); + + reg clk = 0; + reg reset = 1; + + always #10 clk = ~clk; + + initial $dumpfile("radio_ctrl_proc_tb.vcd"); + initial $dumpvars(0,radio_ctrl_proc_tb); + + initial + begin + #1000 reset = 0; + #20000; + $finish; + end + + reg [63:0] vita_time = 64'd0; + always @(posedge clk) + if(reset) vita_time <= 64'd0; + else vita_time <= vita_time + 64'd1; + + reg [63:0] tdata; + wire [63:0] tdata_int; + reg tlast; + wire tlast_int; + reg tvalid = 1'b0; + wire tvalid_int; + wire tready, tready_int; + + wire [7:0] set_addr; + wire [31:0] set_data; + wire set_stb; + wire ready = 1'b1; + + task send_packet; + input ec; + input timed; + input [11:0] seqnum; + input [31:0] sid; + input [63:0] vtime; + input [15:0] addr; + input [31:0] data; + + begin + // Send a packet + @(posedge clk); + tlast <= 1'b0; + tdata <= { ec, 1'b0, timed, 1'b0, seqnum, timed ? 16'd6 : 16'd4, sid }; + tvalid <= 1; + @(posedge clk); + if(timed) + begin + tdata <= vtime; + @(posedge clk); + end + tlast <= 1'b1; + tdata <= { 16'h0, addr, data }; + @(posedge clk); + tvalid <= 0; + @(posedge clk); + end + endtask // send_packet + + initial + begin + tvalid <= 1'b0; + while(reset) + @(posedge clk); + send_packet(1'b1,1'b0,12'h5,32'hDEAD_BEEF,64'h0,16'hB,32'hF00D_1234); + send_packet(1'b1,1'b1,12'h6,32'hDEAD_6789,64'h20,16'hC,32'hABCD_4321); + send_packet(1'b1,1'b1,12'h7,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321); + //send_packet(.ec(1), .timed(0), .seqnum(5), .sid(32'hDEAD_BEEF), .vtime(0), .addr(16'hB), .data(32'hF00D_1234)); + end + + axi_fifo_short #(.WIDTH(65)) axi_fifo_short + (.clk(clk), .reset(reset), .clear(1'b0), + .i_tdata({tlast,tdata}), .i_tvalid(tvalid), .i_tready(tready), + .o_tdata({tlast_int,tdata_int}), .o_tvalid(tvalid_int), .o_tready(tready_int)); + + wire [63:0] resp_tdata; + wire resp_tlast, resp_tvalid, resp_tready; + + radio_ctrl_proc radio_ctrl_proc + (.clk(clk), .reset(reset), .clear(1'b0), + .ctrl_tdata(tdata_int), .ctrl_tlast(tlast_int), .ctrl_tvalid(tvalid_int), .ctrl_tready(tready_int), + .resp_tdata(resp_tdata), .resp_tlast(resp_tlast), .resp_tvalid(resp_tvalid), .resp_tready(resp_tready), + .vita_time(vita_time), .ready(ready), + .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .debug() + ); + + assign resp_tready = 1'b1; + + always @(posedge clk) + if(resp_tvalid & resp_tready) + begin + $display("%x",resp_tdata); + if(resp_tlast) + $display("TLAST"); + end +endmodule // radio_ctrl_proc_tb diff --git a/fpga/usrp3/lib/control/ram_2port.v b/fpga/usrp3/lib/control/ram_2port.v new file mode 100644 index 000000000..434af0ff3 --- /dev/null +++ b/fpga/usrp3/lib/control/ram_2port.v @@ -0,0 +1,49 @@ +// +// Copyright 2011 Ettus Research LLC +// + + + + +module ram_2port + #(parameter DWIDTH=32, + parameter AWIDTH=9) + (input clka, + input ena, + input wea, + input [AWIDTH-1:0] addra, + input [DWIDTH-1:0] dia, + output reg [DWIDTH-1:0] doa, + + input clkb, + input enb, + input web, + input [AWIDTH-1:0] addrb, + input [DWIDTH-1:0] dib, + output reg [DWIDTH-1:0] dob); + + reg [DWIDTH-1:0] ram [(1<<AWIDTH)-1:0]; + /* + integer i; + initial + for(i=0;i<(1<<AWIDTH);i=i+1) + ram[i] <= {DWIDTH{1'b0}}; + */ + + always @(posedge clka) begin + if (ena) + begin + if (wea) + ram[addra] <= dia; + doa <= ram[addra]; + end + end + always @(posedge clkb) begin + if (enb) + begin + if (web) + ram[addrb] <= dib; + dob <= ram[addrb]; + end + end +endmodule // ram_2port diff --git a/fpga/usrp3/lib/control/reset_sync.v b/fpga/usrp3/lib/control/reset_sync.v new file mode 100644 index 000000000..da284e62e --- /dev/null +++ b/fpga/usrp3/lib/control/reset_sync.v @@ -0,0 +1,28 @@ +// +// Copyright 2011 Ettus Research LLC +// + + + + +module reset_sync + (input clk, + input reset_in, + output reset_out); + + reg reset_int; + + reg reset_out_tmp; + + //synthesis attribute async_reg of reset_out_tmp is "true"; + //synthesis attribute async_reg of reset_int is "true"; + always @(posedge clk or posedge reset_in) + if(reset_in) + {reset_out_tmp,reset_int} <= 2'b11; + else + {reset_out_tmp,reset_int} <= {reset_int,1'b0}; + + assign reset_out = reset_out_tmp; + + +endmodule // reset_sync diff --git a/fpga/usrp3/lib/control/setting_reg.v b/fpga/usrp3/lib/control/setting_reg.v new file mode 100644 index 000000000..1664f54e2 --- /dev/null +++ b/fpga/usrp3/lib/control/setting_reg.v @@ -0,0 +1,35 @@ +// +// Copyright 2011-2012 Ettus Research LLC +// + + +//---------------------------------------------------------------------- +//-- A settings register is a peripheral for the settings register bus. +//-- When the settings register sees strobe abd a matching address, +//-- the outputs will be become registered to the given input bus. +//---------------------------------------------------------------------- + +module setting_reg + #(parameter my_addr = 0, + parameter awidth = 8, + parameter width = 32, + parameter at_reset=0) + (input clk, input rst, input strobe, input wire [awidth-1:0] addr, + input wire [31:0] in, output reg [width-1:0] out, output reg changed); + + always @(posedge clk) + if(rst) + begin + out <= at_reset; + changed <= 1'b0; + end + else + if(strobe & (my_addr==addr)) + begin + out <= in[width-1:0]; + changed <= 1'b1; + end + else + changed <= 1'b0; + +endmodule // setting_reg diff --git a/fpga/usrp3/lib/control/settings_bus_crossclock.v b/fpga/usrp3/lib/control/settings_bus_crossclock.v new file mode 100644 index 000000000..2a6e7e7ef --- /dev/null +++ b/fpga/usrp3/lib/control/settings_bus_crossclock.v @@ -0,0 +1,26 @@ +// +// Copyright 2011-2012 Ettus Research LLC +// + + + + +// This module takes the settings bus on one clock domain and crosses it over to another domain +// Typically it will be used with the input settings bus on the wishbone clock, and either +// the system or dsp clock on the output side + +module settings_bus_crossclock + #(parameter FLOW_CTRL=0, parameter AWIDTH=8, parameter DWIDTH=32) + (input clk_i, input rst_i, input set_stb_i, input [AWIDTH-1:0] set_addr_i, input [DWIDTH-1:0] set_data_i, + input clk_o, input rst_o, output set_stb_o, output [AWIDTH-1:0] set_addr_o, output [DWIDTH-1:0] set_data_o, input blocked); + + wire nfull, nempty; + + axi_fifo_2clk #(.WIDTH(AWIDTH + DWIDTH), .SIZE(0)) settings_fifo + (.reset(rst_i), + .i_aclk(clk_i), .i_tdata({set_addr_i,set_data_i}), .i_tvalid(set_stb_i), .i_tready(nfull), + .o_aclk(clk_o), .o_tdata({set_addr_o,set_data_o}), .o_tready(set_stb_o), .o_tvalid(nempty)); + + assign set_stb_o = nempty & (~blocked | ~FLOW_CTRL); + +endmodule // settings_bus_crossclock diff --git a/fpga/usrp3/lib/control/simple_i2c_core.v b/fpga/usrp3/lib/control/simple_i2c_core.v new file mode 100644 index 000000000..47f1ac82a --- /dev/null +++ b/fpga/usrp3/lib/control/simple_i2c_core.v @@ -0,0 +1,104 @@ +// +// Copyright 2012 Ettus Research LLC +// + + +// Simple I2C core + +// Settings reg map: +// +// BASE+0 control register +// byte0 - control bits, data byte, or command bits, prescaler +// byte1 - what to do? (documented in cpp file) +// write prescaler lo +// write prescaler hi +// write control +// write data +// write command +// read data +// read status +// + +// Readback: +// +// byte0 has readback value based on the last read command +// + +module simple_i2c_core + #( + //settings register base address + parameter BASE = 0, + + //i2c line level at reset + parameter ARST_LVL = 1 + ) + ( + //clock and synchronous reset + input clock, input reset, + + //32-bit settings bus inputs + input set_stb, input [7:0] set_addr, input [31:0] set_data, + + //32-bit data readback + output reg [31:0] readback, + + //read is high when i2c core can begin another transaction + output reg ready, + + // I2C signals + // i2c clock line + input scl_pad_i, // SCL-line input + output scl_pad_o, // SCL-line output (always 1'b0) + output scl_padoen_o, // SCL-line output enable (active low) + + // i2c data line + input sda_pad_i, // SDA-line input + output sda_pad_o, // SDA-line output (always 1'b0) + output sda_padoen_o, // SDA-line output enable (active low) + + //optional debug output + output [31:0] debug + ); + + //declare command settings register + wire [7:0] sr_what, sr_data; + wire sr_changed; + setting_reg #(.my_addr(BASE+0),.width(16)) i2c_cmd_sr( + .clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data), + .out({sr_what, sr_data}),.changed(sr_changed)); + + //declare wb interface signals + wire [2:0] wb_addr; + wire [7:0] wb_data_mosi; + wire [7:0] wb_data_miso; + wire wb_we, wb_stb, wb_cyc; + wire wb_ack; + + //create wishbone-based i2c core + i2c_master_top #(.ARST_LVL(ARST_LVL)) i2c + (.wb_clk_i(clock),.wb_rst_i(reset),.arst_i(1'b0), + .wb_adr_i(wb_addr),.wb_dat_i(wb_data_mosi),.wb_dat_o(wb_data_miso), + .wb_we_i(wb_we),.wb_stb_i(wb_stb),.wb_cyc_i(wb_cyc), + .wb_ack_o(wb_ack),.wb_inta_o(), + .scl_pad_i(scl_pad_i),.scl_pad_o(scl_pad_o),.scl_padoen_o(scl_padoen_o), + .sda_pad_i(sda_pad_i),.sda_pad_o(sda_pad_o),.sda_padoen_o(sda_padoen_o) ); + + //not ready between setting register and wishbone ack + always @(posedge clock) begin + if (reset || wb_ack) ready <= 1; + else if (sr_changed) ready <= 0; + end + + //register wishbone data on every ack + always @(posedge clock) begin + if (wb_ack) readback <= {24'b0, wb_data_miso}; + end + + //assign wishbone signals + assign wb_addr = sr_what[2:0]; + assign wb_stb = sr_changed; + assign wb_we = wb_stb && sr_what[3]; + assign wb_cyc = wb_stb; + assign wb_data_mosi = sr_data; + +endmodule //simple_i2c_core diff --git a/fpga/usrp3/lib/control/simple_spi_core.v b/fpga/usrp3/lib/control/simple_spi_core.v new file mode 100644 index 000000000..a18e00709 --- /dev/null +++ b/fpga/usrp3/lib/control/simple_spi_core.v @@ -0,0 +1,207 @@ +// +// Copyright 2012 Ettus Research LLC +// + + +// 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, + + //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 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)) config_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 + 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; + always @(posedge clock) sen_reg <= sen24[WIDTH-1:0]; + assign sen = sen_reg; + + //data output shift register + reg [31:0] dataout_reg; + wire [31:0] dataout_next = {dataout_reg[30:0], 1'b0}; + assign mosi = dataout_reg[31]; + + //data input shift register + // IJB. One pipeline stage to break critical path from register in I/O pads. + reg miso_pipe; + always @(posedge clock) + miso_pipe = miso; + + 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; + end + else begin + case (state) + + WAIT_TRIG: begin + if (trigger_spi) state <= PRE_IDLE; + 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) state <= WAIT_TRIG; + 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 |