diff options
30 files changed, 1587 insertions, 1171 deletions
diff --git a/fpga/usrp2/fifo/Makefile.srcs b/fpga/usrp2/fifo/Makefile.srcs index 6cbd5cd3f..55ba0be2a 100644 --- a/fpga/usrp2/fifo/Makefile.srcs +++ b/fpga/usrp2/fifo/Makefile.srcs @@ -1,5 +1,5 @@ # -# Copyright 2010 Ettus Research LLC +# Copyright 2010-2012 Ettus Research LLC # ################################################## @@ -38,4 +38,5 @@ packet_generator.v \ packet_verifier32.v \ packet_verifier.v \ fifo19_pad.v \ +packet_padder36.v \ )) diff --git a/fpga/usrp2/fifo/packet_padder36.v b/fpga/usrp2/fifo/packet_padder36.v new file mode 100644 index 000000000..7197b5ea5 --- /dev/null +++ b/fpga/usrp2/fifo/packet_padder36.v @@ -0,0 +1,155 @@ +// +// Copyright 2011-2012 Ettus Research LLC +// +// 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/>. +// + +// The packet padder 36 for use with RX VITA stream output. +// Packet padder understands the concept of USB LUTs, +// and will forward packets through the interface, +// adding zero padding as needed to properly flush. +// The padder will never write a packet across a LUT boundary. +// When flushing, padder writes out zeros until the LUT boundary. +// Requires that the input line0 be a VITA header, and SOF set. +// Flush when the LUT is partially filled and timeout is reached, +// or when the LUT is partially filled and the DSP is inactive. + +module packet_padder36 +#( + parameter BASE = 0, + + //default is 16K LUT + parameter DEFAULT_LINES32 = 4096, + + //default about 1ms at 64MHz clock + parameter DEFAULT_IDLE_CYC = 65536 +) +( + input clk, input reset, + + //setting bus + input set_stb, input [7:0] set_addr, input [31:0] set_data, + + //input interface + input [35:0] data_i, + input src_rdy_i, + output dst_rdy_o, + + //output interface + output [35:0] data_o, + output src_rdy_o, + input dst_rdy_i, + + input always_flush +); + + wire lut_lines_changed; + wire [15:0] max_lut_lines32; + setting_reg #(.my_addr(BASE+0),.width(16),.at_reset(DEFAULT_LINES32)) sr_num_lines( + .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data), + .out(max_lut_lines32),.changed(lut_lines_changed)); + + wire idle_cyc_changed; + wire [17:0] idle_flush_cycles; + setting_reg #(.my_addr(BASE+1),.width(18),.at_reset(DEFAULT_IDLE_CYC)) sr_flush_cyc( + .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data), + .out(idle_flush_cycles),.changed(idle_cyc_changed)); + + //state machine definitions + localparam STATE_READ_HDR = 0; + localparam STATE_WRITE_HDR = 1; + localparam STATE_FORWARD = 2; + localparam STATE_WRITE_PAD = 3; + reg [1:0] state; + + //keep track of the outgoing lines + reg [15:0] line_count; + wire line_count_done = line_count == 1; + wire lut_is_empty = line_count == max_lut_lines32; + always @(posedge clk) begin + if (reset || lut_lines_changed) begin + line_count <= max_lut_lines32; + end + else if (src_rdy_o && dst_rdy_i) begin + line_count <= (line_count_done)? max_lut_lines32 : line_count - 1; + end + end + + //count the number of cycles since RX data so we can force a flush + reg [17:0] non_rx_cycles; + wire idle_timeout = (non_rx_cycles == idle_flush_cycles); + always @(posedge clk) begin + if(reset || state != STATE_READ_HDR || idle_cyc_changed) begin + non_rx_cycles <= 0; + end + else if (~idle_timeout) begin + non_rx_cycles <= non_rx_cycles + 1; + end + end + + //flush when we have written data to a LUT and either idle or non active DSP + wire force_flush = ~lut_is_empty && (idle_timeout || always_flush); + + //the padding state machine + reg [31:0] vita_hdr; + reg has_vita_hdr; + always @(posedge clk) begin + if (reset) begin + state <= STATE_READ_HDR; + end + else case(state) + + STATE_READ_HDR: begin + if (src_rdy_i && dst_rdy_o && data_i[32]) begin + vita_hdr <= data_i[31:0]; + has_vita_hdr <= 1; + state <= (data_i[15:0] > line_count)? state <= STATE_WRITE_PAD : STATE_WRITE_HDR; + end + else if (force_flush) begin + has_vita_hdr <= 0; + state <= STATE_WRITE_PAD; + end + end + + STATE_WRITE_HDR: begin + if (src_rdy_o && dst_rdy_i) begin + state <= STATE_FORWARD; + end + end + + STATE_FORWARD: begin + if (src_rdy_i && dst_rdy_o && data_i[33]) begin + state <= STATE_READ_HDR; + end + end + + STATE_WRITE_PAD: begin + if (src_rdy_o && dst_rdy_i && line_count_done) begin + state <= (has_vita_hdr)? STATE_WRITE_HDR : STATE_READ_HDR; + end + end + + endcase //state + end + + //assign outgoing signals + assign dst_rdy_o = (state == STATE_READ_HDR)? 1 : ((state == STATE_FORWARD)? dst_rdy_i : 0); + assign src_rdy_o = (state == STATE_WRITE_HDR || state == STATE_WRITE_PAD)? 1 : ((state == STATE_FORWARD )? src_rdy_i : 0); + assign data_o = (state == STATE_WRITE_HDR)? {4'b0001, vita_hdr} : ((state == STATE_FORWARD)? data_i : 0); + +endmodule // packet_padder36 + + + + diff --git a/fpga/usrp2/gpif/Makefile.srcs b/fpga/usrp2/gpif/Makefile.srcs index 524e3660d..7909fb5ff 100644 --- a/fpga/usrp2/gpif/Makefile.srcs +++ b/fpga/usrp2/gpif/Makefile.srcs @@ -8,4 +8,6 @@ GPIF_SRCS = $(abspath $(addprefix $(BASE_DIR)/../gpif/, \ packet_reframer.v \ slave_fifo.v \ +fifo36_to_gpmc16.v \ +gpmc16_to_fifo36.v \ )) diff --git a/fpga/usrp2/gpif/fifo36_to_gpmc16.v b/fpga/usrp2/gpif/fifo36_to_gpmc16.v new file mode 100644 index 000000000..508cd319c --- /dev/null +++ b/fpga/usrp2/gpif/fifo36_to_gpmc16.v @@ -0,0 +1,54 @@ +// +// Copyright 2012 Ettus Research LLC +// +// 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 fifo36_to_gpmc16 +#( + parameter FIFO_SIZE = 9 +) +( + //input fifo interface + input fifo_clk, input fifo_rst, + input [35:0] in_data, + input in_src_rdy, + output in_dst_rdy, + + //output interface + input gpif_clk, input gpif_rst, + output [15:0] out_data, + output valid, + input enable, + output eof +); + + wire [35:0] data_int; + wire src_rdy_int, dst_rdy_int; + + fifo_2clock_cascade #(.WIDTH(36), .SIZE(FIFO_SIZE)) fifo_2clk + (.wclk(fifo_clk), .datain(in_data), .src_rdy_i(in_src_rdy), .dst_rdy_o(in_dst_rdy), .space(), + .rclk(gpif_clk), .dataout(data_int), .src_rdy_o(src_rdy_int), .dst_rdy_i(dst_rdy_int), .occupied(), + .arst(fifo_rst | gpif_rst)); + + wire [18:0] data18_int; + fifo36_to_fifo19 #(.LE(1)) f36_to_f19 + (.clk(gpif_clk), .reset(gpif_rst), .clear(1'b0), + .f36_datain(data_int), .f36_src_rdy_i(src_rdy_int), .f36_dst_rdy_o(dst_rdy_int), + .f19_dataout(data18_int), .f19_src_rdy_o(valid), .f19_dst_rdy_i(enable) ); + + assign out_data = data18_int[15:0]; + assign eof = data18_int[17]; + +endmodule //fifo_to_gpmc16 diff --git a/fpga/usrp2/gpif/gpmc16_to_fifo36.v b/fpga/usrp2/gpif/gpmc16_to_fifo36.v new file mode 100644 index 000000000..933891715 --- /dev/null +++ b/fpga/usrp2/gpif/gpmc16_to_fifo36.v @@ -0,0 +1,64 @@ +// +// Copyright 2012 Ettus Research LLC +// +// 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 gpmc16_to_fifo36 +#( + parameter FIFO_SIZE = 9, + + //not ready until minimum xfers of space available + parameter MIN_SPACE16 = 128 +) +( + //input interface + input gpif_clk, input gpif_rst, + input [15:0] in_data, + input valid, + output reg ready, + + //output fifo interface + input fifo_clk, input fifo_rst, + output [35:0] out_data, + output out_src_rdy, + input out_dst_rdy +); + + wire [35:0] data_int; + wire src_rdy_int, dst_rdy_int; + wire [18:0] refr_data; + wire refr_src_rdy, refr_dst_rdy; + + wire [15:0] fifo_space; + + always @(posedge gpif_clk) + ready <= (fifo_space >= MIN_SPACE16/2); + + packet_reframer packet_reframer + (.clk(gpif_clk), .reset(gpif_rst), .clear(1'b0), + .data_i(in_data), .src_rdy_i(valid), .dst_rdy_o(), + .data_o(refr_data), .src_rdy_o(refr_src_rdy), .dst_rdy_i(refr_dst_rdy)); + + fifo19_to_fifo36 #(.LE(1)) f19_to_f36 + (.clk(gpif_clk), .reset(gpif_rst), .clear(1'b0), + .f19_datain(refr_data), .f19_src_rdy_i(refr_src_rdy), .f19_dst_rdy_o(refr_dst_rdy), + .f36_dataout(data_int), .f36_src_rdy_o(src_rdy_int), .f36_dst_rdy_i(dst_rdy_int)); + + fifo_2clock_cascade #(.WIDTH(36), .SIZE(FIFO_SIZE)) fifo_2clk + (.wclk(gpif_clk), .datain(data_int), .src_rdy_i(src_rdy_int), .dst_rdy_o(dst_rdy_int), .space(fifo_space), + .rclk(fifo_clk), .dataout(out_data), .src_rdy_o(out_src_rdy), .dst_rdy_i(out_dst_rdy), .occupied(), + .arst(fifo_rst | gpif_rst)); + +endmodule //fifo_to_gpmc16 diff --git a/fpga/usrp2/gpif/slave_fifo.v b/fpga/usrp2/gpif/slave_fifo.v index d1a0a027b..0f301f8a6 100644 --- a/fpga/usrp2/gpif/slave_fifo.v +++ b/fpga/usrp2/gpif/slave_fifo.v @@ -20,85 +20,70 @@ //this is a FIFO master interface for the FX2 in "slave fifo" mode. module slave_fifo - #(parameter TXFIFOSIZE = 12, parameter RXFIFOSIZE = 12) + #( + //how many cycles max in a transfer state + parameter DATA_XFER_COUNT = 256, + parameter CTRL_XFER_COUNT = 32, + + //sizes for fifo36 2 clock cascade fifos + parameter DATA_RX_FIFO_SIZE = 9, + parameter DATA_TX_FIFO_SIZE = 9, + parameter CTRL_RX_FIFO_SIZE = 9, + parameter CTRL_TX_FIFO_SIZE = 9 + ) (// GPIF signals input gpif_clk, input gpif_rst, inout [15:0] gpif_d, input [3:0] gpif_ctl, - output sloe, output slrd, output slwr, output pktend, output [1:0] fifoadr, - - // Wishbone signals - input wb_clk, input wb_rst, - output [15:0] wb_adr_o, output [15:0] wb_dat_mosi, input [15:0] wb_dat_miso, - output [1:0] wb_sel_o, output wb_cyc_o, output wb_stb_o, output wb_we_o, input wb_ack_i, - input [7:0] triggers, - - input dsp_rx_run, - + output reg sloe, output reg slrd, output reg slwr, output reg pktend, output reg [1:0] fifoadr, + // FIFO interface - input fifo_clk, input fifo_rst, input clear_tx, input clear_rx, - output [35:0] tx_data_o, output tx_src_rdy_o, input tx_dst_rdy_i, - input [35:0] rx_data_i, input rx_src_rdy_i, output rx_dst_rdy_o, - input [35:0] tx_err_data_i, input tx_err_src_rdy_i, output tx_err_dst_rdy_o, - output tx_underrun, output rx_overrun, - - input [15:0] test_len, input [7:0] test_rate, input [3:0] test_ctrl, - output [31:0] debug0, output [31:0] debug1 + input fifo_clk, input fifo_rst, + output [35:0] tx_data, output tx_src_rdy, input tx_dst_rdy, + input [35:0] rx_data, input rx_src_rdy, output rx_dst_rdy, + output [35:0] ctrl_data, output ctrl_src_rdy, input ctrl_dst_rdy, + input [35:0] resp_data, input resp_src_rdy, output resp_dst_rdy, + + output [31:0] debug ); - reg FX2_DE, FX2_CE, FX2_DF, FX2_CF; + wire FX2_DE_pre = ~gpif_ctl[0]; //EP2 FX2 FIFO empty (FLAGA) + wire FX2_CE_pre = ~gpif_ctl[1]; //EP4 FX2 FIFO empty (FLAGB) + wire FX2_DF_pre = ~gpif_ctl[2]; //EP6 FX2 FIFO full (FLAGC) + wire FX2_CF_pre = ~gpif_ctl[3]; //EP8 FX2 FIFO full (FLAGD) - // inputs to FPGA (all active low) + reg FX2_DE, FX2_CE, FX2_DF, FX2_CF; always @(posedge gpif_clk) begin - FX2_DE <= ~gpif_ctl[0]; //EP2 FX2 FIFO empty (FLAGA) - FX2_CE <= ~gpif_ctl[1]; //EP4 FX2 FIFO empty (FLAGB) - FX2_DF <= ~gpif_ctl[2]; //EP6 FX2 FIFO full (FLAGC) - FX2_CF <= ~gpif_ctl[3]; //EP8 FX2 FIFO full (FLAGD) + FX2_DE <= FX2_DE_pre; //EP2 FX2 FIFO empty (FLAGA) + FX2_CE <= FX2_CE_pre; //EP4 FX2 FIFO empty (FLAGB) + FX2_DF <= FX2_DF_pre; //EP6 FX2 FIFO full (FLAGC) + FX2_CF <= FX2_CF_pre; //EP8 FX2 FIFO full (FLAGD) end - wire [17:0] gpif_d_out_ctrl, gpif_d_out_data, gpif_d_out; + wire [15:0] gpif_d_out_ctrl, gpif_d_out_data; + reg [15:0] gpif_d_out, gpif_d_in; // //////////////////////////////////////////////////////////////////// // GPIF bus master state machine - //transfer size for GPIF data. this can be anything really, it's specified only for - //fairness in bus sharing. 256 lines is 512 bytes over the wire, half the size of - //the double buffers in B100/B150. this should probably be a toplevel parameter or even - //a settings register value. - localparam data_transfer_size = 256; - localparam ctrl_transfer_size = 16; //probably unnecessary since ctrl xfers won't back up - - // state machine i/o to four fifos - //tx - wire ctrl_tx_dst_rdy; //sm input, ctrl tx path has space - wire ctrl_tx_src_rdy; //sm output, ctrl tx path enable - wire data_tx_dst_rdy; //sm input, data tx path has space - wire data_tx_src_rdy; //sm output, data tx path enable - - //rx - wire ctrl_rx_dst_rdy; //sm output, ctrl rx path enable - wire ctrl_rx_src_rdy; //sm input, ctrl rx path has space - wire data_rx_dst_rdy; //sm output, data rx path enable - wire data_rx_src_rdy; //sm input, data rx path has space - - reg tx_data_enough_space; + wire rx_valid, resp_valid; + reg tx_valid, ctrl_valid; + wire tx_ready, ctrl_ready; + reg rx_enable, resp_enable; reg [9:0] transfer_count; //number of lines (a line is 16 bits) in active transfer - reg pktend_latch; - reg [3:0] state; //state machine current state localparam STATE_IDLE = 0; - localparam STATE_DATA_RX = 5; + localparam STATE_THINK = 1; + localparam STATE_DATA_RX = 2; localparam STATE_DATA_TX = 3; - localparam STATE_CTRL_RX = 6; - localparam STATE_CTRL_TX = 9; - localparam STATE_DATA_TX_SLOE = 2; - localparam STATE_CTRL_TX_SLOE = 8; - localparam STATE_DATA_RX_ADR = 1; - localparam STATE_CTRL_RX_ADR = 4; - localparam STATE_PKTEND_ADR = 10; - localparam STATE_PKTEND = 7; + localparam STATE_CTRL_RX = 4; + localparam STATE_CTRL_TX = 5; + localparam STATE_DATA_TX_SLOE = 6; + localparam STATE_CTRL_TX_SLOE = 7; + localparam STATE_DATA_RX_ADR = 8; + localparam STATE_CTRL_RX_ADR = 9; //logs the last bus user for xfer fairness //we only care about data rx vs. tx since ctrl pkts are so short @@ -106,384 +91,184 @@ module slave_fifo localparam BUS_HOG_RX = 0; localparam BUS_HOG_TX = 1; - //count the number of cycles since RX data so we can force a flush - reg [17:0] non_rx_cycles; - localparam rx_idle_flush_cycles = 65536; //about 1ms at 64MHz clock - always @(posedge gpif_clk) begin - if(gpif_rst || state == STATE_DATA_RX || state == STATE_PKTEND) - non_rx_cycles <= 0; - else if (non_rx_cycles != rx_idle_flush_cycles) - non_rx_cycles <= non_rx_cycles + 1; - end - - //when should we flush aka pktend? - //pktend_latch tells us that its ok to flush -> we just had an RX xfer with EOF - //the RX DSP not running or a cycle counter gives us the flushing response dynamic - wire rx_data_flush = (~dsp_rx_run || non_rx_cycles == rx_idle_flush_cycles) && pktend_latch; + wire resp_eof; + reg [1:0] idle_count; // ////////////////////////////////////////////////////////////// // FX2 slave FIFO bus master state machine // - always @(posedge gpif_clk) - if(gpif_rst) begin - state <= STATE_IDLE; - pktend_latch <= 0; - end - else - begin - case (state) - STATE_IDLE: - begin - transfer_count <= 0; - //handle transitions to other states - if(ctrl_tx_dst_rdy & ~FX2_CE) //if there's room in the ctrl fifo and the FX2 has ctrl data - state <= STATE_CTRL_TX_SLOE; - else if(ctrl_rx_src_rdy & ~FX2_CF) //if the ctrl fifo has data and the FX2 isn't full - state <= STATE_CTRL_RX_ADR; - else if(data_tx_dst_rdy & ~FX2_DE & last_data_bus_hog == BUS_HOG_RX & tx_data_enough_space) //if there's room in the data fifo and the FX2 has data - state <= STATE_DATA_TX_SLOE; - else if(data_rx_src_rdy & ~FX2_DF & last_data_bus_hog == BUS_HOG_TX) //if the data fifo has data and the FX2 isn't full - state <= STATE_DATA_RX_ADR; - else if(data_tx_dst_rdy & ~FX2_DE & tx_data_enough_space) - state <= STATE_DATA_TX_SLOE; - else if(data_rx_src_rdy & ~FX2_DF) - state <= STATE_DATA_RX_ADR; - else if(rx_data_flush & ~FX2_DF) - state <= STATE_PKTEND_ADR; - end - - STATE_DATA_TX_SLOE: //just to assert SLOE one cycle before SLRD - state <= STATE_DATA_TX; - STATE_CTRL_TX_SLOE: - state <= STATE_CTRL_TX; - - STATE_DATA_RX_ADR: //just to assert FIFOADR one cycle before SLWR - state <= STATE_DATA_RX; - STATE_CTRL_RX_ADR: - state <= STATE_CTRL_RX; - - STATE_DATA_RX: - begin - if(data_rx_src_rdy && data_rx_dst_rdy) begin - transfer_count <= transfer_count + 1; - pktend_latch <= gpif_d_out_data[17]; //ok to do pkt end when we complete with EOF - end - else - state <= STATE_IDLE; - last_data_bus_hog <= BUS_HOG_RX; - end - - STATE_PKTEND_ADR: - begin - state <= STATE_PKTEND; - end - - STATE_PKTEND: - begin - state <= STATE_IDLE; - pktend_latch <= 0; - end - - STATE_DATA_TX: - begin - if(data_tx_dst_rdy && data_tx_src_rdy) - transfer_count <= transfer_count + 1; - else - state <= STATE_IDLE; - last_data_bus_hog <= BUS_HOG_TX; - end - STATE_CTRL_RX: - begin - if(ctrl_rx_src_rdy && ctrl_rx_dst_rdy) - transfer_count <= transfer_count + 1; - else - state <= STATE_IDLE; - end - STATE_CTRL_TX: - begin - if(ctrl_tx_dst_rdy && ctrl_tx_src_rdy) - transfer_count <= transfer_count + 1; - else - state <= STATE_IDLE; - end - endcase + always @(posedge gpif_clk) + if(gpif_rst) begin + state <= STATE_IDLE; + sloe <= 1; + slrd <= 1; + slwr <= 1; + pktend <= 1; + rx_enable <= 0; + tx_valid <= 0; + ctrl_valid <= 0; + resp_enable <= 0; + idle_count <= 0; + end + else case (state) + STATE_IDLE: begin + transfer_count <= 0; + sloe <= 1; + slrd <= 1; + slwr <= 1; + pktend <= 1; + rx_enable <= 0; + tx_valid <= 0; + ctrl_valid <= 0; + resp_enable <= 0; + if (idle_count == 2'b11) state <= STATE_THINK; + idle_count <= idle_count + 1; + end + + STATE_THINK: begin + + idle_count <= 0; + + //handle transitions to other states + if(ctrl_ready & ~FX2_CE) begin //if there's room in the ctrl fifo and the FX2 has ctrl data + state <= STATE_CTRL_TX_SLOE; + fifoadr <= 2'b01; + sloe <= 0; end + else if(resp_valid & ~FX2_CF) begin //if the ctrl fifo has data and the FX2 isn't full + state <= STATE_CTRL_RX_ADR; + fifoadr <= 2'b11; + end + else if(tx_ready & ~FX2_DE & last_data_bus_hog == BUS_HOG_RX) begin //if there's room in the data fifo and the FX2 has data + state <= STATE_DATA_TX_SLOE; + last_data_bus_hog <= BUS_HOG_TX; + fifoadr <= 2'b00; + sloe <= 0; + end + else if(rx_valid & ~FX2_DF & last_data_bus_hog == BUS_HOG_TX) begin //if the data fifo has data and the FX2 isn't full + state <= STATE_DATA_RX_ADR; + last_data_bus_hog <= BUS_HOG_RX; + fifoadr <= 2'b10; + end + else if(tx_ready & ~FX2_DE) begin + state <= STATE_DATA_TX_SLOE; + last_data_bus_hog <= BUS_HOG_TX; + fifoadr <= 2'b00; + sloe <= 0; + end + else if(rx_valid & ~FX2_DF) begin + state <= STATE_DATA_RX_ADR; + last_data_bus_hog <= BUS_HOG_RX; + fifoadr <= 2'b10; + end + end + + STATE_DATA_TX_SLOE: begin //just to assert SLOE one cycle before SLRD + state <= STATE_DATA_TX; + slrd <= 0; + end - // /////////////////////////////////////////////////////////////////// - // fifo signal assignments and enables + STATE_CTRL_TX_SLOE: begin + state <= STATE_CTRL_TX; + slrd <= 0; + end - //enable fifos - assign data_rx_dst_rdy = (state == STATE_DATA_RX) && ~FX2_DF && (transfer_count != data_transfer_size); - assign data_tx_src_rdy = (state == STATE_DATA_TX) && ~FX2_DE && (transfer_count != data_transfer_size); - assign ctrl_rx_dst_rdy = (state == STATE_CTRL_RX) && ~FX2_CF; - assign ctrl_tx_src_rdy = (state == STATE_CTRL_TX) && ~FX2_CE; + STATE_DATA_RX_ADR: begin //just to assert FIFOADR one cycle before SLWR + state <= STATE_DATA_RX; + rx_enable <= 1; + end - //framing for TX ctrl packets - wire sop_ctrl, eop_ctrl; - assign sop_ctrl = (transfer_count == 0); - assign eop_ctrl = (transfer_count == (ctrl_transfer_size-1)); + STATE_CTRL_RX_ADR: begin + state <= STATE_CTRL_RX; + resp_enable <= 1; + end + + STATE_DATA_RX: begin + if (FX2_DF_pre || ~rx_valid || transfer_count == DATA_XFER_COUNT-1) begin + state <= STATE_IDLE; + rx_enable <= 0; + end + gpif_d_out <= gpif_d_out_data; + slwr <= ~rx_valid; + transfer_count <= transfer_count + 1; + end + + STATE_DATA_TX: begin + if (FX2_DE_pre || transfer_count == DATA_XFER_COUNT-1) begin + state <= STATE_IDLE; + slrd <= 1; + end + gpif_d_in <= gpif_d; + tx_valid <= 1; + transfer_count <= transfer_count + 1; + end + + STATE_CTRL_RX: begin + if (FX2_CF_pre || ~resp_valid || resp_eof || transfer_count == CTRL_XFER_COUNT-1) begin + state <= STATE_IDLE; + resp_enable <= 0; + end + pktend <= ~resp_eof; + gpif_d_out <= gpif_d_out_ctrl; + slwr <= ~resp_valid; + transfer_count <= transfer_count + 1; + end + + STATE_CTRL_TX: begin + if (FX2_CE_pre || transfer_count == CTRL_XFER_COUNT-1) begin + state <= STATE_IDLE; + slrd <= 1; + end + gpif_d_in <= gpif_d; + ctrl_valid <= 1; + transfer_count <= transfer_count + 1; + end + endcase - // //////////////////////////////////////////////////////////////////// - // set GPIF pins - - //set fifoadr to the appropriate endpoint - // {0,0}: EP2, data TX from host - // {0,1}: EP4, ctrl TX from host - // {1,0}: EP6, data RX to host - // {1,1}: EP8, ctrl RX to host - assign fifoadr = {(state == STATE_DATA_RX) | (state == STATE_CTRL_RX) | (state == STATE_DATA_RX_ADR) | (state == STATE_CTRL_RX_ADR) | (state == STATE_PKTEND) | (state == STATE_PKTEND_ADR), - (state == STATE_CTRL_RX) | (state == STATE_CTRL_RX_ADR) | (state == STATE_CTRL_TX) | (state == STATE_CTRL_TX_SLOE)}; - //set sloe, slwr, slrd (all active low) - //SLOE gets asserted when we want data from the FX2; i.e., TX mode - assign sloe = ~{(state == STATE_DATA_TX) | (state == STATE_CTRL_TX) | (state == STATE_DATA_TX_SLOE) | (state == STATE_CTRL_TX_SLOE)}; - //"read" and "write" here are from the master's point of view; - //so "read" means "transmit" and "write" means "receive" - assign slwr = ~{(data_rx_src_rdy && data_rx_dst_rdy) || (ctrl_rx_src_rdy && ctrl_rx_dst_rdy)}; - assign slrd = ~{(data_tx_src_rdy && data_tx_dst_rdy) || (ctrl_tx_src_rdy && ctrl_tx_dst_rdy)}; - - wire pktend_ctrl, pktend_data; - assign pktend_ctrl = ((~ctrl_rx_src_rdy | gpif_d_out_ctrl[17]) & (state == STATE_CTRL_RX)); - assign pktend_data = (state == STATE_PKTEND); - assign pktend = ~(pktend_ctrl | pktend_data); - - //mux between ctrl/data RX data out based on endpoint selection - assign gpif_d_out = fifoadr[0] ? gpif_d_out_ctrl : gpif_d_out_data; // GPIF output data lines, tristate - assign gpif_d = sloe ? gpif_d_out : 16'bz; + assign gpif_d = (sloe)? gpif_d_out[15:0] : 16'bz; // //////////////////////////////////////////////////////////////////// // TX Data Path - wire [15:0] txfifo_data; - wire txfifo_src_rdy, txfifo_dst_rdy; - wire [35:0] tx36_data; - wire tx36_src_rdy, tx36_dst_rdy; - wire [15:0] data_tx_2clk; - wire tx_src_rdy_2clk, tx_dst_rdy_2clk; - - wire [15:0] wr_fifo_space; - - always @(posedge gpif_clk) - tx_data_enough_space <= (wr_fifo_space >= data_transfer_size); - - fifo_cascade #(.WIDTH(16), .SIZE(12)) wr_fifo - (.clk(gpif_clk), .reset(gpif_rst), .clear(clear_tx), - .datain(gpif_d), .src_rdy_i(data_tx_src_rdy), .dst_rdy_o(data_tx_dst_rdy), .space(wr_fifo_space), - .dataout(txfifo_data), .src_rdy_o(txfifo_src_rdy), .dst_rdy_i(txfifo_dst_rdy), .occupied()); - - fifo_2clock_cascade #(.WIDTH(16), .SIZE(4)) wr_fifo_2clk - (.wclk(gpif_clk), .datain(txfifo_data), .src_rdy_i(txfifo_src_rdy), .dst_rdy_o(txfifo_dst_rdy), .space(), - .rclk(fifo_clk), .dataout(data_tx_2clk), .src_rdy_o(tx_src_rdy_2clk), .dst_rdy_i(tx_dst_rdy_2clk), .occupied(), - .arst(fifo_rst)); - - // join vita packets which are longer than one frame, add SOP/EOP/OCC - wire [18:0] refr_data; - wire refr_src_rdy, refr_dst_rdy; - //below 3 signals for debug only - wire refr_state; - wire refr_eof; - wire [15:0] refr_len; - - packet_reframer tx_packet_reframer - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), - .data_i(data_tx_2clk), .src_rdy_i(tx_src_rdy_2clk), .dst_rdy_o(tx_dst_rdy_2clk), - .data_o(refr_data), .src_rdy_o(refr_src_rdy), .dst_rdy_i(refr_dst_rdy), - .state(refr_state), .eof_out(refr_eof), .length(refr_len)); - - fifo19_to_fifo36 #(.LE(1)) f19_to_f36 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), - .f19_datain(refr_data), .f19_src_rdy_i(refr_src_rdy), .f19_dst_rdy_o(refr_dst_rdy), - .f36_dataout(tx36_data), .f36_src_rdy_o(tx36_src_rdy), .f36_dst_rdy_i(tx36_dst_rdy)); - - fifo_cascade #(.WIDTH(36), .SIZE(TXFIFOSIZE)) tx_fifo36 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), - .datain(tx36_data), .src_rdy_i(tx36_src_rdy), .dst_rdy_o(tx36_dst_rdy), - .dataout(tx_data_o), .src_rdy_o(tx_src_rdy_o), .dst_rdy_i(tx_dst_rdy_i)); + gpmc16_to_fifo36 #(.FIFO_SIZE(DATA_TX_FIFO_SIZE), .MIN_SPACE16(DATA_XFER_COUNT)) fifo36_to_gpmc16_tx( + .gpif_clk(gpif_clk), .gpif_rst(gpif_rst), + .in_data(gpif_d_in), .ready(tx_ready), .valid(tx_valid), + .fifo_clk(fifo_clk), .fifo_rst(fifo_rst), + .out_data(tx_data), .out_src_rdy(tx_src_rdy), .out_dst_rdy(tx_dst_rdy) + ); // //////////////////////////////////////////// // RX Data Path - wire [35:0] rx36_data; - wire rx36_src_rdy, rx36_dst_rdy; - wire [18:0] rx19_data; - wire rx19_src_rdy, rx19_dst_rdy; - wire [15:0] rxfifospace; - - //deep 36 bit wide input fifo buffers from DSP - fifo_cascade #(.WIDTH(36), .SIZE(9)) rx_fifo36 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), - .datain(rx_data_i), .src_rdy_i(rx_src_rdy_i), .dst_rdy_o(rx_dst_rdy_o), - .dataout(rx36_data), .src_rdy_o(rx36_src_rdy), .dst_rdy_i(rx36_dst_rdy)); - - //convert to fifo19 - fifo36_to_fifo19 #(.LE(1)) f36_to_f19 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), - .f36_datain(rx36_data), .f36_src_rdy_i(rx36_src_rdy), .f36_dst_rdy_o(rx36_dst_rdy), - .f19_dataout(rx19_data), .f19_src_rdy_o(rx19_src_rdy), .f19_dst_rdy_i(rx19_dst_rdy) ); - - wire [18:0] data_rx_int; - wire rx_src_rdy_int, rx_dst_rdy_int; - //clock domain crossing fifo for RX data - fifo_2clock_cascade #(.WIDTH(19), .SIZE(4)) rd_fifo_2clk - (.wclk(fifo_clk), .datain(rx19_data), .src_rdy_i(rx19_src_rdy), .dst_rdy_o(rx19_dst_rdy), .space(), - .rclk(~gpif_clk), .dataout(data_rx_int), .src_rdy_o(rx_src_rdy_int), .dst_rdy_i(rx_dst_rdy_int), .occupied(), - .arst(fifo_rst)); - - //rd_fifo buffers writes to the 2clock fifo above - fifo_cascade #(.WIDTH(18), .SIZE(RXFIFOSIZE)) rd_fifo - (.clk(~gpif_clk), .reset(gpif_rst), .clear(clear_rx), - .datain(data_rx_int[17:0]), .src_rdy_i(rx_src_rdy_int), .dst_rdy_o(rx_dst_rdy_int), .space(rxfifospace), - .dataout(gpif_d_out_data), .src_rdy_o(data_rx_src_rdy), .dst_rdy_i(data_rx_dst_rdy), .occupied()); + fifo36_to_gpmc16 #(.FIFO_SIZE(DATA_RX_FIFO_SIZE)) fifo36_to_gpmc16_rx( + .fifo_clk(fifo_clk), .fifo_rst(fifo_rst), + .in_data(rx_data), .in_src_rdy(rx_src_rdy), .in_dst_rdy(rx_dst_rdy), + .gpif_clk(gpif_clk), .gpif_rst(gpif_rst), + .out_data(gpif_d_out_data), .valid(rx_valid), .enable(rx_enable) + ); // //////////////////////////////////////////////////////////////////// - // FIFO to Wishbone interface + // CTRL TX Data Path - wire [18:0] resp_data, resp_int; - wire resp_src_rdy, resp_dst_rdy; - wire resp_src_rdy_int, resp_dst_rdy_int; - - wire [18:0] tx_err19_data; - wire tx_err19_src_rdy, tx_err19_dst_rdy; - - wire [18:0] ctrl_data; - wire ctrl_src_rdy, ctrl_dst_rdy; - - fifo_to_wb fifo_to_wb - (.clk(fifo_clk), .reset(fifo_rst), .clear(0), - .data_i(ctrl_data), .src_rdy_i(ctrl_src_rdy), .dst_rdy_o(ctrl_dst_rdy), - .data_o(resp_int), .src_rdy_o(resp_src_rdy_int), .dst_rdy_i(resp_dst_rdy_int), - .wb_adr_o(wb_adr_o), .wb_dat_mosi(wb_dat_mosi), .wb_dat_miso(wb_dat_miso), .wb_sel_o(wb_sel_o), - .wb_cyc_o(wb_cyc_o), .wb_stb_o(wb_stb_o), .wb_we_o(wb_we_o), .wb_ack_i(wb_ack_i), - .triggers(triggers), - .debug0(), .debug1()); - - // //////////////////////////////////////////////////////////////////// - // TX CTRL PATH (ctrl commands into Wishbone) + gpmc16_to_fifo36 #(.FIFO_SIZE(CTRL_TX_FIFO_SIZE), .MIN_SPACE16(CTRL_XFER_COUNT)) fifo36_to_gpmc16_ctrl( + .gpif_clk(gpif_clk), .gpif_rst(gpif_rst), + .in_data(gpif_d_in), .ready(ctrl_ready), .valid(ctrl_valid), + .fifo_clk(fifo_clk), .fifo_rst(fifo_rst), + .out_data(ctrl_data), .out_src_rdy(ctrl_src_rdy), .out_dst_rdy(ctrl_dst_rdy) + ); - //how does this use fifo_clk instead of wb_clk - //answer: on b100 fifo clk IS wb clk - fifo_2clock_cascade #(.WIDTH(19), .SIZE(4)) ctrl_fifo_2clk - (.wclk(gpif_clk), .datain({1'b0,eop_ctrl,sop_ctrl,gpif_d}), - .src_rdy_i(ctrl_tx_src_rdy), .dst_rdy_o(ctrl_tx_dst_rdy), .space(), - .rclk(fifo_clk), .dataout(ctrl_data), - .src_rdy_o(ctrl_src_rdy), .dst_rdy_i(ctrl_dst_rdy), .occupied(), - .arst(fifo_rst)); + // //////////////////////////////////////////// + // CTRL RX Data Path + + fifo36_to_gpmc16 #(.FIFO_SIZE(CTRL_RX_FIFO_SIZE)) fifo36_to_gpmc16_resp( + .fifo_clk(fifo_clk), .fifo_rst(fifo_rst), + .in_data(resp_data), .in_src_rdy(resp_src_rdy), .in_dst_rdy(resp_dst_rdy), + .gpif_clk(gpif_clk), .gpif_rst(gpif_rst), + .out_data(gpif_d_out_ctrl), .valid(resp_valid), .enable(resp_enable), + .eof(resp_eof) + ); - // //////////////////////////////////////////////////////////////////// - // RX CTRL PATH (async packets, ctrl response data) - - //tx_err_data_i is the 36wide tx async err data clocked on fifo_clk - fifo36_to_fifo19 #(.LE(1)) f36_to_f19_txerr - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), - .f36_datain(tx_err_data_i), .f36_src_rdy_i(tx_err_src_rdy_i), .f36_dst_rdy_o(tx_err_dst_rdy_o), - .f19_dataout(tx_err19_data), .f19_src_rdy_o(tx_err19_src_rdy), .f19_dst_rdy_i(tx_err19_dst_rdy) ); - - //mux FIFO-to-WB along with async tx err pkts into one ctrl resp fifo - //how is this clocked on wb_clk? - fifo19_mux #(.prio(0)) mux_err_stream - (.clk(wb_clk), .reset(wb_rst), .clear(clear_rx), - .data0_i(resp_int), .src0_rdy_i(resp_src_rdy_int), .dst0_rdy_o(resp_dst_rdy_int), - .data1_i(tx_err19_data), .src1_rdy_i(tx_err19_src_rdy), .dst1_rdy_o(tx_err19_dst_rdy), - .data_o(resp_data), .src_rdy_o(resp_src_rdy), .dst_rdy_i(resp_dst_rdy)); - - //clock domain crossing cascade fifo for mux_err_stream to get from wb_clk to gpif_clk - //the output of this fifo is CTRL DATA PENDING FOR GPIF - fifo_2clock_cascade #(.WIDTH(18), .SIZE(4)) resp_fifo_2clk - (.wclk(wb_clk), .datain(resp_data[17:0]), .src_rdy_i(resp_src_rdy), .dst_rdy_o(resp_dst_rdy), .space(), - .rclk(~gpif_clk), .dataout(gpif_d_out_ctrl), - .src_rdy_o(ctrl_rx_src_rdy), .dst_rdy_i(ctrl_rx_dst_rdy), .occupied(), - .arst(wb_rst)); - - - // //////////////////////////////////////////////////////////////////// - // Debug support, timed and loopback - // RX side muxes test data into the same stream + assign debug = 0; - /////////////////////////////////////////////////////////////////////// - // debug lines - wire [31:0] debug_rd, debug_wr, debug_split0, debug_split1; - - wire [35:0] timedrx_data, loopbackrx_data, testrx_data; - wire [35:0] timedtx_data, loopbacktx_data, testtx_data; - wire timedrx_src_rdy, timedrx_dst_rdy, loopbackrx_src_rdy, loopbackrx_dst_rdy, - testrx_src_rdy, testrx_dst_rdy; - wire timedtx_src_rdy, timedtx_dst_rdy, loopbacktx_src_rdy, loopbacktx_dst_rdy, - testtx_src_rdy, testtx_dst_rdy; - wire timedrx_src_rdy_int, timedrx_dst_rdy_int, timedtx_src_rdy_int, timedtx_dst_rdy_int; - - wire [31:0] total, crc_err, seq_err, len_err; - wire sel_testtx = test_ctrl[0]; - wire sel_loopbacktx = test_ctrl[1]; - wire pkt_src_enable = test_ctrl[2]; - wire pkt_sink_enable = test_ctrl[3]; -/* - fifo36_mux rx_test_mux_lvl_1 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), - .data0_i(timedrx_data), .src0_rdy_i(timedrx_src_rdy), .dst0_rdy_o(timedrx_dst_rdy), - .data1_i(loopbackrx_data), .src1_rdy_i(loopbackrx_src_rdy), .dst1_rdy_o(loopbackrx_dst_rdy), - .data_o(testrx_data), .src_rdy_o(testrx_src_rdy), .dst_rdy_i(testrx_dst_rdy)); - - fifo36_mux rx_test_mux_lvl_2 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), - .data0_i(testrx_data), .src0_rdy_i(testrx_src_rdy), .dst0_rdy_o(testrx_dst_rdy), - .data1_i(rx_data_i), .src1_rdy_i(rx_src_rdy_i), .dst1_rdy_o(rx_dst_rdy_o), - .data_o(rx_data), .src_rdy_o(rx_src_rdy), .dst_rdy_i(rx_dst_rdy)); - - fifo_short #(.WIDTH(36)) loopback_fifo - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx | clear_rx), - .datain(loopbacktx_data), .src_rdy_i(loopbacktx_src_rdy), .dst_rdy_o(loopbacktx_dst_rdy), - .dataout(loopbackrx_data), .src_rdy_o(loopbackrx_src_rdy), .dst_rdy_i(loopbackrx_dst_rdy)); - - // Crossbar used as a demux for switching TX stream to main DSP or to test logic - crossbar36 tx_crossbar_lvl_1 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), - .cross(sel_testtx), - .data0_i(tx_data), .src0_rdy_i(tx_src_rdy), .dst0_rdy_o(tx_dst_rdy), - .data1_i(tx_data), .src1_rdy_i(1'b0), .dst1_rdy_o(), // No 2nd input - .data0_o(tx_data_o), .src0_rdy_o(tx_src_rdy_o), .dst0_rdy_i(tx_dst_rdy_i), - .data1_o(testtx_data), .src1_rdy_o(testtx_src_rdy), .dst1_rdy_i(testtx_dst_rdy) ); - - crossbar36 tx_crossbar_lvl_2 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), - .cross(sel_loopbacktx), - .data0_i(testtx_data), .src0_rdy_i(testtx_src_rdy), .dst0_rdy_o(testtx_dst_rdy), - .data1_i(testtx_data), .src1_rdy_i(1'b0), .dst1_rdy_o(), // No 2nd input - .data0_o(timedtx_data), .src0_rdy_o(timedtx_src_rdy), .dst0_rdy_i(timedtx_dst_rdy), - .data1_o(loopbacktx_data), .src1_rdy_o(loopbacktx_src_rdy), .dst1_rdy_i(loopbacktx_dst_rdy) ); - - // Fixed rate TX traffic consumer - fifo_pacer tx_pacer - (.clk(fifo_clk), .reset(fifo_rst), .rate(test_rate), .enable(pkt_sink_enable), - .src1_rdy_i(timedtx_src_rdy), .dst1_rdy_o(timedtx_dst_rdy), - .src2_rdy_o(timedtx_src_rdy_int), .dst2_rdy_i(timedtx_dst_rdy_int), - .underrun(tx_underrun), .overrun()); - - packet_verifier32 pktver32 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), - .data_i(timedtx_data), .src_rdy_i(timedtx_src_rdy_int), .dst_rdy_o(timedtx_dst_rdy_int), - .total(total), .crc_err(crc_err), .seq_err(seq_err), .len_err(len_err)); - - // Fixed rate RX traffic generator - vita_pkt_gen pktgen - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), - .len(test_len), - .data_o(timedrx_data), .src_rdy_o(timedrx_src_rdy_int), .dst_rdy_i(timedrx_dst_rdy_int)); - - fifo_pacer rx_pacer - (.clk(fifo_clk), .reset(fifo_rst), .rate(test_rate), .enable(pkt_src_enable), - .src1_rdy_i(timedrx_src_rdy_int), .dst1_rdy_o(timedrx_dst_rdy_int), - .src2_rdy_o(timedrx_src_rdy), .dst2_rdy_i(timedrx_dst_rdy), - .underrun(), .overrun(rx_overrun)); -*/ - // //////////////////////////////////////////// - // DEBUG - - assign debug0 = { pktend_latch, data_rx_src_rdy, gpif_ctl[3:0], sloe, slrd, slwr, pktend, fifoadr[1:0], state[3:0], gpif_d[15:0]}; - //assign debug0 = { data_tx_src_rdy, data_tx_dst_rdy, tx_src_rdy_int, tx_dst_rdy_int, - // tx19_src_rdy, tx19_dst_rdy, refr_src_rdy, refr_dst_rdy, - // tx36_src_rdy, tx36_dst_rdy, - // gpif_ctl[3:0], fifoadr[1:0], - // wr_fifo_space[15:0]}; - assign debug1 = { 16'b0, transfer_count[7:0], ctrl_rx_src_rdy, ctrl_tx_dst_rdy, data_rx_src_rdy, - data_tx_dst_rdy, ctrl_tx_src_rdy, ctrl_rx_dst_rdy, data_tx_src_rdy, data_rx_dst_rdy}; endmodule // slave_fifo diff --git a/fpga/usrp2/top/B100/B100.v b/fpga/usrp2/top/B100/B100.v index dcda974b4..cb4efa4fa 100644 --- a/fpga/usrp2/top/B100/B100.v +++ b/fpga/usrp2/top/B100/B100.v @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 Ettus Research LLC // // 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 @@ -50,6 +50,8 @@ module B100 // ///////////////////////////////////////////////////////////////////////// // Clocking wire clk_fpga, clk_fpga_in, reset; + wire gpif_clk = IFCLK; + wire gpif_rst; IBUFGDS #(.IOSTANDARD("LVDS_33"), .DIFF_TERM("TRUE")) clk_fpga_pin (.O(clk_fpga_in),.I(CLK_FPGA_P),.IB(CLK_FPGA_N)); @@ -57,6 +59,7 @@ module B100 BUFG clk_fpga_BUFG (.I(clk_fpga_in), .O(clk_fpga)); reset_sync reset_sync(.clk(clk_fpga), .reset_in((~reset_n) | (~ext_reset)), .reset_out(reset)); + reset_sync reset_sync_gpif(.clk(gpif_clk), .reset_in((~reset_n) | (~ext_reset)), .reset_out(gpif_rst)); // ///////////////////////////////////////////////////////////////////////// // SPI @@ -105,6 +108,7 @@ module B100 // ///////////////////////////////////////////////////////////////////////// // RX ADC -- handles deinterleaving + wire rxsync_0, rxsync_1; reg [11:0] rx_i, rx_q; wire [11:0] rx_a, rx_b; @@ -151,24 +155,60 @@ module B100 rx_i <= rx_a; rx_q <= rx_b; end - + // ///////////////////////////////////////////////////////////////////////// - // Main U1E Core - u1plus_core u1p_c(.clk_fpga(clk_fpga), .rst_fpga(reset), - .debug_led(debug_led), .debug(debug), .debug_clk(debug_clk), - .debug_txd(), .debug_rxd(1'b1), - - .gpif_d(GPIF_D), .gpif_ctl(GPIF_CTL), .gpif_pktend(GPIF_PKTEND), - .gpif_sloe(GPIF_SLOE), .gpif_slwr(GPIF_SLWR), .gpif_slrd(GPIF_SLRD), - .gpif_fifoadr(GPIF_ADR), .gpif_clk(IFCLK), - - .db_sda(SDA_FPGA), .db_scl(SCL_FPGA), - .sclk(sclk), .sen({SEN_CODEC,SEN_TX_DB,SEN_RX_DB}), .mosi(mosi), .miso(miso), - .cgen_st_status(cgen_st_status), .cgen_st_ld(cgen_st_ld),.cgen_st_refmon(cgen_st_refmon), - .cgen_sync_b(cgen_sync_b), .cgen_ref_sel(cgen_ref_sel), - .io_tx(io_tx), .io_rx(io_rx), - .tx_i(tx_i), .tx_q(tx_q), - .rx_i(rx_i), .rx_q(rx_q), - .pps_in(PPS_IN) ); + // Main Core + wire [35:0] rx_data, tx_data, ctrl_data, resp_data; + wire rx_src_rdy, rx_dst_rdy, tx_src_rdy, tx_dst_rdy, resp_src_rdy, resp_dst_rdy, ctrl_src_rdy, ctrl_dst_rdy; + wire dsp_rx_run, dsp_tx_run; + wire [7:0] sen8; + assign {SEN_CODEC,SEN_TX_DB,SEN_RX_DB} = sen8[2:0]; + wire [31:0] core_debug; + + assign debug_led = {dsp_tx_run, dsp_rx_run, cgen_st_ld}; + wire cgen_sync; + assign { cgen_sync_b, cgen_ref_sel } = {~cgen_sync, 1'b1}; + + u1plus_core #( + .NUM_RX_DSPS(1), + .DSP_RX_XTRA_FIFOSIZE(11), + .DSP_TX_XTRA_FIFOSIZE(12), + .USE_PACKET_PADDER(1) + ) core( + .clk(clk_fpga), .reset(reset), + .debug(core_debug), .debug_clk(debug_clk), + + .rx_data(rx_data), .rx_src_rdy(rx_src_rdy), .rx_dst_rdy(rx_dst_rdy), + .tx_data(tx_data), .tx_src_rdy(tx_src_rdy), .tx_dst_rdy(tx_dst_rdy), + .ctrl_data(ctrl_data), .ctrl_src_rdy(ctrl_src_rdy), .ctrl_dst_rdy(ctrl_dst_rdy), + .resp_data(resp_data), .resp_src_rdy(resp_src_rdy), .resp_dst_rdy(resp_dst_rdy), + + .dsp_rx_run(dsp_rx_run), .dsp_tx_run(dsp_tx_run), + .clock_sync(cgen_sync), + + .db_sda(SDA_FPGA), .db_scl(SCL_FPGA), + .sclk(sclk), .sen(sen8), .mosi(mosi), .miso(miso), + .io_tx(io_tx), .io_rx(io_rx), + .tx_i(tx_i), .tx_q(tx_q), + .rx_i(rx_i), .rx_q(rx_q), + .pps_in(PPS_IN) ); + + // ///////////////////////////////////////////////////////////////////////// + // Interface from host to/from GPIF + wire [31:0] gpif_debug; + slave_fifo slave_fifo (.gpif_clk(gpif_clk), .gpif_rst(gpif_rst), .gpif_d(GPIF_D), + .gpif_ctl(GPIF_CTL), .sloe(GPIF_SLOE), .slwr(GPIF_SLWR), .slrd(GPIF_SLRD), + .pktend(GPIF_PKTEND), .fifoadr(GPIF_ADR), + + .fifo_clk(clk_fpga), .fifo_rst(reset), + .rx_data(rx_data), .rx_src_rdy(rx_src_rdy), .rx_dst_rdy(rx_dst_rdy), + .tx_data(tx_data), .tx_src_rdy(tx_src_rdy), .tx_dst_rdy(tx_dst_rdy), + .ctrl_data(ctrl_data), .ctrl_src_rdy(ctrl_src_rdy), .ctrl_dst_rdy(ctrl_dst_rdy), + .resp_data(resp_data), .resp_src_rdy(resp_src_rdy), .resp_dst_rdy(resp_dst_rdy), + + .debug(gpif_debug)); + + //assign debug = gpif_debug; + assign debug = core_debug; endmodule // B100 diff --git a/fpga/usrp2/top/B100/core_compile b/fpga/usrp2/top/B100/core_compile index b62cbaee0..2192bfa94 100755 --- a/fpga/usrp2/top/B100/core_compile +++ b/fpga/usrp2/top/B100/core_compile @@ -1 +1 @@ -iverilog -Wall -y. -y ../../control_lib/ -y ../../custom/ -y ../../fifo/ -y ../../gpif/ -y ../../models/ -y ../../sdr_lib/ -y ../../coregen/ -y ../../vrt/ -y ../../opencores/i2c/rtl/verilog/ -y ../../opencores/spi/rtl/verilog/ -y ../../timing/ -y ../../opencores/8b10b/ -I ../../opencores/spi/rtl/verilog/ -I ../../opencores/i2c/rtl/verilog/ -y ../../simple_gemac u1plus_core.v 2>&1 | grep -v timescale | grep -v coregen | grep -v models +iverilog -Wall -y. -y ../../control_lib/ -y ../../custom/ -y ../../fifo/ -y ../../gpif/ -y ../../models/ -y ../../sdr_lib/ -y ../../coregen/ -y ../../vrt/ -y ../../opencores/i2c/rtl/verilog/ -y ../../opencores/spi/rtl/verilog/ -y ../../timing/ -y ../../opencores/8b10b/ -I ../../opencores/spi/rtl/verilog/ -I ../../opencores/i2c/rtl/verilog/ -y ../../simple_gemac B100.v 2>&1 | grep -v timescale | grep -v coregen | grep -v models diff --git a/fpga/usrp2/top/B100/timing.ucf b/fpga/usrp2/top/B100/timing.ucf index 96c47cf2c..c4404e1d0 100644 --- a/fpga/usrp2/top/B100/timing.ucf +++ b/fpga/usrp2/top/B100/timing.ucf @@ -5,10 +5,15 @@ NET "IFCLK" TNM_NET = "IFCLK"; TIMESPEC "TS_IFCLK" = PERIOD "IFCLK" 20833 ps HIGH 50 %; #constrain FX2 IO -NET "GPIF_D<*>" MAXDELAY = 5.5 ns; -NET "GPIF_CTL<*>" MAXDELAY = 5.5 ns; -NET "GPIF_ADR<*>" MAXDELAY = 5.5ns; -NET "GPIF_SLWR" MAXDELAY = 5.5 ns; -NET "GPIF_SLRD" MAXDELAY = 5.5 ns; -NET "GPIF_SLOE" MAXDELAY = 5.5 ns; -NET "GPIF_PKTEND" MAXDELAY = 5.5 ns; +INST "GPIF_D<*>" TNM = gpif_net_in; +INST "GPIF_CTL<*>" TNM = gpif_net_in; + +INST "GPIF_D<*>" TNM = gpif_net_out; +INST "GPIF_ADR<*>" TNM = gpif_net_out; +INST "GPIF_SLWR" TNM = gpif_net_out; +INST "GPIF_SLOE" TNM = gpif_net_out; +INST "GPIF_SLRD" TNM = gpif_net_out; +INST "GPIF_PKTEND" TNM = gpif_net_out; + +TIMEGRP "gpif_net_in" OFFSET = IN 7 ns VALID 14 ns BEFORE "IFCLK" RISING; +TIMEGRP "gpif_net_out" OFFSET = OUT 7 ns AFTER "IFCLK" RISING; diff --git a/fpga/usrp2/top/B100/u1plus_core.v b/fpga/usrp2/top/B100/u1plus_core.v index e5af189cc..691ad1f75 100644 --- a/fpga/usrp2/top/B100/u1plus_core.v +++ b/fpga/usrp2/top/B100/u1plus_core.v @@ -18,435 +18,332 @@ module u1plus_core - (input clk_fpga, input rst_fpga, - output [2:0] debug_led, output [31:0] debug, output [1:0] debug_clk, - output debug_txd, input debug_rxd, - - // GPIF - inout [15:0] gpif_d, input [3:0] gpif_ctl, output gpif_sloe, - output gpif_slwr, output gpif_slrd, output gpif_pktend, output [1:0] gpif_fifoadr, - input gpif_clk, - +#( + parameter NUM_RX_DSPS = 2, + parameter CTRL_ACK_SID = 20, //needed for reply + + parameter DSP_TX_FIFOSIZE = 10, //4K MTU + parameter DSP_RX_FIFOSIZE = 10, //4K MTU + + parameter DSP_RX_XTRA_FIFOSIZE = 11, + parameter DSP_TX_XTRA_FIFOSIZE = 11, + + parameter USE_PACKET_PADDER = 0 +) + (input clk, input reset, + output [31:0] debug, output [1:0] debug_clk, + + // Host Interface + input [35:0] tx_data, input tx_src_rdy, output tx_dst_rdy, + output [35:0] rx_data, output rx_src_rdy, input rx_dst_rdy, + input [35:0] ctrl_data, input ctrl_src_rdy, output ctrl_dst_rdy, + output [35:0] resp_data, output resp_src_rdy, input resp_dst_rdy, + + output dsp_rx_run, output dsp_tx_run, output clock_sync, + inout db_sda, inout db_scl, - output sclk, output [15:0] sen, output mosi, input miso, + output sclk, output [7:0] sen, output mosi, input miso, - input cgen_st_status, input cgen_st_ld, input cgen_st_refmon, output cgen_sync_b, output cgen_ref_sel, - inout [15:0] io_tx, inout [15:0] io_rx, - output [13:0] tx_i, output [13:0] tx_q, - input [11:0] rx_i, input [11:0] rx_q, + inout [15:0] io_tx, inout [15:0] io_rx, + output [13:0] tx_i, output [13:0] tx_q, + input [11:0] rx_i, input [11:0] rx_q, input pps_in ); - localparam TXFIFOSIZE = 11; - localparam RXFIFOSIZE = 12; - - // 64 total regs in address space - localparam SR_RX_CTRL0 = 0; // 9 regs (+0 to +8) - localparam SR_RX_DSP0 = 10; // 4 regs (+0 to +3) - localparam SR_RX_CTRL1 = 16; // 9 regs (+0 to +8) - localparam SR_RX_DSP1 = 26; // 4 regs (+0 to +3) - localparam SR_TX_CTRL = 32; // 4 regs (+0 to +3) - localparam SR_TX_DSP = 38; // 3 regs (+0 to +2) - - localparam SR_TIME64 = 42; // 6 regs (+0 to +5) - localparam SR_RX_FRONT = 48; // 5 regs (+0 to +4) - localparam SR_TX_FRONT = 54; // 5 regs (+0 to +4) - - localparam SR_REG_TEST32 = 60; // 1 reg - localparam SR_CLEAR_FIFO = 61; // 1 reg - localparam SR_GLOBAL_RESET = 63; // 1 reg - localparam SR_USER_REGS = 64; // 2 regs - - localparam SR_GPIO = 128; // 5 regs - - wire wb_clk = clk_fpga; - wire wb_rst, global_reset; - - wire pps_int; - wire [63:0] vita_time, vita_time_pps; - reg [15:0] reg_cgen_ctrl, reg_test; - - wire [7:0] set_addr, set_addr_user; - wire [31:0] set_data, set_data_user; - wire set_stb, set_stb_user; - - wire [31:0] debug0; - wire [31:0] debug1; - - wire [31:0] debug_vt; - wire gpif_rst; - - reg [7:0] frames_per_packet; - - wire rx_overrun_dsp0, rx_overrun_dsp1, rx_overrun_gpif, tx_underrun_dsp, tx_underrun_gpif; - wire rx_overrun = rx_overrun_gpif | rx_overrun_dsp0 | rx_overrun_dsp1; - wire tx_underrun = tx_underrun_gpif | tx_underrun_dsp; - - setting_reg #(.my_addr(SR_GLOBAL_RESET), .width(1)) sr_reset - (.clk(wb_clk),.rst(wb_rst),.strobe(set_stb),.addr(set_addr), - .in(set_data),.out(),.changed(global_reset)); - - reset_sync reset_sync_wb(.clk(wb_clk), .reset_in(rst_fpga | global_reset), .reset_out(wb_rst)); - reset_sync reset_sync_gp(.clk(gpif_clk), .reset_in(rst_fpga | global_reset), .reset_out(gpif_rst)); - - // ///////////////////////////////////////////////////////////////////////////////////// - // GPIF Slave to Wishbone Master - localparam dw = 16; - localparam aw = 11; - localparam sw = 2; - - wire [dw-1:0] m0_dat_mosi, m0_dat_miso; - wire [aw-1:0] m0_adr; - wire [sw-1:0] m0_sel; - wire m0_cyc, m0_stb, m0_we, m0_ack, m0_err, m0_rty; - - wire [31:0] debug_gpif; - - wire [35:0] tx_data, rx_data, tx_err_data; - wire tx_src_rdy, tx_dst_rdy, rx_src_rdy, rx_dst_rdy, - tx_err_src_rdy, tx_err_dst_rdy; - - wire clear_fifo; - - setting_reg #(.my_addr(SR_CLEAR_FIFO), .width(1)) sr_clear_fifo - (.clk(wb_clk),.rst(wb_rst),.strobe(set_stb),.addr(set_addr), - .in(set_data),.out(),.changed(clear_fifo)); - - wire run_rx0, run_rx1; - - slave_fifo #(.TXFIFOSIZE(TXFIFOSIZE), .RXFIFOSIZE(RXFIFOSIZE)) - slave_fifo (.gpif_clk(gpif_clk), .gpif_rst(gpif_rst), .gpif_d(gpif_d), - .gpif_ctl(gpif_ctl), .sloe(gpif_sloe), .slwr(gpif_slwr), .slrd(gpif_slrd), - .pktend(gpif_pktend), .fifoadr(gpif_fifoadr), - - .wb_clk(wb_clk), .wb_rst(wb_rst), - .wb_adr_o(m0_adr), .wb_dat_mosi(m0_dat_mosi), .wb_dat_miso(m0_dat_miso), - .wb_sel_o(m0_sel), .wb_cyc_o(m0_cyc), .wb_stb_o(m0_stb), .wb_we_o(m0_we), - .wb_ack_i(m0_ack), .triggers(8'd0), - - .dsp_rx_run(run_rx0 | run_rx1), - - .fifo_clk(wb_clk), .fifo_rst(wb_rst), .clear_tx(clear_fifo), .clear_rx(clear_fifo), - .tx_data_o(tx_data), .tx_src_rdy_o(tx_src_rdy), .tx_dst_rdy_i(tx_dst_rdy), - .rx_data_i(rx_data), .rx_src_rdy_i(rx_src_rdy), .rx_dst_rdy_o(rx_dst_rdy), - .tx_err_data_i(tx_err_data), .tx_err_src_rdy_i(tx_err_src_rdy), .tx_err_dst_rdy_o(tx_err_dst_rdy), - - .tx_underrun(tx_underrun_gpif), .rx_overrun(rx_overrun_gpif), - - .test_len(0), .test_rate(0), .test_ctrl(0), - .debug0(debug0), .debug1(debug1)); + localparam SR_MISC = 0; // 5 + localparam SR_USER_REGS = 5; // 2 + localparam SR_PADDER = 10; // 2 + + localparam SR_TX_CTRL = 32; // 6 + localparam SR_TX_DSP = 40; // 5 + localparam SR_TX_FE = 48; // 5 + + localparam SR_RX_CTRL0 = 96; // 9 + localparam SR_RX_DSP0 = 106; // 7 + localparam SR_RX_FE = 114; // 5 + + localparam SR_RX_CTRL1 = 128; // 9 + localparam SR_RX_DSP1 = 138; // 7 + + localparam SR_TIME64 = 192; // 6 + localparam SR_SPI = 208; // 3 + localparam SR_I2C = 216; // 1 + localparam SR_GPIO = 224; // 5 + + //compatibility number -> increment when the fpga has been sufficiently altered + localparam compat_num = {16'd11, 16'd0}; //major, minor + + //assign run signals used for ATR logic + wire [NUM_RX_DSPS-1:0] run_rx_n; + wire run_tx; + wire run_rx = |(run_rx_n); + assign dsp_rx_run = run_rx; + assign dsp_tx_run = run_tx; + + //shared time core signals + wire [63:0] vita_time, vita_time_pps; + + //shared settings bus signals + wire set_stb, set_stb_user; + wire [31:0] set_data, set_data_user; + wire [7:0] set_addr, set_addr_user; + + //shared SPI core signals + wire [31:0] spi_readback; + wire spi_ready; + + //shared I2C core signals + wire [31:0] i2c_readback; + wire i2c_ready; + + //shared GPIO core signals + wire [31:0] gpio_readback; + + /////////////////////////////////////////////////////////////////////////// + // Misc Registers - persistent across resets + /////////////////////////////////////////////////////////////////////////// + wire [31:0] config_word0; + setting_reg #(.my_addr(SR_MISC+0), .width(32)) sr_misc_config0 + (.clk(clk), .rst(1'b0/*reset*/), .strobe(set_stb), .addr(set_addr), .in(set_data), .out(config_word0)); + wire [31:0] config_word1; + setting_reg #(.my_addr(SR_MISC+1), .width(32)) sr_misc_config1 + (.clk(clk), .rst(1'b0/*reset*/), .strobe(set_stb), .addr(set_addr), .in(set_data), .out(config_word1)); + + /////////////////////////////////////////////////////////////////////////// + // Settings Bus and Readback + /////////////////////////////////////////////////////////////////////////// + user_settings #(.BASE(SR_USER_REGS)) user_settings + (.clk(clk),.rst(reset), + .set_stb(set_stb), .set_addr(set_addr),.set_data(set_data), + .set_addr_user(set_addr_user),.set_data_user(set_data_user), .set_stb_user(set_stb_user) ); + + wire [35:0] ctrl_out_data, ctrl_int_data; + wire ctrl_out_src_rdy, ctrl_out_dst_rdy; + wire ctrl_int_src_rdy, ctrl_int_dst_rdy; + + fifo_cascade #(.WIDTH(36), .SIZE(9)) ctrl_fifo + (.clk(clk), .reset(reset), .clear(1'b0), + .datain(ctrl_data), .src_rdy_i(ctrl_src_rdy), .dst_rdy_o(ctrl_dst_rdy), .space(), + .dataout(ctrl_int_data), .src_rdy_o(ctrl_int_src_rdy), .dst_rdy_i(ctrl_int_dst_rdy), .occupied()); + + wire [31:0] num_rx_dsps_rb = NUM_RX_DSPS; + + wire [31:0] sfc_debug; + settings_fifo_ctrl #(.PROT_HDR(0), .ACK_SID(CTRL_ACK_SID), .XPORT_HDR(0)) sfc + ( + .clock(clk), .reset(reset), .clear(1'b0), + .vita_time(vita_time), .perfs_ready(spi_ready & i2c_ready), + .in_data(ctrl_int_data), .in_valid(ctrl_int_src_rdy), .in_ready(ctrl_int_dst_rdy), + .out_data(ctrl_out_data), .out_valid(ctrl_out_src_rdy), .out_ready(ctrl_out_dst_rdy), + .strobe(set_stb), .addr(set_addr), .data(set_data), + .word00(spi_readback),.word01(compat_num),.word02(i2c_readback),.word03(gpio_readback), + .word04(config_word0),.word05(config_word1),.word06(num_rx_dsps_rb),.word07(32'hffff_ffff), + .word08(32'hffff_ffff),.word09(32'hffff_ffff),.word10(vita_time[63:32]), + .word11(vita_time[31:0]),.word12(32'hffff_ffff),.word13(32'hffff_ffff), + .word14(vita_time_pps[63:32]),.word15(vita_time_pps[31:0]), + .debug(sfc_debug) + ); + + /////////////////////////////////////////////////////////////////////////// + // Time Core + /////////////////////////////////////////////////////////////////////////// + time_64bit #(.BASE(SR_TIME64)) time_64bit + (.clk(clk), .rst(reset), .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .pps(pps_in), .vita_time(vita_time), .vita_time_pps(vita_time_pps), + .exp_time_in(0)); + assign clock_sync = 1'b0; + + /////////////////////////////////////////////////////////////////////////// + // SPI Core + /////////////////////////////////////////////////////////////////////////// + simple_spi_core #(.BASE(SR_SPI), .WIDTH(8), .CLK_IDLE(0), .SEN_IDLE(8'hff)) + simple_spi_core (.clock(clk), .reset(reset), + .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .readback(spi_readback), .ready(spi_ready), + .sen(sen), .sclk(sclk), .mosi(mosi), .miso(miso)); + + /////////////////////////////////////////////////////////////////////////// + // I2C Core + /////////////////////////////////////////////////////////////////////////// + wire scl_pad_i, scl_pad_o, scl_pad_oen_o, sda_pad_i, sda_pad_o, sda_pad_oen_o; + simple_i2c_core #(.BASE(SR_I2C)) i2c_core + (.clock(clk),.reset(reset), + .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .readback(i2c_readback), .ready(i2c_ready), + .scl_pad_i(scl_pad_i),.scl_pad_o(scl_pad_o),.scl_padoen_o(scl_pad_oen_o), + .sda_pad_i(sda_pad_i),.sda_pad_o(sda_pad_o),.sda_padoen_o(sda_pad_oen_o) ); + + // I2C -- Don't use external transistors for open drain, the FPGA implements this + IOBUF scl_pin(.O(scl_pad_i), .IO(db_scl), .I(scl_pad_o), .T(scl_pad_oen_o)); + IOBUF sda_pin(.O(sda_pad_i), .IO(db_sda), .I(sda_pad_o), .T(sda_pad_oen_o)); + + /////////////////////////////////////////////////////////////////////////// + // GPIO Core + /////////////////////////////////////////////////////////////////////////// + gpio_atr #(.BASE(SR_GPIO), .WIDTH(32)) + 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({io_tx, io_rx}), .gpio_readback(gpio_readback) ); // ///////////////////////////////////////////////////////////////////////// // RX ADC Frontend, does IQ Balance, DC Offset, muxing wire [23:0] rx_fe_i, rx_fe_q; // 24 bits is total overkill here, but it matches u2/u2p - rx_frontend #(.BASE(SR_RX_FRONT)) rx_frontend - (.clk(wb_clk),.rst(wb_rst), + rx_frontend #(.BASE(SR_RX_FE)) rx_frontend + (.clk(clk),.rst(reset), .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), .adc_a({rx_i,4'b00}),.adc_ovf_a(0), .adc_b({rx_q,4'b00}),.adc_ovf_b(0), - .i_out(rx_fe_i), .q_out(rx_fe_q), .run(run_rx0 | run_rx1), .debug()); - - // ///////////////////////////////////////////////////////////////////////// - // DSP RX 0 - - wire [31:0] sample_rx0; - wire strobe_rx0, clear_rx0; - wire [35:0] vita_rx_data0; - wire vita_rx_src_rdy0, vita_rx_dst_rdy0; - - ddc_chain #(.BASE(SR_RX_DSP0), .DSPNO(0)) ddc_chain0 - (.clk(wb_clk), .rst(wb_rst), .clr(clear_rx0), - .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), - .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), - .rx_fe_i(rx_fe_i),.rx_fe_q(rx_fe_q), - .sample(sample_rx0), .run(run_rx0), .strobe(strobe_rx0), - .debug() ); + .i_out(rx_fe_i), .q_out(rx_fe_q), .run(run_rx), .debug()); - vita_rx_chain #(.BASE(SR_RX_CTRL0), .UNIT(0), .FIFOSIZE(10), .PROT_ENG_FLAGS(0), .DSP_NUMBER(0)) vita_rx_chain0 - (.clk(wb_clk),.reset(wb_rst), - .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), - .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), - .vita_time(vita_time), .overrun(rx_overrun_dsp0), - .sample(sample_rx0), .run(run_rx0), .strobe(strobe_rx0), .clear_o(clear_rx0), - .rx_data_o(vita_rx_data0), .rx_dst_rdy_i(vita_rx_dst_rdy0), .rx_src_rdy_o(vita_rx_src_rdy0), - .debug() ); - // ///////////////////////////////////////////////////////////////////////// - // DSP RX 1 - - wire [31:0] sample_rx1; - wire strobe_rx1, clear_rx1; - wire [35:0] vita_rx_data1; - wire vita_rx_src_rdy1, vita_rx_dst_rdy1; - - ddc_chain #(.BASE(SR_RX_DSP1), .DSPNO(1)) ddc_chain1 - (.clk(wb_clk),.rst(wb_rst), .clr(clear_rx1), - .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), - .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), - .rx_fe_i(rx_fe_i),.rx_fe_q(rx_fe_q), - .sample(sample_rx1), .run(run_rx1), .strobe(strobe_rx1), - .debug() ); - - vita_rx_chain #(.BASE(SR_RX_CTRL1), .UNIT(1), .FIFOSIZE(10), .PROT_ENG_FLAGS(0), .DSP_NUMBER(1)) vita_rx_chain1 - (.clk(wb_clk),.reset(wb_rst), - .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), - .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), - .vita_time(vita_time), .overrun(rx_overrun_dsp1), - .sample(sample_rx1), .run(run_rx1), .strobe(strobe_rx1), .clear_o(clear_rx1), - .rx_data_o(vita_rx_data1), .rx_dst_rdy_i(vita_rx_dst_rdy1), .rx_src_rdy_o(vita_rx_src_rdy1), - .debug() ); + // DSP RX * + + wire [35:0] rx_int2_data [NUM_RX_DSPS-1:0]; + wire rx_int2_src_rdy [NUM_RX_DSPS-1:0]; + wire rx_int2_dst_rdy [NUM_RX_DSPS-1:0]; + + genvar dspno; + generate + for(dspno = 0; dspno < NUM_RX_DSPS; dspno = dspno + 1) begin:gen_rx_dsps + + wire [31:0] sample_rx; + wire strobe_rx, clear_rx; + wire [35:0] vita_rx_data; + wire vita_rx_src_rdy, vita_rx_dst_rdy; + wire [35:0] int_rx_data; + wire int_rx_src_rdy, int_rx_dst_rdy; + + ddc_chain #(.BASE(SR_RX_DSP0+dspno*32), .DSPNO(dspno)) ddc_chain + (.clk(clk), .rst(reset), .clr(clear_rx), + .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), + .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), + .rx_fe_i(rx_fe_i),.rx_fe_q(rx_fe_q), + .sample(sample_rx), .run(run_rx_n[dspno]), .strobe(strobe_rx), + .debug() ); + + vita_rx_chain #(.BASE(SR_RX_CTRL0+dspno*32), .UNIT(dspno), .FIFOSIZE(DSP_RX_FIFOSIZE), .PROT_ENG_FLAGS(0), .DSP_NUMBER(dspno)) vita_rx_chain + (.clk(clk),.reset(reset), + .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), + .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), + .vita_time(vita_time), .overrun(), + .sample(sample_rx), .run(run_rx_n[dspno]), .strobe(strobe_rx), .clear_o(clear_rx), + .rx_data_o(vita_rx_data), .rx_dst_rdy_i(vita_rx_dst_rdy), .rx_src_rdy_o(vita_rx_src_rdy), + .debug() ); + + fifo_cascade #(.WIDTH(36), .SIZE(DSP_RX_FIFOSIZE+1)) rx_data_fifo + (.clk(clk), .reset(reset), .clear(1'b0), + .datain(vita_rx_data), .src_rdy_i(vita_rx_src_rdy), .dst_rdy_o(vita_rx_dst_rdy), .space(), + .dataout(int_rx_data), .src_rdy_o(int_rx_src_rdy), .dst_rdy_i(int_rx_dst_rdy), .occupied()); + + if (dspno == 0) begin + assign rx_int2_data[dspno] = int_rx_data; + assign rx_int2_src_rdy[dspno] = int_rx_src_rdy; + assign int_rx_dst_rdy = rx_int2_dst_rdy[dspno]; + end + else begin + fifo36_mux #(.prio(0)) // No priority, fair sharing + combine_rx_dsps ( + .clk(clk), .reset(reset), .clear(1'b0/*noclear*/), + .data0_i(rx_int2_data[dspno-1]), .src0_rdy_i(rx_int2_src_rdy[dspno-1]), .dst0_rdy_o(rx_int2_dst_rdy[dspno-1]), + .data1_i(int_rx_data), .src1_rdy_i(int_rx_src_rdy), .dst1_rdy_o(int_rx_dst_rdy), + .data_o(rx_int2_data[dspno]), .src_rdy_o(rx_int2_src_rdy[dspno]), .dst_rdy_i(rx_int2_dst_rdy[dspno]) + ); + end + + end + endgenerate // ///////////////////////////////////////////////////////////////////////// // RX Stream muxing - fifo36_mux #(.prio(0)) mux_data_streams - (.clk(wb_clk), .reset(wb_rst), .clear(clear_fifo), - .data0_i(vita_rx_data0), .src0_rdy_i(vita_rx_src_rdy0), .dst0_rdy_o(vita_rx_dst_rdy0), - .data1_i(vita_rx_data1), .src1_rdy_i(vita_rx_src_rdy1), .dst1_rdy_o(vita_rx_dst_rdy1), - .data_o(rx_data), .src_rdy_o(rx_src_rdy), .dst_rdy_i(rx_dst_rdy)); + wire [35:0] rx_int3_data; + wire rx_int3_src_rdy, rx_int3_dst_rdy; + + fifo_cascade #(.WIDTH(36), .SIZE(DSP_RX_XTRA_FIFOSIZE)) rx_data_fifo_combined + (.clk(clk), .reset(reset), .clear(1'b0), + .datain(rx_int2_data[NUM_RX_DSPS-1]), .src_rdy_i(rx_int2_src_rdy[NUM_RX_DSPS-1]), .dst_rdy_o(rx_int2_dst_rdy[NUM_RX_DSPS-1]), .space(), + .dataout(rx_int3_data), .src_rdy_o(rx_int3_src_rdy), .dst_rdy_i(rx_int3_dst_rdy), .occupied()); + + generate + if (USE_PACKET_PADDER) begin + packet_padder36 #(.BASE(SR_PADDER)) packet_padder_rx_data36( + .clk(clk), .reset(reset), + .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), + .data_i(rx_int3_data), .src_rdy_i(rx_int3_src_rdy), .dst_rdy_o(rx_int3_dst_rdy), + .data_o(rx_data), .src_rdy_o(rx_src_rdy), .dst_rdy_i(rx_dst_rdy), + .always_flush(~dsp_rx_run)); + end + else begin + assign rx_data = rx_int3_data; + assign rx_src_rdy = rx_int3_src_rdy; + assign rx_int3_dst_rdy = rx_dst_rdy; + end + endgenerate + + /////////////////////////////////////////////////////////////////////////// + // MUX for TX async and resp data + /////////////////////////////////////////////////////////////////////////// + wire [35:0] tx_err_data, resp_data_int; + wire tx_err_src_rdy, resp_src_rdy_int; + wire tx_err_dst_rdy, resp_dst_rdy_int; + + fifo36_mux #(.prio(0)) // No priority, fair sharing + combine_async_and_resp ( + .clk(clk), .reset(reset), .clear(1'b0/*noclear*/), + .data0_i(ctrl_out_data), .src0_rdy_i(ctrl_out_src_rdy), .dst0_rdy_o(ctrl_out_dst_rdy), + .data1_i(tx_err_data), .src1_rdy_i(tx_err_src_rdy), .dst1_rdy_o(tx_err_dst_rdy), + .data_o(resp_data_int), .src_rdy_o(resp_src_rdy_int), .dst_rdy_i(resp_dst_rdy_int) + ); + + fifo_cascade #(.WIDTH(36), .SIZE(9)) resp_fifo + (.clk(clk), .reset(reset), .clear(1'b0), + .datain(resp_data_int), .src_rdy_i(resp_src_rdy_int), .dst_rdy_o(resp_dst_rdy_int), .space(), + .dataout(resp_data), .src_rdy_o(resp_src_rdy), .dst_rdy_i(resp_dst_rdy), .occupied()); // /////////////////////////////////////////////////////////////////////////////////// // DSP TX - wire run_tx; wire [23:0] tx_fe_i, tx_fe_q; wire [31:0] sample_tx; wire strobe_tx, clear_tx; - vita_tx_chain #(.BASE(SR_TX_CTRL), .FIFOSIZE(10), .POST_ENGINE_FIFOSIZE(11), + vita_tx_chain #(.BASE(SR_TX_CTRL), + .FIFOSIZE(DSP_TX_FIFOSIZE), + .POST_ENGINE_FIFOSIZE(DSP_TX_XTRA_FIFOSIZE), .REPORT_ERROR(1), .DO_FLOW_CONTROL(0), .PROT_ENG_FLAGS(0), .USE_TRANS_HEADER(0), - .DSP_NUMBER(0)) + .DSP_NUMBER(0)) vita_tx_chain - (.clk(wb_clk), .reset(wb_rst), + (.clk(clk), .reset(reset), .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), .vita_time(vita_time), .tx_data_i(tx_data), .tx_src_rdy_i(tx_src_rdy), .tx_dst_rdy_o(tx_dst_rdy), .err_data_o(tx_err_data), .err_src_rdy_o(tx_err_src_rdy), .err_dst_rdy_i(tx_err_dst_rdy), .sample(sample_tx), .strobe(strobe_tx), - .underrun(tx_underrun_dsp), .run(run_tx), .clear_o(clear_tx), - .debug(debug_vt)); + .underrun(), .run(run_tx), .clear_o(clear_tx), + .debug()); duc_chain #(.BASE(SR_TX_DSP), .DSPNO(0)) duc_chain - (.clk(wb_clk), .rst(wb_rst), .clr(clear_tx), + (.clk(clk), .rst(reset), .clr(clear_tx), .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), .tx_fe_i(tx_fe_i),.tx_fe_q(tx_fe_q), .sample(sample_tx), .run(run_tx), .strobe(strobe_tx), .debug() ); - tx_frontend #(.BASE(SR_TX_FRONT), .WIDTH_OUT(14)) tx_frontend - (.clk(wb_clk), .rst(wb_rst), + tx_frontend #(.BASE(SR_TX_FE), .WIDTH_OUT(14)) tx_frontend + (.clk(clk), .rst(reset), .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), .tx_i(tx_fe_i), .tx_q(tx_fe_q), .run(1'b1), .dac_a(tx_i), .dac_b(tx_q)); // ///////////////////////////////////////////////////////////////////////////////////// - // Wishbone Intercon, single master - wire [dw-1:0] s0_dat_mosi, s1_dat_mosi, s0_dat_miso, s1_dat_miso, s2_dat_mosi, s3_dat_mosi, s2_dat_miso, s3_dat_miso, - s4_dat_mosi, s5_dat_mosi, s4_dat_miso, s5_dat_miso, s6_dat_mosi, s7_dat_mosi, s6_dat_miso, s7_dat_miso, - s8_dat_mosi, s9_dat_mosi, s8_dat_miso, s9_dat_miso, sa_dat_mosi, sb_dat_mosi, sa_dat_miso, sb_dat_miso, - sc_dat_mosi, sd_dat_mosi, sc_dat_miso, sd_dat_miso, se_dat_mosi, sf_dat_mosi, se_dat_miso, sf_dat_miso; - wire [aw-1:0] s0_adr,s1_adr,s2_adr,s3_adr,s4_adr,s5_adr,s6_adr,s7_adr; - wire [aw-1:0] s8_adr,s9_adr,sa_adr,sb_adr,sc_adr, sd_adr, se_adr, sf_adr; - wire [sw-1:0] s0_sel,s1_sel,s2_sel,s3_sel,s4_sel,s5_sel,s6_sel,s7_sel; - wire [sw-1:0] s8_sel,s9_sel,sa_sel,sb_sel,sc_sel, sd_sel, se_sel, sf_sel; - wire s0_ack,s1_ack,s2_ack,s3_ack,s4_ack,s5_ack,s6_ack,s7_ack; - wire s8_ack,s9_ack,sa_ack,sb_ack,sc_ack, sd_ack, se_ack, sf_ack; - wire s0_stb,s1_stb,s2_stb,s3_stb,s4_stb,s5_stb,s6_stb,s7_stb; - wire s8_stb,s9_stb,sa_stb,sb_stb,sc_stb, sd_stb, se_stb, sf_stb; - wire s0_cyc,s1_cyc,s2_cyc,s3_cyc,s4_cyc,s5_cyc,s6_cyc,s7_cyc; - wire s8_cyc,s9_cyc,sa_cyc,sb_cyc,sc_cyc, sd_cyc, se_cyc, sf_cyc; - wire s0_we,s1_we,s2_we,s3_we,s4_we,s5_we,s6_we,s7_we; - wire s8_we,s9_we,sa_we,sb_we,sc_we,sd_we, se_we, sf_we; - - wb_1master #(.dw(dw), .aw(aw), .sw(sw), .decode_w(4), - .s0_addr(4'h0), .s0_mask(4'hF), // Misc Regs - .s1_addr(4'h1), .s1_mask(4'hF), // Unused - .s2_addr(4'h2), .s2_mask(4'hF), // SPI - .s3_addr(4'h3), .s3_mask(4'hF), // I2C - .s4_addr(4'h4), .s4_mask(4'hF), // Unused - .s5_addr(4'h5), .s5_mask(4'hF), // Unused on B1x0, Async Msg on E1x0 - .s6_addr(4'h6), .s6_mask(4'hF), // Unused - .s7_addr(4'h7), .s7_mask(4'hF), // Readback MUX - .s8_addr(4'h8), .s8_mask(4'h8), // Setting Regs -- slave 8 is 8 slaves wide - // slaves 9-f alias to slave 1, all are unused - .s9_addr(4'h1), .s9_mask(4'hF), - .sa_addr(4'h1), .sa_mask(4'hF), .sb_addr(4'h1), .sb_mask(4'hF), - .sc_addr(4'h1), .sc_mask(4'hF), .sd_addr(4'h1), .sd_mask(4'hF), - .se_addr(4'h1), .se_mask(4'hF), .sf_addr(4'h1), .sf_mask(4'hF)) - wb_1master - (.clk_i(wb_clk),.rst_i(wb_rst), - .m0_dat_o(m0_dat_miso),.m0_ack_o(m0_ack),.m0_err_o(m0_err),.m0_rty_o(m0_rty),.m0_dat_i(m0_dat_mosi), - .m0_adr_i(m0_adr),.m0_sel_i(m0_sel),.m0_we_i(m0_we),.m0_cyc_i(m0_cyc),.m0_stb_i(m0_stb), - .s0_dat_o(s0_dat_mosi),.s0_adr_o(s0_adr),.s0_sel_o(s0_sel),.s0_we_o(s0_we),.s0_cyc_o(s0_cyc),.s0_stb_o(s0_stb), - .s0_dat_i(s0_dat_miso),.s0_ack_i(s0_ack),.s0_err_i(0),.s0_rty_i(0), - .s1_dat_o(s1_dat_mosi),.s1_adr_o(s1_adr),.s1_sel_o(s1_sel),.s1_we_o(s1_we),.s1_cyc_o(s1_cyc),.s1_stb_o(s1_stb), - .s1_dat_i(s1_dat_miso),.s1_ack_i(s1_ack),.s1_err_i(0),.s1_rty_i(0), - .s2_dat_o(s2_dat_mosi),.s2_adr_o(s2_adr),.s2_sel_o(s2_sel),.s2_we_o(s2_we),.s2_cyc_o(s2_cyc),.s2_stb_o(s2_stb), - .s2_dat_i(s2_dat_miso),.s2_ack_i(s2_ack),.s2_err_i(0),.s2_rty_i(0), - .s3_dat_o(s3_dat_mosi),.s3_adr_o(s3_adr),.s3_sel_o(s3_sel),.s3_we_o(s3_we),.s3_cyc_o(s3_cyc),.s3_stb_o(s3_stb), - .s3_dat_i(s3_dat_miso),.s3_ack_i(s3_ack),.s3_err_i(0),.s3_rty_i(0), - .s4_dat_o(s4_dat_mosi),.s4_adr_o(s4_adr),.s4_sel_o(s4_sel),.s4_we_o(s4_we),.s4_cyc_o(s4_cyc),.s4_stb_o(s4_stb), - .s4_dat_i(s4_dat_miso),.s4_ack_i(s4_ack),.s4_err_i(0),.s4_rty_i(0), - .s5_dat_o(s5_dat_mosi),.s5_adr_o(s5_adr),.s5_sel_o(s5_sel),.s5_we_o(s5_we),.s5_cyc_o(s5_cyc),.s5_stb_o(s5_stb), - .s5_dat_i(s5_dat_miso),.s5_ack_i(s5_ack),.s5_err_i(0),.s5_rty_i(0), - .s6_dat_o(s6_dat_mosi),.s6_adr_o(s6_adr),.s6_sel_o(s6_sel),.s6_we_o(s6_we),.s6_cyc_o(s6_cyc),.s6_stb_o(s6_stb), - .s6_dat_i(s6_dat_miso),.s6_ack_i(s6_ack),.s6_err_i(0),.s6_rty_i(0), - .s7_dat_o(s7_dat_mosi),.s7_adr_o(s7_adr),.s7_sel_o(s7_sel),.s7_we_o(s7_we),.s7_cyc_o(s7_cyc),.s7_stb_o(s7_stb), - .s7_dat_i(s7_dat_miso),.s7_ack_i(s7_ack),.s7_err_i(0),.s7_rty_i(0), - .s8_dat_o(s8_dat_mosi),.s8_adr_o(s8_adr),.s8_sel_o(s8_sel),.s8_we_o(s8_we),.s8_cyc_o(s8_cyc),.s8_stb_o(s8_stb), - .s8_dat_i(s8_dat_miso),.s8_ack_i(s8_ack),.s8_err_i(0),.s8_rty_i(0), - .s9_dat_o(s9_dat_mosi),.s9_adr_o(s9_adr),.s9_sel_o(s9_sel),.s9_we_o(s9_we),.s9_cyc_o(s9_cyc),.s9_stb_o(s9_stb), - .s9_dat_i(s9_dat_miso),.s9_ack_i(s9_ack),.s9_err_i(0),.s9_rty_i(0), - .sa_dat_o(sa_dat_mosi),.sa_adr_o(sa_adr),.sa_sel_o(sa_sel),.sa_we_o(sa_we),.sa_cyc_o(sa_cyc),.sa_stb_o(sa_stb), - .sa_dat_i(sa_dat_miso),.sa_ack_i(sa_ack),.sa_err_i(0),.sa_rty_i(0), - .sb_dat_o(sb_dat_mosi),.sb_adr_o(sb_adr),.sb_sel_o(sb_sel),.sb_we_o(sb_we),.sb_cyc_o(sb_cyc),.sb_stb_o(sb_stb), - .sb_dat_i(sb_dat_miso),.sb_ack_i(sb_ack),.sb_err_i(0),.sb_rty_i(0), - .sc_dat_o(sc_dat_mosi),.sc_adr_o(sc_adr),.sc_sel_o(sc_sel),.sc_we_o(sc_we),.sc_cyc_o(sc_cyc),.sc_stb_o(sc_stb), - .sc_dat_i(sc_dat_miso),.sc_ack_i(sc_ack),.sc_err_i(0),.sc_rty_i(0), - .sd_dat_o(sd_dat_mosi),.sd_adr_o(sd_adr),.sd_sel_o(sd_sel),.sd_we_o(sd_we),.sd_cyc_o(sd_cyc),.sd_stb_o(sd_stb), - .sd_dat_i(sd_dat_miso),.sd_ack_i(sd_ack),.sd_err_i(0),.sd_rty_i(0), - .se_dat_o(se_dat_mosi),.se_adr_o(se_adr),.se_sel_o(se_sel),.se_we_o(se_we),.se_cyc_o(se_cyc),.se_stb_o(se_stb), - .se_dat_i(se_dat_miso),.se_ack_i(se_ack),.se_err_i(0),.se_rty_i(0), - .sf_dat_o(sf_dat_mosi),.sf_adr_o(sf_adr),.sf_sel_o(sf_sel),.sf_we_o(sf_we),.sf_cyc_o(sf_cyc),.sf_stb_o(sf_stb), - .sf_dat_i(sf_dat_miso),.sf_ack_i(sf_ack),.sf_err_i(0),.sf_rty_i(0) ); - - assign s1_ack = 0; assign s4_ack = 0; assign s5_ack = 0; assign s6_ack = 0; - assign s9_ack = 0; assign sa_ack = 0; assign sb_ack = 0; - assign sc_ack = 0; assign sd_ack = 0; assign se_ack = 0; assign sf_ack = 0; - - // ///////////////////////////////////////////////////////////////////////////////////// - // Slave 0, Misc LEDs, Switches, controls - - localparam REG_CGEN_CTRL = 7'd4; // out - localparam REG_CGEN_ST = 7'd6; // in - localparam REG_TEST = 7'd8; // out - localparam REG_RX_FRAMELEN = 7'd10; // in - localparam REG_TX_FRAMELEN = 7'd12; // out - - always @(posedge wb_clk) - if(wb_rst) - begin - reg_cgen_ctrl <= 2'b11; - reg_test <= 0; - frames_per_packet <= 0; - end - else - if(s0_cyc & s0_stb & s0_we) - case(s0_adr[6:0]) - REG_CGEN_CTRL : - reg_cgen_ctrl <= s0_dat_mosi; - REG_TEST : - reg_test <= s0_dat_mosi; - REG_RX_FRAMELEN : - frames_per_packet <= s0_dat_mosi[7:0]; - endcase // case (s0_adr[6:0]) - - assign debug_led = {run_tx, (run_rx0 | run_rx1), cgen_st_ld}; - assign { cgen_sync_b, cgen_ref_sel } = reg_cgen_ctrl; - - assign s0_dat_miso = (s0_adr[6:0] == REG_CGEN_CTRL) ? reg_cgen_ctrl : - (s0_adr[6:0] == REG_CGEN_ST) ? {13'b0,cgen_st_status,cgen_st_ld,cgen_st_refmon} : - (s0_adr[6:0] == REG_TEST) ? reg_test : - 16'hBEEF; - - assign s0_ack = s0_stb & s0_cyc; - - // ///////////////////////////////////////////////////////////////////////////////////// - // Slave 2, SPI - - spi_top16 shared_spi - (.wb_clk_i(wb_clk),.wb_rst_i(wb_rst),.wb_adr_i(s2_adr[4:0]),.wb_dat_i(s2_dat_mosi), - .wb_dat_o(s2_dat_miso),.wb_sel_i(s2_sel),.wb_we_i(s2_we),.wb_stb_i(s2_stb), - .wb_cyc_i(s2_cyc),.wb_ack_o(s2_ack),.wb_err_o(),.wb_int_o(), - .ss_pad_o(sen), .sclk_pad_o(sclk), .mosi_pad_o(mosi), .miso_pad_i(miso) ); - - // ///////////////////////////////////////////////////////////////////////// - // Slave 3, I2C - - wire scl_pad_i, scl_pad_o, scl_pad_oen_o, sda_pad_i, sda_pad_o, sda_pad_oen_o; - i2c_master_top #(.ARST_LVL(1)) i2c - (.wb_clk_i(wb_clk),.wb_rst_i(wb_rst),.arst_i(1'b0), - .wb_adr_i(s3_adr[3:1]),.wb_dat_i(s3_dat_mosi[7:0]),.wb_dat_o(s3_dat_miso[7:0]), - .wb_we_i(s3_we),.wb_stb_i(s3_stb),.wb_cyc_i(s3_cyc), - .wb_ack_o(s3_ack),.wb_inta_o(), - .scl_pad_i(scl_pad_i),.scl_pad_o(scl_pad_o),.scl_padoen_o(scl_pad_oen_o), - .sda_pad_i(sda_pad_i),.sda_pad_o(sda_pad_o),.sda_padoen_o(sda_pad_oen_o) ); - - assign s3_dat_miso[15:8] = 8'd0; - - // I2C -- Don't use external transistors for open drain, the FPGA implements this - IOBUF scl_pin(.O(scl_pad_i), .IO(db_scl), .I(scl_pad_o), .T(scl_pad_oen_o)); - IOBUF sda_pin(.O(sda_pad_i), .IO(db_sda), .I(sda_pad_o), .T(sda_pad_oen_o)); - - // ///////////////////////////////////////////////////////////////////////// - // GPIOs - - wire [31:0] gpio_readback; - - gpio_atr #(.BASE(SR_GPIO), .WIDTH(32)) - gpio_atr(.clk(wb_clk),.reset(wb_rst), - .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), - .rx(run_rx0 | run_rx1), .tx(run_tx), - .gpio({io_tx,io_rx}), .gpio_readback(gpio_readback) ); - - // ///////////////////////////////////////////////////////////////////////// - // Settings Bus -- Slave #8 + 9 - - // only have 64 regs, 32 bits each with current setup... - settings_bus_16LE #(.AWIDTH(11),.RWIDTH(8)) settings_bus_16LE - (.wb_clk(wb_clk),.wb_rst(wb_rst),.wb_adr_i(s8_adr),.wb_dat_i(s8_dat_mosi), - .wb_stb_i(s8_stb),.wb_we_i(s8_we),.wb_ack_o(s8_ack), - .strobe(set_stb),.addr(set_addr),.data(set_data) ); - - user_settings #(.BASE(SR_USER_REGS)) user_settings - (.clk(wb_clk),.rst(wb_rst),.set_stb(set_stb), - .set_addr(set_addr),.set_data(set_data), - .set_addr_user(set_addr_user),.set_data_user(set_data_user), - .set_stb_user(set_stb_user) ); - - // ///////////////////////////////////////////////////////////////////////// - // Readback mux 32 -- Slave #7 - - //compatibility number -> increment when the fpga has been sufficiently altered - localparam compat_num = {16'd10, 16'd0}; //major, minor - - wire [31:0] reg_test32; - - setting_reg #(.my_addr(SR_REG_TEST32)) sr_reg_test32 - (.clk(wb_clk),.rst(wb_rst),.strobe(set_stb),.addr(set_addr), - .in(set_data),.out(reg_test32),.changed()); - - wb_readback_mux_16LE readback_mux_32 - (.wb_clk_i(wb_clk), .wb_rst_i(wb_rst), .wb_stb_i(s7_stb), - .wb_adr_i({5'b0,s7_adr}), .wb_dat_o(s7_dat_miso), .wb_ack_o(s7_ack), - - .word00(vita_time[63:32]), .word01(vita_time[31:0]), - .word02(vita_time_pps[63:32]), .word03(vita_time_pps[31:0]), - .word04(reg_test32), .word05(32'b0), - .word06(compat_num), .word07(gpio_readback), - .word08(32'b0), .word09(32'b0), - .word10(32'b0), .word11(32'b0), - .word12(32'b0), .word13(32'b0), - .word14(32'b0), .word15(32'b0) - ); - - // ///////////////////////////////////////////////////////////////////////// - // VITA Timing - - time_64bit #(.BASE(SR_TIME64)) time_64bit - (.clk(wb_clk), .rst(wb_rst), .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), - .pps(pps_in), .vita_time(vita_time), .vita_time_pps(vita_time_pps), .pps_int(pps_int), - .exp_time_in(0)); - - // ///////////////////////////////////////////////////////////////////////////////////// // Debug circuitry - assign debug_clk = 2'b00; // { gpif_clk, clk_fpga }; - assign debug = 0; - + assign debug_clk = 2'b11; + assign debug = 32'hffffffff; + endmodule // u1plus_core diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index 5c47767ba..dd98f6f37 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -198,7 +198,7 @@ INSTALL(FILES # Images download directory for utils/uhd_images_downloader.py ######################################################################## -SET(UHD_IMAGES_DOWNLOAD_SRC "http://files.ettus.com/binaries/master_images/archive/uhd-images_003.004.001-109-g6ca39ad9.zip") +SET(UHD_IMAGES_DOWNLOAD_SRC "http://files.ettus.com/binaries/next_images/archive/uhd-images_003.004.002-177-gd61c9b29.zip") ######################################################################## # Register top level components diff --git a/host/include/uhd/types/ranges.hpp b/host/include/uhd/types/ranges.hpp index f0d0e1c0b..ac632df93 100644 --- a/host/include/uhd/types/ranges.hpp +++ b/host/include/uhd/types/ranges.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 Ettus Research LLC // // 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 @@ -19,7 +19,6 @@ #define INCLUDED_UHD_TYPES_RANGES_HPP #include <uhd/config.hpp> -#include <uhd/utils/pimpl.hpp> #include <string> #include <vector> @@ -60,7 +59,7 @@ namespace uhd{ //! Convert this range to a printable string const std::string to_pp_string(void) const; - private: UHD_PIMPL_DECL(impl) _impl; + private: double _start, _stop, _step; }; /*! diff --git a/host/include/uhd/utils/CMakeLists.txt b/host/include/uhd/utils/CMakeLists.txt index 5a434fd9a..de91993fe 100644 --- a/host/include/uhd/utils/CMakeLists.txt +++ b/host/include/uhd/utils/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2012 Ettus Research LLC # # 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 @@ -19,6 +19,7 @@ INSTALL(FILES algorithm.hpp assert_has.hpp assert_has.ipp + atomic.hpp byteswap.hpp byteswap.ipp csv.hpp diff --git a/host/include/uhd/utils/atomic.hpp b/host/include/uhd/utils/atomic.hpp new file mode 100644 index 000000000..f9bc728cc --- /dev/null +++ b/host/include/uhd/utils/atomic.hpp @@ -0,0 +1,143 @@ +// +// Copyright 2012 Ettus Research LLC +// +// 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/>. +// + +#ifndef INCLUDED_UHD_UTILS_ATOMIC_HPP +#define INCLUDED_UHD_UTILS_ATOMIC_HPP + +#include <uhd/config.hpp> +#include <uhd/types/time_spec.hpp> +#include <boost/thread/thread.hpp> +#include <boost/interprocess/detail/atomic.hpp> + +namespace uhd{ + + //! A 32-bit integer that can be atomically accessed + class UHD_API atomic_uint32_t{ + public: + + //! Create a new atomic 32-bit integer, initialized to zero + UHD_INLINE atomic_uint32_t(void){ + this->write(0); + } + + //! Compare with cmp, swap with newval if same, return old value + UHD_INLINE boost::uint32_t cas(boost::uint32_t newval, boost::uint32_t cmp){ + return boost::interprocess::detail::atomic_cas32(&_num, newval, cmp); + } + + //! Sets the atomic integer to a new value + UHD_INLINE void write(const boost::uint32_t newval){ + boost::interprocess::detail::atomic_write32(&_num, newval); + } + + //! Gets the current value of the atomic integer + UHD_INLINE boost::uint32_t read(void){ + return boost::interprocess::detail::atomic_read32(&_num); + } + + //! Increment by 1 and return the old value + UHD_INLINE boost::uint32_t inc(void){ + return boost::interprocess::detail::atomic_inc32(&_num); + } + + //! Decrement by 1 and return the old value + UHD_INLINE boost::uint32_t dec(void){ + return boost::interprocess::detail::atomic_dec32(&_num); + } + + private: volatile boost::uint32_t _num; + }; + + /*! + * A reusable barrier to sync multiple threads. + * All threads spin on wait() until count is reset. + */ + class UHD_API reusable_barrier{ + public: + + //! Resize the barrier for N threads + void resize(const size_t size){ + _size = size; + _count.write(size); + } + + //! Wait on the barrier condition + UHD_INLINE void wait(void){ + _count.dec(); + _count.cas(_size, 0); + while (_count.read() != _size){ + boost::this_thread::interruption_point(); + boost::this_thread::yield(); + } + } + + private: + size_t _size; + atomic_uint32_t _count; + }; + + /*! + * Spin-wait on a condition with a timeout. + * \param cond an atomic variable to compare + * \param value compare to atomic for true/false + * \param timeout the timeout in seconds + * \return true for cond == value, false for timeout + */ + UHD_INLINE bool spin_wait_with_timeout( + atomic_uint32_t &cond, + boost::uint32_t value, + const double timeout + ){ + if (cond.read() == value) return true; + const time_spec_t exit_time = time_spec_t::get_system_time() + time_spec_t(timeout); + while (cond.read() != value){ + if (time_spec_t::get_system_time() > exit_time) return false; + boost::this_thread::interruption_point(); + boost::this_thread::yield(); + } + return true; + } + + /*! + * Claimer class to provide synchronization for multi-thread access. + * Claiming enables buffer classes to be used with a buffer queue. + */ + class simple_claimer{ + public: + simple_claimer(void){ + this->release(); + } + + UHD_INLINE void release(void){ + _locked.write(0); + } + + UHD_INLINE bool claim_with_wait(const double timeout){ + if (spin_wait_with_timeout(_locked, 0, timeout)){ + _locked.write(1); + return true; + } + return false; + } + + private: + atomic_uint32_t _locked; + }; + +} //namespace uhd + +#endif /* INCLUDED_UHD_UTILS_ATOMIC_HPP */ diff --git a/host/include/uhd/version.hpp b/host/include/uhd/version.hpp index f08d8ae46..ab693f2b0 100644 --- a/host/include/uhd/version.hpp +++ b/host/include/uhd/version.hpp @@ -27,7 +27,7 @@ * The format is oldest API compatible release - ABI compat number. * The compatibility number allows pre-release ABI to be versioned. */ -#define UHD_VERSION_ABI_STRING "3.4.0-2" +#define UHD_VERSION_ABI_STRING "3.4.0-3" namespace uhd{ diff --git a/host/lib/transport/simple_claimer.hpp b/host/lib/transport/simple_claimer.hpp deleted file mode 100644 index 3bbc49a05..000000000 --- a/host/lib/transport/simple_claimer.hpp +++ /dev/null @@ -1,64 +0,0 @@ -// -// Copyright 2012 Ettus Research LLC -// -// 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/>. -// - -#ifndef INCLUDED_LIBUHD_TRANSPORT_SIMPLE_CLAIMER_HPP -#define INCLUDED_LIBUHD_TRANSPORT_SIMPLE_CLAIMER_HPP - -#include <uhd/config.hpp> -#include <boost/thread/condition.hpp> -#include <boost/thread/mutex.hpp> - -namespace uhd{ namespace transport{ - -/*********************************************************************** - * Claimer class to provide synchronization for multi-thread access. - * Claiming enables buffer classes to be used with a buffer queue. - **********************************************************************/ -class simple_claimer{ -public: - simple_claimer(void){ - this->release(); - } - - UHD_INLINE void release(void){ - boost::mutex::scoped_lock lock(_mutex); - _locked = false; - lock.unlock(); - _cond.notify_one(); - } - - UHD_INLINE bool claim_with_wait(const double timeout){ - boost::mutex::scoped_lock lock(_mutex); - while (_locked){ - if (not _cond.timed_wait(lock, boost::posix_time::microseconds(long(timeout*1e6)))){ - break; - } - } - const bool ret = not _locked; - _locked = true; - return ret; - } - -private: - bool _locked; - boost::mutex _mutex; - boost::condition_variable _cond; -}; - -}} //namespace uhd::transport - -#endif /* INCLUDED_LIBUHD_TRANSPORT_SIMPLE_CLAIMER_HPP */ diff --git a/host/lib/transport/super_recv_packet_handler.hpp b/host/lib/transport/super_recv_packet_handler.hpp index 205c7a3a3..4b96199e2 100644 --- a/host/lib/transport/super_recv_packet_handler.hpp +++ b/host/lib/transport/super_recv_packet_handler.hpp @@ -23,6 +23,8 @@ #include <uhd/convert.hpp> #include <uhd/stream.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/tasks.hpp> +#include <uhd/utils/atomic.hpp> #include <uhd/utils/byteswap.hpp> #include <uhd/types/metadata.hpp> #include <uhd/transport/vrt_if_packet.hpp> @@ -31,6 +33,9 @@ #include <boost/foreach.hpp> #include <boost/function.hpp> #include <boost/format.hpp> +#include <boost/bind.hpp> +#include <boost/make_shared.hpp> +#include <boost/thread/barrier.hpp> #include <iostream> #include <vector> @@ -73,12 +78,23 @@ public: set_alignment_failure_threshold(1000); } + ~recv_packet_handler(void){ + _task_handlers.clear(); + } + //! Resize the number of transport channels void resize(const size_t size){ if (this->size() == size) return; + _task_handlers.clear(); _props.resize(size); //re-initialize all buffers infos by re-creating the vector _buffers_infos = std::vector<buffers_info_type>(4, buffers_info_type(size)); + _task_barrier_entry.resize(size); + _task_barrier_exit.resize(size); + _task_handlers.resize(size); + for (size_t i = 1/*skip 0*/; i < size; i++){ + _task_handlers[i] = task::make(boost::bind(&recv_packet_handler::converter_thread_task, this, i)); + }; } //! Get the channel width of this handler @@ -125,7 +141,7 @@ public: //! Set the conversion routine for all channels void set_converter(const uhd::convert::id_type &id){ - _io_buffs.resize(id.num_outputs); + _num_outputs = id.num_outputs; _converter = uhd::convert::get_converter(id)(); this->set_scale_factor(1/32767.); //update after setting converter _bytes_per_otw_item = uhd::convert::get_bytes_per_item(id.input_format); @@ -207,7 +223,7 @@ private: handle_overflow_type handle_overflow; }; std::vector<xport_chan_props_type> _props; - std::vector<void *> _io_buffs; //used in conversion + size_t _num_outputs; size_t _bytes_per_otw_item; //used in conversion size_t _bytes_per_cpu_item; //used in conversion uhd::convert::converter::sptr _converter; //used in conversion @@ -512,24 +528,19 @@ private: //extract the number of samples available to copy const size_t nsamps_available = info.data_bytes_to_copy/_bytes_per_otw_item; - const size_t nsamps_to_copy = std::min(nsamps_per_buff*_io_buffs.size(), nsamps_available); + const size_t nsamps_to_copy = std::min(nsamps_per_buff*_num_outputs, nsamps_available); const size_t bytes_to_copy = nsamps_to_copy*_bytes_per_otw_item; - const size_t nsamps_to_copy_per_io_buff = nsamps_to_copy/_io_buffs.size(); + const size_t nsamps_to_copy_per_io_buff = nsamps_to_copy/_num_outputs; - size_t buff_index = 0; - BOOST_FOREACH(per_buffer_info_type &buff_info, info){ + //setup the data to share with converter threads + _convert_nsamps = nsamps_to_copy_per_io_buff; + _convert_buffs = &buffs; + _convert_buffer_offset_bytes = buffer_offset_bytes; + _convert_bytes_to_copy = bytes_to_copy; - //fill a vector with pointers to the io buffers - BOOST_FOREACH(void *&io_buff, _io_buffs){ - io_buff = reinterpret_cast<char *>(buffs[buff_index++]) + buffer_offset_bytes; - } + //perform N channels of conversion + converter_thread_task(0); - //copy-convert the samples from the recv buffer - _converter->conv(buff_info.copy_buff, _io_buffs, nsamps_to_copy_per_io_buff); - - //update the rx copy buffer to reflect the bytes copied - buff_info.copy_buff += bytes_to_copy; - } //update the copy buffer's availability info.data_bytes_to_copy -= bytes_to_copy; @@ -538,15 +549,53 @@ private: metadata.fragment_offset = info.fragment_offset_in_samps; info.fragment_offset_in_samps += nsamps_to_copy; //set for next call - //done with buffers? this action releases buffers in-order - if (not metadata.more_fragments){ - BOOST_FOREACH(per_buffer_info_type &buff_info, info){ - buff_info.buff.reset(); //effectively a release - } + return nsamps_to_copy_per_io_buff; + } + + /******************************************************************* + * Perform one thread's work of the conversion task. + * The entry and exit use a dual synchronization barrier, + * to wait for data to become ready and block until completion. + ******************************************************************/ + UHD_INLINE void converter_thread_task(const size_t index) + { + _task_barrier_entry.wait(); + + //shortcut references to local data structures + buffers_info_type &buff_info = get_curr_buffer_info(); + per_buffer_info_type &info = buff_info[index]; + const rx_streamer::buffs_type &buffs = *_convert_buffs; + + //fill IO buffs with pointers into the output buffer + void *io_buffs[4/*max interleave*/]; + for (size_t i = 0; i < _num_outputs; i++){ + char *b = reinterpret_cast<char *>(buffs[index*_num_outputs + i]); + io_buffs[i] = b + _convert_buffer_offset_bytes; } + const ref_vector<void *> out_buffs(io_buffs, _num_outputs); - return nsamps_to_copy_per_io_buff; + //perform the conversion operation + _converter->conv(info.copy_buff, out_buffs, _convert_nsamps); + + //advance the pointer for the source buffer + info.copy_buff += _convert_bytes_to_copy; + + //release the buffer if fully consumed + if (buff_info.data_bytes_to_copy == _convert_bytes_to_copy){ + info.buff.reset(); //effectively a release + } + + _task_barrier_exit.wait(); } + + //! Shared variables for the worker threads + reusable_barrier _task_barrier_entry, _task_barrier_exit; + std::vector<task::sptr> _task_handlers; + size_t _convert_nsamps; + const rx_streamer::buffs_type *_convert_buffs; + size_t _convert_buffer_offset_bytes; + size_t _convert_bytes_to_copy; + }; class recv_packet_streamer : public recv_packet_handler, public rx_streamer{ diff --git a/host/lib/transport/super_send_packet_handler.hpp b/host/lib/transport/super_send_packet_handler.hpp index 02cfad80f..8f943effb 100644 --- a/host/lib/transport/super_send_packet_handler.hpp +++ b/host/lib/transport/super_send_packet_handler.hpp @@ -23,6 +23,8 @@ #include <uhd/convert.hpp> #include <uhd/stream.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/tasks.hpp> +#include <uhd/utils/atomic.hpp> #include <uhd/utils/byteswap.hpp> #include <uhd/types/metadata.hpp> #include <uhd/transport/vrt_if_packet.hpp> @@ -58,12 +60,23 @@ public: this->resize(size); } + ~send_packet_handler(void){ + _task_handlers.clear(); + } + //! Resize the number of transport channels void resize(const size_t size){ if (this->size() == size) return; + _task_handlers.clear(); _props.resize(size); static const boost::uint64_t zero = 0; _zero_buffs.resize(size, &zero); + _task_barrier_entry.resize(size); + _task_barrier_exit.resize(size); + _task_handlers.resize(size); + for (size_t i = 1/*skip 0*/; i < size; i++){ + _task_handlers[i] = task::make(boost::bind(&send_packet_handler::converter_thread_task, this, i)); + }; } //! Get the channel width of this handler @@ -104,7 +117,7 @@ public: //! Set the conversion routine for all channels void set_converter(const uhd::convert::id_type &id){ - _io_buffs.resize(id.num_inputs); + _num_inputs = id.num_inputs; _converter = uhd::convert::get_converter(id)(); this->set_scale_factor(32767.); //update after setting converter _bytes_per_otw_item = uhd::convert::get_bytes_per_item(id.output_format); @@ -205,9 +218,10 @@ private: get_buff_type get_buff; bool has_sid; boost::uint32_t sid; + managed_send_buffer::sptr buff; }; std::vector<xport_chan_props_type> _props; - std::vector<const void *> _io_buffs; //used in conversion + size_t _num_inputs; size_t _bytes_per_otw_item; //used in conversion size_t _bytes_per_cpu_item; //used in conversion uhd::convert::converter::sptr _converter; //used in conversion @@ -226,39 +240,77 @@ private: const size_t buffer_offset_bytes = 0 ){ //load the rest of the if_packet_info in here - if_packet_info.num_payload_bytes = nsamps_per_buff*_io_buffs.size()*_bytes_per_otw_item; + if_packet_info.num_payload_bytes = nsamps_per_buff*_num_inputs*_bytes_per_otw_item; if_packet_info.num_payload_words32 = (if_packet_info.num_payload_bytes + 3/*round up*/)/sizeof(boost::uint32_t); if_packet_info.packet_count = _next_packet_seq; - size_t buff_index = 0; + //get a buffer for each channel or timeout BOOST_FOREACH(xport_chan_props_type &props, _props){ - managed_send_buffer::sptr buff = props.get_buff(timeout); - if (buff.get() == NULL) return 0; //timeout - - //fill a vector with pointers to the io buffers - BOOST_FOREACH(const void *&io_buff, _io_buffs){ - io_buff = reinterpret_cast<const char *>(buffs[buff_index++]) + buffer_offset_bytes; - } - boost::uint32_t *otw_mem = buff->cast<boost::uint32_t *>() + _header_offset_words32; - - //pack metadata into a vrt header - if_packet_info.has_sid = props.has_sid; - if_packet_info.sid = props.sid; - _vrt_packer(otw_mem, if_packet_info); - otw_mem += if_packet_info.num_header_words32; + if (not props.buff) props.buff = props.get_buff(timeout); + if (not props.buff) return 0; //timeout + } - //copy-convert the samples into the send buffer - _converter->conv(_io_buffs, otw_mem, nsamps_per_buff); + //setup the data to share with converter threads + _convert_nsamps = nsamps_per_buff; + _convert_buffs = &buffs; + _convert_buffer_offset_bytes = buffer_offset_bytes; + _convert_if_packet_info = &if_packet_info; - //commit the samples to the zero-copy interface - size_t num_bytes_total = (_header_offset_words32+if_packet_info.num_packet_words32)*sizeof(boost::uint32_t); - buff->commit(num_bytes_total); - buff.reset(); //effectively a release + //perform N channels of conversion + converter_thread_task(0); - } _next_packet_seq++; //increment sequence after commits return nsamps_per_buff; } + + /******************************************************************* + * Perform one thread's work of the conversion task. + * The entry and exit use a dual synchronization barrier, + * to wait for data to become ready and block until completion. + ******************************************************************/ + UHD_INLINE void converter_thread_task(const size_t index) + { + _task_barrier_entry.wait(); + + //shortcut references to local data structures + managed_send_buffer::sptr &buff = _props[index].buff; + vrt::if_packet_info_t if_packet_info = *_convert_if_packet_info; + const tx_streamer::buffs_type &buffs = *_convert_buffs; + + //fill IO buffs with pointers into the output buffer + const void *io_buffs[4/*max interleave*/]; + for (size_t i = 0; i < _num_inputs; i++){ + const char *b = reinterpret_cast<const char *>(buffs[index*_num_inputs + i]); + io_buffs[i] = b + _convert_buffer_offset_bytes; + } + const ref_vector<const void *> in_buffs(io_buffs, _num_inputs); + + //pack metadata into a vrt header + boost::uint32_t *otw_mem = buff->cast<boost::uint32_t *>() + _header_offset_words32; + if_packet_info.has_sid = _props[index].has_sid; + if_packet_info.sid = _props[index].sid; + _vrt_packer(otw_mem, if_packet_info); + otw_mem += if_packet_info.num_header_words32; + + //perform the conversion operation + _converter->conv(in_buffs, otw_mem, _convert_nsamps); + + //commit the samples to the zero-copy interface + const size_t num_vita_words32 = _header_offset_words32+if_packet_info.num_packet_words32; + buff->commit(num_vita_words32*sizeof(boost::uint32_t)); + buff.reset(); //effectively a release + + _task_barrier_exit.wait(); + } + + //! Shared variables for the worker threads + reusable_barrier _task_barrier_entry, _task_barrier_exit; + std::vector<task::sptr> _task_handlers; + size_t _convert_nsamps; + const tx_streamer::buffs_type *_convert_buffs; + size_t _convert_buffer_offset_bytes; + vrt::if_packet_info_t *_convert_if_packet_info; + }; class send_packet_streamer : public send_packet_handler, public tx_streamer{ diff --git a/host/lib/transport/udp_zero_copy.cpp b/host/lib/transport/udp_zero_copy.cpp index 9765c19c0..9125be53a 100644 --- a/host/lib/transport/udp_zero_copy.cpp +++ b/host/lib/transport/udp_zero_copy.cpp @@ -16,12 +16,12 @@ // #include "udp_common.hpp" -#include "simple_claimer.hpp" #include <uhd/transport/udp_zero_copy.hpp> #include <uhd/transport/udp_simple.hpp> //mtu #include <uhd/transport/buffer_pool.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/log.hpp> +#include <uhd/utils/atomic.hpp> #include <boost/format.hpp> #include <boost/make_shared.hpp> #include <vector> diff --git a/host/lib/transport/usb_zero_copy_wrapper.cpp b/host/lib/transport/usb_zero_copy_wrapper.cpp index d59ea36ff..d04244ca9 100644 --- a/host/lib/transport/usb_zero_copy_wrapper.cpp +++ b/host/lib/transport/usb_zero_copy_wrapper.cpp @@ -15,12 +15,12 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include "simple_claimer.hpp" #include <uhd/transport/usb_zero_copy.hpp> #include <uhd/transport/buffer_pool.hpp> #include <uhd/utils/byteswap.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/tasks.hpp> +#include <uhd/utils/atomic.hpp> #include <boost/foreach.hpp> #include <boost/make_shared.hpp> #include <boost/thread/mutex.hpp> @@ -29,6 +29,7 @@ #include <vector> #include <iostream> +using namespace uhd; using namespace uhd::transport; static const boost::posix_time::time_duration AUTOFLUSH_TIMEOUT(boost::posix_time::milliseconds(1)); diff --git a/host/lib/types/ranges.cpp b/host/lib/types/ranges.cpp index 6e39bc688..82a9a84e1 100644 --- a/host/lib/types/ranges.cpp +++ b/host/lib/types/ranges.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011-2011 Ettus Research LLC +// Copyright 2011-2012 Ettus Research LLC // // 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 @@ -27,17 +27,8 @@ using namespace uhd; /*********************************************************************** * range_t implementation code **********************************************************************/ -struct range_t::impl{ - impl(double start, double stop, double step): - start(start), stop(stop), step(step) - { - /* NOP */ - } - double start, stop, step; -}; - range_t::range_t(double value): - _impl(UHD_PIMPL_MAKE(impl, (value, value, 0))) + _start(value), _stop(value), _step(0.0) { /* NOP */ } @@ -45,7 +36,7 @@ range_t::range_t(double value): range_t::range_t( double start, double stop, double step ): - _impl(UHD_PIMPL_MAKE(impl, (start, stop, step))) + _start(start), _stop(stop), _step(step) { if (stop < start){ throw uhd::value_error("cannot make range where stop < start"); @@ -53,15 +44,15 @@ range_t::range_t( } double range_t::start(void) const{ - return _impl->start; + return _start; } double range_t::stop(void) const{ - return _impl->stop; + return _stop; } double range_t::step(void) const{ - return _impl->step; + return _step; } const std::string range_t::to_pp_string(void) const{ diff --git a/host/lib/usrp/b100/CMakeLists.txt b/host/lib/usrp/b100/CMakeLists.txt index 1237f52d1..d2c33b512 100644 --- a/host/lib/usrp/b100/CMakeLists.txt +++ b/host/lib/usrp/b100/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011 Ettus Research LLC +# Copyright 2011-2012 Ettus Research LLC # # 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 @@ -26,7 +26,6 @@ LIBUHD_REGISTER_COMPONENT("B100" ENABLE_B100 ON "ENABLE_LIBUHD;ENABLE_USB" OFF) IF(ENABLE_B100) LIBUHD_APPEND_SOURCES( - ${CMAKE_CURRENT_SOURCE_DIR}/b100_ctrl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/b100_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp index eec777842..94dc88db4 100644 --- a/host/lib/usrp/b100/b100_impl.cpp +++ b/host/lib/usrp/b100/b100_impl.cpp @@ -17,12 +17,11 @@ #include "apply_corrections.hpp" #include "b100_impl.hpp" -#include "b100_ctrl.hpp" +#include "b100_regs.hpp" #include "fpga_regs_standard.h" #include "usrp_i2c_addr.h" #include "usrp_commands.h" #include <uhd/transport/usb_control.hpp> -#include "ctrl_packet.hpp" #include <uhd/utils/msg.hpp> #include <uhd/exception.hpp> #include <uhd/utils/static.hpp> @@ -33,7 +32,6 @@ #include <boost/filesystem.hpp> #include <boost/thread/thread.hpp> #include <boost/lexical_cast.hpp> -#include "b100_regs.hpp" #include <cstdio> #include <iostream> @@ -188,10 +186,10 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //create the control transport device_addr_t ctrl_xport_args; - ctrl_xport_args["recv_frame_size"] = boost::lexical_cast<std::string>(CTRL_PACKET_LENGTH); + ctrl_xport_args["recv_frame_size"] = "512"; ctrl_xport_args["num_recv_frames"] = "16"; - ctrl_xport_args["send_frame_size"] = boost::lexical_cast<std::string>(CTRL_PACKET_LENGTH); - ctrl_xport_args["num_send_frames"] = "4"; + ctrl_xport_args["send_frame_size"] = "512"; + ctrl_xport_args["num_send_frames"] = "16"; _ctrl_transport = usb_zero_copy::make( handle, @@ -199,17 +197,22 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ 3, 4, //interface, endpoint ctrl_xport_args ); - while (_ctrl_transport->get_recv_buff(0.0)){} //flush ctrl xport this->enable_gpif(true); //////////////////////////////////////////////////////////////////// - // Initialize FPGA wishbone communication + // Initialize FPGA control communication //////////////////////////////////////////////////////////////////// - _fpga_ctrl = b100_ctrl::make(_ctrl_transport); - _fpga_ctrl->poke32(B100_REG_GLOBAL_RESET, 0); //global fpga reset + fifo_ctrl_excelsior_config fifo_ctrl_config; + fifo_ctrl_config.async_sid_base = B100_TX_ASYNC_SID; + fifo_ctrl_config.num_async_chan = 1; + fifo_ctrl_config.ctrl_sid_base = B100_CTRL_MSG_SID; + fifo_ctrl_config.spi_base = TOREG(SR_SPI); + fifo_ctrl_config.spi_rb = REG_RB_SPI; + _fifo_ctrl = fifo_ctrl_excelsior::make(_ctrl_transport, fifo_ctrl_config); + //perform a test peek operation try{ - _fpga_ctrl->peek32(0); + _fifo_ctrl->peek32(0); } //try reset once in the case of failure catch(const uhd::exception &){ @@ -217,7 +220,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ UHD_MSG(warning) << "The control endpoint was left in a bad state.\n" "Attempting endpoint re-enumeration...\n" << std::endl; - _fpga_ctrl.reset(); + _fifo_ctrl.reset(); _ctrl_transport.reset(); _fx2_ctrl->usrp_fx2_reset(); goto b100_impl_constructor_begin; @@ -227,8 +230,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // Initialize peripherals after reset //////////////////////////////////////////////////////////////////// - _fpga_i2c_ctrl = i2c_core_100::make(_fpga_ctrl, B100_REG_SLAVE(3)); - _fpga_spi_ctrl = spi_core_100::make(_fpga_ctrl, B100_REG_SLAVE(2)); + _fpga_i2c_ctrl = i2c_core_200::make(_fifo_ctrl, TOREG(SR_I2C), REG_RB_I2C); //////////////////////////////////////////////////////////////////// // Create data transport @@ -242,6 +244,10 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ data_xport_args["send_frame_size"] = device_addr.get("send_frame_size", "16384"); data_xport_args["num_send_frames"] = device_addr.get("num_send_frames", "16"); + //let packet padder know the LUT size in number of words32 + const size_t rx_lut_size = size_t(data_xport_args.cast<double>("recv_frame_size", 0.0)); + _fifo_ctrl->poke32(TOREG(SR_PADDER+0), rx_lut_size/sizeof(boost::uint32_t)); + _data_transport = usb_zero_copy::make_wrapper( usb_zero_copy::make( handle, // identifier @@ -251,7 +257,6 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ ), B100_MAX_PKT_BYTE_LIMIT ); - while (_data_transport->get_recv_buff(0.0)){} //flush data xport //////////////////////////////////////////////////////////////////// // Initialize the properties tree @@ -277,12 +282,17 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //^^^ clock created up top, just reg props here... ^^^ _tree->create<double>(mb_path / "tick_rate") .publish(boost::bind(&b100_clock_ctrl::get_fpga_clock_rate, _clock_ctrl)) + .subscribe(boost::bind(&fifo_ctrl_excelsior::set_tick_rate, _fifo_ctrl, _1)) .subscribe(boost::bind(&b100_impl::update_tick_rate, this, _1)); + //subscribe the command time while we are at it + _tree->create<time_spec_t>(mb_path / "time/cmd") + .subscribe(boost::bind(&fifo_ctrl_excelsior::set_time, _fifo_ctrl, _1)); + //////////////////////////////////////////////////////////////////// // create codec control objects //////////////////////////////////////////////////////////////////// - _codec_ctrl = b100_codec_ctrl::make(_fpga_spi_ctrl); + _codec_ctrl = b100_codec_ctrl::make(_fifo_ctrl); const fs_path rx_codec_path = mb_path / "rx_codecs/A"; const fs_path tx_codec_path = mb_path / "tx_codecs/A"; _tree->create<std::string>(rx_codec_path / "name").set("ad9522"); @@ -304,8 +314,8 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // create frontend control objects //////////////////////////////////////////////////////////////////// - _rx_fe = rx_frontend_core_200::make(_fpga_ctrl, B100_REG_SR_ADDR(B100_SR_RX_FRONT)); - _tx_fe = tx_frontend_core_200::make(_fpga_ctrl, B100_REG_SR_ADDR(B100_SR_TX_FRONT)); + _rx_fe = rx_frontend_core_200::make(_fifo_ctrl, TOREG(SR_RX_FE)); + _tx_fe = tx_frontend_core_200::make(_fifo_ctrl, TOREG(SR_TX_FE)); _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec") .subscribe(boost::bind(&b100_impl::update_rx_subdev_spec, this, _1)); @@ -334,13 +344,17 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // create rx dsp control objects //////////////////////////////////////////////////////////////////// - _rx_dsps.push_back(rx_dsp_core_200::make( - _fpga_ctrl, B100_REG_SR_ADDR(B100_SR_RX_DSP0), B100_REG_SR_ADDR(B100_SR_RX_CTRL0), B100_RX_SID_BASE + 0 - )); - _rx_dsps.push_back(rx_dsp_core_200::make( - _fpga_ctrl, B100_REG_SR_ADDR(B100_SR_RX_DSP1), B100_REG_SR_ADDR(B100_SR_RX_CTRL1), B100_RX_SID_BASE + 1 - )); - for (size_t dspno = 0; dspno < _rx_dsps.size(); dspno++){ + const size_t num_rx_dsps = _fifo_ctrl->peek32(REG_RB_NUM_RX_DSP); + for (size_t dspno = 0; dspno < num_rx_dsps; dspno++) + { + const size_t sr_off = dspno*32; + _rx_dsps.push_back(rx_dsp_core_200::make( + _fifo_ctrl, + TOREG(SR_RX_DSP0+sr_off), + TOREG(SR_RX_CTRL0+sr_off), + B100_RX_SID_BASE + dspno + )); + _rx_dsps[dspno]->set_link_rate(B100_LINK_RATE_BPS); _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&rx_dsp_core_200::set_tick_rate, _rx_dsps[dspno], _1)); @@ -363,7 +377,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ // create tx dsp control objects //////////////////////////////////////////////////////////////////// _tx_dsp = tx_dsp_core_200::make( - _fpga_ctrl, B100_REG_SR_ADDR(B100_SR_TX_DSP), B100_REG_SR_ADDR(B100_SR_TX_CTRL), B100_TX_ASYNC_SID + _fifo_ctrl, TOREG(SR_TX_DSP), TOREG(SR_TX_CTRL), B100_TX_ASYNC_SID ); _tx_dsp->set_link_rate(B100_LINK_RATE_BPS); _tree->access<double>(mb_path / "tick_rate") @@ -383,12 +397,12 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ // create time control objects //////////////////////////////////////////////////////////////////// time64_core_200::readback_bases_type time64_rb_bases; - time64_rb_bases.rb_hi_now = B100_REG_RB_TIME_NOW_HI; - time64_rb_bases.rb_lo_now = B100_REG_RB_TIME_NOW_LO; - time64_rb_bases.rb_hi_pps = B100_REG_RB_TIME_PPS_HI; - time64_rb_bases.rb_lo_pps = B100_REG_RB_TIME_PPS_LO; + time64_rb_bases.rb_hi_now = REG_RB_TIME_NOW_HI; + time64_rb_bases.rb_lo_now = REG_RB_TIME_NOW_LO; + time64_rb_bases.rb_hi_pps = REG_RB_TIME_PPS_HI; + time64_rb_bases.rb_lo_pps = REG_RB_TIME_PPS_LO; _time64 = time64_core_200::make( - _fpga_ctrl, B100_REG_SR_ADDR(B100_SR_TIME64), time64_rb_bases + _fifo_ctrl, TOREG(SR_TIME64), time64_rb_bases ); _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&time64_core_200::set_tick_rate, _time64, _1)); @@ -412,7 +426,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // create user-defined control objects //////////////////////////////////////////////////////////////////// - _user = user_settings_core_200::make(_fpga_ctrl, B100_REG_SR_ADDR(B100_SR_USER_REGS)); + _user = user_settings_core_200::make(_fifo_ctrl, TOREG(SR_USER_REGS)); _tree->create<user_settings_core_200::user_reg_t>(mb_path / "user/regs") .subscribe(boost::bind(&user_settings_core_200::set_reg, _user, _1)); @@ -438,7 +452,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ .subscribe(boost::bind(&b100_impl::set_db_eeprom, this, "gdb", _1)); //create a new dboard interface and manager - _dboard_iface = make_b100_dboard_iface(_fpga_ctrl, _fpga_i2c_ctrl, _fpga_spi_ctrl, _clock_ctrl, _codec_ctrl); + _dboard_iface = make_b100_dboard_iface(_fifo_ctrl, _fpga_i2c_ctrl, _fifo_ctrl/*spi*/, _clock_ctrl, _codec_ctrl); _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_dboard_iface); _dboard_manager = dboard_manager::make( rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id, @@ -458,7 +472,11 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ } //initialize io handling - this->io_init(); + _recv_demuxer = recv_packet_demuxer::make(_data_transport, _rx_dsps.size(), B100_RX_SID_BASE); + + //allocate streamer weak ptrs containers + _rx_streamers.resize(_rx_dsps.size()); + _tx_streamers.resize(1/*known to be 1 dsp*/); //////////////////////////////////////////////////////////////////// // do some post-init tasks @@ -483,8 +501,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ } b100_impl::~b100_impl(void){ - //set an empty async callback now that we deconstruct - _fpga_ctrl->set_async_cb(b100_ctrl::async_cb_type()); + //NOP } void b100_impl::check_fw_compat(void){ @@ -502,7 +519,7 @@ void b100_impl::check_fw_compat(void){ } void b100_impl::check_fpga_compat(void){ - const boost::uint32_t fpga_compat_num = _fpga_ctrl->peek32(B100_REG_RB_COMPAT); + const boost::uint32_t fpga_compat_num = _fifo_ctrl->peek32(REG_RB_COMPAT); boost::uint16_t fpga_major = fpga_compat_num >> 16, fpga_minor = fpga_compat_num & 0xffff; if (fpga_major == 0){ //old version scheme fpga_major = fpga_minor; diff --git a/host/lib/usrp/b100/b100_impl.hpp b/host/lib/usrp/b100/b100_impl.hpp index 22e22bcff..250229fb8 100644 --- a/host/lib/usrp/b100/b100_impl.hpp +++ b/host/lib/usrp/b100/b100_impl.hpp @@ -19,20 +19,19 @@ #define INCLUDED_B100_IMPL_HPP #include "fx2_ctrl.hpp" -#include "b100_ctrl.hpp" #include "clock_ctrl.hpp" #include "codec_ctrl.hpp" -#include "spi_core_100.hpp" -#include "i2c_core_100.hpp" +#include "i2c_core_200.hpp" #include "rx_frontend_core_200.hpp" #include "tx_frontend_core_200.hpp" #include "rx_dsp_core_200.hpp" #include "tx_dsp_core_200.hpp" #include "time64_core_200.hpp" +#include "fifo_ctrl_excelsior.hpp" #include "user_settings_core_200.hpp" +#include "recv_packet_demuxer.hpp" #include <uhd/device.hpp> #include <uhd/property_tree.hpp> -#include <uhd/utils/pimpl.hpp> #include <uhd/types/dict.hpp> #include <uhd/types/sensors.hpp> #include <uhd/types/clock_config.hpp> @@ -47,10 +46,11 @@ static const double B100_LINK_RATE_BPS = 256e6/5; //pratical link rate (< 480 Mbps) static const std::string B100_FW_FILE_NAME = "usrp_b100_fw.ihx"; static const std::string B100_FPGA_FILE_NAME = "usrp_b100_fpga.bin"; -static const boost::uint16_t B100_FW_COMPAT_NUM = 0x03; -static const boost::uint16_t B100_FPGA_COMPAT_NUM = 10; -static const boost::uint32_t B100_RX_SID_BASE = 2; -static const boost::uint32_t B100_TX_ASYNC_SID = 1; +static const boost::uint16_t B100_FW_COMPAT_NUM = 0x04; +static const boost::uint16_t B100_FPGA_COMPAT_NUM = 11; +static const boost::uint32_t B100_RX_SID_BASE = 30; +static const boost::uint32_t B100_TX_ASYNC_SID = 10; +static const boost::uint32_t B100_CTRL_MSG_SID = 20; static const double B100_DEFAULT_TICK_RATE = 64e6; static const size_t B100_MAX_PKT_BYTE_LIMIT = 2048; static const std::string B100_EEPROM_MAP_KEY = "B100"; @@ -80,8 +80,8 @@ private: uhd::property_tree::sptr _tree; //controllers - spi_core_100::sptr _fpga_spi_ctrl; - i2c_core_100::sptr _fpga_i2c_ctrl; + fifo_ctrl_excelsior::sptr _fifo_ctrl; + i2c_core_200::sptr _fpga_i2c_ctrl; rx_frontend_core_200::sptr _rx_fe; tx_frontend_core_200::sptr _tx_fe; std::vector<rx_dsp_core_200::sptr> _rx_dsps; @@ -90,20 +90,17 @@ private: user_settings_core_200::sptr _user; b100_clock_ctrl::sptr _clock_ctrl; b100_codec_ctrl::sptr _codec_ctrl; - b100_ctrl::sptr _fpga_ctrl; uhd::usrp::fx2_ctrl::sptr _fx2_ctrl; //transports - uhd::transport::zero_copy_if::sptr _data_transport, _ctrl_transport; + uhd::transport::zero_copy_if::sptr _ctrl_transport; + uhd::transport::zero_copy_if::sptr _data_transport; + uhd::usrp::recv_packet_demuxer::sptr _recv_demuxer; //dboard stuff uhd::usrp::dboard_manager::sptr _dboard_manager; uhd::usrp::dboard_iface::sptr _dboard_iface; - //handle io stuff - UHD_PIMPL_DECL(io_impl) _io_impl; - void io_init(void); - //device properties interface uhd::property_tree::sptr get_tree(void) const{ return _tree; @@ -126,7 +123,6 @@ private: void update_clock_source(const std::string &); void enable_gpif(const bool); void clear_fpga_fifo(void); - void handle_async_message(uhd::transport::managed_recv_buffer::sptr); uhd::sensor_value_t get_ref_locked(void); void set_rx_fe_corrections(const double); void set_tx_fe_corrections(const double); diff --git a/host/lib/usrp/b100/b100_regs.hpp b/host/lib/usrp/b100/b100_regs.hpp index 987a09f03..48eb0460d 100644 --- a/host/lib/usrp/b100/b100_regs.hpp +++ b/host/lib/usrp/b100/b100_regs.hpp @@ -15,109 +15,49 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -//////////////////////////////////////////////////////////////// -// -// Memory map for wishbone bus -// -//////////////////////////////////////////////////////////////// - -// All addresses are byte addresses. All accesses are word (16-bit) accesses. -// This means that address bit 0 is usually 0. -// There are 11 bits of address for the control. - #ifndef INCLUDED_B100_REGS_HPP #define INCLUDED_B100_REGS_HPP -///////////////////////////////////////////////////// -// Slave pointers +#include <boost/cstdint.hpp> -#define B100_REG_SLAVE(n) ((n)<<7) +#define TOREG(x) ((x)*4) -///////////////////////////////////////////////////// -// Slave 0 -- Misc Regs +#define localparam static const int -#define B100_REG_MISC_BASE B100_REG_SLAVE(0) +localparam SR_MISC = 0; // 5 +localparam SR_USER_REGS = 5; // 2 +localparam SR_PADDER = 10; // 2 -#define B100_REG_MISC_LED B100_REG_MISC_BASE + 0 -#define B100_REG_MISC_SW B100_REG_MISC_BASE + 2 -#define B100_REG_MISC_CGEN_CTRL B100_REG_MISC_BASE + 4 -#define B100_REG_MISC_CGEN_ST B100_REG_MISC_BASE + 6 +localparam SR_TX_CTRL = 32; // 6 +localparam SR_TX_DSP = 40; // 5 +localparam SR_TX_FE = 48; // 5 -///////////////////////////////////////////////////// -// Slave 1 -- UART -// CLKDIV is 16 bits, others are only 8 +localparam SR_RX_CTRL0 = 96; // 9 +localparam SR_RX_DSP0 = 106; // 7 +localparam SR_RX_FE = 114; // 5 -#define B100_REG_UART_BASE B100_REG_SLAVE(1) +localparam SR_RX_CTRL1 = 128; // 9 +localparam SR_RX_DSP1 = 138; // 7 -#define B100_REG_UART_CLKDIV B100_REG_UART_BASE + 0 -#define B100_REG_UART_TXLEVEL B100_REG_UART_BASE + 2 -#define B100_REG_UART_RXLEVEL B100_REG_UART_BASE + 4 -#define B100_REG_UART_TXCHAR B100_REG_UART_BASE + 6 -#define B100_REG_UART_RXCHAR B100_REG_UART_BASE + 8 +localparam SR_TIME64 = 192; // 6 +localparam SR_SPI = 208; // 3 +localparam SR_I2C = 216; // 1 +localparam SR_GPIO = 224; // 5 -///////////////////////////////////////////////////// -// Slave 2 -- SPI Core -//these are 32-bit registers mapped onto the 16-bit Wishbone bus. -//Using peek32/poke32 should allow transparent use of these registers. -#define B100_REG_SPI_BASE B100_REG_SLAVE(2) +#define REG_RB_TIME_NOW_HI TOREG(10) +#define REG_RB_TIME_NOW_LO TOREG(11) +#define REG_RB_TIME_PPS_HI TOREG(14) +#define REG_RB_TIME_PPS_LO TOREG(15) +#define REG_RB_SPI TOREG(0) +#define REG_RB_COMPAT TOREG(1) +#define REG_RB_GPIO TOREG(3) +#define REG_RB_I2C TOREG(2) +#define REG_RB_NUM_RX_DSP TOREG(6) //spi slave constants #define B100_SPI_SS_AD9862 (1 << 2) #define B100_SPI_SS_TX_DB (1 << 1) #define B100_SPI_SS_RX_DB (1 << 0) -//////////////////////////////////////////////// -// Slave 3 -- I2C Core - -#define B100_REG_I2C_BASE B100_REG_SLAVE(3) - -/////////////////////////////////////////////////// -// Slave 7 -- Readback Mux 32 - -#define B100_REG_RB_MUX_32_BASE B100_REG_SLAVE(7) - -#define B100_REG_RB_TIME_NOW_HI B100_REG_RB_MUX_32_BASE + 0 -#define B100_REG_RB_TIME_NOW_LO B100_REG_RB_MUX_32_BASE + 4 -#define B100_REG_RB_TIME_PPS_HI B100_REG_RB_MUX_32_BASE + 8 -#define B100_REG_RB_TIME_PPS_LO B100_REG_RB_MUX_32_BASE + 12 -#define B100_REG_RB_MISC_TEST32 B100_REG_RB_MUX_32_BASE + 16 -#define B100_REG_RB_COMPAT B100_REG_RB_MUX_32_BASE + 24 -#define B100_REG_RB_GPIO B100_REG_RB_MUX_32_BASE + 28 - -//////////////////////////////////////////////////// -// Slaves 8 & 9 -- Settings Bus -// -// Output-only, no readback, 64 registers total -// Each register must be written 32 bits at a time -// First the address xxx_xx00 and then xxx_xx10 -// 64 total regs in address space -#define B100_SR_RX_CTRL0 0 // 9 regs (+0 to +8) -#define B100_SR_RX_DSP0 10 // 4 regs (+0 to +3) -#define B100_SR_RX_CTRL1 16 // 9 regs (+0 to +8) -#define B100_SR_RX_DSP1 26 // 4 regs (+0 to +3) -#define B100_SR_TX_CTRL 32 // 4 regs (+0 to +3) -#define B100_SR_TX_DSP 38 // 3 regs (+0 to +2) - -#define B100_SR_TIME64 42 // 6 regs (+0 to +5) -#define B100_SR_RX_FRONT 48 // 5 regs (+0 to +4) -#define B100_SR_TX_FRONT 54 // 5 regs (+0 to +4) - -#define B100_SR_REG_TEST32 60 // 1 reg -#define B100_SR_CLEAR_FIFO 61 // 1 reg -#define B100_SR_GLOBAL_RESET 63 // 1 reg -#define B100_SR_USER_REGS 64 // 2 regs - -#define B100_SR_GPIO 128 - -#define B100_REG_SR_ADDR(n) (B100_REG_SLAVE(8) + (4*(n))) - -#define B100_REG_SR_MISC_TEST32 B100_REG_SR_ADDR(B100_SR_REG_TEST32) - -///////////////////////////////////////////////// -// Magic reset regs -//////////////////////////////////////////////// -#define B100_REG_CLEAR_FIFO B100_REG_SR_ADDR(B100_SR_CLEAR_FIFO) -#define B100_REG_GLOBAL_RESET B100_REG_SR_ADDR(B100_SR_GLOBAL_RESET) - -#endif +#endif /*INCLUDED_B100_REGS_HPP*/ diff --git a/host/lib/usrp/b100/dboard_iface.cpp b/host/lib/usrp/b100/dboard_iface.cpp index 39ad5c5ac..25604da72 100644 --- a/host/lib/usrp/b100/dboard_iface.cpp +++ b/host/lib/usrp/b100/dboard_iface.cpp @@ -45,7 +45,7 @@ public: _spi_iface = spi_iface; _clock = clock; _codec = codec; - _gpio = gpio_core_200::make(_wb_iface, B100_REG_SR_ADDR(B100_SR_GPIO), B100_REG_RB_GPIO); + _gpio = gpio_core_200::make(_wb_iface, TOREG(SR_GPIO), REG_RB_GPIO); //init the clock rate shadows this->set_clock_rate(UNIT_RX, _clock->get_fpga_clock_rate()); diff --git a/host/lib/usrp/b100/io_impl.cpp b/host/lib/usrp/b100/io_impl.cpp index 674380cca..723756dcc 100644 --- a/host/lib/usrp/b100/io_impl.cpp +++ b/host/lib/usrp/b100/io_impl.cpp @@ -15,16 +15,10 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include "recv_packet_demuxer.hpp" #include "validate_subdev_spec.hpp" -#include "async_packet_handler.hpp" #include "../../transport/super_recv_packet_handler.hpp" #include "../../transport/super_send_packet_handler.hpp" -#include "usrp_commands.h" #include "b100_impl.hpp" -#include "b100_regs.hpp" -#include <uhd/utils/thread_priority.hpp> -#include <uhd/transport/bounded_buffer.hpp> #include <boost/bind.hpp> #include <boost/format.hpp> #include <boost/bind.hpp> @@ -37,66 +31,6 @@ using namespace uhd; using namespace uhd::usrp; using namespace uhd::transport; -/*********************************************************************** - * IO Implementation Details - **********************************************************************/ -struct b100_impl::io_impl{ - io_impl(void): - async_msg_fifo(1000/*messages deep*/) - { /* NOP */ } - - zero_copy_if::sptr data_transport; - bounded_buffer<async_metadata_t> async_msg_fifo; - recv_packet_demuxer::sptr demuxer; - double tick_rate; -}; - -/*********************************************************************** - * Initialize internals within this file - **********************************************************************/ -void b100_impl::io_init(void){ - - //clear fifo state machines - _fpga_ctrl->poke32(B100_REG_CLEAR_FIFO, 0); - - //allocate streamer weak ptrs containers - _rx_streamers.resize(_rx_dsps.size()); - _tx_streamers.resize(1/*known to be 1 dsp*/); - - //create new io impl - _io_impl = UHD_PIMPL_MAKE(io_impl, ()); - _io_impl->demuxer = recv_packet_demuxer::make(_data_transport, _rx_dsps.size(), B100_RX_SID_BASE); - - //now its safe to register the async callback - _fpga_ctrl->set_async_cb(boost::bind(&b100_impl::handle_async_message, this, _1)); -} - -void b100_impl::handle_async_message(managed_recv_buffer::sptr rbuf){ - vrt::if_packet_info_t if_packet_info; - if_packet_info.num_packet_words32 = rbuf->size()/sizeof(boost::uint32_t); - const boost::uint32_t *vrt_hdr = rbuf->cast<const boost::uint32_t *>(); - try{ - vrt::if_hdr_unpack_le(vrt_hdr, if_packet_info); - } - catch(const std::exception &e){ - UHD_MSG(error) << "Error (handle_async_message): " << e.what() << std::endl; - } - - if (if_packet_info.sid == B100_TX_ASYNC_SID and if_packet_info.packet_type != vrt::if_packet_info_t::PACKET_TYPE_DATA){ - - //fill in the async metadata - async_metadata_t metadata; - load_metadata_from_buff(uhd::wtohx<boost::uint32_t>, metadata, if_packet_info, vrt_hdr, _io_impl->tick_rate); - - //push the message onto the queue - _io_impl->async_msg_fifo.push_with_pop_on_full(metadata); - - //print some fastpath messages - standard_async_msg_prints(metadata); - } - else UHD_MSG(error) << "Unknown async packet" << std::endl; -} - void b100_impl::update_rates(void){ const fs_path mb_path = "/mboards/0"; _tree->access<double>(mb_path / "tick_rate").update(); @@ -111,7 +45,6 @@ void b100_impl::update_rates(void){ } void b100_impl::update_tick_rate(const double rate){ - _io_impl->tick_rate = rate; //update the tick rate on all existing streamers -> thread safe for (size_t i = 0; i < _rx_streamers.size(); i++){ @@ -181,8 +114,7 @@ void b100_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ bool b100_impl::recv_async_msg( async_metadata_t &async_metadata, double timeout ){ - boost::this_thread::disable_interruption di; //disable because the wait can throw - return _io_impl->async_msg_fifo.pop_with_timed_wait(async_metadata, timeout); + return _fifo_ctrl->pop_async_msg(async_metadata, timeout); } /*********************************************************************** @@ -227,7 +159,7 @@ rx_streamer::sptr b100_impl::get_rx_stream(const uhd::stream_args_t &args_){ _rx_dsps[dsp]->set_nsamps_per_packet(spp); //seems to be a good place to set this _rx_dsps[dsp]->setup(args); my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( - &recv_packet_demuxer::get_recv_buff, _io_impl->demuxer, dsp, _1 + &recv_packet_demuxer::get_recv_buff, _recv_demuxer, dsp, _1 ), true /*flush*/); my_streamer->set_overflow_handler(chan_i, boost::bind( &rx_dsp_core_200::handle_overflow, _rx_dsps[dsp] diff --git a/host/lib/usrp/common/CMakeLists.txt b/host/lib/usrp/common/CMakeLists.txt index 9abd34afa..fa07e3d1d 100644 --- a/host/lib/usrp/common/CMakeLists.txt +++ b/host/lib/usrp/common/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011 Ettus Research LLC +# Copyright 2011-2012 Ettus Research LLC # # 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 @@ -32,4 +32,5 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/apply_corrections.cpp ${CMAKE_CURRENT_SOURCE_DIR}/validate_subdev_spec.cpp ${CMAKE_CURRENT_SOURCE_DIR}/recv_packet_demuxer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/fifo_ctrl_excelsior.cpp ) diff --git a/host/lib/usrp/common/fifo_ctrl_excelsior.cpp b/host/lib/usrp/common/fifo_ctrl_excelsior.cpp new file mode 100644 index 000000000..5e4a1e243 --- /dev/null +++ b/host/lib/usrp/common/fifo_ctrl_excelsior.cpp @@ -0,0 +1,293 @@ +// +// Copyright 2012 Ettus Research LLC +// +// 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/>. +// + +#include "fifo_ctrl_excelsior.hpp" +#include "async_packet_handler.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/tasks.hpp> +#include <uhd/utils/safe_call.hpp> +#include <uhd/utils/thread_priority.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/thread.hpp> +#include <boost/format.hpp> +#include <boost/bind.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +static const size_t POKE32_CMD = (1 << 8); +static const size_t PEEK32_CMD = 0; +static const double ACK_TIMEOUT = 0.5; +static const double MASSIVE_TIMEOUT = 10.0; //for when we wait on a timed command +static const boost::uint32_t MAX_SEQS_OUT = 15; + +#define SPI_DIV _config.spi_base + 0 +#define SPI_CTRL _config.spi_base + 4 +#define SPI_DATA _config.spi_base + 8 +#define SPI_DIVIDER 4 + +struct ctrl_result_t{ + boost::uint32_t msg[2]; +}; + +class fifo_ctrl_excelsior_impl : public fifo_ctrl_excelsior{ +public: + + fifo_ctrl_excelsior_impl(zero_copy_if::sptr xport, const fifo_ctrl_excelsior_config &config): + _xport(xport), + _config(config), + _seq_out(0), + _seq_ack(0), + _timeout(ACK_TIMEOUT), + _async_fifo(1000), + _ctrl_fifo(MAX_SEQS_OUT+1) + { + while (_xport->get_recv_buff(0.0)){} //flush + this->set_time(uhd::time_spec_t(0.0)); + this->set_tick_rate(1.0); //something possible but bogus + _msg_task = task::make(boost::bind(&fifo_ctrl_excelsior_impl::handle_msg, this)); + this->init_spi(); + } + + ~fifo_ctrl_excelsior_impl(void){ + _timeout = ACK_TIMEOUT; //reset timeout to something small + UHD_SAFE_CALL( + this->peek32(0); //dummy peek with the purpose of ack'ing all packets + ) + } + + bool pop_async_msg(async_metadata_t &async_metadata, double timeout){ + boost::this_thread::disable_interruption di; //disable because the wait can throw + return _async_fifo.pop_with_timed_wait(async_metadata, timeout); + } + + void handle_msg(void){ + set_thread_priority_safe(); + while (not boost::this_thread::interruption_requested()){ + this->handle_msg1(); + } + } + + void handle_msg1(void){ + managed_recv_buffer::sptr buff = _xport->get_recv_buff(); + if (not buff) return; + const boost::uint32_t *pkt = buff->cast<const boost::uint32_t *>(); + vrt::if_packet_info_t packet_info; + packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); + try{ + vrt::if_hdr_unpack_le(pkt, packet_info); + } + catch(const std::exception &ex){ + UHD_MSG(error) << "FIFO ctrl bad VITA packet: " << ex.what() << std::endl; + } + if (packet_info.has_sid and packet_info.sid == _config.ctrl_sid_base){ + ctrl_result_t res = ctrl_result_t(); + res.msg[0] = uhd::wtohx(pkt[packet_info.num_header_words32+0]); + res.msg[1] = uhd::wtohx(pkt[packet_info.num_header_words32+1]); + _ctrl_fifo.push_with_haste(res); + } + else if (packet_info.has_sid and packet_info.sid >= _config.async_sid_base and packet_info.sid <= _config.async_sid_base + _config.num_async_chan){ + async_metadata_t metadata; + load_metadata_from_buff(uhd::wtohx<boost::uint32_t>, metadata, packet_info, pkt, _tick_rate, packet_info.sid - _config.async_sid_base); + _async_fifo.push_with_pop_on_full(metadata); + standard_async_msg_prints(metadata); + } + else{ + UHD_MSG(error) << "FIFO ctrl got unknown SID: " << packet_info.sid << std::endl; + } + } + + /******************************************************************* + * Peek and poke 32 bit implementation + ******************************************************************/ + void poke32(wb_addr_type addr, boost::uint32_t data){ + boost::mutex::scoped_lock lock(_mutex); + + this->send_pkt(addr, data, POKE32_CMD); + + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + } + + boost::uint32_t peek32(wb_addr_type addr){ + boost::mutex::scoped_lock lock(_mutex); + + this->send_pkt(addr, 0, PEEK32_CMD); + + return this->wait_for_ack(_seq_out); + } + + /******************************************************************* + * Peek and poke 16 bit not implemented + ******************************************************************/ + void poke16(wb_addr_type, boost::uint16_t){ + throw uhd::not_implemented_error("poke16 not implemented in fifo ctrl module"); + } + + boost::uint16_t peek16(wb_addr_type){ + throw uhd::not_implemented_error("peek16 not implemented in fifo ctrl module"); + } + + /******************************************************************* + * FIFO controlled SPI implementation + ******************************************************************/ + void init_spi(void){ + boost::mutex::scoped_lock lock(_mutex); + + this->send_pkt(SPI_DIV, SPI_DIVIDER, POKE32_CMD); + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + + _ctrl_word_cache = 0; // force update first time around + } + + boost::uint32_t transact_spi( + int which_slave, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits, + bool readback + ){ + boost::mutex::scoped_lock lock(_mutex); + + //load control word + boost::uint32_t ctrl_word = 0; + ctrl_word |= ((which_slave & 0xffffff) << 0); + ctrl_word |= ((num_bits & 0x3ff) << 24); + if (config.mosi_edge == spi_config_t::EDGE_FALL) ctrl_word |= (1 << 31); + if (config.miso_edge == spi_config_t::EDGE_RISE) ctrl_word |= (1 << 30); + + //load data word (must be in upper bits) + const boost::uint32_t data_out = data << (32 - num_bits); + + //conditionally send control word + if (_ctrl_word_cache != ctrl_word){ + this->send_pkt(SPI_CTRL, ctrl_word, POKE32_CMD); + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + _ctrl_word_cache = ctrl_word; + } + + //send data word + this->send_pkt(SPI_DATA, data_out, POKE32_CMD); + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + + //conditional readback + if (readback){ + this->send_pkt(_config.spi_rb, 0, PEEK32_CMD); + return this->wait_for_ack(_seq_out); + } + + return 0; + } + + /******************************************************************* + * Update methods for time + ******************************************************************/ + void set_time(const uhd::time_spec_t &time){ + boost::mutex::scoped_lock lock(_mutex); + _time = time; + _use_time = _time != uhd::time_spec_t(0.0); + if (_use_time) _timeout = MASSIVE_TIMEOUT; //permanently sets larger timeout + } + + void set_tick_rate(const double rate){ + boost::mutex::scoped_lock lock(_mutex); + _tick_rate = rate; + } + +private: + + /******************************************************************* + * Primary control and interaction private methods + ******************************************************************/ + UHD_INLINE void send_pkt(wb_addr_type addr, boost::uint32_t data, int cmd){ + managed_send_buffer::sptr buff = _xport->get_send_buff(0.0); + if (not buff){ + throw uhd::runtime_error("fifo ctrl timed out getting a send buffer"); + } + boost::uint32_t *pkt = buff->cast<boost::uint32_t *>(); + + //load packet info + vrt::if_packet_info_t packet_info; + packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_CONTEXT; + packet_info.num_payload_words32 = 2; + packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t); + packet_info.packet_count = ++_seq_out; + packet_info.tsf = _time.to_ticks(_tick_rate); + packet_info.sob = false; + packet_info.eob = false; + packet_info.has_sid = false; + packet_info.has_cid = false; + packet_info.has_tsi = false; + packet_info.has_tsf = _use_time; + packet_info.has_tlr = false; + + //load header + vrt::if_hdr_pack_le(pkt, packet_info); + + //load payload + const boost::uint32_t ctrl_word = (addr/4 & 0xff) | cmd | (_seq_out << 16); + pkt[packet_info.num_header_words32+0] = uhd::htowx(ctrl_word); + pkt[packet_info.num_header_words32+1] = uhd::htowx(data); + + //send the buffer over the interface + buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32)); + } + + UHD_INLINE bool wraparound_lt16(const boost::int16_t i0, const boost::int16_t i1){ + if (((i0 ^ i1) & 0x8000) == 0) //same sign bits + return boost::uint16_t(i0) < boost::uint16_t(i1); + return boost::int16_t(i1 - i0) > 0; + } + + UHD_INLINE boost::uint32_t wait_for_ack(const boost::uint16_t seq_to_ack){ + + while (wraparound_lt16(_seq_ack, seq_to_ack)){ + ctrl_result_t res = ctrl_result_t(); + if (not _ctrl_fifo.pop_with_timed_wait(res, _timeout)){ + throw uhd::runtime_error("fifo ctrl timed out looking for acks"); + } + _seq_ack = res.msg[0] >> 16; + if (_seq_ack == seq_to_ack) return res.msg[1]; + } + + return 0; + } + + zero_copy_if::sptr _xport; + const fifo_ctrl_excelsior_config _config; + boost::mutex _mutex; + boost::uint16_t _seq_out; + boost::uint16_t _seq_ack; + uhd::time_spec_t _time; + bool _use_time; + double _tick_rate; + double _timeout; + boost::uint32_t _ctrl_word_cache; + bounded_buffer<async_metadata_t> _async_fifo; + bounded_buffer<ctrl_result_t> _ctrl_fifo; + task::sptr _msg_task; +}; + + +fifo_ctrl_excelsior::sptr fifo_ctrl_excelsior::make(zero_copy_if::sptr xport, const fifo_ctrl_excelsior_config &config) +{ + return sptr(new fifo_ctrl_excelsior_impl(xport, config)); +} diff --git a/host/lib/usrp/common/fifo_ctrl_excelsior.hpp b/host/lib/usrp/common/fifo_ctrl_excelsior.hpp new file mode 100644 index 000000000..c3ef65a2c --- /dev/null +++ b/host/lib/usrp/common/fifo_ctrl_excelsior.hpp @@ -0,0 +1,63 @@ +// +// Copyright 2012 Ettus Research LLC +// +// 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/>. +// + +#ifndef INCLUDED_B200_CTRL_HPP +#define INCLUDED_B200_CTRL_HPP + +#include <uhd/types/time_spec.hpp> +#include <uhd/types/metadata.hpp> +#include <uhd/types/serial.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include "wb_iface.hpp" +#include <string> + + +struct fifo_ctrl_excelsior_config +{ + size_t async_sid_base; + size_t num_async_chan; + size_t ctrl_sid_base; + size_t spi_base; + size_t spi_rb; +}; + +/*! + * Provide access to peek, poke, spi, and async messages. + */ +class fifo_ctrl_excelsior : public wb_iface, public uhd::spi_iface{ +public: + typedef boost::shared_ptr<fifo_ctrl_excelsior> sptr; + + //! Make a new control object + static sptr make( + uhd::transport::zero_copy_if::sptr xport, + const fifo_ctrl_excelsior_config &config + ); + + //! Set the command time that will activate + virtual void set_time(const uhd::time_spec_t &time) = 0; + + //! Set the tick rate (converting time into ticks) + virtual void set_tick_rate(const double rate) = 0; + + //! Pop an async message from the queue or timeout + virtual bool pop_async_msg(uhd::async_metadata_t &async_metadata, double timeout) = 0; +}; + +#endif /* INCLUDED_B200_CTRL_HPP */ |