diff options
194 files changed, 9736 insertions, 2958 deletions
diff --git a/fpga/usrp2/control_lib/Makefile.srcs b/fpga/usrp2/control_lib/Makefile.srcs index bc8e4d5bc..383ed97d8 100644 --- a/fpga/usrp2/control_lib/Makefile.srcs +++ b/fpga/usrp2/control_lib/Makefile.srcs @@ -42,4 +42,7 @@ pic.v \  longfifo.v \  shortfifo.v \  medfifo.v \ +nsgpio16LE.v \ +settings_bus_16LE.v \ +atr_controller16.v \  )) diff --git a/fpga/usrp2/control_lib/atr_controller16.v b/fpga/usrp2/control_lib/atr_controller16.v new file mode 100644 index 000000000..3d8b5b1e9 --- /dev/null +++ b/fpga/usrp2/control_lib/atr_controller16.v @@ -0,0 +1,60 @@ + +// Automatic transmit/receive switching of control pins to daughterboards +// Store everything in registers for now, but could use a RAM for more +// complex state machines in the future + +module atr_controller16 +  (input clk_i, input rst_i, +   input [5:0] adr_i, input [1:0] sel_i, input [15:0] dat_i, output reg [15:0] dat_o, +   input we_i, input stb_i, input cyc_i, output reg ack_o, +   input run_rx, input run_tx, input [31:0] master_time, +   output [31:0] ctrl_lines); +    +   reg [3:0] state; +   reg [31:0] atr_ram [0:15];  // DP distributed RAM + +   wire [3:0] sel_int = { (sel_i[1] & adr_i[1]), (sel_i[0] & adr_i[1]), +			  (sel_i[1] & ~adr_i[1]), (sel_i[0] & ~adr_i[1]) }; +    +   // WB Interface +   always @(posedge clk_i) +     if(we_i & stb_i & cyc_i) +       begin +	  if(sel_int[3]) +	    atr_ram[adr_i[5:2]][31:24] <= dat_i[15:8]; +	  if(sel_int[2]) +	    atr_ram[adr_i[5:2]][23:16] <= dat_i[7:0]; +	  if(sel_int[1]) +	    atr_ram[adr_i[5:2]][15:8] <= dat_i[15:8]; +	  if(sel_int[0]) +	    atr_ram[adr_i[5:2]][7:0] <= dat_i[7:0]; +       end // if (we_i & stb_i & cyc_i) + +   always @(posedge clk_i) +     dat_o <= adr_i[1] ? atr_ram[adr_i[5:2]][31:16] : atr_ram[adr_i[5:2]][15:0]; +    +   always @(posedge clk_i) +     ack_o <= stb_i & cyc_i & ~ack_o; + +   // Control side of DP RAM +   assign     ctrl_lines = atr_ram[state]; + +   // Put a more complex state machine with time delays and multiple states here +   //  if daughterboard requires more complex sequencing +   localparam ATR_IDLE = 4'd0; +   localparam ATR_TX = 4'd1; +   localparam ATR_RX = 4'd2; +   localparam ATR_FULL_DUPLEX = 4'd3; +    +   always @(posedge clk_i) +     if(rst_i) +       state <= ATR_IDLE; +     else +       case ({run_rx,run_tx}) +	 2'b00 : state <= ATR_IDLE; +	 2'b01 : state <= ATR_TX; +	 2'b10 : state <= ATR_RX; +	 2'b11 : state <= ATR_FULL_DUPLEX; +       endcase // case({run_rx,run_tx}) +    +endmodule // atr_controller16 diff --git a/fpga/usrp2/control_lib/newfifo/fifo_pacer.v b/fpga/usrp2/control_lib/newfifo/fifo_pacer.v new file mode 100644 index 000000000..1bf03ab6e --- /dev/null +++ b/fpga/usrp2/control_lib/newfifo/fifo_pacer.v @@ -0,0 +1,24 @@ + + +module fifo_pacer +  (input clk, +   input reset, +   input [7:0] rate, +   input enable, +   input src1_rdy_i, output dst1_rdy_o, +   output src2_rdy_o, input dst2_rdy_i, +   output underrun, overrun); + +   wire   strobe; +    +   cic_strober strober (.clock(clk), .reset(reset), .enable(enable), +			.rate(rate), .strobe_fast(1), .strobe_slow(strobe)); + +   wire   all_ready = src1_rdy_i & dst2_rdy_i; +   assign dst1_rdy_o = all_ready & strobe; +   assign src2_rdy_o = dst1_rdy_o; + +   assign underrun = strobe & ~src1_rdy_i; +   assign overrun = strobe & ~dst2_rdy_i; +    +endmodule // fifo_pacer diff --git a/fpga/usrp2/control_lib/newfifo/packet32_tb.v b/fpga/usrp2/control_lib/newfifo/packet32_tb.v new file mode 100644 index 000000000..82bb09c29 --- /dev/null +++ b/fpga/usrp2/control_lib/newfifo/packet32_tb.v @@ -0,0 +1,27 @@ + + +module packet32_tb(); + +   wire [35:0] data; +   wire       src_rdy, dst_rdy; + +   wire       clear = 0; +   reg 	      clk = 0; +   reg 	      reset = 1; + +   always #10 clk <= ~clk; +   initial #1000 reset <= 0; + +   initial $dumpfile("packet32_tb.vcd"); +   initial $dumpvars(0,packet32_tb); + +   wire [31:0] total, crc_err, seq_err, len_err; +    +   packet_generator32 pkt_gen (.clk(clk), .reset(reset), .clear(clear), +			       .data_o(data), .src_rdy_o(src_rdy), .dst_rdy_i(dst_rdy)); + +   packet_verifier32 pkt_ver (.clk(clk), .reset(reset), .clear(clear), +			      .data_i(data), .src_rdy_i(src_rdy), .dst_rdy_o(dst_rdy), +			      .total(total), .crc_err(crc_err), .seq_err(seq_err), .len_err(len_err)); + +endmodule // packet32_tb diff --git a/fpga/usrp2/control_lib/newfifo/packet_generator.v b/fpga/usrp2/control_lib/newfifo/packet_generator.v new file mode 100644 index 000000000..6e8b45ccd --- /dev/null +++ b/fpga/usrp2/control_lib/newfifo/packet_generator.v @@ -0,0 +1,59 @@ + + +module packet_generator +  (input clk, input reset, input clear, +   output reg [7:0] data_o, output sof_o, output eof_o,  +   output src_rdy_o, input dst_rdy_i); + +   localparam len = 32'd2000; + +   reg [31:0] state; +   reg [31:0] seq; +   wire [31:0] crc_out; +   wire        calc_crc = src_rdy_o & dst_rdy_i & ~(state[31:2] == 30'h3FFF_FFFF); +    +	 +   always @(posedge clk) +     if(reset | clear) +       seq <= 0; +     else +       if(eof_o & src_rdy_o & dst_rdy_i) +	 seq <= seq + 1; +    +   always @(posedge clk) +     if(reset | clear) +       state <= 0; +     else +       if(src_rdy_o & dst_rdy_i) +	 if(state == (len - 1)) +	   state <= 32'hFFFF_FFFC; +	 else +	   state <= state + 1; + +   always @* +     case(state) +       0 :   data_o <= len[7:0]; +       1 :   data_o <= len[15:8]; +       2 :   data_o <= len[23:16]; +       3 :   data_o <= len[31:24]; +       4 :   data_o <= seq[7:0]; +       5 :   data_o <= seq[15:8]; +       6 :   data_o <= seq[23:16]; +       7 :   data_o <= seq[31:24]; +       32'hFFFF_FFFC : data_o <= crc_out[31:24]; +       32'hFFFF_FFFD : data_o <= crc_out[23:16]; +       32'hFFFF_FFFE : data_o <= crc_out[15:8]; +       32'hFFFF_FFFF : data_o <= crc_out[7:0]; +       default : data_o <= state[7:0]; +     endcase // case (state) +    +   assign src_rdy_o = 1; +   assign sof_o = (state == 0); +   assign eof_o = (state == 32'hFFFF_FFFF); + +   wire        clear_crc = eof_o & src_rdy_o & dst_rdy_i; +    +   crc crc(.clk(clk), .reset(reset), .clear(clear_crc), .data(data_o),  +	   .calc(calc_crc), .crc_out(crc_out), .match()); +    +endmodule // packet_generator diff --git a/fpga/usrp2/control_lib/newfifo/packet_generator32.v b/fpga/usrp2/control_lib/newfifo/packet_generator32.v new file mode 100644 index 000000000..6f8004964 --- /dev/null +++ b/fpga/usrp2/control_lib/newfifo/packet_generator32.v @@ -0,0 +1,21 @@ + + +module packet_generator32 +  (input clk, input reset, input clear, +   output [35:0] data_o, output src_rdy_o, input dst_rdy_i); + +   wire [7:0] 	     ll_data; +   wire 	     ll_sof, ll_eof, ll_src_rdy, ll_dst_rdy_n; +    +   packet_generator pkt_gen +     (.clk(clk), .reset(reset), .clear(clear), +      .data_o(ll_data), .sof_o(ll_sof), .eof_o(ll_eof), +      .src_rdy_o(ll_src_rdy), .dst_rdy_i(~ll_dst_rdy_n)); + +   ll8_to_fifo36 ll8_to_f36 +     (.clk(clk), .reset(reset), .clear(clear), +      .ll_data(ll_data), .ll_sof_n(~ll_sof), .ll_eof_n(~ll_eof), +      .ll_src_rdy_n(~ll_src_rdy), .ll_dst_rdy_n(ll_dst_rdy_n), +      .f36_data(data_o), .f36_src_rdy_o(src_rdy_o), .f36_dst_rdy_i(dst_rdy_i)); +    +endmodule // packet_generator32 diff --git a/fpga/usrp2/control_lib/newfifo/packet_tb.v b/fpga/usrp2/control_lib/newfifo/packet_tb.v new file mode 100644 index 000000000..3c423d2ba --- /dev/null +++ b/fpga/usrp2/control_lib/newfifo/packet_tb.v @@ -0,0 +1,29 @@ + + +module packet_tb(); + +   wire [7:0] data; +   wire       sof, eof, src_rdy, dst_rdy; + +   wire       clear = 0; +   reg 	      clk = 0; +   reg 	      reset = 1; + +   always #10 clk <= ~clk; +   initial #1000 reset <= 0; + +   initial $dumpfile("packet_tb.vcd"); +   initial $dumpvars(0,packet_tb); + +   wire [31:0] total, crc_err, seq_err, len_err; +    +   packet_generator pkt_gen (.clk(clk), .reset(reset), .clear(clear), +			     .data_o(data), .sof_o(sof), .eof_o(eof), +			     .src_rdy_o(src_rdy), .dst_rdy_i(dst_rdy)); + +   packet_verifier pkt_ver (.clk(clk), .reset(reset), .clear(clear), +			    .data_i(data), .sof_i(sof), .eof_i(eof), +			    .src_rdy_i(src_rdy), .dst_rdy_o(dst_rdy), +			    .total(total), .crc_err(crc_err), .seq_err(seq_err), .len_err(len_err)); + +endmodule // packet_tb diff --git a/fpga/usrp2/control_lib/newfifo/packet_verifier.v b/fpga/usrp2/control_lib/newfifo/packet_verifier.v new file mode 100644 index 000000000..b49ad1bbb --- /dev/null +++ b/fpga/usrp2/control_lib/newfifo/packet_verifier.v @@ -0,0 +1,61 @@ + + +// Packet format -- +//    Line 1 -- Length, 32 bits +//    Line 2 -- Sequence number, 32 bits +//    Last line -- CRC, 32 bits + +module packet_verifier +  (input clk, input reset, input clear, +   input [7:0] data_i, input sof_i, input eof_i, input src_rdy_i, output dst_rdy_o, + +   output reg [31:0] total,  +   output reg [31:0] crc_err,  +   output reg [31:0] seq_err,  +   output reg [31:0] len_err); + +   reg [31:0] 	     seq_num; +   reg [31:0] 	     length; +   wire 	     first_byte, last_byte; +   reg 		     second_byte, last_byte_d1; + +   wire 	     calc_crc = src_rdy_i & dst_rdy_o; +    +   crc crc(.clk(clk), .reset(reset), .clear(last_byte_d1), .data(data_i),  +	   .calc(calc_crc), .crc_out(), .match(match_crc)); + +   assign first_byte = src_rdy_i & dst_rdy_o & sof_i; +   assign last_byte = src_rdy_i & dst_rdy_o & eof_i; +   assign dst_rdy_o = ~last_byte_d1; + +   // stubs for now +   wire 	     match_seq = 1; +   wire 	     match_len = 1; +    +   always @(posedge clk) +     if(reset | clear) +       last_byte_d1 <= 0; +     else  +       last_byte_d1 <= last_byte; + +   always @(posedge clk) +     if(reset | clear) +       begin +	  total <= 0; +	  crc_err <= 0; +	  seq_err <= 0; +	  len_err <= 0; +       end +     else +       if(last_byte_d1) +	 begin +	    total <= total + 1; +	    if(~match_crc) +	      crc_err <= crc_err + 1; +	    else if(~match_seq) +	      seq_err <= seq_err + 1; +	    else if(~match_len) +	      seq_err <= len_err + 1; +	 end +    +endmodule // packet_verifier diff --git a/fpga/usrp2/control_lib/newfifo/packet_verifier32.v b/fpga/usrp2/control_lib/newfifo/packet_verifier32.v new file mode 100644 index 000000000..06a13d242 --- /dev/null +++ b/fpga/usrp2/control_lib/newfifo/packet_verifier32.v @@ -0,0 +1,30 @@ + + +module packet_verifier32 +  (input clk, input reset, input clear, +   input [35:0] data_i, input src_rdy_i, output dst_rdy_o, +   output [31:0] total, output [31:0] crc_err, output [31:0] seq_err, output [31:0] len_err); + +   wire [7:0] 	     ll_data; +   wire 	     ll_sof_n, ll_eof_n, ll_src_rdy_n, ll_dst_rdy; +   wire [35:0] 	     data_int; +   wire 	     src_rdy_int, dst_rdy_int; +    +   fifo_short #(.WIDTH(36)) fifo_short +     (.clk(clk), .reset(reset), .clear(clear), +      .datain(data_i), .src_rdy_i(src_rdy_i), .dst_rdy_o(dst_rdy_o), +      .dataout(data_int), .src_rdy_o(src_rdy_int), .dst_rdy_i(dst_rdy_int)); +    +   fifo36_to_ll8 f36_to_ll8 +     (.clk(clk), .reset(reset), .clear(clear), +      .f36_data(data_int), .f36_src_rdy_i(src_rdy_int), .f36_dst_rdy_o(dst_rdy_int), +      .ll_data(ll_data), .ll_sof_n(ll_sof_n), .ll_eof_n(ll_eof_n), +      .ll_src_rdy_n(ll_src_rdy_n), .ll_dst_rdy_n(~ll_dst_rdy)); +    +   packet_verifier pkt_ver +     (.clk(clk), .reset(reset), .clear(clear), +      .data_i(ll_data), .sof_i(~ll_sof_n), .eof_i(~ll_eof_n), +      .src_rdy_i(~ll_src_rdy_n), .dst_rdy_o(ll_dst_rdy), +      .total(total), .crc_err(crc_err), .seq_err(seq_err), .len_err(len_err)); + +endmodule // packet_verifier32 diff --git a/fpga/usrp2/control_lib/nsgpio16LE.v b/fpga/usrp2/control_lib/nsgpio16LE.v new file mode 100644 index 000000000..8aef0c7ae --- /dev/null +++ b/fpga/usrp2/control_lib/nsgpio16LE.v @@ -0,0 +1,123 @@ +// Modified from code originally by Richard Herveille, his copyright is below + +///////////////////////////////////////////////////////////////////// +////                                                             //// +////  OpenCores Simple General Purpose IO core                   //// +////                                                             //// +////  Author: Richard Herveille                                  //// +////          richard@asics.ws                                   //// +////          www.asics.ws                                       //// +////                                                             //// +///////////////////////////////////////////////////////////////////// +////                                                             //// +//// Copyright (C) 2002 Richard Herveille                        //// +////                    richard@asics.ws                         //// +////                                                             //// +//// This source file may be used and distributed without        //// +//// restriction provided that this copyright statement is not   //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +////                                                             //// +////     THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY     //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS   //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR      //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,         //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE   //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR        //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  //// +//// LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT  //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT  //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE         //// +//// POSSIBILITY OF SUCH DAMAGE.                                 //// +////                                                             //// +///////////////////////////////////////////////////////////////////// + + +module nsgpio16LE +  (input clk_i, input rst_i,  +   input cyc_i, input stb_i, input [3:0] adr_i, input we_i, input [15:0] dat_i,  +   output reg [15:0] dat_o, output reg ack_o, +   input [31:0] atr, input [31:0] debug_0, input [31:0] debug_1,  +   inout [31:0] gpio +   ); + +   reg [31:0] 	ctrl, line, ddr, dbg, lgpio; +    +   wire 	wb_acc = cyc_i & stb_i;            // WISHBONE access +   wire 	wb_wr  = wb_acc & we_i;            // WISHBONE write access + +   always @(posedge clk_i or posedge rst_i) +     if (rst_i) +       begin +          ctrl <= 32'h0; +          line <= 32'h0; +	  ddr <= 32'h0; +	  dbg <= 32'h0; +       end +     else if (wb_wr) +       case( adr_i[3:1] ) +	 3'b000 :  +           line[15:0] <= dat_i; +	 3'b001 :  +           line[31:16] <= dat_i; +	 3'b010 : +	   ddr[15:0] <= dat_i; +	 3'b011 : +	   ddr[31:16] <= dat_i; +	 3'b100 : +	   ctrl[15:0] <= dat_i; +	 3'b101 : +	   ctrl[31:16] <= dat_i; +	 3'b110 : +	   dbg[15:0] <= dat_i; +	 3'b111 : +	   dbg[31:16] <= dat_i; +       endcase // case ( adr_i[3:1] ) +    +   always @(posedge clk_i) +     case (adr_i[3:1]) +       3'b000 : +	 dat_o <= lgpio[15:0]; +       3'b001 : +	 dat_o <= lgpio[31:16]; +       3'b010 : +	 dat_o <= ddr[15:0]; +       3'b011 : +	 dat_o <= ddr[31:16]; +       3'b100 : +	 dat_o <= ctrl[15:0]; +       3'b101 : +	 dat_o <= ctrl[31:16]; +       3'b110 : +	 dat_o <= dbg[15:0]; +       3'b111 : +	 dat_o <= dbg[31:16]; +     endcase // case (adr_i[3:1]) +    +    +   always @(posedge clk_i or posedge rst_i) +     if (rst_i) +       ack_o <= 1'b0; +     else +       ack_o <= wb_acc & !ack_o; +    +   // latch GPIO input pins +   always @(posedge clk_i) +     lgpio <= gpio; +    +   // assign GPIO outputs +   integer   n; +   reg [31:0] igpio; // temporary internal signal +    +   always @(ctrl or line or debug_1 or debug_0 or atr or ddr or dbg) +     for(n=0;n<32;n=n+1) +       igpio[n] <= ddr[n] ? (dbg[n] ? (ctrl[n] ? debug_1[n] : debug_0[n]) :  +			     (ctrl[n] ?  atr[n] : line[n]) ) +	 : 1'bz; +    +   assign     gpio = igpio; +    +endmodule + diff --git a/fpga/usrp2/control_lib/ram_2port_mixed_width.v b/fpga/usrp2/control_lib/ram_2port_mixed_width.v new file mode 100644 index 000000000..fae7d8de3 --- /dev/null +++ b/fpga/usrp2/control_lib/ram_2port_mixed_width.v @@ -0,0 +1,120 @@ + +module ram_2port_mixed_width +  (input clk16, +   input en16, +   input we16, +   input [10:0] addr16, +   input [15:0] di16, +   output [15:0] do16, +   input clk32, +   input en32, +   input we32, +   input [9:0] addr32, +   input [31:0] di32, +   output [31:0] do32); + +   wire 	 en32a = en32 & ~addr32[9]; +   wire 	 en32b = en32 & addr32[9]; +   wire 	 en16a = en16 & ~addr16[10]; +   wire 	 en16b = en16 & addr16[10]; + +   wire [31:0] 	 do32a, do32b; +   wire [15:0] 	 do16a, do16b; +    +   assign do32 = addr32[9] ? do32b : do32a; +   assign do16 = addr16[10] ? do16b : do16a; +    +   RAMB16BWE_S36_S18 #(.INIT_A(36'h000000000), +		       .INIT_B(18'h00000), +		       .SIM_COLLISION_CHECK("ALL"), // "NONE", "WARNING_ONLY", "GENERATE_X_ONLY", "ALL" +		       .SRVAL_A(36'h000000000), // Port A output value upon SSR assertion +		       .SRVAL_B(18'h00000),      // Port B output value upon SSR assertion +		       .WRITE_MODE_A("WRITE_FIRST"), // WRITE_FIRST, READ_FIRST or NO_CHANGE +		       .WRITE_MODE_B("WRITE_FIRST") // WRITE_FIRST, READ_FIRST or NO_CHANGE +		       )  +   RAMB16BWE_S36_S18_0 (.DOA(do32a),       // Port A 32-bit Data Output +			.DOB(do16a),       // Port B 16-bit Data Output +			.DOPA(),     // Port A 4-bit Parity Output +			.DOPB(),     // Port B 2-bit Parity Output +			.ADDRA(addr32[8:0]),   // Port A 9-bit Address Input +			.ADDRB(addr16[9:0]),   // Port B 10-bit Address Input +			.CLKA(clk32),     // Port A 1-bit Clock +			.CLKB(clk16),     // Port B 1-bit Clock +			.DIA(di32),       // Port A 32-bit Data Input +			.DIB(di16),       // Port B 16-bit Data Input +			.DIPA(0),     // Port A 4-bit parity Input +			.DIPB(0),     // Port-B 2-bit parity Input +			.ENA(en32a),       // Port A 1-bit RAM Enable Input +			.ENB(en16a),       // Port B 1-bit RAM Enable Input +			.SSRA(0),     // Port A 1-bit Synchronous Set/Reset Input +			.SSRB(0),     // Port B 1-bit Synchronous Set/Reset Input +			.WEA({4{we32}}),       // Port A 4-bit Write Enable Input +			.WEB({2{we16}})        // Port B 2-bit Write Enable Input +			); + +   RAMB16BWE_S36_S18 #(.INIT_A(36'h000000000), +		       .INIT_B(18'h00000), +		       .SIM_COLLISION_CHECK("ALL"), // "NONE", "WARNING_ONLY", "GENERATE_X_ONLY", "ALL" +		       .SRVAL_A(36'h000000000), // Port A output value upon SSR assertion +		       .SRVAL_B(18'h00000),      // Port B output value upon SSR assertion +		       .WRITE_MODE_A("WRITE_FIRST"), // WRITE_FIRST, READ_FIRST or NO_CHANGE +		       .WRITE_MODE_B("WRITE_FIRST") // WRITE_FIRST, READ_FIRST or NO_CHANGE +		       )  +   RAMB16BWE_S36_S18_1 (.DOA(do32b),       // Port A 32-bit Data Output +			.DOB(do16b),       // Port B 16-bit Data Output +			.DOPA(),     // Port A 4-bit Parity Output +			.DOPB(),     // Port B 2-bit Parity Output +			.ADDRA(addr32[8:0]),   // Port A 9-bit Address Input +			.ADDRB(addr16[9:0]),   // Port B 10-bit Address Input +			.CLKA(clk32),     // Port A 1-bit Clock +			.CLKB(clk16),     // Port B 1-bit Clock +			.DIA(di32),       // Port A 32-bit Data Input +			.DIB(di16),       // Port B 16-bit Data Input +			.DIPA(0),     // Port A 4-bit parity Input +			.DIPB(0),     // Port-B 2-bit parity Input +			.ENA(en32b),       // Port A 1-bit RAM Enable Input +			.ENB(en16b),       // Port B 1-bit RAM Enable Input +			.SSRA(0),     // Port A 1-bit Synchronous Set/Reset Input +			.SSRB(0),     // Port B 1-bit Synchronous Set/Reset Input +			.WEA({4{we32}}),       // Port A 4-bit Write Enable Input +			.WEB({2{we16}})        // Port B 2-bit Write Enable Input +			); + +endmodule // ram_2port_mixed_width + + + +    +// ISE 10.1.03 chokes on the following +    +/* +    +   reg [31:0] 	       ram [(1<<AWIDTH)-1:0]; +   integer 	       i; +   initial +     for(i=0;i<512;i=i+1) +       ram[i] <= 32'b0; +    +   always @(posedge clk16) +     if (en16) +       begin +          if (we16) +            if(addr16[0]) +	      ram[addr16[10:1]][15:0] <= di16; +	    else +	      ram[addr16[10:1]][31:16] <= di16; +	  do16 <= addr16[0] ? ram[addr16[10:1]][15:0] : ram[addr16[10:1]][31:16]; +       end + +   always @(posedge clk32) +     if (en32) +       begin +          if (we32) +            ram[addr32] <= di32; +          do32 <= ram[addr32]; +       end + +endmodule // ram_2port_mixed_width + +  + */ diff --git a/fpga/usrp2/control_lib/settings_bus.v b/fpga/usrp2/control_lib/settings_bus.v index fc960e456..aec179516 100644 --- a/fpga/usrp2/control_lib/settings_bus.v +++ b/fpga/usrp2/control_lib/settings_bus.v @@ -10,7 +10,7 @@ module settings_bus       input wb_stb_i,       input wb_we_i,       output reg wb_ack_o, -     output strobe, +     output reg strobe,       output reg [7:0] addr,       output reg [31:0] data); @@ -19,18 +19,18 @@ module settings_bus     always @(posedge wb_clk)       if(wb_rst)         begin -	  stb_int <= 1'b0; +	  strobe <= 1'b0;  	  addr <= 8'd0;  	  data <= 32'd0;         end -     else if(wb_we_i & wb_stb_i) +     else if(wb_we_i & wb_stb_i & ~wb_ack_o)         begin -	  stb_int <= 1'b1; +	  strobe <= 1'b1;  	  addr <= wb_adr_i[9:2];  	  data <= wb_dat_i;         end       else -       stb_int <= 1'b0; +       strobe <= 1'b0;     always @(posedge wb_clk)       if(wb_rst) @@ -38,11 +38,4 @@ module settings_bus       else         wb_ack_o <= wb_stb_i & ~wb_ack_o; -   always @(posedge wb_clk) -     stb_int_d1 <= stb_int; - -   //assign strobe = stb_int & ~stb_int_d1; -   assign strobe = stb_int & wb_ack_o; -            endmodule // settings_bus - diff --git a/fpga/usrp2/control_lib/settings_bus_16LE.v b/fpga/usrp2/control_lib/settings_bus_16LE.v new file mode 100644 index 000000000..76061e9e0 --- /dev/null +++ b/fpga/usrp2/control_lib/settings_bus_16LE.v @@ -0,0 +1,54 @@ + +// Grab settings off the wishbone bus, send them out to settings bus +// 16 bits little endian, but all registers need to be written 32 bits at a time. +// This means that you write the low 16 bits first and then the high 16 bits. +// The setting regs are strobed when the high 16 bits are written + +module settings_bus_16LE +  #(parameter AWIDTH=16, RWIDTH=8) +    (input wb_clk,  +     input wb_rst,  +     input [AWIDTH-1:0] wb_adr_i, +     input [15:0] wb_dat_i, +     input wb_stb_i, +     input wb_we_i, +     output reg wb_ack_o, +     output strobe, +     output reg [7:0] addr, +     output reg [31:0] data); + +   reg 		       stb_int; +    +   always @(posedge wb_clk) +     if(wb_rst) +       begin +	  stb_int <= 1'b0; +	  addr <= 8'd0; +	  data <= 32'd0; +       end +     else if(wb_we_i & wb_stb_i) +       begin +	  addr <= wb_adr_i[RWIDTH+1:2];  // Zero pad high bits +	  if(wb_adr_i[1]) +	    begin +	       stb_int <= 1'b1;     // We now have both halves +	       data[31:16] <= wb_dat_i; +	    end +	  else +	    begin +	       stb_int <= 1'b0;     // Don't strobe, we need other half +	       data[15:0] <= wb_dat_i; +	    end +       end +     else +       stb_int <= 1'b0; + +   always @(posedge wb_clk) +     if(wb_rst) +       wb_ack_o <= 0; +     else +       wb_ack_o <= wb_stb_i & ~wb_ack_o; + +   assign strobe = stb_int & wb_ack_o; +           +endmodule // settings_bus_16LE diff --git a/fpga/usrp2/control_lib/simple_uart.v b/fpga/usrp2/control_lib/simple_uart.v index 22f0e70a2..0dd58b5f5 100644 --- a/fpga/usrp2/control_lib/simple_uart.v +++ b/fpga/usrp2/control_lib/simple_uart.v @@ -1,11 +1,12 @@  module simple_uart    #(parameter TXDEPTH = 1, -    parameter RXDEPTH = 1) -    (input clk_i, input rst_i, -     input we_i, input stb_i, input cyc_i, output reg ack_o, -     input [2:0] adr_i, input [31:0] dat_i, output reg [31:0] dat_o, -     output rx_int_o, output tx_int_o, output tx_o, input rx_i, output baud_o); +    parameter RXDEPTH = 1, +    parameter CLKDIV_DEFAULT = 16'd0) +   (input clk_i, input rst_i, +    input we_i, input stb_i, input cyc_i, output reg ack_o, +    input [2:0] adr_i, input [31:0] dat_i, output reg [31:0] dat_o, +    output rx_int_o, output tx_int_o, output tx_o, input rx_i, output baud_o);     // Register Map     localparam SUART_CLKDIV = 0; @@ -30,7 +31,7 @@ module simple_uart     always @(posedge clk_i)       if (rst_i) -       clkdiv <= 0; +       clkdiv <= CLKDIV_DEFAULT;       else if (wb_wr)         case(adr_i)  	 SUART_CLKDIV : clkdiv <= dat_i[15:0]; diff --git a/fpga/usrp2/fifo/.gitignore b/fpga/usrp2/fifo/.gitignore index cba7efc8e..866f1faad 100644 --- a/fpga/usrp2/fifo/.gitignore +++ b/fpga/usrp2/fifo/.gitignore @@ -1 +1,3 @@ +*.vcd +*.lxt  a.out diff --git a/fpga/usrp2/fifo/fifo19_to_fifo36.v b/fpga/usrp2/fifo/fifo19_to_fifo36.v index 5f9aeff9b..0e6bcea68 100644 --- a/fpga/usrp2/fifo/fifo19_to_fifo36.v +++ b/fpga/usrp2/fifo/fifo19_to_fifo36.v @@ -1,26 +1,31 @@ +// Parameter LE tells us if we are little-endian.   +// Little-endian means send lower 16 bits first. +// Default is big endian (network order), send upper bits first. +  module fifo19_to_fifo36 -  (input clk, input reset, input clear, -   input [18:0] f19_datain, -   input f19_src_rdy_i, -   output f19_dst_rdy_o, +  #(parameter LE=0) +   (input clk, input reset, input clear, +    input [18:0] f19_datain, +    input f19_src_rdy_i, +    output f19_dst_rdy_o, -   output [35:0] f36_dataout, -   output f36_src_rdy_o, -   input f36_dst_rdy_i, -   output [31:0] debug -   ); +    output [35:0] f36_dataout, +    output f36_src_rdy_o, +    input f36_dst_rdy_i, +    output [31:0] debug +    ); -   reg 	 f36_sof, f36_eof, f36_occ; +   reg 		  f36_sof, f36_eof, f36_occ; -   reg [1:0] state; -   reg [15:0] dat0, dat1; +   reg [1:0] 	  state; +   reg [15:0] 	  dat0, dat1; -   wire f19_sof  = f19_datain[16]; -   wire f19_eof  = f19_datain[17]; -   wire f19_occ  = f19_datain[18]; +   wire 	  f19_sof  = f19_datain[16]; +   wire 	  f19_eof  = f19_datain[17]; +   wire 	  f19_occ  = f19_datain[18]; -   wire xfer_out = f36_src_rdy_o & f36_dst_rdy_i; +   wire 	  xfer_out = f36_src_rdy_o & f36_dst_rdy_i;     always @(posedge clk)       if(f19_src_rdy_i & ((state==0)|xfer_out)) @@ -68,7 +73,8 @@ module fifo19_to_fifo36         dat0 		   <= f19_datain;     assign    f19_dst_rdy_o  = xfer_out | (state != 2); -   assign    f36_dataout    = {f36_occ,f36_eof,f36_sof,dat0,dat1}; +   assign    f36_dataout    = LE ? {f36_occ,f36_eof,f36_sof,dat1,dat0} : +			      {f36_occ,f36_eof,f36_sof,dat0,dat1};     assign    f36_src_rdy_o  = (state == 2);     assign    debug = state; diff --git a/fpga/usrp2/fifo/fifo36_to_fifo18.v b/fpga/usrp2/fifo/fifo36_to_fifo18.v deleted file mode 100644 index b636ab9ca..000000000 --- a/fpga/usrp2/fifo/fifo36_to_fifo18.v +++ /dev/null @@ -1,40 +0,0 @@ - -module fifo36_to_fifo18 -  (input clk, input reset, input clear, -   input [35:0] f36_datain, -   input f36_src_rdy_i, -   output f36_dst_rdy_o, -    -   output [17:0] f18_dataout, -   output f18_src_rdy_o, -   input f18_dst_rdy_i ); - -   wire   f36_sof  = f36_datain[32]; -   wire   f36_eof  = f36_datain[33]; -   wire   f36_occ  = f36_datain[35:34]; - -   reg phase; - -   wire half_line 	   = f36_eof & ((f36_occ==1)|(f36_occ==2)); -    -   assign f18_dataout[15:0] = phase ? f36_datain[15:0] : f36_datain[31:16]; -   assign f18_dataout[16]  = phase ? 0 : f36_sof; -   assign f18_dataout[17]  = phase ? f36_eof : half_line; -    -   assign f18_src_rdy_o    = f36_src_rdy_i; -   assign f36_dst_rdy_o    = (phase | half_line) & f18_dst_rdy_i; -	 -   wire f18_xfer 	   = f18_src_rdy_o & f18_dst_rdy_i; -   wire f36_xfer 	   = f36_src_rdy_i & f36_dst_rdy_o; - -   always @(posedge clk) -     if(reset) -       phase 		  <= 0; -     else if(f36_xfer) -       phase 		  <= 0; -     else if(f18_xfer) -       phase 		  <= 1; -    -        -endmodule // fifo36_to_fifo18 - diff --git a/fpga/usrp2/fifo/fifo36_to_fifo19.v b/fpga/usrp2/fifo/fifo36_to_fifo19.v index de249aaeb..517a2a476 100644 --- a/fpga/usrp2/fifo/fifo36_to_fifo19.v +++ b/fpga/usrp2/fifo/fifo36_to_fifo19.v @@ -1,33 +1,38 @@ -module fifo36_to_fifo19 -  (input clk, input reset, input clear, -   input [35:0] f36_datain, -   input f36_src_rdy_i, -   output f36_dst_rdy_o, -    -   output [18:0] f19_dataout, -   output f19_src_rdy_o, -   input f19_dst_rdy_i ); +// Parameter LE tells us if we are little-endian.   +// Little-endian means send lower 16 bits first. +// Default is big endian (network order), send upper bits first. +module fifo36_to_fifo19 +  #(parameter LE=0) +   (input clk, input reset, input clear, +    input [35:0] f36_datain, +    input f36_src_rdy_i, +    output f36_dst_rdy_o, +     +    output [18:0] f19_dataout, +    output f19_src_rdy_o, +    input f19_dst_rdy_i ); +        wire   f36_sof  = f36_datain[32];     wire   f36_eof  = f36_datain[33];     wire   f36_occ  = f36_datain[35:34]; - -   reg phase; - -   wire half_line 	   = f36_eof & ((f36_occ==1)|(f36_occ==2)); -   assign f19_dataout[15:0] = phase ? f36_datain[15:0] : f36_datain[31:16]; +   reg 	  phase; +    +   wire   half_line 	   = f36_eof & ((f36_occ==1)|(f36_occ==2)); +    +   assign f19_dataout[15:0] = (LE ^ phase) ? f36_datain[15:0] : f36_datain[31:16];     assign f19_dataout[16]  = phase ? 0 : f36_sof;     assign f19_dataout[17]  = phase ? f36_eof : half_line;     assign f19_dataout[18]  = f19_dataout[17] & ((f36_occ==1)|(f36_occ==3));     assign f19_src_rdy_o    = f36_src_rdy_i;     assign f36_dst_rdy_o    = (phase | half_line) & f19_dst_rdy_i; -	 -   wire f19_xfer 	   = f19_src_rdy_o & f19_dst_rdy_i; -   wire f36_xfer 	   = f36_src_rdy_i & f36_dst_rdy_o; - +    +   wire   f19_xfer 	   = f19_src_rdy_o & f19_dst_rdy_i; +   wire   f36_xfer 	   = f36_src_rdy_i & f36_dst_rdy_o; +        always @(posedge clk)       if(reset)         phase 		  <= 0; @@ -36,6 +41,5 @@ module fifo36_to_fifo19       else if(f19_xfer)         phase 		  <= 1; -        +     endmodule // fifo36_to_fifo19 - diff --git a/fpga/usrp2/fifo/fifo36_to_ll8.v b/fpga/usrp2/fifo/fifo36_to_ll8.v index 0dee1dfc6..9604d0e38 100644 --- a/fpga/usrp2/fifo/fifo36_to_ll8.v +++ b/fpga/usrp2/fifo/fifo36_to_ll8.v @@ -55,6 +55,5 @@ module fifo36_to_ll8     assign advance 	 = ll_src_rdy & ll_dst_rdy;     assign f36_dst_rdy_o  = advance & ((state==3)|ll_eof); -   assign debug 	 = state;  endmodule // ll8_to_fifo36 diff --git a/fpga/usrp2/gpmc/.gitignore b/fpga/usrp2/gpmc/.gitignore new file mode 100644 index 000000000..3e14fa4f7 --- /dev/null +++ b/fpga/usrp2/gpmc/.gitignore @@ -0,0 +1,2 @@ +*.gif + diff --git a/fpga/usrp2/gpmc/Makefile.srcs b/fpga/usrp2/gpmc/Makefile.srcs new file mode 100644 index 000000000..bff6ae3e0 --- /dev/null +++ b/fpga/usrp2/gpmc/Makefile.srcs @@ -0,0 +1,20 @@ +# +# Copyright 2010 Ettus Research LLC +# + +################################################## +# SERDES Sources +################################################## +GPMC_SRCS = $(abspath $(addprefix $(BASE_DIR)/../gpmc/, \ +dbsm.v \ +edge_sync.v \ +fifo_to_gpmc_async.v \ +fifo_to_gpmc_sync.v \ +fifo_watcher.v \ +gpmc_async.v \ +gpmc_sync.v \ +gpmc_to_fifo_async.v \ +gpmc_to_fifo_sync.v \ +gpmc_wb.v \ +ram_to_fifo.v \ +)) diff --git a/fpga/usrp2/gpmc/burst_data_write.txt b/fpga/usrp2/gpmc/burst_data_write.txt new file mode 100644 index 000000000..3b5dfc785 --- /dev/null +++ b/fpga/usrp2/gpmc/burst_data_write.txt @@ -0,0 +1,16 @@ +# OMAP burst writes to FPGA + +CLK=0,nWE=1,nCS=1,nOE=1,DATA=Z. +CLK=1. +CLK=0,nWE=0,nCS=0,DATA=WR_DATA1. +CLK=1. +CLK=0,nWE=0,nCS=0,DATA=WR_DATA2. +CLK=1. +CLK=0,nWE=0,nCS=0,DATA=WR_DATA3. +CLK=1. +CLK=0,nWE=0,nCS=0,DATA=WR_DATA4. +CLK=1. +CLK=0,nWE=1,nCS=1,DATA=Z. +CLK=1. + + diff --git a/fpga/usrp2/gpmc/dbsm.v b/fpga/usrp2/gpmc/dbsm.v new file mode 100644 index 000000000..530af7205 --- /dev/null +++ b/fpga/usrp2/gpmc/dbsm.v @@ -0,0 +1,80 @@ + +module bsm +  (input clk, input reset, input clear, +   input write_done, +   input read_done, +   output readable, +   output writeable); + +   reg 	  state; +   localparam ST_WRITEABLE = 0; +   localparam ST_READABLE = 1; +    +   always @(posedge clk) +     if(reset | clear) +       state <= ST_WRITEABLE; +     else +       case(state) +	 ST_WRITEABLE : +	   if(write_done) +	     state <= ST_READABLE; +	 ST_READABLE : +	   if(read_done) +	     state <= ST_WRITEABLE; +       endcase // case (state) + +   assign readable = (state == ST_READABLE); +   assign writeable = (state == ST_WRITEABLE); +    +endmodule // bsm + +module dbsm +  (input clk, input reset, input clear, +   output reg read_sel, output read_ready, input read_done, +   output reg write_sel, output write_ready, input write_done); + +   localparam NUM_BUFS = 2; + +   wire       [NUM_BUFS-1:0] readable, writeable, read_done_buf, write_done_buf; +    +   // Two of these buffer state machines +   genvar     i; +   generate +      for(i=0;i<NUM_BUFS;i=i+1) +	begin : BSMS +	   bsm bsm(.clk(clk), .reset(reset), .clear(clear), +		   .write_done((write_sel == i) & write_done), +		   .read_done((read_sel == i) & read_done), +		   .readable(readable[i]), .writeable(writeable[i])); +	end +   endgenerate +    +   reg 	 full; +    +   always @(posedge clk) +     if(reset | clear) +       begin +	  write_sel <= 0; +	  full <= 0; +       end +     else +       if(write_done & writeable[write_sel]) +	 if(write_sel ==(NUM_BUFS-1)) +	   write_sel <= 0; +	 else +	   write_sel <= write_sel + 1; +    +   always @(posedge clk) +     if(reset | clear) +       read_sel <= 0; +     else +       if(read_done & readable[read_sel]) +	 if(read_sel==(NUM_BUFS-1)) +	   read_sel <= 0; +	 else +	   read_sel <= read_sel + 1; +           +   assign write_ready = writeable[write_sel]; +   assign read_ready = readable[read_sel]; + +endmodule // dbsm diff --git a/fpga/usrp2/gpmc/edge_sync.v b/fpga/usrp2/gpmc/edge_sync.v new file mode 100644 index 000000000..5d9417c08 --- /dev/null +++ b/fpga/usrp2/gpmc/edge_sync.v @@ -0,0 +1,22 @@ + + +module edge_sync +  #(parameter POSEDGE = 1) +   (input clk, +    input rst, +    input sig, +    output trig); +    +   reg [1:0] delay; +    +   always @(posedge clk) +     if(rst) +       delay <= 2'b00; +     else +       delay <= {delay[0],sig}; +    +   assign trig = POSEDGE ? (delay==2'b01) : (delay==2'b10); +    +endmodule // edge_sync + + diff --git a/fpga/usrp2/gpmc/fifo_to_gpmc_async.v b/fpga/usrp2/gpmc/fifo_to_gpmc_async.v new file mode 100644 index 000000000..cf8b6e861 --- /dev/null +++ b/fpga/usrp2/gpmc/fifo_to_gpmc_async.v @@ -0,0 +1,37 @@ + +// Assumes an asynchronous GPMC cycle +//   If a packet bigger or smaller than we are told is sent, behavior is undefined. +//   If dst_rdy_i is low when we get data, behavior is undefined and we signal bus error. +//   If there is a bus error, we should be reset + +module fifo_to_gpmc_async +  (input clk, input reset, input clear, +   input [17:0] data_i, input src_rdy_i, output dst_rdy_o, +   output [15:0] EM_D, input EM_NCS, input EM_NOE, +   input [15:0] frame_len); + +   // Synchronize the async control signals +   reg [2:0] 	cs_del, oe_del; +   reg [15:0] 	counter; +    +   always @(posedge clk) +     if(reset) +       begin +	  cs_del <= 3'b11; +	  oe_del <= 3'b11; +       end +     else +       begin +	  cs_del <= { cs_del[1:0], EM_NCS }; +	  oe_del <= { oe_del[1:0], EM_NOE }; +       end + +   wire do_read = ( (~cs_del[1] | ~cs_del[2]) & (oe_del[1:0] == 2'b01));  // change output on trailing edge +   wire first_read = (counter == 0); +   wire last_read = ((counter+1) == frame_len); + +   assign EM_D = data_i[15:0]; + +   assign dst_rdy_o = do_read; + +endmodule // fifo_to_gpmc_async diff --git a/fpga/usrp2/gpmc/fifo_to_gpmc_sync.v b/fpga/usrp2/gpmc/fifo_to_gpmc_sync.v new file mode 100644 index 000000000..ef59d7137 --- /dev/null +++ b/fpga/usrp2/gpmc/fifo_to_gpmc_sync.v @@ -0,0 +1,26 @@ + +// Assumes a GPMC cycle with GPMC clock, as in the timing diagrams +//   If a packet bigger or smaller than we are told is sent, behavior is undefined. +//   If dst_rdy_i is low when we get data, behavior is undefined and we signal bus error. +//   If there is a bus error, we should be reset + +module fifo_to_gpmc_sync +  (input arst, +   input [17:0] data_i, input src_rdy_i, output dst_rdy_o, +   input EM_CLK, output [15:0] EM_D, input EM_NCS, input EM_NOE, +   output fifo_ready,  +   output reg bus_error); + +   assign EM_D = data_i[15:0]; +   wire       read_access = ~EM_NCS & ~EM_NOE; + +   assign dst_rdy_o = read_access; + +   always @(posedge EM_CLK or posedge arst) +     if(arst) +       bus_error <= 0; +     else if(dst_rdy_o & ~src_rdy_i) +       bus_error <= 1; +    + +endmodule // fifo_to_gpmc_sync diff --git a/fpga/usrp2/gpmc/fifo_watcher.v b/fpga/usrp2/gpmc/fifo_watcher.v new file mode 100644 index 000000000..fe4e35de3 --- /dev/null +++ b/fpga/usrp2/gpmc/fifo_watcher.v @@ -0,0 +1,56 @@ + + +module fifo_watcher +  (input clk, input reset, input clear, +   input src_rdy1, input dst_rdy1, input sof1, input eof1, +   input src_rdy2, input dst_rdy2, input sof2, input eof2, +   output reg have_packet, output [15:0] length, output reg bus_error, +   output [31:0] debug); + +   wire   write = src_rdy1 & dst_rdy1 & eof1; +   wire   read = src_rdy2 & dst_rdy2 & eof2; +   wire   have_packet_int; +   reg [15:0] counter; +   wire [4:0] pkt_count; +   assign debug = pkt_count; +    +   fifo_short #(.WIDTH(16)) frame_lengths +     (.clk(clk), .reset(reset), .clear(clear), +      .datain(counter), .src_rdy_i(write), .dst_rdy_o(), +      .dataout(length), .src_rdy_o(have_packet_int), .dst_rdy_i(read), +      .occupied(pkt_count), .space()); + +   always @(posedge clk) +     if(reset | clear) +       counter <= 1;   // Start at 1 +     else if(src_rdy1 & dst_rdy1) +       if(eof1) +	 counter <= 1; +       else +	 counter <= counter + 1; + +   always @(posedge clk) +     if(reset | clear) +       bus_error <= 0; +     else if(dst_rdy2 & ~src_rdy2) +       bus_error <= 1; +     else if(read & ~have_packet_int) +       bus_error <= 1; + +   reg 	      in_packet; +   always @(posedge clk) +     if(reset | clear) +       have_packet <= 0; +     else  +       have_packet <= (have_packet_int & ~in_packet) | (pkt_count>1) ; +    +   always @(posedge clk) +     if(reset | clear) +       in_packet <= 0; +     else if(src_rdy2 & dst_rdy2) +       if(eof2) +	 in_packet <= 0; +       else +	 in_packet <= 1; +    +endmodule // fifo_watcher diff --git a/fpga/usrp2/gpmc/gpmc_async.v b/fpga/usrp2/gpmc/gpmc_async.v new file mode 100644 index 000000000..23bad56ae --- /dev/null +++ b/fpga/usrp2/gpmc/gpmc_async.v @@ -0,0 +1,130 @@ +////////////////////////////////////////////////////////////////////////////////// + +module gpmc_async +  #(parameter TXFIFOSIZE = 11, parameter RXFIFOSIZE = 11) +   (// GPMC signals +    input arst, +    input EM_CLK, inout [15:0] EM_D, input [10:1] EM_A, input [1:0] EM_NBE, +    input EM_WAIT0, input EM_NCS4, input EM_NCS6, input EM_NWE, input EM_NOE, +     +    // GPIOs for FIFO signalling +    output rx_have_data, output tx_have_space, output reg bus_error, input bus_reset, +     +    // Wishbone signals +    input wb_clk, input wb_rst, +    output [10: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, +     +    // 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 [15:0] tx_frame_len, output [15:0] rx_frame_len, +     +    output [31:0] debug +    ); + +   wire 	  EM_output_enable = (~EM_NOE & (~EM_NCS4 | ~EM_NCS6)); +   wire [15:0] 	  EM_D_fifo; +   wire [15:0] 	  EM_D_wb; +    +   assign EM_D = ~EM_output_enable ? 16'bz : ~EM_NCS4 ? EM_D_fifo : EM_D_wb; +    +   wire 	  bus_error_tx, bus_error_rx; +    +   always @(posedge fifo_clk) +     if(fifo_rst | clear_tx | clear_rx) +       bus_error <= 0; +     else +       bus_error <= bus_error_tx | bus_error_rx; +    +   // CS4 is RAM_2PORT for DATA PATH (high-speed data) +   //    Writes go into one RAM, reads come from the other +   // CS6 is for CONTROL PATH (wishbone) + +   // //////////////////////////////////////////// +   // TX Data Path + +   wire [17:0] 	  tx18_data, tx18b_data; +   wire 	  tx18_src_rdy, tx18_dst_rdy, tx18b_src_rdy, tx18b_dst_rdy; +   wire [15:0] 	  tx_fifo_space; +   wire [35:0] 	  tx36_data; +   wire 	  tx36_src_rdy, tx36_dst_rdy; +    +   gpmc_to_fifo_async gpmc_to_fifo_async +     (.EM_D(EM_D), .EM_NBE(EM_NBE), .EM_NCS(EM_NCS4), .EM_NWE(EM_NWE), +      .fifo_clk(fifo_clk), .fifo_rst(fifo_rst), .clear(clear_tx), +      .data_o(tx18_data), .src_rdy_o(tx18_src_rdy), .dst_rdy_i(tx18_dst_rdy), +      .frame_len(tx_frame_len), .fifo_space(tx_fifo_space), .fifo_ready(tx_have_space), +      .bus_error(bus_error_tx) ); +    +   fifo_cascade #(.WIDTH(18), .SIZE(10)) tx_fifo +     (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), +      .datain(tx18_data), .src_rdy_i(tx18_src_rdy), .dst_rdy_o(tx18_dst_rdy), .space(tx_fifo_space), +      .dataout(tx18b_data), .src_rdy_o(tx18b_src_rdy), .dst_rdy_i(tx18b_dst_rdy), .occupied()); + +   fifo19_to_fifo36 #(.LE(1)) f19_to_f36   // Little endian because ARM is LE +     (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), +      .f19_datain({1'b0,tx18b_data}), .f19_src_rdy_i(tx18b_src_rdy), .f19_dst_rdy_o(tx18b_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(wb_clk), .reset(wb_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)); + +   // //////////////////////////////////////////// +   // RX Data Path +    +   wire [17:0] 	  rx18_data, rx18b_data; +   wire 	  rx18_src_rdy, rx18_dst_rdy, rx18b_src_rdy, rx18b_dst_rdy; +   wire [15:0] 	  rx_fifo_space; +   wire [35:0] 	  rx36_data; +   wire 	  rx36_src_rdy, rx36_dst_rdy; +   wire 	  dummy; +    +   fifo_cascade #(.WIDTH(36), .SIZE(RXFIFOSIZE)) rx_fifo36 +     (.clk(wb_clk), .reset(wb_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)); + +   fifo36_to_fifo19 #(.LE(1)) f36_to_f19   // Little endian because ARM is LE +     (.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({dummy,rx18_data}), .f19_src_rdy_o(rx18_src_rdy), .f19_dst_rdy_i(rx18_dst_rdy) ); + +   fifo_cascade #(.WIDTH(18), .SIZE(12)) rx_fifo +     (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), +      .datain(rx18_data), .src_rdy_i(rx18_src_rdy), .dst_rdy_o(rx18_dst_rdy), .space(rx_fifo_space), +      .dataout(rx18b_data), .src_rdy_o(rx18b_src_rdy), .dst_rdy_i(rx18b_dst_rdy), .occupied()); + +   fifo_to_gpmc_async fifo_to_gpmc_async +     (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), +      .data_i(rx18b_data), .src_rdy_i(rx18b_src_rdy), .dst_rdy_o(rx18b_dst_rdy), +      .EM_D(EM_D_fifo), .EM_NCS(EM_NCS4), .EM_NOE(EM_NOE), +      .frame_len(rx_frame_len) ); + +   wire [31:0] 	pkt_count; +    +   fifo_watcher fifo_watcher +     (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), +      .src_rdy1(rx18_src_rdy), .dst_rdy1(rx18_dst_rdy), .sof1(rx18_data[16]), .eof1(rx18_data[17]), +      .src_rdy2(rx18b_src_rdy), .dst_rdy2(rx18b_dst_rdy), .sof2(rx18b_data[16]), .eof2(rx18b_data[17]), +      .have_packet(rx_have_data), .length(rx_frame_len), .bus_error(bus_error_rx), +      .debug(pkt_count)); + +   // //////////////////////////////////////////// +   // Control path on CS6 +    +   gpmc_wb gpmc_wb +     (.EM_CLK(EM_CLK), .EM_D_in(EM_D), .EM_D_out(EM_D_wb), .EM_A(EM_A), .EM_NBE(EM_NBE), +      .EM_NCS(EM_NCS6), .EM_NWE(EM_NWE), .EM_NOE(EM_NOE), +      .wb_clk(wb_clk), .wb_rst(wb_rst), +      .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) ); +    +      assign debug = pkt_count; +    +endmodule // gpmc_async diff --git a/fpga/usrp2/gpmc/gpmc_sync.v b/fpga/usrp2/gpmc/gpmc_sync.v new file mode 100644 index 000000000..61c54a793 --- /dev/null +++ b/fpga/usrp2/gpmc/gpmc_sync.v @@ -0,0 +1,108 @@ +////////////////////////////////////////////////////////////////////////////////// + +module gpmc_sync +  (// GPMC signals +   input arst, +   input EM_CLK, inout [15:0] EM_D, input [10:1] EM_A, input [1:0] EM_NBE, +   input EM_WAIT0, input EM_NCS4, input EM_NCS6, input EM_NWE, input EM_NOE, +    +   // GPIOs for FIFO signalling +   output rx_have_data, output tx_have_space, output bus_error, input bus_reset, +    +   // Wishbone signals +   input wb_clk, input wb_rst, +   output [10: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, + +   // FIFO interface +   input fifo_clk, input fifo_rst, +   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, + +   output [31:0] debug +   ); + +   wire 	EM_output_enable = (~EM_NOE & (~EM_NCS4 | ~EM_NCS6)); +   wire [15:0] 	EM_D_fifo; +   wire [15:0] 	EM_D_wb; + +   assign EM_D = ~EM_output_enable ? 16'bz : ~EM_NCS4 ? EM_D_fifo : EM_D_wb; + +   wire 	bus_error_tx, bus_error_rx; +   assign bus_error = bus_error_tx | bus_error_rx; +    +   // CS4 is RAM_2PORT for DATA PATH (high-speed data) +   //    Writes go into one RAM, reads come from the other +   // CS6 is for CONTROL PATH (wishbone) + +   // //////////////////////////////////////////// +   // TX Data Path + +   wire [17:0] 	tx18_data, tx18b_data; +   wire 	tx18_src_rdy, tx18_dst_rdy, tx18b_src_rdy, tx18b_dst_rdy; +   wire [15:0] 	tx_fifo_space, tx_frame_len; +    +   assign tx_frame_len = 10; +    +   gpmc_to_fifo_sync gpmc_to_fifo_sync +     (.arst(arst), +      .EM_CLK(EM_CLK), .EM_D(EM_D), .EM_NBE(EM_NBE), .EM_NCS(EM_NCS4), .EM_NWE(EM_NWE), +      .data_o(tx18_data), .src_rdy_o(tx18_src_rdy), .dst_rdy_i(tx18_dst_rdy), +      .frame_len(tx_frame_len), .fifo_space(tx_fifo_space), .fifo_ready(tx_have_space), +      .bus_error(bus_error_tx) ); +    +   fifo_2clock_cascade #(.WIDTH(18), .SIZE(4)) tx_fifo +     (.wclk(EM_CLK), .datain(tx18_data),  +      .src_rdy_i(tx18_src_rdy), .dst_rdy_o(tx18_dst_rdy), .space(tx_fifo_space), +      .rclk(fifo_clk), .dataout(tx18b_data),  +      .src_rdy_o(tx18b_src_rdy), .dst_rdy_i(tx18b_dst_rdy), .occupied(), .arst(arst)); + +   fifo19_to_fifo36 #(.LE(1)) f19_to_f36   // Little endian because ARM is LE +     (.clk(fifo_clk), .reset(fifo_rst), .clear(0), +      .f19_datain({1'b0,tx18b_data}), .f19_src_rdy_i(tx18b_src_rdy), .f19_dst_rdy_o(tx18b_dst_rdy), +      .f36_dataout(tx_data_o), .f36_src_rdy_o(tx_src_rdy_o), .f36_dst_rdy_i(tx_dst_rdy_i)); +    +   // //////////////////////////////////////////// +   // RX Data Path + +   wire [17:0] 	rx18_data, rx18b_data; +   wire 	rx18_src_rdy, rx18_dst_rdy, rx18b_src_rdy, rx18b_dst_rdy; +   wire [15:0] 	rx_fifo_space, rx_frame_len; +   wire 	dummy; +    +   fifo36_to_fifo19 #(.LE(1)) f36_to_f19   // Little endian because ARM is LE +     (.clk(fifo_clk), .reset(fifo_rst), .clear(0), +      .f36_datain(rx_data_i), .f36_src_rdy_i(rx_src_rdy_i), .f36_dst_rdy_o(rx_dst_rdy_o), +      .f19_dataout({dummy,rx18_data}), .f19_src_rdy_o(rx18_src_rdy), .f19_dst_rdy_i(rx18_dst_rdy) ); + +   fifo_2clock_cascade #(.WIDTH(18), .SIZE(10)) rx_fifo +     (.wclk(fifo_clk), .datain(rx18_data),  +      .src_rdy_i(rx18_src_rdy), .dst_rdy_o(rx18_dst_rdy), .space(rx_fifo_space), +      .rclk(EM_CLK), .dataout(rx18b_data),  +      .src_rdy_o(rx18b_src_rdy), .dst_rdy_i(rx18b_dst_rdy), .occupied(), .arst(arst)); + +   fifo_to_gpmc_sync fifo_to_gpmc_sync +     (.arst(arst), +      .data_i(rx18b_data), .src_rdy_i(rx18b_src_rdy), .dst_rdy_o(rx18b_dst_rdy), +      .EM_CLK(EM_CLK), .EM_D(EM_D_fifo), .EM_NCS(EM_NCS4), .EM_NOE(EM_NOE), +      .fifo_ready(rx_have_data) ); + +   fifo_watcher fifo_watcher +     (.clk(fifo_clk), .reset(fifo_rst), .clear(0), +      .src_rdy(rx18_src_rdy), .dst_rdy(rx18_dst_rdy), .sof(rx18_data[16]), .eof(rx18_data[17]), +      .have_packet(), .length(), .next() ); +    +   // //////////////////////////////////////////// +   // Control path on CS6 +    +   gpmc_wb gpmc_wb +     (.EM_CLK(EM_CLK), .EM_D_in(EM_D), .EM_D_out(EM_D_wb), .EM_A(EM_A), .EM_NBE(EM_NBE), +      .EM_NCS(EM_NCS6), .EM_NWE(EM_NWE), .EM_NOE(EM_NOE), +      .wb_clk(wb_clk), .wb_rst(wb_rst), +      .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) ); +    +      assign debug = 0; +    +endmodule // gpmc_sync diff --git a/fpga/usrp2/gpmc/gpmc_to_fifo_async.v b/fpga/usrp2/gpmc/gpmc_to_fifo_async.v new file mode 100644 index 000000000..55c0cef50 --- /dev/null +++ b/fpga/usrp2/gpmc/gpmc_to_fifo_async.v @@ -0,0 +1,68 @@ + +module gpmc_to_fifo_async +  (input [15:0] EM_D, input [1:0] EM_NBE, input EM_NCS, input EM_NWE, + +   input fifo_clk, input fifo_rst, input clear, +   output reg [17:0] data_o, output reg src_rdy_o, input dst_rdy_i, + +   input [15:0] frame_len, input [15:0] fifo_space, output reg fifo_ready, +   output reg bus_error ); + +   reg [15:0] counter; +   // Synchronize the async control signals +   reg [1:0] 	cs_del, we_del; +   always @(posedge fifo_clk) +     if(fifo_rst) +       begin +	  cs_del <= 2'b11; +	  we_del <= 2'b11; +       end +     else +       begin +	  cs_del <= { cs_del[0], EM_NCS }; +	  we_del <= { we_del[0], EM_NWE }; +       end + +   wire do_write = (~cs_del[0] & (we_del == 2'b10)); +   wire first_write = (counter == 0); +   wire last_write = ((counter+1) == frame_len); + +   always @(posedge fifo_clk) +     if(do_write) +       begin +	  data_o[15:0] <= EM_D; +	  data_o[16] <= first_write; +	  data_o[17] <= last_write; +	  //  no byte writes data_o[18] <= |EM_NBE;  // mark half full if either is not enabled  FIXME +       end + +   always @(posedge fifo_clk) +     if(fifo_rst | clear) +       src_rdy_o <= 0; +     else if(do_write) +       src_rdy_o <= 1; +     else +       src_rdy_o <= 0;    // Assume it was taken + +   always @(posedge fifo_clk) +     if(fifo_rst | clear) +       counter <= 0; +     else if(do_write) +       if(last_write) +	 counter <= 0; +       else +	 counter <= counter + 1; + +   always @(posedge fifo_clk) +     if(fifo_rst | clear) +       fifo_ready <= 0; +     else +       fifo_ready <= /* first_write & */ (fifo_space > 16'd1023); + +   always @(posedge fifo_clk) +     if(fifo_rst | clear) +       bus_error <= 0; +     else if(src_rdy_o & ~dst_rdy_i) +       bus_error <= 1; +    +endmodule // gpmc_to_fifo_async diff --git a/fpga/usrp2/gpmc/gpmc_to_fifo_sync.v b/fpga/usrp2/gpmc/gpmc_to_fifo_sync.v new file mode 100644 index 000000000..688de0e17 --- /dev/null +++ b/fpga/usrp2/gpmc/gpmc_to_fifo_sync.v @@ -0,0 +1,57 @@ + +// Assumes a GPMC cycle with GPMC clock, as in the timing diagrams +//   If a packet bigger or smaller than we are told is sent, behavior is undefined. +//   If dst_rdy_i is low when we get data, behavior is undefined and we signal bus error. +//   If there is a bus error, we should be reset + +module gpmc_to_fifo_sync +  (input arst, +   input EM_CLK, input [15:0] EM_D, input [1:0] EM_NBE, +   input EM_NCS, input EM_NWE, +   output reg [17:0] data_o, output reg src_rdy_o, input dst_rdy_i, +   input [15:0] frame_len, input [15:0] fifo_space, output fifo_ready,  +   output reg bus_error); +    +   reg [10:0] 	counter; +   wire 	first_write = (counter == 0); +   wire 	last_write = ((counter+1) == frame_len); +   wire 	do_write = ~EM_NCS & ~EM_NWE; +    +   always @(posedge EM_CLK or posedge arst) +     if(arst) +       data_o <= 0; +     else if(do_write) +       begin +	  data_o[15:0] <= EM_D; +	  data_o[16] <= first_write; +	  data_o[17] <= last_write; +	  //  no byte writes data_o[18] <= |EM_NBE;  // mark half full if either is not enabled  FIXME +       end + +   always @(posedge EM_CLK or posedge arst) +     if(arst) +       src_rdy_o <= 0; +     else if(do_write & ~bus_error)  // Don't put junk in if there is a bus error +       src_rdy_o <= 1; +     else +       src_rdy_o <= 0;    // Assume it was taken, ignore dst_rdy_i + +   always @(posedge EM_CLK or posedge arst) +     if(arst) +       counter <= 0; +     else if(do_write) +       if(last_write) +	 counter <= 0; +       else +	 counter <= counter + 1; + +   assign fifo_ready = first_write & (fifo_space > frame_len); +    +   always @(posedge EM_CLK or posedge arst) +     if(arst) +       bus_error <= 0; +     else if(src_rdy_o & ~dst_rdy_i) +       bus_error <= 1; +   // must be reset to make the error go away + +endmodule // gpmc_to_fifo_sync diff --git a/fpga/usrp2/gpmc/gpmc_wb.v b/fpga/usrp2/gpmc/gpmc_wb.v new file mode 100644 index 000000000..db6fbc6e9 --- /dev/null +++ b/fpga/usrp2/gpmc/gpmc_wb.v @@ -0,0 +1,57 @@ + + +module gpmc_wb +  (input EM_CLK, input [15:0] EM_D_in, output [15:0] EM_D_out, input [10:1] EM_A, input [1:0] EM_NBE, +   input EM_NCS, input EM_NWE, input EM_NOE, + +   input wb_clk, input wb_rst, +   output reg [10:0] wb_adr_o, output reg [15:0] wb_dat_mosi, input [15:0] wb_dat_miso, +   output reg [1:0] wb_sel_o, output wb_cyc_o, output reg wb_stb_o, output reg wb_we_o, input wb_ack_i); +    +   // //////////////////////////////////////////// +   // Control Path, Wishbone bus bridge (wb master) +   reg [1:0] 	cs_del, we_del, oe_del; + +   // Synchronize the async control signals +   always @(posedge wb_clk) +     begin +	cs_del <= { cs_del[0], EM_NCS }; +	we_del <= { we_del[0], EM_NWE }; +	oe_del <= { oe_del[0], EM_NOE }; +     end + +   always @(posedge wb_clk) +     if(cs_del == 2'b10)  // Falling Edge +       wb_adr_o <= { EM_A, 1'b0 }; + +   always @(posedge wb_clk) +     if(we_del == 2'b10)  // Falling Edge +       begin +	  wb_dat_mosi <= EM_D_in; +	  wb_sel_o <= ~EM_NBE; +       end + +   reg [15:0] EM_D_hold; +    +   always @(posedge wb_clk) +     if(wb_ack_i) +       EM_D_hold <= wb_dat_miso; + +   assign EM_D_out = wb_ack_i ? wb_dat_miso : EM_D_hold; +    +   assign wb_cyc_o = wb_stb_o; + +   always @(posedge wb_clk) +     if(~cs_del[0] & (we_del == 2'b10) ) +       wb_we_o <= 1; +     else if(wb_ack_i)  // Turn off we when done.  Could also use we_del[0], others... +       wb_we_o <= 0; + +   // FIXME should this look at cs_del[1]? +   always @(posedge wb_clk) +     if(~cs_del[0] & ((we_del == 2'b10) | (oe_del == 2'b10))) +       wb_stb_o <= 1; +     else if(wb_ack_i) +       wb_stb_o <= 0; +    +endmodule // gpmc_wb diff --git a/fpga/usrp2/gpmc/make_timing_diag b/fpga/usrp2/gpmc/make_timing_diag new file mode 100755 index 000000000..03166ad35 --- /dev/null +++ b/fpga/usrp2/gpmc/make_timing_diag @@ -0,0 +1,6 @@ +#!/bin/sh +drawtiming -o single_data_write.gif single_data_write.txt +drawtiming -o single_data_read.gif single_data_read.txt +drawtiming -o burst_data_write.gif burst_data_write.txt +#drawtiming -o burst_data_read.gif burst_data_read.txt + diff --git a/fpga/usrp2/gpmc/ram_to_fifo.v b/fpga/usrp2/gpmc/ram_to_fifo.v new file mode 100644 index 000000000..8549dcc35 --- /dev/null +++ b/fpga/usrp2/gpmc/ram_to_fifo.v @@ -0,0 +1,46 @@ + + +module ram_to_fifo +  (input clk, input reset, +   input [10:0] read_length,  // From the dbsm (?) +   output read_en, output reg [8:0] read_addr, input [31:0] read_data, input read_ready, output read_done, +   output [35:0] data_o, output src_rdy_o, input dst_rdy_i); + +   // read_length/2 = number of 32 bit lines, numbered 0 through read_length/2-1 +   wire [8:0] 	 last_line = (read_length[10:1]-1);  + +   reg 		 read_phase, sop; + +   assign read_en = (read_phase == 0) | dst_rdy_i; +   assign src_rdy_o = (read_phase == 1); +    +   always @(posedge clk) +     if(reset) +       begin +	  read_addr <= 0; +	  read_phase <= 0; +	  sop <= 1; +       end +     else +       if(read_phase == 0) +	 begin +	    read_addr <= read_ready; +	    read_phase <= read_ready; +	 end +       else if(dst_rdy_i) +	 begin +	    sop <= 0; +	    if(read_addr == last_line) +	      begin +		 read_addr <= 0; +		 read_phase <= 0; +	      end +	    else +	      read_addr <= read_addr + 1; +	 end +    +   assign read_done = (read_phase == 1) & (read_addr == last_line) & dst_rdy_i; +   wire eop = (read_addr == last_line); +   assign data_o = { 2'b00, eop, sop, read_data }; +    +endmodule // ram_to_fifo diff --git a/fpga/usrp2/gpmc/single_data_read.txt b/fpga/usrp2/gpmc/single_data_read.txt new file mode 100644 index 000000000..1dc0e3a78 --- /dev/null +++ b/fpga/usrp2/gpmc/single_data_read.txt @@ -0,0 +1,12 @@ +# OMAP writes to FPGA +# initialize the signals +CLK=0,nWE=1,nCS=1,nOE=1,DATA=Z. +CLK=1. +CLK=0,nOE=0,nCS=0,DATA=RD_DATA. +CLK=1. +CLK=0. +CLK=1. +CLK=0,nOE=1,nCS=1,DATA=Z. +CLK=1. + + diff --git a/fpga/usrp2/gpmc/single_data_write.txt b/fpga/usrp2/gpmc/single_data_write.txt new file mode 100644 index 000000000..287e3e2c1 --- /dev/null +++ b/fpga/usrp2/gpmc/single_data_write.txt @@ -0,0 +1,10 @@ +# OMAP writes to FPGA +# initialize the signals +CLK=0,nWE=1,nCS=1,nOE=1,DATA=Z. +CLK=1. +CLK=0,nWE=0,nCS=0,DATA=WR_DATA. +CLK=1. +CLK=0,nWE=1,nCS=1,DATA=Z. +CLK=1. + + diff --git a/fpga/usrp2/models/gpmc_model_async.v b/fpga/usrp2/models/gpmc_model_async.v new file mode 100644 index 000000000..beeaee028 --- /dev/null +++ b/fpga/usrp2/models/gpmc_model_async.v @@ -0,0 +1,130 @@ +`timescale 1ps/1ps + +module gpmc_model_async +  (output EM_CLK, inout [15:0] EM_D, output reg [10:1] EM_A, output reg [1:0] EM_NBE, +   output reg EM_WAIT0, output reg EM_NCS4, output reg EM_NCS6,  +   output reg EM_NWE, output reg EM_NOE ); + +   assign EM_CLK = 0; +   reg [15:0] EM_D_int; +   assign EM_D = EM_D_int; +    +   initial +     begin +	EM_A <= 10'bz; +	EM_NBE <= 2'b11; +	EM_NWE <= 1; +	EM_NOE <= 1; +	EM_NCS4 <= 1; +	EM_NCS6 <= 1; +	EM_D_int <= 16'bz; + 	EM_WAIT0 <= 0;  // FIXME this is actually an input +     end +    +   task GPMC_Write; +      input ctrl; +      input [10:0] addr; +      input [15:0] data; +      begin +	 #23000; +	 EM_A <= addr[10:1]; +	 EM_D_int <= data; +	 #20100; +	 if(ctrl) +	   EM_NCS6 <= 0; +	 else +	   EM_NCS4 <= 0; +	 #14000; +	 EM_NWE <= 0; +	 #77500; +	 EM_NCS4 <= 1; +	 EM_NCS6 <= 1; +	 //#1.5; +	 EM_NWE <= 1; +	 #60000; +	 EM_A <= 10'bz; +	 EM_D_int <= 16'bz; +      end +   endtask // GPMC_Write + +   task GPMC_Read; +      input ctrl; +      input [10:0] addr; +      begin +	 #13000; +	 EM_A <= addr[10:1]; +	 #3000; +	 if(ctrl) +	   EM_NCS6 <= 0; +	 else +	   EM_NCS4 <= 0; +	 #14000; +	 EM_NOE <= 0; +	 #77500; +	 EM_NCS4 <= 1; +	 EM_NCS6 <= 1; +	 //#1.5; +	 $display("Data Read from GPMC: %X",EM_D); +	 EM_NOE <= 1; +	 #254000; +	 EM_A <= 10'bz; +      end +   endtask // GPMC_Read +    +   initial +     begin +	#1000000; +	GPMC_Write(1,36,16'hF00D); +	#1000000; +	GPMC_Read(1,36); +	#1000000; +	GPMC_Write(0,0,16'h1234); +	GPMC_Write(0,0,16'h5678); +	GPMC_Write(0,0,16'h9abc); +	GPMC_Write(0,0,16'hF00D); +	GPMC_Write(0,0,16'hDEAD); +	GPMC_Write(0,0,16'hDEAD); +	GPMC_Write(0,0,16'hDEAD); +	GPMC_Write(0,0,16'hDEAD); +	GPMC_Write(0,0,16'hDEAD); +	GPMC_Write(0,0,16'hDEAD); +	#1000000; +	GPMC_Write(0,0,16'h1234); +	GPMC_Write(0,0,16'h5678); +	GPMC_Write(0,0,16'h9abc); +	GPMC_Write(0,0,16'hF00D); +	GPMC_Write(0,0,16'hDEAD); +	GPMC_Write(0,0,16'hDEAD); +	GPMC_Write(0,0,16'hDEAD); +	GPMC_Write(0,0,16'hDEAD); +	GPMC_Write(0,0,16'hDEAD); +	GPMC_Write(0,0,16'h9876); +	#1000000; +	GPMC_Read(0,0); +	GPMC_Read(0,0); +	GPMC_Read(0,0); +	GPMC_Read(0,0); +	GPMC_Read(0,0); +	GPMC_Read(0,0); +	GPMC_Read(0,0); +	GPMC_Read(0,0); +	GPMC_Read(0,0); +	GPMC_Read(0,0); +	#1000000; +	GPMC_Read(0,0); +	GPMC_Read(0,0); +	GPMC_Read(0,0); +	GPMC_Read(0,0); +	GPMC_Read(0,0); +	GPMC_Read(0,0); +	GPMC_Read(0,0); +	GPMC_Read(0,0); +	GPMC_Read(0,0); +	GPMC_Read(0,0); +	#1000000; +	GPMC_Read(0,0); +	#100000000; +	$finish; +     end +    +endmodule // gpmc_model_async diff --git a/fpga/usrp2/models/gpmc_model_sync.v b/fpga/usrp2/models/gpmc_model_sync.v new file mode 100644 index 000000000..641720c15 --- /dev/null +++ b/fpga/usrp2/models/gpmc_model_sync.v @@ -0,0 +1,97 @@ + + +module gpmc_model_sync +  (output reg EM_CLK, inout [15:0] EM_D, output reg [10:1] EM_A, output reg [1:0] EM_NBE, +   output reg EM_WAIT0, output reg EM_NCS4, output reg EM_NCS6,  +   output reg EM_NWE, output reg EM_NOE ); + +   reg [15:0] EM_D_int; +   assign EM_D = EM_D_int; + +   initial +     begin +	EM_CLK <= 0; +	EM_A <= 10'bz; +	EM_NBE <= 2'b11; +	EM_NWE <= 1; +	EM_NOE <= 1; +	EM_NCS4 <= 1; +	EM_NCS6 <= 1; +	EM_D_int <= 16'bz; + 	EM_WAIT0 <= 0;  // FIXME this is actually an input +     end +    +   task GPMC_Write; +      input ctrl; +      input [10:0] addr; +      input [15:0] data; +      begin +	 EM_CLK <= 1; +	 #10; +	 EM_CLK <= 0; +	 EM_NWE <= 0; +	 if(ctrl) +	   EM_NCS6 <= 0; +	 else +	   EM_NCS4 <= 0; +	 EM_A <= addr[10:1]; +	 EM_D_int <= data; +	 #10; +	 EM_CLK <= 1; +	 #10; +	 EM_CLK <= 0; +	 EM_NWE <= 1; +	 EM_NCS4 <= 1; +	 EM_NCS6 <= 1; +	 EM_A <= 10'bz; +	 EM_D_int <= 16'bz; +	 #100; +      end +   endtask // GPMC_Write + +   task GPMC_Read; +      input ctrl; +      input [10:0] addr; +      begin +	 #1.3; +	 EM_A <= addr[10:1]; +	 #3; +	 if(ctrl) +	   EM_NCS6 <= 0; +	 else +	   EM_NCS4 <= 0; +	 #14; +	 EM_NOE <= 0; +	 #77.5; +	 EM_NCS4 <= 1; +	 EM_NCS6 <= 1; +	 //#1.5; +	 $display("Data Read from GPMC: %X",EM_D); +	 EM_NOE <= 1; +	 #254; +	 EM_A <= 10'bz; +      end +   endtask // GPMC_Read +    +   initial +     begin +	#1000; +	GPMC_Write(1,36,16'hF00D); +	#1000; +	GPMC_Read(1,36); +	#1000; +	GPMC_Write(0,36,16'h1234); +	GPMC_Write(0,38,16'h5678); +	GPMC_Write(0,40,16'h9abc); +	GPMC_Write(0,11'h2F4,16'hF00D); +	GPMC_Write(0,11'h7FE,16'hDEAD); +	GPMC_Write(0,11'h7FE,16'hDEAD); +	GPMC_Write(0,11'h7FE,16'hDEAD); +	GPMC_Write(0,11'h7FE,16'hDEAD); +	GPMC_Write(0,11'h7FE,16'hDEAD); +	GPMC_Write(0,11'h7FE,16'hDEAD); +	#100000; +	$finish; +     end +    +endmodule // gpmc_model diff --git a/fpga/usrp2/opencores/Makefile.srcs b/fpga/usrp2/opencores/Makefile.srcs index 30360a17d..1ccecf337 100644 --- a/fpga/usrp2/opencores/Makefile.srcs +++ b/fpga/usrp2/opencores/Makefile.srcs @@ -23,6 +23,5 @@ i2c/rtl/verilog/timescale.v \  spi/rtl/verilog/spi_clgen.v \  spi/rtl/verilog/spi_defines.v \  spi/rtl/verilog/spi_shift.v \ -spi/rtl/verilog/spi_top.v \ -spi/rtl/verilog/timescale.v \ +spi/rtl/verilog/spi_top16.v \  )) diff --git a/fpga/usrp2/opencores/spi/rtl/verilog/spi_clgen.v b/fpga/usrp2/opencores/spi/rtl/verilog/spi_clgen.v index 7bc4f6e5e..2d9c34f40 100644 --- a/fpga/usrp2/opencores/spi/rtl/verilog/spi_clgen.v +++ b/fpga/usrp2/opencores/spi/rtl/verilog/spi_clgen.v @@ -39,12 +39,9 @@  //////////////////////////////////////////////////////////////////////  `include "spi_defines.v" -`include "timescale.v"  module spi_clgen (clk_in, rst, go, enable, last_clk, divider, clk_out, pos_edge, neg_edge);  -  parameter Tp = 1; -      input                            clk_in;   // input clock (system clock)    input                            rst;      // reset    input                            enable;   // clock enable @@ -68,40 +65,40 @@ module spi_clgen (clk_in, rst, go, enable, last_clk, divider, clk_out, pos_edge,    assign cnt_one  = cnt == {{`SPI_DIVIDER_LEN-1{1'b0}}, 1'b1};    // Counter counts half period -  always @(posedge clk_in or posedge rst) +  always @(posedge clk_in)    begin      if(rst) -      cnt <= #Tp {`SPI_DIVIDER_LEN{1'b1}}; +      cnt <= {`SPI_DIVIDER_LEN{1'b1}};      else        begin          if(!enable || cnt_zero) -          cnt <= #Tp divider; +          cnt <= divider;          else -          cnt <= #Tp cnt - {{`SPI_DIVIDER_LEN-1{1'b0}}, 1'b1}; +          cnt <= cnt - {{`SPI_DIVIDER_LEN-1{1'b0}}, 1'b1};        end    end    // clk_out is asserted every other half period -  always @(posedge clk_in or posedge rst) +  always @(posedge clk_in)    begin      if(rst) -      clk_out <= #Tp 1'b0; +      clk_out <= 1'b0;      else -      clk_out <= #Tp (enable && cnt_zero && (!last_clk || clk_out)) ? ~clk_out : clk_out; +      clk_out <= (enable && cnt_zero && (!last_clk || clk_out)) ? ~clk_out : clk_out;    end    // Pos and neg edge signals -  always @(posedge clk_in or posedge rst) +  always @(posedge clk_in)    begin      if(rst)        begin -        pos_edge  <= #Tp 1'b0; -        neg_edge  <= #Tp 1'b0; +        pos_edge  <= 1'b0; +        neg_edge  <= 1'b0;        end      else        begin -        pos_edge  <= #Tp (enable && !clk_out && cnt_one) || (!(|divider) && clk_out) || (!(|divider) && go && !enable); -        neg_edge  <= #Tp (enable && clk_out && cnt_one) || (!(|divider) && !clk_out && enable); +        pos_edge  <= (enable && !clk_out && cnt_one) || (!(|divider) && clk_out) || (!(|divider) && go && !enable); +        neg_edge  <= (enable && clk_out && cnt_one) || (!(|divider) && !clk_out && enable);        end    end  endmodule diff --git a/fpga/usrp2/opencores/spi/rtl/verilog/spi_defines.v b/fpga/usrp2/opencores/spi/rtl/verilog/spi_defines.v index a6925918e..963a680a8 100644 --- a/fpga/usrp2/opencores/spi/rtl/verilog/spi_defines.v +++ b/fpga/usrp2/opencores/spi/rtl/verilog/spi_defines.v @@ -43,8 +43,8 @@  // low frequency of system clock this can be reduced.  // Use SPI_DIVIDER_LEN for fine tuning theexact number.  // -//`define SPI_DIVIDER_LEN_8 -`define SPI_DIVIDER_LEN_16 +`define SPI_DIVIDER_LEN_8 +//`define SPI_DIVIDER_LEN_16  //`define SPI_DIVIDER_LEN_24  //`define SPI_DIVIDER_LEN_32 @@ -66,9 +66,9 @@  // Use SPI_MAX_CHAR for fine tuning the exact number, when using  // SPI_MAX_CHAR_32, SPI_MAX_CHAR_24, SPI_MAX_CHAR_16, SPI_MAX_CHAR_8.  // -`define SPI_MAX_CHAR_128 +//`define SPI_MAX_CHAR_128  //`define SPI_MAX_CHAR_64 -//`define SPI_MAX_CHAR_32 +`define SPI_MAX_CHAR_32  //`define SPI_MAX_CHAR_24  //`define SPI_MAX_CHAR_16  //`define SPI_MAX_CHAR_8 @@ -137,7 +137,7 @@  `define SPI_TX_2                2  `define SPI_TX_3                3  `define SPI_CTRL                4 -`define SPI_DEVIDE              5 +`define SPI_DIVIDE              5  `define SPI_SS                  6  // diff --git a/fpga/usrp2/opencores/spi/rtl/verilog/spi_shift.v b/fpga/usrp2/opencores/spi/rtl/verilog/spi_shift.v index b17ac8b1f..ac3bb3f48 100644 --- a/fpga/usrp2/opencores/spi/rtl/verilog/spi_shift.v +++ b/fpga/usrp2/opencores/spi/rtl/verilog/spi_shift.v @@ -39,15 +39,12 @@  //////////////////////////////////////////////////////////////////////  `include "spi_defines.v" -`include "timescale.v"  module spi_shift (clk, rst, latch, byte_sel, len, lsb, go,                    pos_edge, neg_edge, rx_negedge, tx_negedge,                    tip, last,                     p_in, p_out, s_clk, s_in, s_out); -  parameter Tp = 1; -      input                          clk;          // system clock    input                          rst;          // reset    input                    [3:0] latch;        // latch signal for storing the data in shift register @@ -89,149 +86,149 @@ module spi_shift (clk, rst, latch, byte_sel, len, lsb, go,    assign tx_clk = (tx_negedge ? neg_edge : pos_edge) && !last;    // Character bit counter -  always @(posedge clk or posedge rst) +  always @(posedge clk)    begin      if(rst) -      cnt <= #Tp {`SPI_CHAR_LEN_BITS+1{1'b0}}; +      cnt <= {`SPI_CHAR_LEN_BITS+1{1'b0}};      else        begin          if(tip) -          cnt <= #Tp pos_edge ? (cnt - {{`SPI_CHAR_LEN_BITS{1'b0}}, 1'b1}) : cnt; +          cnt <= pos_edge ? (cnt - {{`SPI_CHAR_LEN_BITS{1'b0}}, 1'b1}) : cnt;          else -          cnt <= #Tp !(|len) ? {1'b1, {`SPI_CHAR_LEN_BITS{1'b0}}} : {1'b0, len}; +          cnt <= !(|len) ? {1'b1, {`SPI_CHAR_LEN_BITS{1'b0}}} : {1'b0, len};        end    end    // Transfer in progress -  always @(posedge clk or posedge rst) +  always @(posedge clk)    begin      if(rst) -      tip <= #Tp 1'b0; +      tip <= 1'b0;    else if(go && ~tip) -    tip <= #Tp 1'b1; +    tip <= 1'b1;    else if(tip && last && pos_edge) -    tip <= #Tp 1'b0; +    tip <= 1'b0;    end    // Sending bits to the line -  always @(posedge clk or posedge rst) +  always @(posedge clk)    begin      if (rst) -      s_out   <= #Tp 1'b0; +      s_out   <= 1'b0;      else -      s_out <= #Tp (tx_clk || !tip) ? data[tx_bit_pos[`SPI_CHAR_LEN_BITS-1:0]] : s_out; +      s_out <= (tx_clk || !tip) ? data[tx_bit_pos[`SPI_CHAR_LEN_BITS-1:0]] : s_out;    end    // Receiving bits from the line -  always @(posedge clk or posedge rst) +  always @(posedge clk)    begin      if (rst) -      data   <= #Tp {`SPI_MAX_CHAR{1'b0}}; +      data   <= {`SPI_MAX_CHAR{1'b0}};  `ifdef SPI_MAX_CHAR_128      else if (latch[0] && !tip)        begin          if (byte_sel[3]) -          data[31:24] <= #Tp p_in[31:24]; +          data[31:24] <= p_in[31:24];          if (byte_sel[2]) -          data[23:16] <= #Tp p_in[23:16]; +          data[23:16] <= p_in[23:16];          if (byte_sel[1]) -          data[15:8] <= #Tp p_in[15:8]; +          data[15:8] <= p_in[15:8];          if (byte_sel[0]) -          data[7:0] <= #Tp p_in[7:0]; +          data[7:0] <= p_in[7:0];        end      else if (latch[1] && !tip)        begin          if (byte_sel[3]) -          data[63:56] <= #Tp p_in[31:24]; +          data[63:56] <= p_in[31:24];          if (byte_sel[2]) -          data[55:48] <= #Tp p_in[23:16]; +          data[55:48] <= p_in[23:16];          if (byte_sel[1]) -          data[47:40] <= #Tp p_in[15:8]; +          data[47:40] <= p_in[15:8];          if (byte_sel[0]) -          data[39:32] <= #Tp p_in[7:0]; +          data[39:32] <= p_in[7:0];        end      else if (latch[2] && !tip)        begin          if (byte_sel[3]) -          data[95:88] <= #Tp p_in[31:24]; +          data[95:88] <= p_in[31:24];          if (byte_sel[2]) -          data[87:80] <= #Tp p_in[23:16]; +          data[87:80] <= p_in[23:16];          if (byte_sel[1]) -          data[79:72] <= #Tp p_in[15:8]; +          data[79:72] <= p_in[15:8];          if (byte_sel[0]) -          data[71:64] <= #Tp p_in[7:0]; +          data[71:64] <= p_in[7:0];        end      else if (latch[3] && !tip)        begin          if (byte_sel[3]) -          data[127:120] <= #Tp p_in[31:24]; +          data[127:120] <= p_in[31:24];          if (byte_sel[2]) -          data[119:112] <= #Tp p_in[23:16]; +          data[119:112] <= p_in[23:16];          if (byte_sel[1]) -          data[111:104] <= #Tp p_in[15:8]; +          data[111:104] <= p_in[15:8];          if (byte_sel[0]) -          data[103:96] <= #Tp p_in[7:0]; +          data[103:96] <= p_in[7:0];        end  `else  `ifdef SPI_MAX_CHAR_64      else if (latch[0] && !tip)        begin          if (byte_sel[3]) -          data[31:24] <= #Tp p_in[31:24]; +          data[31:24] <= p_in[31:24];          if (byte_sel[2]) -          data[23:16] <= #Tp p_in[23:16]; +          data[23:16] <= p_in[23:16];          if (byte_sel[1]) -          data[15:8] <= #Tp p_in[15:8]; +          data[15:8] <= p_in[15:8];          if (byte_sel[0]) -          data[7:0] <= #Tp p_in[7:0]; +          data[7:0] <= p_in[7:0];        end      else if (latch[1] && !tip)        begin          if (byte_sel[3]) -          data[63:56] <= #Tp p_in[31:24]; +          data[63:56] <= p_in[31:24];          if (byte_sel[2]) -          data[55:48] <= #Tp p_in[23:16]; +          data[55:48] <= p_in[23:16];          if (byte_sel[1]) -          data[47:40] <= #Tp p_in[15:8]; +          data[47:40] <= p_in[15:8];          if (byte_sel[0]) -          data[39:32] <= #Tp p_in[7:0]; +          data[39:32] <= p_in[7:0];        end  `else      else if (latch[0] && !tip)        begin        `ifdef SPI_MAX_CHAR_8          if (byte_sel[0]) -          data[`SPI_MAX_CHAR-1:0] <= #Tp p_in[`SPI_MAX_CHAR-1:0]; +          data[`SPI_MAX_CHAR-1:0] <= p_in[`SPI_MAX_CHAR-1:0];        `endif        `ifdef SPI_MAX_CHAR_16          if (byte_sel[0]) -          data[7:0] <= #Tp p_in[7:0]; +          data[7:0] <= p_in[7:0];          if (byte_sel[1]) -          data[`SPI_MAX_CHAR-1:8] <= #Tp p_in[`SPI_MAX_CHAR-1:8]; +          data[`SPI_MAX_CHAR-1:8] <= p_in[`SPI_MAX_CHAR-1:8];        `endif        `ifdef SPI_MAX_CHAR_24          if (byte_sel[0]) -          data[7:0] <= #Tp p_in[7:0]; +          data[7:0] <= p_in[7:0];          if (byte_sel[1]) -          data[15:8] <= #Tp p_in[15:8]; +          data[15:8] <= p_in[15:8];          if (byte_sel[2]) -          data[`SPI_MAX_CHAR-1:16] <= #Tp p_in[`SPI_MAX_CHAR-1:16]; +          data[`SPI_MAX_CHAR-1:16] <= p_in[`SPI_MAX_CHAR-1:16];        `endif        `ifdef SPI_MAX_CHAR_32          if (byte_sel[0]) -          data[7:0] <= #Tp p_in[7:0]; +          data[7:0] <= p_in[7:0];          if (byte_sel[1]) -          data[15:8] <= #Tp p_in[15:8]; +          data[15:8] <= p_in[15:8];          if (byte_sel[2]) -          data[23:16] <= #Tp p_in[23:16]; +          data[23:16] <= p_in[23:16];          if (byte_sel[3]) -          data[`SPI_MAX_CHAR-1:24] <= #Tp p_in[`SPI_MAX_CHAR-1:24]; +          data[`SPI_MAX_CHAR-1:24] <= p_in[`SPI_MAX_CHAR-1:24];        `endif        end  `endif  `endif      else -      data[rx_bit_pos[`SPI_CHAR_LEN_BITS-1:0]] <= #Tp rx_clk ? s_in : data[rx_bit_pos[`SPI_CHAR_LEN_BITS-1:0]]; +      data[rx_bit_pos[`SPI_CHAR_LEN_BITS-1:0]] <= rx_clk ? s_in : data[rx_bit_pos[`SPI_CHAR_LEN_BITS-1:0]];    end  endmodule diff --git a/fpga/usrp2/opencores/spi/rtl/verilog/spi_top.v b/fpga/usrp2/opencores/spi/rtl/verilog/spi_top.v index 09b2e50e1..8289449a9 100644 --- a/fpga/usrp2/opencores/spi/rtl/verilog/spi_top.v +++ b/fpga/usrp2/opencores/spi/rtl/verilog/spi_top.v @@ -1,3 +1,6 @@ + +// Modified 2010 by Matt Ettus to remove old verilog style +  //////////////////////////////////////////////////////////////////////  ////                                                              ////  ////  spi_top.v                                                   //// @@ -40,7 +43,6 @@  `include "spi_defines.v" -`include "timescale.v"  module spi_top  ( @@ -52,8 +54,6 @@ module spi_top    ss_pad_o, sclk_pad_o, mosi_pad_o, miso_pad_i  ); -  parameter Tp = 1; -    // Wishbone signals    input                            wb_clk_i;         // master clock input    input                            wb_rst_i;         // synchronous active high reset @@ -101,7 +101,7 @@ module spi_top    wire                             last_bit;         // marks last character bit    // Address decoder -  assign spi_divider_sel = wb_cyc_i & wb_stb_i & (wb_adr_i[`SPI_OFS_BITS] == `SPI_DEVIDE); +  assign spi_divider_sel = wb_cyc_i & wb_stb_i & (wb_adr_i[`SPI_OFS_BITS] == `SPI_DIVIDE);    assign spi_ctrl_sel    = wb_cyc_i & wb_stb_i & (wb_adr_i[`SPI_OFS_BITS] == `SPI_CTRL);    assign spi_tx_sel[0]   = wb_cyc_i & wb_stb_i & (wb_adr_i[`SPI_OFS_BITS] == `SPI_TX_0);    assign spi_tx_sel[1]   = wb_cyc_i & wb_stb_i & (wb_adr_i[`SPI_OFS_BITS] == `SPI_TX_1); @@ -132,96 +132,96 @@ module spi_top  `endif  `endif        `SPI_CTRL:    wb_dat = {{32-`SPI_CTRL_BIT_NB{1'b0}}, ctrl}; -      `SPI_DEVIDE:  wb_dat = {{32-`SPI_DIVIDER_LEN{1'b0}}, divider}; +      `SPI_DIVIDE:  wb_dat = {{32-`SPI_DIVIDER_LEN{1'b0}}, divider};        `SPI_SS:      wb_dat = {{32-`SPI_SS_NB{1'b0}}, ss};        default:      wb_dat = 32'bx;      endcase    end    // Wb data out -  always @(posedge wb_clk_i or posedge wb_rst_i) +  always @(posedge wb_clk_i)    begin      if (wb_rst_i) -      wb_dat_o <= #Tp 32'b0; +      wb_dat_o <= 32'b0;      else -      wb_dat_o <= #Tp wb_dat; +      wb_dat_o <= wb_dat;    end    // Wb acknowledge -  always @(posedge wb_clk_i or posedge wb_rst_i) +  always @(posedge wb_clk_i)    begin      if (wb_rst_i) -      wb_ack_o <= #Tp 1'b0; +      wb_ack_o <= 1'b0;      else -      wb_ack_o <= #Tp wb_cyc_i & wb_stb_i & ~wb_ack_o; +      wb_ack_o <= wb_cyc_i & wb_stb_i & ~wb_ack_o;    end    // Wb error    assign wb_err_o = 1'b0;    // Interrupt -  always @(posedge wb_clk_i or posedge wb_rst_i) +  always @(posedge wb_clk_i)    begin      if (wb_rst_i) -      wb_int_o <= #Tp 1'b0; +      wb_int_o <= 1'b0;      else if (ie && tip && last_bit && pos_edge) -      wb_int_o <= #Tp 1'b1; +      wb_int_o <= 1'b1;      else if (wb_ack_o) -      wb_int_o <= #Tp 1'b0; +      wb_int_o <= 1'b0;    end    // Divider register -  always @(posedge wb_clk_i or posedge wb_rst_i) +  always @(posedge wb_clk_i)    begin      if (wb_rst_i) -        divider <= #Tp {`SPI_DIVIDER_LEN{1'b0}}; +        divider <= {`SPI_DIVIDER_LEN{1'b0}};      else if (spi_divider_sel && wb_we_i && !tip)        begin        `ifdef SPI_DIVIDER_LEN_8          if (wb_sel_i[0]) -          divider <= #Tp wb_dat_i[`SPI_DIVIDER_LEN-1:0]; +          divider <= wb_dat_i[`SPI_DIVIDER_LEN-1:0];        `endif        `ifdef SPI_DIVIDER_LEN_16          if (wb_sel_i[0]) -          divider[7:0] <= #Tp wb_dat_i[7:0]; +          divider[7:0] <= wb_dat_i[7:0];          if (wb_sel_i[1]) -          divider[`SPI_DIVIDER_LEN-1:8] <= #Tp wb_dat_i[`SPI_DIVIDER_LEN-1:8]; +          divider[`SPI_DIVIDER_LEN-1:8] <= wb_dat_i[`SPI_DIVIDER_LEN-1:8];        `endif        `ifdef SPI_DIVIDER_LEN_24          if (wb_sel_i[0]) -          divider[7:0] <= #Tp wb_dat_i[7:0]; +          divider[7:0] <= wb_dat_i[7:0];          if (wb_sel_i[1]) -          divider[15:8] <= #Tp wb_dat_i[15:8]; +          divider[15:8] <= wb_dat_i[15:8];          if (wb_sel_i[2]) -          divider[`SPI_DIVIDER_LEN-1:16] <= #Tp wb_dat_i[`SPI_DIVIDER_LEN-1:16]; +          divider[`SPI_DIVIDER_LEN-1:16] <= wb_dat_i[`SPI_DIVIDER_LEN-1:16];        `endif        `ifdef SPI_DIVIDER_LEN_32          if (wb_sel_i[0]) -          divider[7:0] <= #Tp wb_dat_i[7:0]; +          divider[7:0] <= wb_dat_i[7:0];          if (wb_sel_i[1]) -          divider[15:8] <= #Tp wb_dat_i[15:8]; +          divider[15:8] <= wb_dat_i[15:8];          if (wb_sel_i[2]) -          divider[23:16] <= #Tp wb_dat_i[23:16]; +          divider[23:16] <= wb_dat_i[23:16];          if (wb_sel_i[3]) -          divider[`SPI_DIVIDER_LEN-1:24] <= #Tp wb_dat_i[`SPI_DIVIDER_LEN-1:24]; +          divider[`SPI_DIVIDER_LEN-1:24] <= wb_dat_i[`SPI_DIVIDER_LEN-1:24];        `endif        end    end    // Ctrl register -  always @(posedge wb_clk_i or posedge wb_rst_i) +  always @(posedge wb_clk_i)    begin      if (wb_rst_i) -      ctrl <= #Tp {`SPI_CTRL_BIT_NB{1'b0}}; +      ctrl <= {`SPI_CTRL_BIT_NB{1'b0}};      else if(spi_ctrl_sel && wb_we_i && !tip)        begin          if (wb_sel_i[0]) -          ctrl[7:0] <= #Tp wb_dat_i[7:0] | {7'b0, ctrl[0]}; +          ctrl[7:0] <= wb_dat_i[7:0] | {7'b0, ctrl[0]};          if (wb_sel_i[1]) -          ctrl[`SPI_CTRL_BIT_NB-1:8] <= #Tp wb_dat_i[`SPI_CTRL_BIT_NB-1:8]; +          ctrl[`SPI_CTRL_BIT_NB-1:8] <= wb_dat_i[`SPI_CTRL_BIT_NB-1:8];        end      else if(tip && last_bit && pos_edge) -      ctrl[`SPI_CTRL_GO] <= #Tp 1'b0; +      ctrl[`SPI_CTRL_GO] <= 1'b0;    end    assign rx_negedge = ctrl[`SPI_CTRL_RX_NEGEDGE]; @@ -233,39 +233,39 @@ module spi_top    assign ass        = ctrl[`SPI_CTRL_ASS];    // Slave select register -  always @(posedge wb_clk_i or posedge wb_rst_i) +  always @(posedge wb_clk_i)    begin      if (wb_rst_i) -      ss <= #Tp {`SPI_SS_NB{1'b0}}; +      ss <= {`SPI_SS_NB{1'b0}};      else if(spi_ss_sel && wb_we_i && !tip)        begin        `ifdef SPI_SS_NB_8          if (wb_sel_i[0]) -          ss <= #Tp wb_dat_i[`SPI_SS_NB-1:0]; +          ss <= wb_dat_i[`SPI_SS_NB-1:0];        `endif        `ifdef SPI_SS_NB_16          if (wb_sel_i[0]) -          ss[7:0] <= #Tp wb_dat_i[7:0]; +          ss[7:0] <= wb_dat_i[7:0];          if (wb_sel_i[1]) -          ss[`SPI_SS_NB-1:8] <= #Tp wb_dat_i[`SPI_SS_NB-1:8]; +          ss[`SPI_SS_NB-1:8] <= wb_dat_i[`SPI_SS_NB-1:8];        `endif        `ifdef SPI_SS_NB_24          if (wb_sel_i[0]) -          ss[7:0] <= #Tp wb_dat_i[7:0]; +          ss[7:0] <= wb_dat_i[7:0];          if (wb_sel_i[1]) -          ss[15:8] <= #Tp wb_dat_i[15:8]; +          ss[15:8] <= wb_dat_i[15:8];          if (wb_sel_i[2]) -          ss[`SPI_SS_NB-1:16] <= #Tp wb_dat_i[`SPI_SS_NB-1:16]; +          ss[`SPI_SS_NB-1:16] <= wb_dat_i[`SPI_SS_NB-1:16];        `endif        `ifdef SPI_SS_NB_32          if (wb_sel_i[0]) -          ss[7:0] <= #Tp wb_dat_i[7:0]; +          ss[7:0] <= wb_dat_i[7:0];          if (wb_sel_i[1]) -          ss[15:8] <= #Tp wb_dat_i[15:8]; +          ss[15:8] <= wb_dat_i[15:8];          if (wb_sel_i[2]) -          ss[23:16] <= #Tp wb_dat_i[23:16]; +          ss[23:16] <= wb_dat_i[23:16];          if (wb_sel_i[3]) -          ss[`SPI_SS_NB-1:24] <= #Tp wb_dat_i[`SPI_SS_NB-1:24]; +          ss[`SPI_SS_NB-1:24] <= wb_dat_i[`SPI_SS_NB-1:24];        `endif        end    end diff --git a/fpga/usrp2/opencores/spi/rtl/verilog/spi_top16.v b/fpga/usrp2/opencores/spi/rtl/verilog/spi_top16.v new file mode 100644 index 000000000..ee808a8ab --- /dev/null +++ b/fpga/usrp2/opencores/spi/rtl/verilog/spi_top16.v @@ -0,0 +1,182 @@ + +// Modified 2010 by Matt Ettus to remove old verilog style and +// allow 16-bit operation + +////////////////////////////////////////////////////////////////////// +////                                                              //// +////  spi_top.v                                                   //// +////                                                              //// +////  This file is part of the SPI IP core project                //// +////  http://www.opencores.org/projects/spi/                      //// +////                                                              //// +////  Author(s):                                                  //// +////      - Simon Srot (simons@opencores.org)                     //// +////                                                              //// +////  All additional information is avaliable in the Readme.txt   //// +////  file.                                                       //// +////                                                              //// +////////////////////////////////////////////////////////////////////// +////                                                              //// +//// Copyright (C) 2002 Authors                                   //// +////                                                              //// +//// This source file may be used and distributed without         //// +//// restriction provided that this copyright statement is not    //// +//// removed from the file and that any derivative work contains  //// +//// the original copyright notice and the associated disclaimer. //// +////                                                              //// +//// This source file is free software; you can redistribute it   //// +//// and/or modify it under the terms of the GNU Lesser General   //// +//// Public License as published by the Free Software Foundation; //// +//// either version 2.1 of the License, or (at your option) any   //// +//// later version.                                               //// +////                                                              //// +//// This source 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 Lesser General Public License for more //// +//// details.                                                     //// +////                                                              //// +//// You should have received a copy of the GNU Lesser General    //// +//// Public License along with this source; if not, download it   //// +//// from http://www.opencores.org/lgpl.shtml                     //// +////                                                              //// +////////////////////////////////////////////////////////////////////// + + +`include "spi_defines.v" + +module spi_top16 +  (input wb_clk_i, input wb_rst_i,  +   input [4:0] wb_adr_i,  +   input [15:0] wb_dat_i,  +   output reg [15:0] wb_dat_o,  +   input [1:0] wb_sel_i, +   input wb_we_i, input wb_stb_i, input wb_cyc_i,  +   output reg wb_ack_o, output wb_err_o, output reg wb_int_o, +    +   // SPI signals +   output [15:0] ss_pad_o, output sclk_pad_o, output mosi_pad_o, input miso_pad_i); +    +   // Internal signals +   reg [15:0] divider;          // Divider register +   reg [`SPI_CTRL_BIT_NB-1:0] ctrl;             // Control and status register +   reg [15:0] 		      ss;               // Slave select register +   reg [31:0] 		      wb_dat;           // wb data out +   wire [31:0] 		      rx;               // Rx register +   wire 		      rx_negedge;       // miso is sampled on negative edge +   wire 		      tx_negedge;       // mosi is driven on negative edge +   wire [`SPI_CHAR_LEN_BITS-1:0] char_len;         // char len +   wire 			 go;               // go +   wire 			 lsb;              // lsb first on line +   wire 			 ie;               // interrupt enable +   wire 			 ass;              // automatic slave select +   wire 			 spi_divider_sel;  // divider register select +   wire 			 spi_ctrl_sel;     // ctrl register select +   wire [3:0] 			 spi_tx_sel;       // tx_l register select +   wire 			 spi_ss_sel;       // ss register select +   wire 			 tip;              // transfer in progress +   wire 			 pos_edge;         // recognize posedge of sclk +   wire 			 neg_edge;         // recognize negedge of sclk +   wire 			 last_bit;         // marks last character bit +    +   // Address decoder +   assign spi_divider_sel = wb_cyc_i & wb_stb_i & (wb_adr_i[4:2] == `SPI_DIVIDE); +   assign spi_ctrl_sel    = wb_cyc_i & wb_stb_i & (wb_adr_i[4:2] == `SPI_CTRL); +   assign spi_tx_sel[0]   = wb_cyc_i & wb_stb_i & (wb_adr_i[4:2] == `SPI_TX_0); +   assign spi_tx_sel[1]   = wb_cyc_i & wb_stb_i & (wb_adr_i[4:2] == `SPI_TX_1); +   assign spi_tx_sel[2]   = wb_cyc_i & wb_stb_i & (wb_adr_i[4:2] == `SPI_TX_2); +   assign spi_tx_sel[3]   = wb_cyc_i & wb_stb_i & (wb_adr_i[4:2] == `SPI_TX_3); +   assign spi_ss_sel      = wb_cyc_i & wb_stb_i & (wb_adr_i[4:2] == `SPI_SS); +    +   always @(wb_adr_i or rx or ctrl or divider or ss) +     case (wb_adr_i[4:2])    +       `SPI_RX_0:    wb_dat = rx[31:0]; +       `SPI_CTRL:    wb_dat = {{32-`SPI_CTRL_BIT_NB{1'b0}}, ctrl}; +       `SPI_DIVIDE:  wb_dat = {16'b0, divider}; +       `SPI_SS:      wb_dat = {16'b0, ss}; +       default : wb_dat = 32'd0; +     endcase // case (wb_adr_i[4:2]) +    +   always @(posedge wb_clk_i) +     if (wb_rst_i) +       wb_dat_o <= 32'b0; +     else +       wb_dat_o <= wb_adr_i[1] ? wb_dat[31:16] : wb_dat[15:0]; +    +   always @(posedge wb_clk_i) +     if (wb_rst_i) +       wb_ack_o <= 1'b0; +     else +       wb_ack_o <= wb_cyc_i & wb_stb_i & ~wb_ack_o; +    +   assign wb_err_o = 1'b0; +    +   // Interrupt +   always @(posedge wb_clk_i) +     if (wb_rst_i) +       wb_int_o <= 1'b0; +     else if (ie && tip && last_bit && pos_edge) +       wb_int_o <= 1'b1; +     else if (wb_ack_o) +       wb_int_o <= 1'b0; +    +   // Divider register +   always @(posedge wb_clk_i) +     if (wb_rst_i) +       divider <= 16'b0; +     else if (spi_divider_sel && wb_we_i && !tip && ~wb_adr_i[1]) +       divider <= wb_dat_i; +    +   // Ctrl register +   always @(posedge wb_clk_i) +     if (wb_rst_i) +       ctrl <= {`SPI_CTRL_BIT_NB{1'b0}}; +     else if(spi_ctrl_sel && wb_we_i && !tip && ~wb_adr_i[1]) +       begin +          if (wb_sel_i[0]) +            ctrl[7:0] <= wb_dat_i[7:0] | {7'b0, ctrl[0]}; +          if (wb_sel_i[1]) +            ctrl[`SPI_CTRL_BIT_NB-1:8] <= wb_dat_i[`SPI_CTRL_BIT_NB-1:8]; +       end +     else if(tip && last_bit && pos_edge) +       ctrl[`SPI_CTRL_GO] <= 1'b0; +    +   assign rx_negedge = ctrl[`SPI_CTRL_RX_NEGEDGE]; +   assign tx_negedge = ctrl[`SPI_CTRL_TX_NEGEDGE]; +   assign go         = ctrl[`SPI_CTRL_GO]; +   assign char_len   = ctrl[`SPI_CTRL_CHAR_LEN]; +   assign lsb        = ctrl[`SPI_CTRL_LSB]; +   assign ie         = ctrl[`SPI_CTRL_IE]; +   assign ass        = ctrl[`SPI_CTRL_ASS]; +    +   // Slave select register +   always @(posedge wb_clk_i) +     if (wb_rst_i) +       ss <= 16'b0; +     else if(spi_ss_sel && wb_we_i && !tip & ~wb_adr_i[1]) +       begin +          if (wb_sel_i[0]) +            ss[7:0] <= wb_dat_i[7:0]; +          if (wb_sel_i[1]) +            ss[15:8] <= wb_dat_i[15:8]; +       end +    +   assign ss_pad_o = ~((ss & {16{tip & ass}}) | (ss & {16{!ass}})); +    +   spi_clgen clgen (.clk_in(wb_clk_i), .rst(wb_rst_i), .go(go), .enable(tip), .last_clk(last_bit), +                    .divider(divider[`SPI_DIVIDER_LEN-1:0]), .clk_out(sclk_pad_o), .pos_edge(pos_edge),  +                    .neg_edge(neg_edge)); + +   wire [3:0] new_sels = { (wb_adr_i[1] & wb_sel_i[1]), (wb_adr_i[1] & wb_sel_i[0]),  +			   (~wb_adr_i[1] & wb_sel_i[1]), (~wb_adr_i[1] & wb_sel_i[0]) }; +    +    +   spi_shift shift (.clk(wb_clk_i), .rst(wb_rst_i), .len(char_len[`SPI_CHAR_LEN_BITS-1:0]), +                    .latch(spi_tx_sel[3:0] & {4{wb_we_i}}), .byte_sel(new_sels), .lsb(lsb),  +                    .go(go), .pos_edge(pos_edge), .neg_edge(neg_edge),  +                    .rx_negedge(rx_negedge), .tx_negedge(tx_negedge), +                    .tip(tip), .last(last_bit),  +                    .p_in({wb_dat_i,wb_dat_i}), .p_out(rx),  +                    .s_clk(sclk_pad_o), .s_in(miso_pad_i), .s_out(mosi_pad_o)); + +endmodule // spi_top16 diff --git a/fpga/usrp2/opencores/spi/rtl/verilog/timescale.v b/fpga/usrp2/opencores/spi/rtl/verilog/timescale.v deleted file mode 100644 index 60d4ecbd1..000000000 --- a/fpga/usrp2/opencores/spi/rtl/verilog/timescale.v +++ /dev/null @@ -1,2 +0,0 @@ -`timescale 1ns / 10ps - diff --git a/fpga/usrp2/top/.gitignore b/fpga/usrp2/top/.gitignore index bf1b77066..0d90f1698 100644 --- a/fpga/usrp2/top/.gitignore +++ b/fpga/usrp2/top/.gitignore @@ -1 +1,2 @@  /*.sav +build* diff --git a/fpga/usrp2/top/u1e/.gitignore b/fpga/usrp2/top/u1e/.gitignore new file mode 100644 index 000000000..8d872713e --- /dev/null +++ b/fpga/usrp2/top/u1e/.gitignore @@ -0,0 +1,6 @@ +*~ +build +*.log +*.cmd +tb_u1e +*.lxt diff --git a/fpga/usrp2/top/u1e/Makefile b/fpga/usrp2/top/u1e/Makefile new file mode 100644 index 000000000..3cb9fd8f3 --- /dev/null +++ b/fpga/usrp2/top/u1e/Makefile @@ -0,0 +1,101 @@ +# +# Copyright 2008 Ettus Research LLC +# + +################################################## +# Project Setup +################################################## +TOP_MODULE = u1e +BUILD_DIR = $(abspath build$(ISE)) + +################################################## +# Include other makefiles +################################################## + +include ../Makefile.common +include ../../fifo/Makefile.srcs +include ../../control_lib/Makefile.srcs +include ../../sdr_lib/Makefile.srcs +include ../../serdes/Makefile.srcs +include ../../simple_gemac/Makefile.srcs +include ../../timing/Makefile.srcs +include ../../opencores/Makefile.srcs +include ../../vrt/Makefile.srcs +include ../../udp/Makefile.srcs +include ../../coregen/Makefile.srcs +include ../../extram/Makefile.srcs +include ../../gpmc/Makefile.srcs + +################################################## +# Project Properties +################################################## +export PROJECT_PROPERTIES := \ +family "Spartan-3A DSP" \ +device xc3sd1800a \ +package cs484 \ +speed -4 \ +top_level_module_type "HDL" \ +synthesis_tool "XST (VHDL/Verilog)" \ +simulator "ISE Simulator (VHDL/Verilog)" \ +"Preferred Language" "Verilog" \ +"Enable Message Filtering" FALSE \ +"Display Incremental Messages" FALSE  + +################################################## +# Sources +################################################## +TOP_SRCS = \ +u1e_core.v \ +u1e.v \ +u1e.ucf \ +timing.ucf + +SOURCES = $(abspath $(TOP_SRCS)) $(FIFO_SRCS) \ +$(CONTROL_LIB_SRCS) $(SDR_LIB_SRCS) $(SERDES_SRCS) \ +$(SIMPLE_GEMAC_SRCS) $(TIMING_SRCS) $(OPENCORES_SRCS) \ +$(VRT_SRCS) $(UDP_SRCS) $(COREGEN_SRCS) $(EXTRAM_SRCS) \ +$(GPMC_SRCS) + +################################################## +# Process Properties +################################################## +SYNTHESIZE_PROPERTIES = \ +"Number of Clock Buffers" 8 \ +"Pack I/O Registers into IOBs" Yes \ +"Optimization Effort" High \ +"Optimize Instantiated Primitives" TRUE \ +"Register Balancing" Yes \ +"Use Clock Enable" Auto \ +"Use Synchronous Reset" Auto \ +"Use Synchronous Set" Auto + +TRANSLATE_PROPERTIES = \ +"Macro Search Path" "$(shell pwd)/../../coregen/" + +MAP_PROPERTIES = \ +"Allow Logic Optimization Across Hierarchy" TRUE \ +"Map to Input Functions" 4 \ +"Optimization Strategy (Cover Mode)" Speed \ +"Pack I/O Registers/Latches into IOBs" "For Inputs and Outputs" \ +"Perform Timing-Driven Packing and Placement" TRUE \ +"Map Effort Level" High \ +"Extra Effort" Normal \ +"Combinatorial Logic Optimization" TRUE \ +"Register Duplication" TRUE + +PLACE_ROUTE_PROPERTIES = \ +"Place & Route Effort Level (Overall)" High  + +STATIC_TIMING_PROPERTIES = \ +"Number of Paths in Error/Verbose Report" 10 \ +"Report Type" "Error Report" + +GEN_PROG_FILE_PROPERTIES = \ +"Configuration Rate" 6 \ +"Create Binary Configuration File" TRUE \ +"Done (Output Events)" 5 \ +"Enable Bitstream Compression" TRUE \ +"Enable Outputs (Output Events)" 6 \ +"Unused IOB Pins" "Pull Up" + +SIM_MODEL_PROPERTIES = "" diff --git a/fpga/usrp2/top/u1e/README b/fpga/usrp2/top/u1e/README new file mode 100644 index 000000000..14c7a4955 --- /dev/null +++ b/fpga/usrp2/top/u1e/README @@ -0,0 +1,4 @@ + +make clean +make sim +./tb_u1e -lxt2 diff --git a/fpga/usrp2/top/u1e/cmdfile b/fpga/usrp2/top/u1e/cmdfile new file mode 100644 index 000000000..291c723b8 --- /dev/null +++ b/fpga/usrp2/top/u1e/cmdfile @@ -0,0 +1,20 @@ + +# My stuff +-y . +-y ../../control_lib +-y ../../control_lib/newfifo +-y ../../sdr_lib +-y ../../timing +-y ../../coregen +-y ../../gpmc + +# Models +-y ../../models +-y /opt/Xilinx/10.1/ISE/verilog/src/unisims + +# Open Cores +-y ../../opencores/spi/rtl/verilog ++incdir+../../opencores/spi/rtl/verilog +-y ../../opencores/i2c/rtl/verilog ++incdir+../../opencores/i2c/rtl/verilog + diff --git a/fpga/usrp2/top/u1e/make.sim b/fpga/usrp2/top/u1e/make.sim new file mode 100644 index 000000000..1c163884c --- /dev/null +++ b/fpga/usrp2/top/u1e/make.sim @@ -0,0 +1,7 @@ +all: sim + +sim:	 +	iverilog -Wimplicit -Wportbind -c cmdfile tb_u1e.v -o tb_u1e + +clean: +	rm -f tb_u1e *.vcd *.lxt a.out diff --git a/fpga/usrp2/top/u1e/tb_u1e.v b/fpga/usrp2/top/u1e/tb_u1e.v new file mode 100644 index 000000000..5fc8134fb --- /dev/null +++ b/fpga/usrp2/top/u1e/tb_u1e.v @@ -0,0 +1,41 @@ +`timescale 1ps / 1ps +////////////////////////////////////////////////////////////////////////////////// + +module tb_u1e(); +    +   wire [2:0] debug_led; +   wire [31:0] debug; +   wire [1:0] debug_clk; + +   xlnx_glbl glbl (.GSR(),.GTS()); + +   initial begin +      $dumpfile("tb_u1e.lxt"); +      $dumpvars(0,tb_u1e); +   end +     +   // GPMC +   wire       EM_CLK, EM_WAIT0, EM_NCS4, EM_NCS6, EM_NWE, EM_NOE; +   wire [15:0] EM_D; +   wire [10:1] EM_A; +   wire [1:0]  EM_NBE; +    +   reg  clk_fpga = 0, rst_fpga = 1; +   always #15625 clk_fpga = ~clk_fpga; + +   initial #200000 +     @(posedge clk_fpga) +       rst_fpga <= 0; +    +   u1e_core u1e_core(.clk_fpga(clk_fpga), .rst_fpga(rst_fpga),  +		     .debug_led(debug_led), .debug(debug), .debug_clk(debug_clk), +		     .EM_CLK(EM_CLK), .EM_D(EM_D), .EM_A(EM_A), .EM_NBE(EM_NBE), +		     .EM_WAIT0(EM_WAIT0), .EM_NCS4(EM_NCS4), .EM_NCS6(EM_NCS6),  +		     .EM_NWE(EM_NWE), .EM_NOE(EM_NOE) ); + +   gpmc_model_async gpmc_model_async +     (.EM_CLK(EM_CLK), .EM_D(EM_D), .EM_A(EM_A), .EM_NBE(EM_NBE), +      .EM_WAIT0(EM_WAIT0), .EM_NCS4(EM_NCS4), .EM_NCS6(EM_NCS6),  +      .EM_NWE(EM_NWE), .EM_NOE(EM_NOE) ); +    +endmodule // tb_u1e diff --git a/fpga/usrp2/top/u1e/timing.ucf b/fpga/usrp2/top/u1e/timing.ucf new file mode 100644 index 000000000..8df28c9d3 --- /dev/null +++ b/fpga/usrp2/top/u1e/timing.ucf @@ -0,0 +1,13 @@ + +NET "CLK_FPGA_P" TNM_NET = "CLK_FPGA_P"; +TIMESPEC "TS_clk_fpga_p" = PERIOD "CLK_FPGA_P" 15625 ps HIGH 50 %; + + + + +#NET "adc_a<*>" TNM_NET = ADC_DATA_GRP; +#NET "adc_b<*>" TNM_NET = ADC_DATA_GRP; +#TIMEGRP "ADC_DATA_GRP" OFFSET = IN 1 ns VALID 5 ns BEFORE "clk_fpga_p" RISING; + +#NET "adc_a<*>" OFFSET = IN 1 ns VALID 5 ns BEFORE "clk_fpga_p" RISING; +#NET "adc_b<*>" OFFSET = IN 1 ns VALID 5 ns BEFORE "clk_fpga_p" RISING; diff --git a/fpga/usrp2/top/u1e/u1e.ucf b/fpga/usrp2/top/u1e/u1e.ucf new file mode 100644 index 000000000..0c487a601 --- /dev/null +++ b/fpga/usrp2/top/u1e/u1e.ucf @@ -0,0 +1,259 @@ + +NET "CLK_FPGA_P"  LOC = "Y11"  ; +NET "CLK_FPGA_N"  LOC = "Y10"  ; + +## GPMC +NET "EM_D<15>"  LOC = "D13"  ; +NET "EM_D<14>"  LOC = "D15"  ; +NET "EM_D<13>"  LOC = "C16"  ; +NET "EM_D<12>"  LOC = "B20"  ; +NET "EM_D<11>"  LOC = "A19"  ; +NET "EM_D<10>"  LOC = "A17"  ; +NET "EM_D<9>"  LOC = "E15"  ; +NET "EM_D<8>"  LOC = "F15"  ; +NET "EM_D<7>"  LOC = "E16"  ; +NET "EM_D<6>"  LOC = "F16"  ; +NET "EM_D<5>"  LOC = "B17"  ; +NET "EM_D<4>"  LOC = "C17"  ; +NET "EM_D<3>"  LOC = "B19"  ; +NET "EM_D<2>"  LOC = "D19"  ; +NET "EM_D<1>"  LOC = "C19"  ; +NET "EM_D<0>"  LOC = "A20"  ; + +NET "EM_A<10>"  LOC = "C14"  ; +NET "EM_A<9>"  LOC = "C10"  ; +NET "EM_A<8>"  LOC = "C5"  ; +NET "EM_A<7>"  LOC = "A18"  ; +NET "EM_A<6>"  LOC = "A15"  ; +NET "EM_A<5>"  LOC = "A12"  ; +NET "EM_A<4>"  LOC = "A10"  ; +NET "EM_A<3>"  LOC = "E7"  ; +NET "EM_A<2>"  LOC = "A7"  ; +NET "EM_A<1>"  LOC = "C15"  ; + +NET "EM_NCS6"  LOC = "E17"  ; +NET "EM_NCS5"  LOC = "E10"  ; +NET "EM_NCS4"  LOC = "E6"  ; +#NET "EM_NCS1"  LOC = "D18"  ; +#NET "EM_NCS0"  LOC = "D17"  ; + +NET "EM_CLK"  LOC = "F11"  ; +NET "EM_WAIT0"  LOC = "F14"  ; +NET "EM_NBE<1>"  LOC = "D14"  ; +NET "EM_NBE<0>"  LOC = "A13"  ; +NET "EM_NWE"  LOC = "B13"  ; +NET "EM_NOE"  LOC = "A14"  ; +#NET "EM_NADV_ALE"  LOC = "B15"  ; +#NET "EM_NWP"  LOC = "F13"  ; + +## Overo GPIO +NET "overo_gpio0"  LOC = "F9"  ;  # MISC GPIO for debug +NET "overo_gpio14"  LOC = "C4"  ;  # MISC GPIO for debug +NET "overo_gpio21"  LOC = "D5"  ;  # MISC GPIO for debug +NET "overo_gpio22"  LOC = "A3"  ;  # MISC GPIO for debug +NET "overo_gpio23"  LOC = "B3"  ;  # MISC GPIO for debug +NET "overo_gpio64"  LOC = "A4"  ;  # MISC GPIO for debug +NET "overo_gpio65"  LOC = "F8"  ;  # MISC GPIO for debug + +NET "overo_gpio127"  LOC = "C8"  ;  # Changed name to gpio10 +NET "overo_gpio128"  LOC = "G8"  ;  # Changed name to gpio186 + +NET "overo_gpio144"  LOC = "A5"  ;  # tx_have_space +NET "overo_gpio145"  LOC = "C7"  ;  # tx_underrun +NET "overo_gpio146"  LOC = "A6"  ;  # rx_have_data +NET "overo_gpio147"  LOC = "B6"  ;  # rx_overrun +NET "overo_gpio163"  LOC = "D7"  ;  # MISC GPIO for debug +NET "overo_gpio170"  LOC = "E8"  ;  # MISC GPIO for debug +NET "overo_gpio176"  LOC = "B4"  ;  # MISC GPIO for debug + +## Overo UART +#NET "overo_txd1"  LOC = "C6"  ; +#NET "overo_rxd1"  LOC = "D6"  ; + +## FTDI UART to USB converter +NET "FPGA_TXD"  LOC = "G19"  ; +NET "FPGA_RXD"  LOC = "F20"  ; + +#NET "SYSEN"  LOC = "C11"  ; + +## I2C +NET "db_scl"  LOC = "F19"  ; +NET "db_sda"  LOC = "F18"  ; + +## SPI +### DBoard SPI +NET "db_sclk_rx"  LOC = "D21"  ; +NET "db_miso_rx"  LOC = "D22"  ; +NET "db_mosi_rx"  LOC = "D20"  ; +NET "db_sen_rx"  LOC = "E19"  ; +NET "db_sclk_tx"  LOC = "F21"  ; +NET "db_miso_tx"  LOC = "E20"  ; +NET "db_mosi_tx"  LOC = "G17"  ; +NET "db_sen_tx"  LOC = "G18"  ; + +### AD9862 SPI and aux SPI Interfaces +#NET "aux_sdi_codec"  LOC = "G3"  ; +#NET "aux_sdo_codec"  LOC = "F3"  ; +#NET "aux_sclk_codec"  LOC = "C1"  ; +NET "sen_codec"  LOC = "F5"  |IOSTANDARD = LVCMOS33; +NET "mosi_codec"  LOC = "F4"  |IOSTANDARD = LVCMOS33; +NET "miso_codec"  LOC = "H4"  ; +NET "sclk_codec"  LOC = "H3"  |IOSTANDARD = LVCMOS33; + +### Clock Gen SPI +NET "cgen_miso"  LOC = "F22"  ; +NET "cgen_mosi"  LOC = "E22"  ; +NET "cgen_sclk"  LOC = "J19"  ; +NET "cgen_sen_b"  LOC = "H20"  ; + +## Clock gen control +NET "cgen_st_status"  LOC = "P20"  ; +NET "cgen_st_ld"  LOC = "R17"  ; +NET "cgen_st_refmon"  LOC = "P17"  ; +NET "cgen_sync_b"  LOC = "U18"  ; +NET "cgen_ref_sel"  LOC = "U19"  ; + +## Debug pins +NET "debug_led<3>"  LOC = "Y15"  ; +NET "debug_led<2>"  LOC = "K16"  ; +NET "debug_led<1>"  LOC = "J17"  ; +NET "debug_led<0>"  LOC = "H22"  ; +NET "debug<0>"  LOC = "G22"  ; +NET "debug<1>"  LOC = "H17"  ; +NET "debug<2>"  LOC = "H18"  ; +NET "debug<3>"  LOC = "K20"  ; +NET "debug<4>"  LOC = "J20"  ; +NET "debug<5>"  LOC = "K19"  ; +NET "debug<6>"  LOC = "K18"  ; +NET "debug<7>"  LOC = "L22"  ; +NET "debug<8>"  LOC = "K22"  ; +NET "debug<9>"  LOC = "N22"  ; +NET "debug<10>"  LOC = "M22"  ; +NET "debug<11>"  LOC = "N20"  ; +NET "debug<12>"  LOC = "N19"  ; +NET "debug<13>"  LOC = "R22"  ; +NET "debug<14>"  LOC = "P22"  ; +NET "debug<15>"  LOC = "N17"  ; +NET "debug<16>"  LOC = "P16"  ; +NET "debug<17>"  LOC = "U22"  ; +NET "debug<18>"  LOC = "P19"  ; +NET "debug<19>"  LOC = "R18"  ; +NET "debug<20>"  LOC = "U20"  ; +NET "debug<21>"  LOC = "T20"  ; +NET "debug<22>"  LOC = "R19"  ; +NET "debug<23>"  LOC = "R20"  ; +NET "debug<24>"  LOC = "W22"  ; +NET "debug<25>"  LOC = "Y22"  ; +NET "debug<26>"  LOC = "T18"  ; +NET "debug<27>"  LOC = "T17"  ; +NET "debug<28>"  LOC = "W19"  ; +NET "debug<29>"  LOC = "V20"  ; +NET "debug<30>"  LOC = "Y21"  ; +NET "debug<31>"  LOC = "AA22"  ; +NET "debug_clk<0>"  LOC = "N18"  ; +NET "debug_clk<1>"  LOC = "M17"  ; + +NET "debug_pb"  LOC = "C22"  ; + +#NET "reset_codec"  LOC = "C2"  ; + +NET "RXSYNC"  LOC = "F2"  ; +NET "DB<11>"  LOC = "G6"  ; +NET "DB<10>"  LOC = "G5"  ; +NET "DB<9>"  LOC = "E4"  ; +NET "DB<8>"  LOC = "E3"  ; +NET "DB<7>"  LOC = "H6"  ; +NET "DB<6>"  LOC = "H5"  ; +NET "DB<5>"  LOC = "H1"  ; +NET "DB<4>"  LOC = "G1"  ; +NET "DB<3>"  LOC = "K5"  ; +NET "DB<2>"  LOC = "K4"  ; +NET "DB<1>"  LOC = "H2"  ; +NET "DB<0>"  LOC = "L5"  ; + +NET "DA<11>"  LOC = "K6"  ; +NET "DA<10>"  LOC = "K3"  ; +NET "DA<9>"  LOC = "K2"  ; +NET "DA<8>"  LOC = "N1"  ; +NET "DA<7>"  LOC = "N5"  ; +NET "DA<6>"  LOC = "N6"  ; +NET "DA<5>"  LOC = "P2"  ; +NET "DA<4>"  LOC = "P1"  ; +NET "DA<3>"  LOC = "R6"  ; +NET "DA<2>"  LOC = "P6"  ; +NET "DA<1>"  LOC = "R1"  ; +NET "DA<0>"  LOC = "R2"  ; + +NET "TX<13>"  LOC = "T6"  |IOSTANDARD = LVCMOS33  |DRIVE = 12  |SLEW = FAST ; +NET "TX<12>"  LOC = "U1"  |IOSTANDARD = LVCMOS33  |DRIVE = 12  |SLEW = FAST ; +NET "TX<11>"  LOC = "T1"  |IOSTANDARD = LVCMOS33  |DRIVE = 12  |SLEW = FAST ; +NET "TX<10>"  LOC = "R5"  |IOSTANDARD = LVCMOS33  |DRIVE = 12  |SLEW = FAST ; +NET "TX<9>"  LOC = "V1"  |IOSTANDARD = LVCMOS33  |DRIVE = 12  |SLEW = FAST ; +NET "TX<8>"  LOC = "U2"  |IOSTANDARD = LVCMOS33  |DRIVE = 12  |SLEW = FAST ; +NET "TX<7>"  LOC = "T4"  |IOSTANDARD = LVCMOS33  |DRIVE = 12  |SLEW = FAST ; +NET "TX<6>"  LOC = "R3"  |IOSTANDARD = LVCMOS33  |DRIVE = 12  |SLEW = FAST ; +NET "TX<5>"  LOC = "W1"  |IOSTANDARD = LVCMOS33  |DRIVE = 12  |SLEW = FAST ; +NET "TX<4>"  LOC = "Y1"  |IOSTANDARD = LVCMOS33  |DRIVE = 12  |SLEW = FAST ; +NET "TX<3>"  LOC = "V3"  |IOSTANDARD = LVCMOS33  |DRIVE = 12  |SLEW = FAST ; +NET "TX<2>"  LOC = "V4"  |IOSTANDARD = LVCMOS33  |DRIVE = 12  |SLEW = FAST ; +NET "TX<1>"  LOC = "W2"  |IOSTANDARD = LVCMOS33  |DRIVE = 12  |SLEW = FAST ; +NET "TX<0>"  LOC = "W3"  |IOSTANDARD = LVCMOS33  |DRIVE = 12  |SLEW = FAST ; +NET "TXSYNC"  LOC = "U5"   |IOSTANDARD = LVCMOS33  |DRIVE = 12  |SLEW = FAST ; +NET "TXBLANK"  LOC = "U4"  |IOSTANDARD = LVCMOS33  |DRIVE = 12  |SLEW = FAST ; + +NET "PPS_IN"  LOC = "M5"  ; + +NET "io_tx<0>"  LOC = "AB20"  ; +NET "io_tx<1>"  LOC = "Y17"  ; +NET "io_tx<2>"  LOC = "Y16"  ; +NET "io_tx<3>"  LOC = "U16"  ; +NET "io_tx<4>"  LOC = "V16"  ; +NET "io_tx<5>"  LOC = "AB19"  ; +NET "io_tx<6>"  LOC = "AA19"  ; +NET "io_tx<7>"  LOC = "U14"  ; +NET "io_tx<8>"  LOC = "U15"  ; +NET "io_tx<9>"  LOC = "AB17"  ; +NET "io_tx<10>"  LOC = "AB18"  ; +NET "io_tx<11>"  LOC = "Y13"  ; +NET "io_tx<12>"  LOC = "W14"  ; +NET "io_tx<13>"  LOC = "U13"  ; +NET "io_tx<14>"  LOC = "AA15"  ; +NET "io_tx<15>"  LOC = "AB14"  ; + +NET "io_rx<0>"  LOC = "Y8"  ; +NET "io_rx<1>"  LOC = "Y9"  ; +NET "io_rx<2>"  LOC = "V7"  ; +NET "io_rx<3>"  LOC = "U8"  ; +NET "io_rx<4>"  LOC = "V10"  ; +NET "io_rx<5>"  LOC = "U9"  ; +NET "io_rx<6>"  LOC = "AB7"  ; +NET "io_rx<7>"  LOC = "AA8"  ; +NET "io_rx<8>"  LOC = "W8"  ; +NET "io_rx<9>"  LOC = "V8"  ; +NET "io_rx<10>"  LOC = "AB5"  ; +NET "io_rx<11>"  LOC = "AB6"  ; +NET "io_rx<12>"  LOC = "AB4"  ; +NET "io_rx<13>"  LOC = "AA4"  ; +NET "io_rx<14>"  LOC = "W5"  ; +NET "io_rx<15>"  LOC = "Y4"  ; + +#NET "CLKOUT2_CODEC"  LOC = "U12"  ; +#NET "CLKOUT1_CODEC"  LOC = "V12"  ; + +## FPGA Config Pins +#NET "fpga_cfg_prog_b"  LOC = "A2"  ; +#NET "fpga_cfg_done"  LOC = "AB21"  ; +#NET "fpga_cfg_din"  LOC = "W17"  ; +#NET "fpga_cfg_cclk"  LOC = "V17"  ; +#NET "fpga_cfg_init_b"  LOC = "W15"  ; + +## Unused +#NET "unnamed_net53"  LOC = "B1"  ;  # TMS +#NET "unnamed_net52"  LOC = "B22"  ; # TDO +#NET "unnamed_net51"  LOC = "D2"  ;  # TDI +#NET "unnamed_net50"  LOC = "A21"  ; # TCK +#NET "unnamed_net59"  LOC = "F7"  ;  # PUDC_B +#NET "unnamed_net58"  LOC = "V6"  ;  # M2 +#NET "unnamed_net57"  LOC = "AA3"  ; # M1 +#NET "unnamed_net56"  LOC = "AB3"  ; # M0 +#NET "GND"  LOC = "V19"  ;  # Suspend, unused diff --git a/fpga/usrp2/top/u1e/u1e.v b/fpga/usrp2/top/u1e/u1e.v new file mode 100644 index 000000000..445b14a03 --- /dev/null +++ b/fpga/usrp2/top/u1e/u1e.v @@ -0,0 +1,141 @@ +`timescale 1ns / 1ps +////////////////////////////////////////////////////////////////////////////////// + +module u1e +  (input CLK_FPGA_P, input CLK_FPGA_N,  // Diff +   output [3:0] debug_led, output [31:0] debug, output [1:0] debug_clk, +   input debug_pb, output FPGA_TXD, input FPGA_RXD, + +   // GPMC +   input EM_CLK, inout [15:0] EM_D, input [10:1] EM_A, input [1:0] EM_NBE, +   input EM_WAIT0, input EM_NCS4, input EM_NCS5, input EM_NCS6, +   input EM_NWE, input EM_NOE, + +   inout db_sda, inout db_scl, // I2C + +   output db_sclk_tx, output db_sen_tx, output db_mosi_tx, input db_miso_tx,   // DB TX SPI +   output db_sclk_rx, output db_sen_rx, output db_mosi_rx, input db_miso_rx,   // DB TX SPI +   output sclk_codec, output sen_codec, output mosi_codec, input miso_codec,   // AD9862 main SPI +   output cgen_sclk, output cgen_sen_b, output cgen_mosi, input cgen_miso,     // Clock gen SPI + +   input cgen_st_status, input cgen_st_ld, input cgen_st_refmon, output cgen_sync_b, output cgen_ref_sel, +    +   output overo_gpio144, output overo_gpio145, output overo_gpio146, output overo_gpio147,  // Fifo controls +   input overo_gpio0, input overo_gpio14, input overo_gpio21, input overo_gpio22,  // Misc GPIO +   input overo_gpio23, input overo_gpio64, input overo_gpio65, input overo_gpio127, // Misc GPIO +   input overo_gpio128, input overo_gpio163, input overo_gpio170, input overo_gpio176, // Misc GPIO +    +   inout [15:0] io_tx, inout [15:0] io_rx, + +   output [13:0] TX, output TXSYNC, output TXBLANK, +   input [11:0] DA, input [11:0] DB, input RXSYNC, +   +   input PPS_IN +   ); + +   // ///////////////////////////////////////////////////////////////////////// +   // Clocking +   wire  clk_fpga, clk_fpga_in; +    +   IBUFGDS #(.IOSTANDARD("LVDS_33"), .DIFF_TERM("TRUE"))  +   clk_fpga_pin (.O(clk_fpga_in),.I(CLK_FPGA_P),.IB(CLK_FPGA_N)); + +   wire  clk_2x, dcm_rst, dcm_locked, clk_fb; +   DCM #(.CLK_FEEDBACK ( "1X" ), +	 .CLKDV_DIVIDE ( 2 ), +	 .CLKFX_DIVIDE ( 2 ), +	 .CLKFX_MULTIPLY ( 2 ), +	 .CLKIN_DIVIDE_BY_2 ( "FALSE" ), +	 .CLKIN_PERIOD ( 15.625 ), +	 .CLKOUT_PHASE_SHIFT ( "NONE" ), +	 .DESKEW_ADJUST ( "SYSTEM_SYNCHRONOUS" ), +	 .DFS_FREQUENCY_MODE ( "LOW" ), +	 .DLL_FREQUENCY_MODE ( "LOW" ), +	 .DUTY_CYCLE_CORRECTION ( "TRUE" ), +	 .FACTORY_JF ( 16'h8080 ), +	 .PHASE_SHIFT ( 0 ), +	 .STARTUP_WAIT ( "FALSE" )) +   clk_doubler (.CLKFB(clk_fb), .CLKIN(clk_fpga_in), .RST(dcm_rst),  +                .DSSEN(0), .PSCLK(0), .PSEN(0), .PSINCDEC(0), .PSDONE(),  +		.CLKDV(), .CLKFX(), .CLKFX180(),  +                .CLK2X(), .CLK2X180(),  +                .CLK0(clk_fb), .CLK90(clk_fpga), .CLK180(), .CLK270(),  +                .LOCKED(dcm_locked), .STATUS()); +    +   // ///////////////////////////////////////////////////////////////////////// +   // SPI +   wire  mosi, sclk, miso; +   assign { db_sclk_tx, db_mosi_tx } = ~db_sen_tx ? {sclk,mosi} : 2'b0; +   assign { db_sclk_rx, db_mosi_rx } = ~db_sen_rx ? {sclk,mosi} : 2'b0; +   assign { sclk_codec, mosi_codec } = ~sen_codec ? {sclk,mosi} : 2'b0; +   assign { cgen_sclk, cgen_mosi } = ~cgen_sen_b ? {sclk,mosi} : 2'b0; +   assign miso = (~db_sen_tx & db_miso_tx) | (~db_sen_rx & db_miso_rx) | +		 (~sen_codec & miso_codec) | (~cgen_sen_b & cgen_miso); + +   // ///////////////////////////////////////////////////////////////////////// +   // TX DAC -- handle the interleaved data bus to DAC, with clock doubling DLL + +   assign TXBLANK = 0; +   wire [13:0] tx_i, tx_q; + +   reg[13:0] delay_q; +   always @(posedge clk_fpga) +     delay_q <= tx_q; +    +   genvar i; +   generate +      for(i=0;i<14;i=i+1) +	begin : gen_dacout +	   ODDR2 #(.DDR_ALIGNMENT("NONE"), // Sets output alignment to "NONE", "C0" or "C1"  +		   .INIT(1'b0),            // Sets initial state of the Q output to 1'b0 or 1'b1 +		   .SRTYPE("SYNC"))        // Specifies "SYNC" or "ASYNC" set/reset +	   ODDR2_inst (.Q(TX[i]),      // 1-bit DDR output data +		       .C0(clk_fpga),  // 1-bit clock input +		       .C1(~clk_fpga), // 1-bit clock input +		       .CE(1'b1),      // 1-bit clock enable input +		       .D0(tx_i[i]),   // 1-bit data input (associated with C0) +		       .D1(delay_q[i]),   // 1-bit data input (associated with C1) +		       .R(1'b0),       // 1-bit reset input +		       .S(1'b0));      // 1-bit set input +	end // block: gen_dacout +      endgenerate +   ODDR2 #(.DDR_ALIGNMENT("NONE"), // Sets output alignment to "NONE", "C0" or "C1"  +	   .INIT(1'b0),            // Sets initial state of the Q output to 1'b0 or 1'b1 +	   .SRTYPE("SYNC"))        // Specifies "SYNC" or "ASYNC" set/reset +   ODDR2_txsnc (.Q(TXSYNC),      // 1-bit DDR output data +		.C0(clk_fpga),  // 1-bit clock input +		.C1(~clk_fpga), // 1-bit clock input +		.CE(1'b1),      // 1-bit clock enable input +		.D0(1'b0),   // 1-bit data input (associated with C0) +		.D1(1'b1),   // 1-bit data input (associated with C1) +		.R(1'b0),       // 1-bit reset input +		.S(1'b0));      // 1-bit set input +    +   // ///////////////////////////////////////////////////////////////////////// +   // Main U1E Core +   u1e_core u1e_core(.clk_fpga(clk_fpga), .rst_fpga(~debug_pb), +		     .debug_led(debug_led), .debug(debug), .debug_clk(debug_clk), +		     .debug_txd(FPGA_TXD), .debug_rxd(FPGA_RXD), +		     .EM_CLK(EM_CLK), .EM_D(EM_D), .EM_A(EM_A), .EM_NBE(EM_NBE), +		     .EM_WAIT0(EM_WAIT0), .EM_NCS4(EM_NCS4), .EM_NCS5(EM_NCS5),  +		     .EM_NCS6(EM_NCS6), .EM_NWE(EM_NWE), .EM_NOE(EM_NOE), +		     .db_sda(db_sda), .db_scl(db_scl), +		     .sclk(sclk), .sen({cgen_sen_b,sen_codec,db_sen_tx,db_sen_rx}), .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), +		     .tx_have_space(overo_gpio144), .tx_underrun(overo_gpio145), +		     .rx_have_data(overo_gpio146), .rx_overrun(overo_gpio147), +		     .io_tx(io_tx), .io_rx(io_rx), +		     .tx_i(tx_i), .tx_q(tx_q),  +		     .rx_i(DA), .rx_q(DB), +		     .misc_gpio( {{overo_gpio128,overo_gpio163,overo_gpio170,overo_gpio176}, +				  {overo_gpio0,overo_gpio14,overo_gpio21,overo_gpio22}, +				  {overo_gpio23,overo_gpio64,overo_gpio65,overo_gpio127}}), +		     .pps_in(PPS_IN) ); + +   // ///////////////////////////////////////////////////////////////////////// +   // Local Debug +   // assign debug_clk = {clk_fpga, clk_2x }; +   // assign debug = { TXSYNC, TXBLANK, TX }; +    +endmodule // u1e diff --git a/fpga/usrp2/top/u1e/u1e_core.v b/fpga/usrp2/top/u1e/u1e_core.v new file mode 100644 index 000000000..619e44b8a --- /dev/null +++ b/fpga/usrp2/top/u1e/u1e_core.v @@ -0,0 +1,459 @@ + + +//`define LOOPBACK 1 +//`define TIMED 1 +`define DSP 1 + +module u1e_core +  (input clk_fpga, input rst_fpga, +   output [3:0] debug_led, output [31:0] debug, output [1:0] debug_clk, +   output debug_txd, input debug_rxd, +    +   // GPMC +   input EM_CLK, inout [15:0] EM_D, input [10:1] EM_A, input [1:0] EM_NBE, +   input EM_WAIT0, input EM_NCS4, input EM_NCS5, input EM_NCS6, +   input EM_NWE, input EM_NOE, +    +   inout db_sda, inout db_scl, +   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,    +   output tx_have_space, output tx_underrun, output rx_have_data, output rx_overrun, +   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 [11:0] misc_gpio, input pps_in +   ); + +   localparam TXFIFOSIZE = 13; +   localparam RXFIFOSIZE = 13; + +   localparam SR_RX_DSP = 0;     // 5 regs +   localparam SR_CLEAR_FIFO = 6; // 1 reg +   localparam SR_RX_CTRL = 8;    // 9 regs +   localparam SR_TX_DSP = 17;    // 5 regs +   localparam SR_TX_CTRL = 24;   // 2 regs +   localparam SR_TIME64 = 28;    // 4 regs + +   wire 	COMPAT_NUM = 8'd2; +    +   wire 	wb_clk = clk_fpga; +   wire 	wb_rst = rst_fpga; +    +   wire 	pps_int; +   wire [63:0] 	vita_time; +   reg [15:0] 	reg_leds, reg_cgen_ctrl, reg_test, xfer_rate; +    +   wire [7:0] 	set_addr; +   wire [31:0] 	set_data; +   wire 	set_stb; + +   wire [31:0] 	debug_vt; + +   // ///////////////////////////////////////////////////////////////////////////////////// +   // GPMC 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_gpmc; + +   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; +   reg [15:0] 	 tx_frame_len; +   wire [15:0] 	 rx_frame_len; +   wire [7:0] 	 rate; + +   wire 	 bus_error; + +   wire 	 clear_rx_int, clear_tx_int, clear_tx, clear_rx, do_clear; +    +   setting_reg #(.my_addr(SR_CLEAR_FIFO), .width(2)) sr_clear +     (.clk(wb_clk),.rst(wb_rst),.strobe(set_stb),.addr(set_addr), +      .in(set_data),.out({clear_tx_int,clear_rx_int}),.changed(do_clear)); +   assign clear_tx = clear_tx_int & do_clear; +   assign clear_rx = clear_rx_int & do_clear; +    +   gpmc_async #(.TXFIFOSIZE(TXFIFOSIZE), .RXFIFOSIZE(RXFIFOSIZE)) +   gpmc (.arst(wb_rst), +	 .EM_CLK(EM_CLK), .EM_D(EM_D), .EM_A(EM_A), .EM_NBE(EM_NBE), +	 .EM_WAIT0(EM_WAIT0), .EM_NCS4(EM_NCS4), .EM_NCS6(EM_NCS6), .EM_NWE(EM_NWE),  +	 .EM_NOE(EM_NOE), +	  +	 .rx_have_data(rx_have_data), .tx_have_space(tx_have_space), +	 .bus_error(bus_error), .bus_reset(0), +	  +	 .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), +	  +	 .fifo_clk(wb_clk), .fifo_rst(wb_rst), .clear_tx(clear_tx), .clear_rx(clear_rx), +	 .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_frame_len(tx_frame_len), .rx_frame_len(rx_frame_len), +	 .debug(debug_gpmc)); + +   wire 	 rx_sof = rx_data[32]; +   wire 	 rx_eof = rx_data[33]; +   wire 	 rx_src_rdy_int, rx_dst_rdy_int, tx_src_rdy_int, tx_dst_rdy_int; +    +`ifdef LOOPBACK +   wire [7:0] 	 WHOAMI = 1; +    +   fifo_cascade #(.WIDTH(36), .SIZE(12)) loopback_fifo +     (.clk(wb_clk), .reset(wb_rst), .clear(clear_tx | clear_rx), +      .datain(tx_data), .src_rdy_i(tx_src_rdy), .dst_rdy_o(tx_dst_rdy), +      .dataout(rx_data), .src_rdy_o(rx_src_rdy), .dst_rdy_i(rx_dst_rdy)); + +   assign tx_underrun = 0; +   assign rx_overrun = 0; + +   wire 	 run_tx, run_rx, strobe_tx, strobe_rx; +`endif // LOOPBACK + +`ifdef TIMED +   wire [7:0] 	 WHOAMI = 2; +    +   // TX side +   wire 	 tx_enable; +    +   fifo_pacer tx_pacer +     (.clk(wb_clk), .reset(wb_rst), .rate(rate), .enable(tx_enable), +      .src1_rdy_i(tx_src_rdy), .dst1_rdy_o(tx_dst_rdy), +      .src2_rdy_o(tx_src_rdy_int), .dst2_rdy_i(tx_dst_rdy_int), +      .underrun(tx_underrun), .overrun()); +    +   packet_verifier32 pktver32 +     (.clk(wb_clk), .reset(wb_rst), .clear(clear_tx), +      .data_i(tx_data), .src_rdy_i(tx_src_rdy_int), .dst_rdy_o(tx_dst_rdy_int), +      .total(total), .crc_err(crc_err), .seq_err(seq_err), .len_err(len_err)); + +   // RX side +   wire 	 rx_enable; + +   packet_generator32 pktgen32 +     (.clk(wb_clk), .reset(wb_rst), .clear(clear_rx), +      .data_o(rx_data), .src_rdy_o(rx_src_rdy_int), .dst_rdy_i(rx_dst_rdy_int)); + +   fifo_pacer rx_pacer +     (.clk(wb_clk), .reset(wb_rst), .rate(rate), .enable(rx_enable), +      .src1_rdy_i(rx_src_rdy_int), .dst1_rdy_o(rx_dst_rdy_int), +      .src2_rdy_o(rx_src_rdy), .dst2_rdy_i(rx_dst_rdy), +      .underrun(), .overrun(rx_overrun)); +    +`endif //  `ifdef TIMED + +`ifdef DSP +   wire [7:0] 	 WHOAMI = 0; +    +   wire [31:0] 	 debug_rx_dsp, vrc_debug, vrf_debug; +    +   // ///////////////////////////////////////////////////////////////////////// +   // DSP RX +   wire [31:0] 	 sample_rx, sample_tx; +   wire 	 strobe_rx, strobe_tx; +   wire 	 rx1_dst_rdy, rx1_src_rdy; +   wire [99:0] 	 rx1_data; +   wire 	 run_rx; +   wire [35:0] 	 vita_rx_data; +   wire 	 vita_rx_src_rdy, vita_rx_dst_rdy; +       +   dsp_core_rx #(.BASE(SR_RX_DSP)) dsp_core_rx +     (.clk(wb_clk),.rst(wb_rst), +      .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), +      .adc_a({rx_i,2'b0}),.adc_ovf_a(0),.adc_b({rx_q,2'b0}),.adc_ovf_b(0), +      .sample(sample_rx), .run(run_rx), .strobe(strobe_rx), +      .debug(debug_rx_dsp) ); + +   vita_rx_control #(.BASE(SR_RX_CTRL), .WIDTH(32)) vita_rx_control +     (.clk(wb_clk), .reset(wb_rst), .clear(clear_rx), +      .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), +      .vita_time(vita_time), .overrun(rx_overrun), +      .sample(sample_rx), .run(run_rx), .strobe(strobe_rx), +      .sample_fifo_o(rx1_data), .sample_fifo_dst_rdy_i(rx1_dst_rdy), .sample_fifo_src_rdy_o(rx1_src_rdy), +      .debug_rx(vrc_debug)); + +   vita_rx_framer #(.BASE(SR_RX_CTRL), .MAXCHAN(1)) vita_rx_framer +     (.clk(wb_clk), .reset(wb_rst), .clear(clear_rx), +      .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), +      .sample_fifo_i(rx1_data), .sample_fifo_dst_rdy_o(rx1_dst_rdy), .sample_fifo_src_rdy_i(rx1_src_rdy), +      .data_o(vita_rx_data), .dst_rdy_i(vita_rx_dst_rdy), .src_rdy_o(vita_rx_src_rdy), +      .fifo_occupied(), .fifo_full(), .fifo_empty(), +      .debug_rx(vrf_debug) ); +    +   fifo36_mux #(.prio(0)) mux_err_stream +     (.clk(wb_clk), .reset(wb_rst), .clear(0), +      .data0_i(vita_rx_data), .src0_rdy_i(vita_rx_src_rdy), .dst0_rdy_o(vita_rx_dst_rdy), +      .data1_i(tx_err_data), .src1_rdy_i(tx_err_src_rdy), .dst1_rdy_o(tx_err_dst_rdy), +      .data_o(rx_data), .src_rdy_o(rx_src_rdy), .dst_rdy_i(rx_dst_rdy)); +    +   // /////////////////////////////////////////////////////////////////////////////////// +   // DSP TX + +   wire [15:0] 	 tx_i_int, tx_q_int; +   wire 	 run_tx; +    +   vita_tx_chain #(.BASE_CTRL(SR_TX_CTRL), .BASE_DSP(SR_TX_DSP),  +		   .REPORT_ERROR(1), .PROT_ENG_FLAGS(0))  +   vita_tx_chain +     (.clk(wb_clk), .reset(wb_rst), +      .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), +      .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), +      .dac_a(tx_i_int),.dac_b(tx_q_int), +      .underrun(underrun), .run(run_tx), +      .debug(debug_vt)); +    +   assign tx_i = tx_i_int[15:2]; +   assign tx_q = tx_q_int[15:2]; +    +`else // !`ifdef DSP +   // Dummy DSP signal generator for test purposes +   wire [23:0] 	 tx_i_int, tx_q_int; +   wire [23:0] 	 freq = {reg_test,8'd0}; +   reg [23:0] 	 phase; +    +   always @(posedge wb_clk) +     phase <= phase + freq; +    +   cordic_z24 #(.bitwidth(24)) tx_cordic +     (.clock(wb_clk), .reset(wb_rst), .enable(1), +      .xi(24'd2500000), .yi(24'd0), .zi(phase), .xo(tx_i_int), .yo(tx_q_int), .zo()); + +   assign tx_i = tx_i_int[23:10]; +   assign tx_q = tx_q_int[23:10]; +`endif // !`ifdef DSP +       +   // ///////////////////////////////////////////////////////////////////////////////////// +   // 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), .s1_addr(4'h1), .s1_mask(4'hF), +		.s2_addr(4'h2), .s2_mask(4'hF),	.s3_addr(4'h3), .s3_mask(4'hF), +		.s4_addr(4'h4), .s4_mask(4'hF),	.s5_addr(4'h5), .s5_mask(4'hF), +		.s6_addr(4'h6), .s6_mask(4'hF),	.s7_addr(4'h7), .s7_mask(4'hF), +		.s8_addr(4'h8), .s8_mask(4'hF),	.s9_addr(4'h9), .s9_mask(4'hF), +		.sa_addr(4'ha), .sa_mask(4'hF),	.sb_addr(4'hb), .sb_mask(4'hF), +		.sc_addr(4'hc), .sc_mask(4'hF),	.sd_addr(4'hd), .sd_mask(4'hF), +		.se_addr(4'he), .se_mask(4'hF),	.sf_addr(4'hf), .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 s7_ack = 0; +   assign s8_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_LEDS = 7'd0;         // out +   localparam REG_SWITCHES = 7'd2;     // in +   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 +   localparam REG_XFER_RATE = 7'd14;   // out +   localparam REG_COMPAT = 7'd16;      // in +    +   always @(posedge wb_clk) +     if(wb_rst) +       begin +	  reg_leds <= 0; +	  reg_cgen_ctrl <= 2'b11; +	  reg_test <= 0; +	  tx_frame_len <= 0; +	  xfer_rate <= 0; +       end +     else +       if(s0_cyc & s0_stb & s0_we)  +	 case(s0_adr[6:0]) +	   REG_LEDS : +	     reg_leds <= s0_dat_mosi; +	   REG_CGEN_CTRL : +	     reg_cgen_ctrl <= s0_dat_mosi; +	   REG_TEST : +	     reg_test <= s0_dat_mosi; +	   REG_TX_FRAMELEN : +	     tx_frame_len <= s0_dat_mosi; +	   REG_XFER_RATE : +	     xfer_rate <= s0_dat_mosi; +	 endcase // case (s0_adr[6:0]) + +   assign tx_enable = xfer_rate[15]; +   assign rx_enable = xfer_rate[14]; +   assign rate = xfer_rate[7:0]; +    +   assign { debug_led[3:0] } = {run_rx,run_tx,reg_leds[1:0]}; +   assign { cgen_sync_b, cgen_ref_sel } = reg_cgen_ctrl; +    +   assign s0_dat_miso = (s0_adr[6:0] == REG_LEDS) ? reg_leds :  +			(s0_adr[6:0] == REG_SWITCHES) ? { 16'd0 } : +			(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 : +			(s0_adr[6:0] == REG_RX_FRAMELEN) ? rx_frame_len : +			(s0_adr[6:0] == REG_COMPAT) ? { WHOAMI, COMPAT_NUM } : +			16'hBEEF; +    +   assign s0_ack = s0_stb & s0_cyc; + +   // ///////////////////////////////////////////////////////////////////////////////////// +   // Slave 1, UART +   //    depth of 3 is 128 entries, clkdiv of 278 gives 230.4k with a 64 MHz system clock +    +   simple_uart #(.TXDEPTH(3),.RXDEPTH(3), .CLKDIV_DEFAULT(278)) uart  +     (.clk_i(wb_clk),.rst_i(wb_rst), +      .we_i(s1_we),.stb_i(s1_stb),.cyc_i(s1_cyc),.ack_o(s1_ack), +      .adr_i(s1_adr[3:1]),.dat_i({16'd0,s1_dat_mosi}),.dat_o(s1_dat_miso), +      .rx_int_o(),.tx_int_o(), +      .tx_o(debug_txd),.rx_i(debug_rxd),.baud_o()); + +   // ///////////////////////////////////////////////////////////////////////////////////// +   // 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[4:2]),.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 -- Slave #4 + +   wire [31:0] 	atr_lines; +   wire [31:0] 	debug_gpio_0, debug_gpio_1; +    +   nsgpio16LE  +     nsgpio16LE(.clk_i(wb_clk),.rst_i(wb_rst), +		.cyc_i(s4_cyc),.stb_i(s4_stb),.adr_i(s4_adr[3:0]),.we_i(s4_we), +		.dat_i(s4_dat_mosi),.dat_o(s4_dat_miso),.ack_o(s4_ack), +		.atr(atr_lines),.debug_0(debug_gpio_0),.debug_1(debug_gpio_1), +		.gpio( {io_tx,io_rx} ) ); + +   // ///////////////////////////////////////////////////////////////////////// +   // Settings Bus -- Slave #5 + +   // only have 32 regs, 32 bits each with current setup... +   settings_bus_16LE #(.AWIDTH(11),.RWIDTH(11-4-2)) settings_bus_16LE +     (.wb_clk(wb_clk),.wb_rst(wb_rst),.wb_adr_i(s5_adr),.wb_dat_i(s5_dat_mosi), +      .wb_stb_i(s5_stb),.wb_we_i(s5_we),.wb_ack_o(s5_ack), +      .strobe(set_stb),.addr(set_addr),.data(set_data) ); +    +   // ///////////////////////////////////////////////////////////////////////// +   // ATR Controller -- Slave #6 + +   atr_controller16 atr_controller16 +     (.clk_i(wb_clk), .rst_i(wb_rst), +      .adr_i(s6_adr), .sel_i(s6_sel), .dat_i(s6_dat_mosi), .dat_o(s6_dat_miso), +      .we_i(s6_we), .stb_i(s6_stb), .cyc_i(s6_cyc), .ack_o(s6_ack), +      .run_rx(run_rx), .run_tx(run_tx), .ctrl_lines(atr_lines)); + + +   // ///////////////////////////////////////////////////////////////////////// +   // VITA Timing + +   time_64bit #(.TICKS_PER_SEC(32'd64000000),.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), .pps_int(pps_int)); +    +   // ///////////////////////////////////////////////////////////////////////////////////// +   // Debug circuitry + +   assign debug_clk = { EM_CLK, clk_fpga }; + +   assign debug = { { rx_have_data, tx_have_space, EM_NCS6, EM_NCS5, EM_NCS4, EM_NWE, EM_NOE, rx_overrun }, +		    { tx_src_rdy, tx_src_rdy_int, tx_dst_rdy, tx_dst_rdy_int, rx_src_rdy, rx_src_rdy_int, rx_dst_rdy, rx_dst_rdy_int }, +		    { EM_D } }; + +   assign debug_gpio_0 = { {run_tx, strobe_tx, run_rx, strobe_rx, tx_i[11:0]},  +			   {2'b00, tx_src_rdy, tx_dst_rdy, tx_q[11:0]} }; + +   assign debug_gpio_1 = debug_vt; +    +/*    +   assign debug_gpio_1 = { {rx_enable, rx_src_rdy, rx_dst_rdy, rx_src_rdy & ~rx_dst_rdy}, +			   {tx_enable, tx_src_rdy, tx_dst_rdy, tx_dst_rdy & ~tx_src_rdy}, +			   {rx_sof, rx_eof, rx_src_rdy, rx_dst_rdy, rx_data[33:32],2'b0}, +			   {2'b0, bus_error, debug_gpmc[4:0] }, +			   {misc_gpio[7:0]} }; +  */  +endmodule // u1e_core diff --git a/fpga/usrp2/top/u1e_ethdebug/.gitignore b/fpga/usrp2/top/u1e_ethdebug/.gitignore new file mode 100644 index 000000000..8d872713e --- /dev/null +++ b/fpga/usrp2/top/u1e_ethdebug/.gitignore @@ -0,0 +1,6 @@ +*~ +build +*.log +*.cmd +tb_u1e +*.lxt diff --git a/fpga/usrp2/top/u1e_ethdebug/Makefile b/fpga/usrp2/top/u1e_ethdebug/Makefile new file mode 100644 index 000000000..751b52970 --- /dev/null +++ b/fpga/usrp2/top/u1e_ethdebug/Makefile @@ -0,0 +1,83 @@ +# +# Copyright 2008 Ettus Research LLC +# + +################################################## +# Project Setup +################################################## +TOP_MODULE = u1e +BUILD_DIR = $(abspath build$(ISE)) + +################################################## +# Include other makefiles +################################################## + +include ../Makefile.common + +################################################## +# Project Properties +################################################## +export PROJECT_PROPERTIES := \ +family "Spartan-3A DSP" \ +device xc3sd1800a \ +package cs484 \ +speed -4 \ +top_level_module_type "HDL" \ +synthesis_tool "XST (VHDL/Verilog)" \ +simulator "ISE Simulator (VHDL/Verilog)" \ +"Preferred Language" "Verilog" \ +"Enable Message Filtering" FALSE \ +"Display Incremental Messages" FALSE  + +################################################## +# Sources +################################################## +TOP_SRCS = \ +u1e.v \ +u1e.ucf + +SOURCES = $(abspath $(TOP_SRCS)) + +################################################## +# Process Properties +################################################## +SYNTHESIZE_PROPERTIES = \ +"Number of Clock Buffers" 8 \ +"Pack I/O Registers into IOBs" Yes \ +"Optimization Effort" High \ +"Optimize Instantiated Primitives" TRUE \ +"Register Balancing" Yes \ +"Use Clock Enable" Auto \ +"Use Synchronous Reset" Auto \ +"Use Synchronous Set" Auto + +TRANSLATE_PROPERTIES = \ +"Macro Search Path" "$(shell pwd)/../../coregen/" + +MAP_PROPERTIES = \ +"Allow Logic Optimization Across Hierarchy" TRUE \ +"Map to Input Functions" 4 \ +"Optimization Strategy (Cover Mode)" Speed \ +"Pack I/O Registers/Latches into IOBs" "For Inputs and Outputs" \ +"Perform Timing-Driven Packing and Placement" TRUE \ +"Map Effort Level" High \ +"Extra Effort" Normal \ +"Combinatorial Logic Optimization" TRUE \ +"Register Duplication" TRUE + +PLACE_ROUTE_PROPERTIES = \ +"Place & Route Effort Level (Overall)" High  + +STATIC_TIMING_PROPERTIES = \ +"Number of Paths in Error/Verbose Report" 10 \ +"Report Type" "Error Report" + +GEN_PROG_FILE_PROPERTIES = \ +"Configuration Rate" 6 \ +"Create Binary Configuration File" TRUE \ +"Done (Output Events)" 5 \ +"Enable Bitstream Compression" TRUE \ +"Enable Outputs (Output Events)" 6 \ +"Unused IOB Pins" "Pull Up" + +SIM_MODEL_PROPERTIES = "" diff --git a/fpga/usrp2/top/u1e_ethdebug/u1e.ucf b/fpga/usrp2/top/u1e_ethdebug/u1e.ucf new file mode 100644 index 000000000..d6a2ea4ed --- /dev/null +++ b/fpga/usrp2/top/u1e_ethdebug/u1e.ucf @@ -0,0 +1,88 @@ + +## GPMC +NET "EM_D<15>"  LOC = "D13"  ; +NET "EM_D<14>"  LOC = "D15"  ; +NET "EM_D<13>"  LOC = "C16"  ; +NET "EM_D<12>"  LOC = "B20"  ; +NET "EM_D<11>"  LOC = "A19"  ; +NET "EM_D<10>"  LOC = "A17"  ; +NET "EM_D<9>"  LOC = "E15"  ; +NET "EM_D<8>"  LOC = "F15"  ; +NET "EM_D<7>"  LOC = "E16"  ; +NET "EM_D<6>"  LOC = "F16"  ; +NET "EM_D<5>"  LOC = "B17"  ; +NET "EM_D<4>"  LOC = "C17"  ; +NET "EM_D<3>"  LOC = "B19"  ; +NET "EM_D<2>"  LOC = "D19"  ; +NET "EM_D<1>"  LOC = "C19"  ; +NET "EM_D<0>"  LOC = "A20"  ; + +NET "EM_A<10>"  LOC = "C14"  ; +NET "EM_A<9>"  LOC = "C10"  ; +NET "EM_A<8>"  LOC = "C5"  ; +NET "EM_A<7>"  LOC = "A18"  ; +NET "EM_A<6>"  LOC = "A15"  ; +NET "EM_A<5>"  LOC = "A12"  ; +NET "EM_A<4>"  LOC = "A10"  ; +NET "EM_A<3>"  LOC = "E7"  ; +NET "EM_A<2>"  LOC = "A7"  ; +NET "EM_A<1>"  LOC = "C15"  ; + +NET "EM_NCS6"  LOC = "E17"  ; +NET "EM_NCS5"  LOC = "E10"  ; +NET "EM_NCS4"  LOC = "E6"  ; +#NET "EM_NCS1"  LOC = "D18"  ; +#NET "EM_NCS0"  LOC = "D17"  ; + +NET "EM_CLK"  LOC = "F11"  ; +NET "EM_WAIT0"  LOC = "F14"  ; +#NET "EM_NBE<1>"  LOC = "D14"  ; +#NET "EM_NBE<0>"  LOC = "A13"  ; +NET "EM_NWE"  LOC = "B13"  ; +NET "EM_NOE"  LOC = "A14"  ; +NET "EM_NADV_ALE"  LOC = "B15"  ; +#NET "EM_NWP"  LOC = "F13"  ; +NET "overo_gpio64"  LOC = "A4"  ;  # nRESET +NET "overo_gpio176"  LOC = "B4"  ;  # IRQ + +## Debug pins +NET "debug_led<3>"  LOC = "Y15"  ; +NET "debug_led<2>"  LOC = "K16"  ; +NET "debug_led<1>"  LOC = "J17"  ; +NET "debug_led<0>"  LOC = "H22"  ; +NET "debug<0>"  LOC = "G22"  ; +NET "debug<1>"  LOC = "H17"  ; +NET "debug<2>"  LOC = "H18"  ; +NET "debug<3>"  LOC = "K20"  ; +NET "debug<4>"  LOC = "J20"  ; +NET "debug<5>"  LOC = "K19"  ; +NET "debug<6>"  LOC = "K18"  ; +NET "debug<7>"  LOC = "L22"  ; +NET "debug<8>"  LOC = "K22"  ; +NET "debug<9>"  LOC = "N22"  ; +NET "debug<10>"  LOC = "M22"  ; +NET "debug<11>"  LOC = "N20"  ; +NET "debug<12>"  LOC = "N19"  ; +NET "debug<13>"  LOC = "R22"  ; +NET "debug<14>"  LOC = "P22"  ; +NET "debug<15>"  LOC = "N17"  ; +NET "debug<16>"  LOC = "P16"  ; +NET "debug<17>"  LOC = "U22"  ; +NET "debug<18>"  LOC = "P19"  ; +NET "debug<19>"  LOC = "R18"  ; +NET "debug<20>"  LOC = "U20"  ; +NET "debug<21>"  LOC = "T20"  ; +NET "debug<22>"  LOC = "R19"  ; +NET "debug<23>"  LOC = "R20"  ; +NET "debug<24>"  LOC = "W22"  ; +NET "debug<25>"  LOC = "Y22"  ; +NET "debug<26>"  LOC = "T18"  ; +NET "debug<27>"  LOC = "T17"  ; +NET "debug<28>"  LOC = "W19"  ; +NET "debug<29>"  LOC = "V20"  ; +NET "debug<30>"  LOC = "Y21"  ; +NET "debug<31>"  LOC = "AA22"  ; +NET "debug_clk<0>"  LOC = "N18"  ; +NET "debug_clk<1>"  LOC = "M17"  ; + +NET "debug_pb"  LOC = "C22"  ; diff --git a/fpga/usrp2/top/u1e_ethdebug/u1e.v b/fpga/usrp2/top/u1e_ethdebug/u1e.v new file mode 100644 index 000000000..2a543a313 --- /dev/null +++ b/fpga/usrp2/top/u1e_ethdebug/u1e.v @@ -0,0 +1,28 @@ +`timescale 1ns / 1ps +////////////////////////////////////////////////////////////////////////////////// + +//`define DCM 1 + +module u1e +  (output [3:0] debug_led, output [31:0] debug, output [1:0] debug_clk, +   input debug_pb, + +   // GPMC +   input EM_CLK, input [15:0] EM_D, input [10:1] EM_A, +   input EM_WAIT0, input EM_NCS4, input EM_NCS5, input EM_NCS6, input EM_NWE, input EM_NOE, +   input EM_NADV_ALE, + +   input overo_gpio64, input overo_gpio176 +   ); + +   assign debug_clk = {EM_CLK, EM_NADV_ALE}; + +   assign debug_led = {1'b0, EM_A[9], EM_A[8], debug_pb}; + +   assign debug = { {overo_gpio64, overo_gpio176, EM_WAIT0, EM_NCS4, EM_NCS5, EM_NCS6, EM_NWE, EM_NOE }, +		    { EM_A[10], EM_A[7:1] }, +		    { EM_D[15:8] }, +		    { EM_D[7:0] } }; +    + +endmodule // u1e diff --git a/fpga/usrp2/top/u1e_passthru/.gitignore b/fpga/usrp2/top/u1e_passthru/.gitignore new file mode 100644 index 000000000..1b2211df0 --- /dev/null +++ b/fpga/usrp2/top/u1e_passthru/.gitignore @@ -0,0 +1 @@ +build* diff --git a/fpga/usrp2/top/u1e_passthru/Makefile b/fpga/usrp2/top/u1e_passthru/Makefile new file mode 100644 index 000000000..d1950629b --- /dev/null +++ b/fpga/usrp2/top/u1e_passthru/Makefile @@ -0,0 +1,99 @@ +# +# Copyright 2008 Ettus Research LLC +# + +################################################## +# Project Setup +################################################## +TOP_MODULE = passthru +BUILD_DIR = $(abspath build$(ISE)) + +################################################## +# Include other makefiles +################################################## + +include ../Makefile.common +include ../../fifo/Makefile.srcs +include ../../control_lib/Makefile.srcs +include ../../sdr_lib/Makefile.srcs +include ../../serdes/Makefile.srcs +include ../../simple_gemac/Makefile.srcs +include ../../timing/Makefile.srcs +include ../../opencores/Makefile.srcs +include ../../vrt/Makefile.srcs +include ../../udp/Makefile.srcs +include ../../coregen/Makefile.srcs +include ../../extram/Makefile.srcs +include ../../gpmc/Makefile.srcs + +################################################## +# Project Properties +################################################## +export PROJECT_PROPERTIES := \ +family "Spartan-3A DSP" \ +device xc3sd1800a \ +package cs484 \ +speed -4 \ +top_level_module_type "HDL" \ +synthesis_tool "XST (VHDL/Verilog)" \ +simulator "ISE Simulator (VHDL/Verilog)" \ +"Preferred Language" "Verilog" \ +"Enable Message Filtering" FALSE \ +"Display Incremental Messages" FALSE  + +################################################## +# Sources +################################################## +TOP_SRCS = \ +passthru.v \ +passthru.ucf + +SOURCES = $(abspath $(TOP_SRCS)) $(FIFO_SRCS) \ +$(CONTROL_LIB_SRCS) $(SDR_LIB_SRCS) $(SERDES_SRCS) \ +$(SIMPLE_GEMAC_SRCS) $(TIMING_SRCS) $(OPENCORES_SRCS) \ +$(VRT_SRCS) $(UDP_SRCS) $(COREGEN_SRCS) $(EXTRAM_SRCS) \ +$(GPMC_SRCS) + +################################################## +# Process Properties +################################################## +SYNTHESIZE_PROPERTIES = \ +"Number of Clock Buffers" 8 \ +"Pack I/O Registers into IOBs" Yes \ +"Optimization Effort" High \ +"Optimize Instantiated Primitives" TRUE \ +"Register Balancing" Yes \ +"Use Clock Enable" Auto \ +"Use Synchronous Reset" Auto \ +"Use Synchronous Set" Auto + +TRANSLATE_PROPERTIES = \ +"Macro Search Path" "$(shell pwd)/../../coregen/" + +MAP_PROPERTIES = \ +"Allow Logic Optimization Across Hierarchy" TRUE \ +"Map to Input Functions" 4 \ +"Optimization Strategy (Cover Mode)" Speed \ +"Pack I/O Registers/Latches into IOBs" "For Inputs and Outputs" \ +"Perform Timing-Driven Packing and Placement" TRUE \ +"Map Effort Level" High \ +"Extra Effort" Normal \ +"Combinatorial Logic Optimization" TRUE \ +"Register Duplication" TRUE + +PLACE_ROUTE_PROPERTIES = \ +"Place & Route Effort Level (Overall)" High  + +STATIC_TIMING_PROPERTIES = \ +"Number of Paths in Error/Verbose Report" 10 \ +"Report Type" "Error Report" + +GEN_PROG_FILE_PROPERTIES = \ +"Configuration Rate" 6 \ +"Create Binary Configuration File" TRUE \ +"Done (Output Events)" 5 \ +"Enable Bitstream Compression" TRUE \ +"Enable Outputs (Output Events)" 6 \ +"Unused IOB Pins" "Pull Up" + +SIM_MODEL_PROPERTIES = "" diff --git a/fpga/usrp2/top/u1e_passthru/passthru.ucf b/fpga/usrp2/top/u1e_passthru/passthru.ucf new file mode 100644 index 000000000..64e6f0440 --- /dev/null +++ b/fpga/usrp2/top/u1e_passthru/passthru.ucf @@ -0,0 +1,6 @@ +NET "overo_gpio145"  LOC = "C7"  ; +NET "cgen_mosi"  LOC = "E22"  ; +NET "cgen_sclk"  LOC = "J19"  ; +NET "cgen_sen_b"  LOC = "H20"  ; +NET "fpga_cfg_din"  LOC = "W17"  ; +NET "fpga_cfg_cclk"  LOC = "V17"  ; diff --git a/fpga/usrp2/top/u1e_passthru/passthru.v b/fpga/usrp2/top/u1e_passthru/passthru.v new file mode 100644 index 000000000..12e4db017 --- /dev/null +++ b/fpga/usrp2/top/u1e_passthru/passthru.v @@ -0,0 +1,18 @@ +`timescale 1ns / 1ps +////////////////////////////////////////////////////////////////////////////////// + +module passthru +  (input overo_gpio145, +   output cgen_sclk, +   output cgen_sen_b, +   output cgen_mosi, +   input fpga_cfg_din, +   input fpga_cfg_cclk +   ); +    +   assign cgen_sclk = fpga_cfg_cclk; +   assign cgen_sen_b = overo_gpio145; +   assign cgen_mosi = fpga_cfg_din; +    +    +endmodule // passthru diff --git a/host/AUTHORS b/host/AUTHORS index e0775f3a1..512d4752e 100644 --- a/host/AUTHORS +++ b/host/AUTHORS @@ -24,6 +24,7 @@ Tom Tsou - ttsou@vt.edu      USRP1 host code      USRP1 firmware -Nick Foster - nick@nerdnetworks.org +Nick Foster - nick@ettus.com      LIBUSB host code      USRP1 host code +    TVRX host code diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index 6c96706a5..e5ce78782 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -84,7 +84,7 @@ IF(UNIX AND EXISTS "/usr/lib64")      LIST(APPEND BOOST_LIBRARYDIR "/usr/lib64") #fedora 64-bit fix  ENDIF(UNIX AND EXISTS "/usr/lib64") -SET(Boost_ADDITIONAL_VERSIONS "1.42.0" "1.42" "1.43.0" "1.43") +SET(Boost_ADDITIONAL_VERSIONS "1.42.0" "1.42" "1.43.0" "1.43" "1.44.0" "1.44")  FIND_PACKAGE(Boost ${BOOST_MIN_VERSION} REQUIRED COMPONENTS      date_time      filesystem diff --git a/host/README b/host/README index 5018ef541..cab1e0b10 100644 --- a/host/README +++ b/host/README @@ -6,7 +6,8 @@ The hardware driver for Ettus Research products.  ########################################################################  # Supported USRP Motherboards  ######################################################################## -USRP2 - udp over gigabit ethernet +USRP1 +USRP2  ########################################################################  # Supported USRP Daughterboards @@ -19,6 +20,7 @@ RFX Series  XCVR 2450  WBX Series  DBSRX +TVRX  ########################################################################  # Documentation diff --git a/host/apps/omap_debug/usrp_e.h b/host/apps/omap_debug/usrp_e.h index fd38027d4..f96706c4a 100644 --- a/host/apps/omap_debug/usrp_e.h +++ b/host/apps/omap_debug/usrp_e.h @@ -41,6 +41,9 @@ struct usrp_e_ctl32 {  #define UE_SPI_PUSH_FALL	UE_SPI_CTRL_TXNEG  #define UE_SPI_LATCH_RISE	0  #define UE_SPI_LATCH_FALL	UE_SPI_CTRL_RXNEG +#define USRP_E_GET_COMPAT_NUMBER _IO(USRP_E_IOC_MAGIC, 0x28) + +#define USRP_E_COMPAT_NUMBER 1  struct usrp_e_spi {  	__u8 readback; diff --git a/host/docs/CMakeLists.txt b/host/docs/CMakeLists.txt index bbb8812b0..65db3befc 100644 --- a/host/docs/CMakeLists.txt +++ b/host/docs/CMakeLists.txt @@ -25,6 +25,7 @@ SET(manual_sources      dboards.rst      general.rst      images.rst +    transport.rst      usrp1.rst      usrp2.rst  ) diff --git a/host/docs/build.rst b/host/docs/build.rst index f37b5dce7..a41ce8331 100644 --- a/host/docs/build.rst +++ b/host/docs/build.rst @@ -47,7 +47,7 @@ CMake  Boost  ^^^^^^^^^^^^^^^^  * **Purpose:** C++ library -* **Version:** at least 3.6 unix, at least 4.0 windows +* **Version:** at least 1.36 unix, at least 1.40 windows  * **Usage:** build time + run time (required)  * **Download URL:** http://www.boost.org/users/download/  * **Download URL (windows installer):** http://www.boostpro.com/download @@ -58,7 +58,8 @@ LibUSB  * **Purpose:** USB-based hardware support  * **Version:** at least 1.0  * **Usage:** build time + run time (optional) -* **Download URL:** http://www.libusb.org/ +* **Download URL:** http://sourceforge.net/projects/libusb/files/libusb-1.0/ +* **Download URL (windows binaries):** http://www.libusb.org/wiki/windows_backend#LatestBinarySnapshots  ^^^^^^^^^^^^^^^^  Python @@ -152,6 +153,19 @@ Generate the project with cmake  * Click generate and a project file will be created in the build directory.  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LibUSB cmake notes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +On Windows, cmake does not have the advantage of pkg-config, +so we must manually tell cmake how to locate the LibUSB header and lib. + +From the cmake gui, select "Advanded View": + +* Set LIBUSB_INCLUDE_DIR to the directory with "libusb.h". +* Set LIBUSB_LIBRARIES to the full path for "libusb-1.0.lib". + +Then check the boxes to enable USRP1 support, click configure and generate. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  Build the project in MSVC  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  * Open the generated project file in MSVC. @@ -177,3 +191,8 @@ Setup the PATH environment variable  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  * Add the boost library path to %PATH% (usually c:\\program files\\boost\\<version>\\lib)  * Add the uhd library path to %PATH% (usually c:\\program files\\uhd\\lib) +* Add the libusb library to %PATH% (if using usb support) + +**Note:** +The interface for editing environment variable paths in Windows is very poor. +I recommend using "Rapid Environment Editor" (http://www.rapidee.com) over the default editor. diff --git a/host/docs/coding.rst b/host/docs/coding.rst index d6a19d250..7533445ea 100644 --- a/host/docs/coding.rst +++ b/host/docs/coding.rst @@ -41,13 +41,13 @@ The single usrp provides ways to:  See the documentation in *usrp/single_usrp.hpp* for reference.  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -High-Level: The mimo usrp +High-Level: The multi usrp  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The mimo usrp API provides a wrapper around a device that represents several motherboards. +The multi usrp API provides a wrapper around a device that represents several motherboards.  This API provides convenience calls just like the single usrp,  however the calls either work across all channels in the configuration,  or take a channel argument to specify which channel to configure. -The mimo usrp provides ways to: +The multi usrp provides ways to:  * Set and get the sample rate across all channels.  * Issue a stream command across all channels. @@ -57,7 +57,7 @@ The mimo usrp provides ways to:  * Tune individual DSPs and daughterboards.  * Get the underlying device (as discussed above). -See the documentation in *usrp/mimo_usrp.hpp* for reference. +See the documentation in *usrp/multi_usrp.hpp* for reference.  ------------------------------------------------------------------------  Integrating custom hardware diff --git a/host/docs/dboards.rst b/host/docs/dboards.rst index 738a0696d..d93fb9d6a 100644 --- a/host/docs/dboards.rst +++ b/host/docs/dboards.rst @@ -26,6 +26,10 @@ The boards have no tunable elements or programmable gains.  Though the magic of aliasing, you can down-convert signals  greater than the Nyquist rate of the ADC. +BasicRX Bandwidth (Hz): 250M + +LFRX Bandwidth (Hz): 30M +  ^^^^^^^^^^^^^^^^^^^^^^^^^^^  Basic TX and and LFTX  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,6 +44,10 @@ The boards have no tunable elements or programmable gains.  Though the magic of aliasing, you can up-convert signals  greater than the Nyquist rate of the DAC. +BasicTX Bandwidth (Hz): 250M + +LFTX Bandwidth (Hz): 30M +  ^^^^^^^^^^^^^^^^^^^^^^^^^^^  DBSRX  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -49,10 +57,12 @@ Receive Antennas: **J3**  The board has no user selectable antenna setting -Recieve Gains:  +Receive Gains:       **GC1**, Range: 0-56dB      **GC2**, Range: 0-24dB +Bandwidth (Hz): 8M-66M +  ^^^^^^^^^^^^^^^^^^^^^^^^^^^  RFX Series  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -64,26 +74,29 @@ The user may set the receive antenna to be TX/RX or RX2.  However, when using an RFX board in full-duplex mode,  the receive antenna will always be set to RX2, regardless of the settings. -Recieve Gains: **PGA0**, Range: 0-70dB (except RFX400 range is 0-45dB) +Receive Gains: **PGA0**, Range: 0-70dB (except RFX400 range is 0-45dB) + +Bandwidths (Hz): + * **RX**: 40M + * **TX**: 40M  ^^^^^^^^^^^^^^^^^^^^^^^^^^^  XCVR 2450  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The XCVR2450 has a non-contiguous tuning range consisting of a high band and a low band. -The high band consists of frequencies between...TODO +The XCVR2450 has a non-contiguous tuning range consisting of a  +high band (4.9-6.0GHz) and a low band (2.4-2.5GHz).  Transmit Antennas: **J1** or **J2**  Receive Antennas: **J1** or **J2** -When using the XCVR2450 in full-duplex mode, -the user must set the receive antenna and the transmit antenna to be different; -not doing so will yeild undefined results. -  The XCVR2450 uses a common LO for both receive and transmit.  Even though the API allows the RX and TX LOs to be individually set,  a change of one LO setting will be reflected in the other LO setting. +The XCVR2450 does not support full-duplex mode, attempting to operate  +in full-duplex will result in transmit-only operation. +  Transmit Gains:   * **VGA**, Range: 0-30dB   * **BB**, Range: 0-5dB @@ -92,6 +105,10 @@ Receive Gains:   * **LNA**, Range: 0-30.5dB   * **VGA**, Range: 0-62dB +Bandwidths (Hz): + * **RX**: 15M, 19M, 28M, 36M; (each +-0, 5, or 10%) + * **TX**: 24M, 36M, 48M +  ^^^^^^^^^^^^^^^^^^^^^^^^^^^  WBX Series  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -105,7 +122,22 @@ the receive antenna will always be set to RX2, regardless of the settings.  Transmit Gains: **PGA0**, Range: 0-25dB -Recieve Gains: **PGA0**, Range: 0-31.5dB +Receive Gains: **PGA0**, Range: 0-31.5dB + +Bandwidths (Hz): + * **RX**: 40M + * **TX**: 40M + +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +TVRX +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Receive Antennas: RX + +Receive Gains: + * **RF**, Range: -13.3-50.3dB (frequency-dependent) + * **IF**, Range: -1.5-32.5dB + +Bandwidth: 6MHz  ------------------------------------------------------------------------  Daughterboard Modifications diff --git a/host/docs/index.rst b/host/docs/index.rst index bd55edc0b..7f8129e2d 100644 --- a/host/docs/index.rst +++ b/host/docs/index.rst @@ -20,11 +20,12 @@ Building the UHD  ^^^^^^^^^^^^^^^^^^^^^  Application Notes  ^^^^^^^^^^^^^^^^^^^^^ -* `General App Notes <./general.html>`_ +* `General Application Notes <./general.html>`_  * `Firmware and FPGA Image Notes <./images.html>`_ -* `USRP1 App Notes <./usrp1.html>`_ -* `USRP2 App Notes <./usrp2.html>`_ -* `Daughterboard App Notes <./dboards.html>`_ +* `USRP1 Application Notes <./usrp1.html>`_ +* `USRP2 Application Notes <./usrp2.html>`_ +* `Daughterboard Application Notes <./dboards.html>`_ +* `Transport Application Notes <./transport.html>`_  ^^^^^^^^^^^^^^^^^^^^^  API Documentation diff --git a/host/docs/transport.rst b/host/docs/transport.rst new file mode 100644 index 000000000..432db4bb5 --- /dev/null +++ b/host/docs/transport.rst @@ -0,0 +1,88 @@ +======================================================================== +UHD - Transport Application Notes +======================================================================== + +.. contents:: Table of Contents + +------------------------------------------------------------------------ +Introduction +------------------------------------------------------------------------ +A transport is the layer between the packet interface and a device IO interface. +The advanced user can pass optional parameters +into the underlying transport layer through the device address. +These optional parameters control how the transport object allocates memory, +resizes kernel buffers, spawns threads, etc. +When not spcified, the transport layer will use values for these parameters +that are known to perform well on a variety of systems. +The transport parameters are defined below for the various transports in the UHD: + +------------------------------------------------------------------------ +UDP transport (ASIO) +------------------------------------------------------------------------ +The UDP transport is implemented with Boost's ASIO library. +ASIO provides an asynchronous API for user-space sockets. +The transport implementation allocates a number of buffers +and submits asynchronous requests for send and receive. +IO service threads run in the background to process these requests. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Transport parameters +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The following parameters can be used to alter the transport's default behavior: + +* **recv_frame_size:** The size of a single receive buffer in bytes +* **num_recv_frames:** The number of receive buffers to allocate +* **send_frame_size:** The size of a single send buffer in bytes +* **num_send_frames:** The number of send buffers to allocate +* **concurrency_hint:** The number of threads to run the IO service + +**Note:** num_send_frames and concurrency_hint will not have an effect +as the asynchronous send implementation is currently disabled. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Resize socket buffers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +It may be useful increase the size of the socket buffers to +move the burden of buffering samples into the kernel, or to +buffer incoming samples faster than they can be processed. +However, if your application cannot process samples fast enough, +no amount of buffering can save you. +The following parameters can be used to alter socket's buffer sizes: + +* **recv_buff_size:** The desired size of the receive buffer in bytes +* **send_buff_size:** The desired size of the send buffer in bytes + +**Note:** Large send buffers tend to decrease transmit performance. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Linux specific notes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +On linux, the maximum buffer sizes are capped by the sysctl values +**net.core.rmem_max** and **net.core.wmem_max**. +To change the maximum values, run the following commands: +:: + +    sudo sysctl -w net.core.rmem_max=<new value> +    sudo sysctl -w net.core.wmem_max=<new value> + +Set the values permanently by editing */etc/sysctl.conf* + +------------------------------------------------------------------------ +USB transport (libusb) +------------------------------------------------------------------------ +The USB transport is implemented with libusb. +Libusb provides an asynchronous API for USB bulk transfers. +The transport implementation allocates a number of buffers +and submits asynchronous requests through libusb. +Event handler threads run in the background to process these requests. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Transport parameters +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The following parameters can be used to alter the transport's default behavior: + +* **recv_frame_size:** The size of a single receive transfers in bytes +* **num_recv_frames:** The number of simultaneous receive transfers +* **send_frame_size:** The size of a single send transfers in bytes +* **num_send_frames:** The number of simultaneous send transfers +* **concurrency_hint:** The number of threads to run the event handler diff --git a/host/docs/usrp1.rst b/host/docs/usrp1.rst index 3c1431d30..3443fd871 100644 --- a/host/docs/usrp1.rst +++ b/host/docs/usrp1.rst @@ -100,10 +100,10 @@ OS Specific Notes  ------------------------------------------------------------------------  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Setup Udev on Linux +Linux - setup udev  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -On Linux, Udev handles USB plug and unplug events. -The following command creates a Udev rule for the USRP1 +On Linux, udev handles USB plug and unplug events. +The following commands create a udev rule for the USRP1  so that non-root users may access the device:  :: @@ -113,3 +113,9 @@ so that non-root users may access the device:      sudo mv tmpfile /etc/udev/rules.d/10-usrp.rules      sudo udevadm control --reload-rules +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Windows - install driver +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +On Windows, a driver must be installed the first time the USRP1 is attached to the host computer. +A download link for this driver can be found on the UHD wiki page. +Download and unpack the driver, and direct the Windows driver install wizard to the .inf file. diff --git a/host/docs/usrp2.rst b/host/docs/usrp2.rst index 4c95fb24c..0ddcaa4e5 100644 --- a/host/docs/usrp2.rst +++ b/host/docs/usrp2.rst @@ -158,7 +158,7 @@ The value for the addr key is a white-space separated list  of IPv4 addresses or resolvable hostnames.  The first address in the list will represent channel 0,  the second channel 1, and so on... -Use this addressing scheme with the *mimo_usrp* interface. +Use this addressing scheme with the *multi_usrp* interface.  The device address string representation for 2 USRP2s with IPv4 addresses 192.168.10.2 and 192.168.20.2  :: @@ -166,49 +166,22 @@ The device address string representation for 2 USRP2s with IPv4 addresses 192.16      addr=192.168.10.2 192.168.20.2  ------------------------------------------------------------------------ -Resize the send and receive buffers +Hardware setup notes  ------------------------------------------------------------------------ -It may be useful increase the size of the socket buffers to -move the burden of buffering samples into the kernel, or to -buffer incoming samples faster than they can be processed. -However, if you application cannot process samples fast enough, -no amount of buffering can save you. - -By default, the UHD will try to resize both the send and receive buffer for optimum performance. -A warning will be printed on instantiation if the actual buffer size is insufficient. -See the OS specific notes below: - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -OS specific notes -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -On linux, the maximum buffer sizes are capped by the sysctl values -**net.core.rmem_max** and **net.core.wmem_max**. -To change the maximum values, run the following commands: -:: - -    sudo sysctl -w net.core.rmem_max=<new value> -    sudo sysctl -w net.core.wmem_max=<new value> - -Set the values permanently by editing */etc/sysctl.conf*  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Device address params +Front panel LEDs  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -To manually set the size of the buffers, -the usrp2 will accept two optional parameters in the device address. -Each parameter will accept a numeric value for the number of bytes. - -* recv_buff_size -* send_buff_size - -Example, set the args string to the following: -:: +The LEDs on the front panel can be useful in debugging hardware and software issues. +The LEDs reveal the following about the state of the device: -    addr=192.168.10.2, recv_buff_size=100e6 +* **LED A:** transmitting +* **LED B:** undocumented +* **LED C:** receiving +* **LED D:** firmware loaded +* **LED E:** undocumented +* **LED F:** FPGA loaded ------------------------------------------------------------------------- -Hardware setup notes -------------------------------------------------------------------------  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  Ref Clock - 10MHz diff --git a/host/examples/CMakeLists.txt b/host/examples/CMakeLists.txt index fb7777d42..ce2ca9640 100644 --- a/host/examples/CMakeLists.txt +++ b/host/examples/CMakeLists.txt @@ -16,38 +16,35 @@  #  ######################################################################## -ADD_EXECUTABLE(benchmark_rx_rate benchmark_rx_rate.cpp) -TARGET_LINK_LIBRARIES(benchmark_rx_rate uhd) - -ADD_EXECUTABLE(rx_timed_samples rx_timed_samples.cpp) -TARGET_LINK_LIBRARIES(rx_timed_samples uhd) - -ADD_EXECUTABLE(rx_to_file rx_to_file.cpp) -TARGET_LINK_LIBRARIES(rx_to_file uhd) - -ADD_EXECUTABLE(test_async_messages test_async_messages.cpp) -TARGET_LINK_LIBRARIES(test_async_messages uhd) - -ADD_EXECUTABLE(test_pps_input test_pps_input.cpp) -TARGET_LINK_LIBRARIES(test_pps_input uhd) - -ADD_EXECUTABLE(tx_timed_samples tx_timed_samples.cpp) -TARGET_LINK_LIBRARIES(tx_timed_samples uhd) - -ADD_EXECUTABLE(tx_from_file tx_from_file.cpp) -TARGET_LINK_LIBRARIES(tx_from_file uhd) +# example applications +######################################################################## +SET(example_sources +    benchmark_rx_rate.cpp +    rx_samples_to_file.cpp +    rx_samples_to_udp.cpp +    rx_timed_samples.cpp +    test_async_messages.cpp +    test_pps_input.cpp +    tx_timed_samples.cpp +    tx_waveforms.cpp +) -ADD_EXECUTABLE(tx_waveforms tx_waveforms.cpp) -TARGET_LINK_LIBRARIES(tx_waveforms uhd) +#for each source: build an executable and install +FOREACH(example_source ${example_sources}) +    GET_FILENAME_COMPONENT(example_name ${example_source} NAME_WE) +    ADD_EXECUTABLE(${example_name} ${example_source}) +    TARGET_LINK_LIBRARIES(${example_name} uhd) +    INSTALL(TARGETS ${example_name} RUNTIME DESTINATION ${PKG_DATA_DIR}/examples) +ENDFOREACH(example_source) -INSTALL(TARGETS -    benchmark_rx_rate -    rx_timed_samples -    test_async_messages -    test_pps_input -    tx_timed_samples -    tx_from_file -    rx_to_file -    tx_waveforms -    RUNTIME DESTINATION ${PKG_DATA_DIR}/examples -) +######################################################################## +# ASCII Art DFT - requires curses, so this part is optional +######################################################################## +INCLUDE(FindCurses) + +IF(CURSES_FOUND) +    INCLUDE_DIRECTORIES(${CURSES_INCLUDE_DIR}) +    ADD_EXECUTABLE(rx_ascii_art_dft rx_ascii_art_dft.cpp) +    TARGET_LINK_LIBRARIES(rx_ascii_art_dft uhd ${CURSES_LIBRARIES}) +    INSTALL(TARGETS rx_ascii_art_dft RUNTIME DESTINATION ${PKG_DATA_DIR}/examples) +ENDIF(CURSES_FOUND) diff --git a/host/examples/ascii_art_dft.hpp b/host/examples/ascii_art_dft.hpp new file mode 100644 index 000000000..ee2267c2d --- /dev/null +++ b/host/examples/ascii_art_dft.hpp @@ -0,0 +1,320 @@ +// +// ASCII Art DFT Plotter - Josh Blum +// + +#ifndef ASCII_ART_DFT_HPP +#define ASCII_ART_DFT_HPP + +#include <string> +#include <cstddef> +#include <vector> +#include <complex> +#include <stdexcept> + +namespace acsii_art_dft{ + +    //! Type produced by the log power DFT function +    typedef std::vector<float> log_pwr_dft_type; + +    /*! +     * Get a logarithmic power DFT of the input samples. +     * Samples are expected to be in the range [-1.0, 1.0]. +     * \param samps a pointer to an array of complex samples +     * \param nsamps the number of samples in the array +     * \return a real range of DFT bins in units of dB +     */ +    template <typename T> log_pwr_dft_type log_pwr_dft( +        const std::complex<T> *samps, size_t nsamps +    ); + +    /*! +     * Convert a DFT to a piroundable ascii plot. +     * \param dft the log power dft bins +     * \param width the frame width in characters +     * \param height the frame height in characters +     * \param samp_rate the sample rate in Sps +     * \param dc_freq the DC frequency in Hz +     * \param dyn_rng the dynamic range in dB +     * \param ref_lvl the reference level in dB +     * \return the plot as an ascii string +     */ +    std::string dft_to_plot( +        const log_pwr_dft_type &dft, +        size_t width, +        size_t height, +        double samp_rate, +        double dc_freq, +        float dyn_rng, +        float ref_lvl +    ); + +} //namespace ascii_dft + +/*********************************************************************** + * Implementation includes + **********************************************************************/ +#include <cmath> +#include <sstream> +#include <algorithm> + +/*********************************************************************** + * Helper functions + **********************************************************************/ +namespace {/*anon*/ + +    static const double pi = double(std::acos(-1.0)); + +    //! Round a floating-point value to the nearest integer +    template <typename T> int iround(T val){ +        return (val > 0)? int(val + 0.5) : int(val - 0.5); +    } + +    //! Pick the closest number that is nice to display +    template <typename T> T to_clean_num(const T num){ +        if (num == 0) return 0; +        const T pow10 = std::pow(T(10), int(std::floor(std::log10(std::abs(num))))); +        const T norm = std::abs(num)/pow10; +        static const int cleans[] = {1, 2, 5, 10}; +        int clean = cleans[0]; +        for (size_t i = 1; i < sizeof(cleans)/sizeof(cleans[0]); i++){ +            if (std::abs(norm - cleans[i]) < std::abs(norm - clean)) +                clean = cleans[i]; +        } +        return ((num < 0)? -1 : 1)*clean*pow10; +    } + +    //! Compute an FFT with pre-computed factors using Cooley-Tukey +    template <typename T> std::complex<T> ct_fft_f( +        const std::complex<T> *samps, size_t nsamps, +        const std::complex<T> *factors, +        size_t start = 0, size_t step = 1 +    ){ +        if (nsamps == 1) return samps[start]; +        std::complex<T> E_k = ct_fft_f(samps, nsamps/2, factors+1, start,      step*2); +        std::complex<T> O_k = ct_fft_f(samps, nsamps/2, factors+1, start+step, step*2); +        return E_k + factors[0]*O_k; +    } + +    //! Compute an FFT for a particular bin k using Cooley-Tukey +    template <typename T> std::complex<T> ct_fft_k( +        const std::complex<T> *samps, size_t nsamps, size_t k +    ){ +        //pre-compute the factors to use in Cooley-Tukey +        std::vector<std::complex<T> > factors; +        for (size_t N = nsamps; N != 0; N /= 2){ +            factors.push_back(std::exp(std::complex<T>(0, T(-2*pi*k/N)))); +        } +        return ct_fft_f(samps, nsamps, &factors.front()); +    } + +    //! Helper class to build a DFT plot frame +    class frame_type{ +    public: +        frame_type(size_t width, size_t height): +            _frame(width-1, std::vector<char>(height, ' ')) +        { +            /* NOP */ +        } + +        //accessors to parts of the frame +        char &get_plot(size_t b, size_t z){return _frame.at(b+albl_w).at(z+flbl_h);} +        char &get_albl(size_t b, size_t z){return _frame.at(b)       .at(z+flbl_h);} +        char &get_ulbl(size_t b)          {return _frame.at(b)       .at(flbl_h-1);} +        char &get_flbl(size_t b)          {return _frame.at(b+albl_w).at(flbl_h-1);} + +        //dimension accessors +        size_t get_plot_h(void) const{return _frame.front().size() - flbl_h;} +        size_t get_plot_w(void) const{return _frame.size() - albl_w;} +        size_t get_albl_w(void) const{return albl_w;} + +        std::string to_string(void){ +            std::stringstream frame_ss; +            for (size_t z = 0; z < _frame.front().size(); z++){ +                for (size_t b = 0; b < _frame.size(); b++){ +                    frame_ss << _frame[b][_frame[b].size()-z-1]; +                } +                frame_ss << std::endl; +            } +            return frame_ss.str(); +        } + +    private: +        static const size_t albl_w = 6, flbl_h = 1; +        std::vector<std::vector<char> > _frame; +    }; + +} //namespace /*anon*/ + +/*********************************************************************** + * Implementation code + **********************************************************************/ +namespace acsii_art_dft{ + +    //! skip constants for amplitude and frequency labels +    static const size_t albl_skip = 5, flbl_skip = 20; + +    template <typename T> log_pwr_dft_type log_pwr_dft( +        const std::complex<T> *samps, size_t nsamps +    ){ +        if (nsamps & (nsamps - 1)) +            throw std::runtime_error("num samps is not a power of 2"); + +        //compute the window +        double win_pwr = 0; +        std::vector<std::complex<T> > win_samps; +        for(size_t n = 0; n < nsamps; n++){ +            //double w_n = 1; +            //double w_n = 0.54 //hamming window +            //    -0.46*std::cos(2*pi*n/(nsamps-1)) +            //; +            double w_n = 0.35875 //blackman-harris window +                -0.48829*std::cos(2*pi*n/(nsamps-1)) +                +0.14128*std::cos(4*pi*n/(nsamps-1)) +                -0.01168*std::cos(6*pi*n/(nsamps-1)) +            ; +            //double w_n = 1 // flat top window +            //    -1.930*std::cos(2*pi*n/(nsamps-1)) +            //    +1.290*std::cos(4*pi*n/(nsamps-1)) +            //    -0.388*std::cos(6*pi*n/(nsamps-1)) +            //    +0.032*std::cos(8*pi*n/(nsamps-1)) +            //; +            win_samps.push_back(T(w_n)*samps[n]); +            win_pwr += w_n*w_n; +        } + +        //compute the log-power dft +        log_pwr_dft_type log_pwr_dft; +        for(size_t k = 0; k < nsamps; k++){ +            std::complex<T> dft_k = ct_fft_k(&win_samps.front(), nsamps, k); +            log_pwr_dft.push_back(float( +                + 20*std::log10(std::abs(dft_k)) +                - 20*std::log10(T(nsamps)) +                - 10*std::log10(win_pwr/nsamps) +                + 3 +            )); +        } + +        return log_pwr_dft; +    } + +    std::string dft_to_plot( +        const log_pwr_dft_type &dft_, +        size_t width, +        size_t height, +        double samp_rate, +        double dc_freq, +        float dyn_rng, +        float ref_lvl +    ){ +        frame_type frame(width, height); //fill this frame + +        //re-order the dft so dc in in the center +        const size_t num_bins = dft_.size() - 1 + dft_.size()%2; //make it odd +        log_pwr_dft_type dft(num_bins); +        for (size_t n = 0; n < num_bins; n++){ +            dft[n] = dft_[(n + num_bins/2)%num_bins]; +        } + +        //fill the plot with dft bins +        for (size_t b = 0; b < frame.get_plot_w(); b++){ +            //indexes from the dft to grab for the plot +            const size_t n_start = std::max(iround(double(b-0.5)*(num_bins-1)/(frame.get_plot_w()-1)), 0); +            const size_t n_stop  = std::min(iround(double(b+0.5)*(num_bins-1)/(frame.get_plot_w()-1)), int(num_bins)); + +            //calculate val as the max across points +            float val = dft.at(n_start); +            for (size_t n = n_start; n < n_stop; n++) val = std::max(val, dft.at(n)); + +            const float scaled = (val - (ref_lvl - dyn_rng))*(frame.get_plot_h()-1)/dyn_rng; +            for (size_t z = 0; z < frame.get_plot_h(); z++){ +                static const std::string syms(".:!|"); +                if      (scaled-z > 1) frame.get_plot(b, z) = syms.at(syms.size()-1); +                else if (scaled-z > 0) frame.get_plot(b, z) = syms.at(size_t((scaled-z)*syms.size())); +            } +        } + +        //create vertical amplitude labels +        const float db_step = to_clean_num(dyn_rng/(frame.get_plot_h()-1)*albl_skip); +        for ( +            float db = db_step*(int((ref_lvl - dyn_rng)/db_step)); +            db      <=  db_step*(int(ref_lvl/db_step)); +            db      +=  db_step +        ){ +            const int z = iround((db - (ref_lvl - dyn_rng))*(frame.get_plot_h()-1)/dyn_rng); +            if (z < 0 or size_t(z) >= frame.get_plot_h()) continue; +            std::stringstream ss; ss << db; std::string lbl = ss.str(); +            for (size_t i = 0; i < lbl.size() and i < frame.get_albl_w(); i++){ +                frame.get_albl(i, z) = lbl[i]; +            } +        } + +        //create vertical units label +        std::string ulbl = "dBfs"; +        for (size_t i = 0; i < ulbl.size(); i++){ +            frame.get_ulbl(i+1) = ulbl[i]; +        } + +        //create horizontal frequency labels +        const double f_step = to_clean_num(samp_rate/frame.get_plot_w()*flbl_skip); +        for ( +            double freq = f_step*int((-samp_rate/2/f_step)); +            freq       <= f_step*int((+samp_rate/2/f_step)); +            freq       += f_step +        ){ +            const int b = iround((freq + samp_rate/2)*(frame.get_plot_w()-1)/samp_rate); +            std::stringstream ss; ss << (freq+dc_freq)/1e6 << "MHz"; std::string lbl = ss.str(); +            if (b < int(lbl.size()/2) or b + lbl.size() - lbl.size()/2 >= frame.get_plot_w()) continue; +            for (size_t i = 0; i < lbl.size(); i++){ +                frame.get_flbl(b + i - lbl.size()/2) = lbl[i]; +            } +        } + +        return frame.to_string(); +    } +} //namespace ascii_dft + +#endif /*ASCII_ART_DFT_HPP*/ + +/* + +//example main function to test the dft + +#include <iostream> +#include <cstdlib> +#include <curses.h> + +int main(void){ +    initscr(); + +    while (true){ +        clear(); + +        std::vector<std::complex<float> > samples; +        for(size_t i = 0; i < 512; i++){ +            samples.push_back(std::complex<float>( +                float(std::rand() - RAND_MAX/2)/(RAND_MAX)/4, +                float(std::rand() - RAND_MAX/2)/(RAND_MAX)/4 +            )); +            samples[i] += 0.5*std::sin(i*3.14/2) + 0.7; +        } + +        acsii_art_dft::log_pwr_dft_type dft; +        dft = acsii_art_dft::log_pwr_dft(&samples.front(), samples.size()); + +        printw("%s", acsii_art_dft::dft_to_plot( +            dft, COLS, LINES, +            12.5e4, 2.45e9, +            60, 0 +        ).c_str()); + +        sleep(1); +    } + + +    endwin(); +    std::cout << "here\n"; +    return 0; +} + +*/ + diff --git a/host/examples/benchmark_rx_rate.cpp b/host/examples/benchmark_rx_rate.cpp index 36611f97f..b189368f9 100644 --- a/host/examples/benchmark_rx_rate.cpp +++ b/host/examples/benchmark_rx_rate.cpp @@ -137,7 +137,6 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl;      uhd::usrp::single_usrp::sptr sdev = uhd::usrp::single_usrp::make(args);      std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl; -    sdev->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); //stop if left running      if (not vm.count("rate")){          sdev->set_rx_rate(500e3); //initial rate diff --git a/host/examples/rx_ascii_art_dft.cpp b/host/examples/rx_ascii_art_dft.cpp new file mode 100644 index 000000000..5a24867b4 --- /dev/null +++ b/host/examples/rx_ascii_art_dft.cpp @@ -0,0 +1,143 @@ +// +// Copyright 2010 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 <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/single_usrp.hpp> +#include "ascii_art_dft.hpp" //implementation +#include <boost/program_options.hpp> +#include <boost/thread.hpp> //gets time +#include <boost/format.hpp> +#include <curses.h> +#include <iostream> +#include <complex> + +namespace po = boost::program_options; + +int UHD_SAFE_MAIN(int argc, char *argv[]){ +    uhd::set_thread_priority_safe(); + +    //variables to be set by po +    std::string args; +    size_t num_bins; +    double rate, freq, frame_rate; +    float gain, ref_lvl, dyn_rng; + +    //setup the program options +    po::options_description desc("Allowed options"); +    desc.add_options() +        ("help", "help message") +        ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args") +        // hardware parameters +        ("rate", po::value<double>(&rate), "rate of incoming samples (sps)") +        ("freq", po::value<double>(&freq)->default_value(0), "RF center frequency in Hz") +        ("gain", po::value<float>(&gain)->default_value(0), "gain for the RF chain") +        // display parameters +        ("num-bins", po::value<size_t>(&num_bins)->default_value(512), "the number of bins in the DFT") +        ("frame-rate", po::value<double>(&frame_rate)->default_value(5), "frame rate of the display (fps)") +        ("ref-lvl", po::value<float>(&ref_lvl)->default_value(0), "reference level for the display (dB)") +        ("dyn-rng", po::value<float>(&dyn_rng)->default_value(60), "dynamic range for the display (dB)") +    ; +    po::variables_map vm; +    po::store(po::parse_command_line(argc, argv, desc), vm); +    po::notify(vm); + +    //print the help message +    if (vm.count("help") or not vm.count("rate")){ +        std::cout << boost::format("UHD RX ASCII Art DFT %s") % desc << std::endl; +        return ~0; +    } + +    //create a usrp device +    std::cout << std::endl; +    std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; +    uhd::usrp::single_usrp::sptr sdev = uhd::usrp::single_usrp::make(args); +    std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl; + +    //set the rx sample rate +    std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate/1e6) << std::endl; +    sdev->set_rx_rate(rate); +    std::cout << boost::format("Actual RX Rate: %f Msps...") % (sdev->get_rx_rate()/1e6) << std::endl << std::endl; + +    //set the rx center frequency +    std::cout << boost::format("Setting RX Freq: %f Mhz...") % (freq/1e6) << std::endl; +    sdev->set_rx_freq(freq); +    std::cout << boost::format("Actual RX Freq: %f Mhz...") % (sdev->get_rx_freq()/1e6) << std::endl << std::endl; + +    //set the rx rf gain +    std::cout << boost::format("Setting RX Gain: %f dB...") % gain << std::endl; +    sdev->set_rx_gain(gain); +    std::cout << boost::format("Actual RX Gain: %f dB...") % sdev->get_rx_gain() << std::endl << std::endl; + +    //allocate recv buffer and metatdata +    uhd::rx_metadata_t md; +    std::vector<std::complex<float> > buff(num_bins); +    //------------------------------------------------------------------ +    //-- Initialize +    //------------------------------------------------------------------ +    initscr(); //curses init +    sdev->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); +    boost::system_time next_refresh = boost::get_system_time(); + +    //------------------------------------------------------------------ +    //-- Main loop +    //------------------------------------------------------------------ +    while (true){ +        //read a buffer's worth of samples every iteration +        size_t num_rx_samps = sdev->get_device()->recv( +            &buff.front(), buff.size(), md, +            uhd::io_type_t::COMPLEX_FLOAT32, +            uhd::device::RECV_MODE_FULL_BUFF +        ); +        if (num_rx_samps != buff.size()) continue; + +        //check and update the display refresh condition +        if (boost::get_system_time() < next_refresh) continue; +        next_refresh = boost::get_system_time() + boost::posix_time::microseconds(long(1e6/frame_rate)); + +        //calculate the dft and create the ascii art frame +        acsii_art_dft::log_pwr_dft_type lpdft( +            acsii_art_dft::log_pwr_dft(&buff.front(), num_rx_samps) +        ); +        std::string frame = acsii_art_dft::dft_to_plot( +            lpdft, COLS, LINES, +            sdev->get_rx_rate(), +            sdev->get_rx_freq(), +            dyn_rng, ref_lvl +        ); + +        //curses screen handling: clear and print frame +        clear(); +        printw("%s", frame.c_str()); + +        //curses key handling: no timeout, any key to exit +        timeout(0); +        int ch = getch(); +        if (ch != KEY_RESIZE and ch != ERR) break; +    } + +    //------------------------------------------------------------------ +    //-- Cleanup +    //------------------------------------------------------------------ +    sdev->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); +    endwin(); //curses done + +    //finished +    std::cout << std::endl << "Done!" << std::endl << std::endl; + +    return 0; +} diff --git a/host/examples/rx_to_file.cpp b/host/examples/rx_samples_to_file.cpp index 79d3e9d8b..c80d2a6de 100644 --- a/host/examples/rx_to_file.cpp +++ b/host/examples/rx_samples_to_file.cpp @@ -17,35 +17,35 @@  #include <uhd/utils/thread_priority.hpp>  #include <uhd/utils/safe_main.hpp> -#include <uhd/usrp/simple_usrp.hpp> +#include <uhd/usrp/single_usrp.hpp>  #include <boost/program_options.hpp>  #include <boost/format.hpp> +#include <boost/thread.hpp>  #include <iostream>  #include <fstream>  #include <complex>  namespace po = boost::program_options; -  int UHD_SAFE_MAIN(int argc, char *argv[]){      uhd::set_thread_priority_safe();      //variables to be set by po -    std::string args; -    time_t seconds_in_future; +    std::string args, file;      size_t total_num_samps; -    double rx_rate, freq; +    double rate, freq; +    float gain;      //setup the program options      po::options_description desc("Allowed options");      desc.add_options()          ("help", "help message") -        ("args", po::value<std::string>(&args)->default_value(""), "simple uhd device address args") -        ("secs", po::value<time_t>(&seconds_in_future)->default_value(3), "number of seconds in the future to receive") +        ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args") +        ("file", po::value<std::string>(&file)->default_value("out.16sc.dat"), "name of the file to write binary samples to")          ("nsamps", po::value<size_t>(&total_num_samps)->default_value(1000), "total number of samples to receive") -        ("rxrate", po::value<double>(&rx_rate)->default_value(100e6/16), "rate of incoming samples") +        ("rate", po::value<double>(&rate)->default_value(100e6/16), "rate of incoming samples")          ("freq", po::value<double>(&freq)->default_value(0), "rf center frequency in Hz") -        ("dilv", "specify to disable inner-loop verbose") +        ("gain", po::value<float>(&gain)->default_value(0), "gain for the RF chain")      ;      po::variables_map vm;      po::store(po::parse_command_line(argc, argv, desc), vm); @@ -53,50 +53,46 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      //print the help message      if (vm.count("help")){ -        std::cout << boost::format("UHD RX Timed Samples %s") % desc << std::endl; +        std::cout << boost::format("UHD RX to File %s") % desc << std::endl;          return ~0;      } -    bool verbose = vm.count("dilv") == 0; -      //create a usrp device      std::cout << std::endl;      std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; -    uhd::usrp::simple_usrp::sptr sdev = uhd::usrp::simple_usrp::make(args); +    uhd::usrp::single_usrp::sptr sdev = uhd::usrp::single_usrp::make(args);      uhd::device::sptr dev = sdev->get_device();      std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl; -    //set properties on the device -    std::cout << boost::format("Setting RX Rate: %f Msps...") % (rx_rate/1e6) << std::endl; -    sdev->set_rx_rate(rx_rate); -    std::cout << boost::format("Actual RX Rate: %f Msps...") % (sdev->get_rx_rate()/1e6) << std::endl; -    std::cout << boost::format("Setting device timestamp to 0...") << std::endl; +    //set the rx sample rate +    std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate/1e6) << std::endl; +    sdev->set_rx_rate(rate); +    std::cout << boost::format("Actual RX Rate: %f Msps...") % (sdev->get_rx_rate()/1e6) << std::endl << std::endl; + +    //set the rx center frequency +    std::cout << boost::format("Setting RX Freq: %f Mhz...") % (freq/1e6) << std::endl;      sdev->set_rx_freq(freq); -    sdev->set_time_now(uhd::time_spec_t(0.0)); +    std::cout << boost::format("Actual RX Freq: %f Mhz...") % (sdev->get_rx_freq()/1e6) << std::endl << std::endl; -    uhd::gain_range_t rx_gain = sdev->get_rx_gain_range(); -    std::cout << "Setting RX Gain to: " << rx_gain.max << std::endl; -    sdev->set_rx_gain(rx_gain.max); +    //set the rx rf gain +    std::cout << boost::format("Setting RX Gain: %f dB...") % gain << std::endl; +    sdev->set_rx_gain(gain); +    std::cout << boost::format("Actual RX Gain: %f dB...") % sdev->get_rx_gain() << std::endl << std::endl; -    sleep(1); +    boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for some setup time      std::cout << "LO Locked = " << sdev->get_rx_lo_locked() << std::endl;      //setup streaming -    std::cout << std::endl; -    std::cout << boost::format( -        "Begin streaming %u samples, %d seconds in the future..." -    ) % total_num_samps % seconds_in_future << std::endl;      uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE);      stream_cmd.num_samps = total_num_samps; -    stream_cmd.stream_now = false; -    stream_cmd.time_spec = uhd::time_spec_t(seconds_in_future); +    stream_cmd.stream_now = true;      sdev->issue_stream_cmd(stream_cmd);      //loop until total number of samples reached      size_t num_acc_samps = 0; //number of accumulated samples      uhd::rx_metadata_t md;      std::vector<std::complex<short> > buff(dev->get_max_recv_samps_per_packet()); -    std::ofstream outfile("out.dat", std::ofstream::binary); +    std::ofstream outfile(file.c_str(), std::ofstream::binary);      while(num_acc_samps < total_num_samps){          size_t num_rx_samps = dev->recv( @@ -124,11 +120,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){              goto done_loop;          } -	outfile.write((const char*)&buff[0], num_rx_samps * sizeof(std::complex<short>)); - -        if(verbose) std::cout << boost::format( -            "Got packet: %u samples, %u full secs, %f frac secs" -        ) % num_rx_samps % md.time_spec.get_full_secs() % md.time_spec.get_frac_secs() << std::endl; +        //write complex short integer samples to the binary file +        outfile.write((const char*)&buff[0], num_rx_samps * sizeof(std::complex<short>));          num_acc_samps += num_rx_samps;      } done_loop: diff --git a/host/examples/rx_samples_to_udp.cpp b/host/examples/rx_samples_to_udp.cpp new file mode 100644 index 000000000..488c95494 --- /dev/null +++ b/host/examples/rx_samples_to_udp.cpp @@ -0,0 +1,135 @@ +// +// Copyright 2010 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 <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/single_usrp.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <boost/thread.hpp> +#include <iostream> +#include <complex> + +namespace po = boost::program_options; + +int UHD_SAFE_MAIN(int argc, char *argv[]){ +    uhd::set_thread_priority_safe(); + +    //variables to be set by po +    std::string args; +    size_t total_num_samps; +    double rate, freq; +    float gain; +    std::string addr, port; + +    //setup the program options +    po::options_description desc("Allowed options"); +    desc.add_options() +        ("help", "help message") +        ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args") +        ("nsamps", po::value<size_t>(&total_num_samps)->default_value(1000), "total number of samples to receive") +        ("rate", po::value<double>(&rate)->default_value(100e6/16), "rate of incoming samples") +        ("freq", po::value<double>(&freq)->default_value(0), "rf center frequency in Hz") +        ("gain", po::value<float>(&gain)->default_value(0), "gain for the RF chain") +        ("port", po::value<std::string>(&port)->default_value("7124"), "server udp port") +        ("addr", po::value<std::string>(&addr)->default_value("192.168.1.10"), "resolvable server address") +    ; +    po::variables_map vm; +    po::store(po::parse_command_line(argc, argv, desc), vm); +    po::notify(vm); + +    //print the help message +    if (vm.count("help")){ +        std::cout << boost::format("UHD RX to UDP %s") % desc << std::endl; +        return ~0; +    } + +    //create a usrp device +    std::cout << std::endl; +    std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; +    uhd::usrp::single_usrp::sptr sdev = uhd::usrp::single_usrp::make(args); +    uhd::device::sptr dev = sdev->get_device(); +    std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl; + +    //set the rx sample rate +    std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate/1e6) << std::endl; +    sdev->set_rx_rate(rate); +    std::cout << boost::format("Actual RX Rate: %f Msps...") % (sdev->get_rx_rate()/1e6) << std::endl << std::endl; + +    //set the rx center frequency +    std::cout << boost::format("Setting RX Freq: %f Mhz...") % (freq/1e6) << std::endl; +    sdev->set_rx_freq(freq); +    std::cout << boost::format("Actual RX Freq: %f Mhz...") % (sdev->get_rx_freq()/1e6) << std::endl << std::endl; + +    //set the rx rf gain +    std::cout << boost::format("Setting RX Gain: %f dB...") % gain << std::endl; +    sdev->set_rx_gain(gain); +    std::cout << boost::format("Actual RX Gain: %f dB...") % sdev->get_rx_gain() << std::endl << std::endl; + +    boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for some setup time +    std::cout << "LO Locked = " << sdev->get_rx_lo_locked() << std::endl; + +    //setup streaming +    uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); +    stream_cmd.num_samps = total_num_samps; +    stream_cmd.stream_now = true; +    sdev->issue_stream_cmd(stream_cmd); + +    //loop until total number of samples reached +    size_t num_acc_samps = 0; //number of accumulated samples +    uhd::rx_metadata_t md; +    std::vector<std::complex<float> > buff(dev->get_max_recv_samps_per_packet()); +    uhd::transport::udp_simple::sptr udp_xport = uhd::transport::udp_simple::make_connected(addr, port); + +    while(num_acc_samps < total_num_samps){ +        size_t num_rx_samps = dev->recv( +            &buff.front(), buff.size(), md, +            uhd::io_type_t::COMPLEX_FLOAT32, +            uhd::device::RECV_MODE_ONE_PACKET +        ); + +        //handle the error codes +        switch(md.error_code){ +        case uhd::rx_metadata_t::ERROR_CODE_NONE: +            break; + +        case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT: +            if (num_acc_samps == 0) continue; +            std::cout << boost::format( +                "Got timeout before all samples received, possible packet loss, exiting loop..." +            ) << std::endl; +            goto done_loop; + +        default: +            std::cout << boost::format( +                "Got error code 0x%x, exiting loop..." +            ) % md.error_code << std::endl; +            goto done_loop; +        } + +        //send complex single precision floating point samples over udp +        udp_xport->send(boost::asio::buffer(buff, num_rx_samps)); + +        num_acc_samps += num_rx_samps; +    } done_loop: + +    //finished +    std::cout << std::endl << "Done!" << std::endl << std::endl; + +    return 0; +} diff --git a/host/examples/rx_timed_samples.cpp b/host/examples/rx_timed_samples.cpp index 441665900..8a810811f 100644 --- a/host/examples/rx_timed_samples.cpp +++ b/host/examples/rx_timed_samples.cpp @@ -32,7 +32,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      std::string args;      time_t seconds_in_future;      size_t total_num_samps; -    double rx_rate, freq; +    double rate, freq;      //setup the program options      po::options_description desc("Allowed options"); @@ -41,7 +41,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){          ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args")          ("secs", po::value<time_t>(&seconds_in_future)->default_value(3), "number of seconds in the future to receive")          ("nsamps", po::value<size_t>(&total_num_samps)->default_value(1000), "total number of samples to receive") -        ("rxrate", po::value<double>(&rx_rate)->default_value(100e6/16), "rate of incoming samples") +        ("rate", po::value<double>(&rate)->default_value(100e6/16), "rate of incoming samples")          ("freq", po::value<double>(&freq)->default_value(0), "rf center frequency in Hz")          ("dilv", "specify to disable inner-loop verbose")      ; @@ -64,12 +64,17 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      uhd::device::sptr dev = sdev->get_device();      std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl; -    //set properties on the device -    std::cout << boost::format("Setting RX Rate: %f Msps...") % (rx_rate/1e6) << std::endl; -    sdev->set_rx_rate(rx_rate); -    std::cout << boost::format("Actual RX Rate: %f Msps...") % (sdev->get_rx_rate()/1e6) << std::endl; -    std::cout << boost::format("Setting device timestamp to 0...") << std::endl; +    //set the rx sample rate +    std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate/1e6) << std::endl; +    sdev->set_rx_rate(rate); +    std::cout << boost::format("Actual RX Rate: %f Msps...") % (sdev->get_rx_rate()/1e6) << std::endl << std::endl; + +    //set the rx center frequency +    std::cout << boost::format("Setting RX Freq: %f Mhz...") % (freq/1e6) << std::endl;      sdev->set_rx_freq(freq); +    std::cout << boost::format("Actual RX Freq: %f Mhz...") % (sdev->get_rx_freq()/1e6) << std::endl << std::endl; + +    std::cout << boost::format("Setting device timestamp to 0...") << std::endl;      sdev->set_time_now(uhd::time_spec_t(0.0));      //setup streaming diff --git a/host/examples/test_async_messages.cpp b/host/examples/test_async_messages.cpp index 4c9d18121..e4a996ef5 100644 --- a/host/examples/test_async_messages.cpp +++ b/host/examples/test_async_messages.cpp @@ -26,8 +26,6 @@  namespace po = boost::program_options; -static const size_t async_to_ms = 100; -  /*!   * Test that no messages are received:   *    Send a burst of many samples that will fragment internally. @@ -52,13 +50,13 @@ void test_no_async_message(uhd::usrp::single_usrp::sptr sdev){      );      uhd::async_metadata_t async_md; -    if (dev->recv_async_msg(async_md, async_to_ms)){ +    if (dev->recv_async_msg(async_md)){          std::cout << boost::format(              "failed:\n"              "    Got unexpected event code 0x%x.\n"          ) % async_md.event_code << std::endl;          //clear the async messages -        while (dev->recv_async_msg(async_md, 0)); +        while (dev->recv_async_msg(async_md, 0)){};      }      else{          std::cout << boost::format( @@ -88,7 +86,7 @@ void test_underflow_message(uhd::usrp::single_usrp::sptr sdev){      );      uhd::async_metadata_t async_md; -    if (not dev->recv_async_msg(async_md, async_to_ms)){ +    if (not dev->recv_async_msg(async_md)){          std::cout << boost::format(              "failed:\n"              "    Async message recv timed out.\n" @@ -135,7 +133,7 @@ void test_time_error_message(uhd::usrp::single_usrp::sptr sdev){      );      uhd::async_metadata_t async_md; -    if (not dev->recv_async_msg(async_md, async_to_ms)){ +    if (not dev->recv_async_msg(async_md)){          std::cout << boost::format(              "failed:\n"              "    Async message recv timed out.\n" diff --git a/host/examples/tx_timed_samples.cpp b/host/examples/tx_timed_samples.cpp index f34c121d5..799da37e0 100644 --- a/host/examples/tx_timed_samples.cpp +++ b/host/examples/tx_timed_samples.cpp @@ -33,7 +33,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      time_t seconds_in_future;      size_t total_num_samps;      size_t samps_per_packet; -    double tx_rate, freq; +    double rate, freq;      float ampl;      //setup the program options @@ -44,7 +44,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){          ("secs", po::value<time_t>(&seconds_in_future)->default_value(3), "number of seconds in the future to transmit")          ("nsamps", po::value<size_t>(&total_num_samps)->default_value(1000), "total number of samples to transmit")          ("spp", po::value<size_t>(&samps_per_packet)->default_value(1000), "number of samples per packet") -        ("txrate", po::value<double>(&tx_rate)->default_value(100e6/16), "rate of outgoing samples") +        ("rate", po::value<double>(&rate)->default_value(100e6/16), "rate of outgoing samples")          ("freq", po::value<double>(&freq)->default_value(0), "rf center frequency in Hz")          ("ampl", po::value<float>(&l)->default_value(float(0.3)), "amplitude of each sample")          ("dilv", "specify to disable inner-loop verbose") @@ -68,12 +68,17 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      uhd::device::sptr dev = sdev->get_device();      std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl; -    //set properties on the device -    std::cout << boost::format("Setting TX Rate: %f Msps...") % (tx_rate/1e6) << std::endl; -    sdev->set_tx_rate(tx_rate); -    std::cout << boost::format("Actual TX Rate: %f Msps...") % (sdev->get_tx_rate()/1e6) << std::endl; -    std::cout << boost::format("Setting device timestamp to 0...") << std::endl; +    //set the tx sample rate +    std::cout << boost::format("Setting TX Rate: %f Msps...") % (rate/1e6) << std::endl; +    sdev->set_tx_rate(rate); +    std::cout << boost::format("Actual TX Rate: %f Msps...") % (sdev->get_tx_rate()/1e6) << std::endl << std::endl; + +    //set the tx center frequency +    std::cout << boost::format("Setting TX Freq: %f Mhz...") % (freq/1e6) << std::endl;      sdev->set_tx_freq(freq); +    std::cout << boost::format("Actual TX Freq: %f Mhz...") % (sdev->get_tx_freq()/1e6) << std::endl << std::endl; + +    std::cout << boost::format("Setting device timestamp to 0...") << std::endl;      sdev->set_time_now(uhd::time_spec_t(0.0));      //allocate data to send @@ -95,8 +100,11 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){          size_t num_tx_samps = dev->send(              &buff.front(), samps_to_send, md,              uhd::io_type_t::COMPLEX_FLOAT32, -            uhd::device::SEND_MODE_FULL_BUFF +            uhd::device::SEND_MODE_FULL_BUFF, +            //send will backup into the host this many seconds before sending: +            seconds_in_future + 0.1 //timeout (delay before transmit + padding)          ); +        if (num_tx_samps < samps_to_send) std::cout << "Send timeout..." << std::endl;          if(verbose) std::cout << std::endl << boost::format("Sent %d samples") % num_tx_samps << std::endl;      } diff --git a/host/include/linux/usrp_e.h b/host/include/linux/usrp_e.h index 80f3a287b..4c6a5dd89 100644 --- a/host/include/linux/usrp_e.h +++ b/host/include/linux/usrp_e.h @@ -65,12 +65,16 @@ struct usrp_e_i2c {  #define USRP_E_I2C_READ		_IOWR(USRP_E_IOC_MAGIC, 0x25, struct usrp_e_i2c)  #define USRP_E_I2C_WRITE	_IOW(USRP_E_IOC_MAGIC, 0x26, struct usrp_e_i2c)  #define USRP_E_GET_RB_INFO      _IOR(USRP_E_IOC_MAGIC, 0x27, struct usrp_e_ring_buffer_size_t) +#define USRP_E_GET_COMPAT_NUMBER _IO(USRP_E_IOC_MAGIC, 0x28) + +#define USRP_E_COMPAT_NUMBER 1  /* Flag defines */  #define RB_USER (1<<0)  #define RB_KERNEL (1<<1)  #define RB_OVERRUN (1<<2)  #define RB_DMA_ACTIVE (1<<3) +#define RB_USER_PROCESS (1<<4)  struct ring_buffer_info {  	int flags; diff --git a/host/include/uhd/config.hpp b/host/include/uhd/config.hpp index dacd3a96b..2918c2340 100644 --- a/host/include/uhd/config.hpp +++ b/host/include/uhd/config.hpp @@ -31,11 +31,12 @@  # pragma warning(disable: 4251) // class 'A<T>' needs to have dll-interface to be used by clients of class 'B'  //# pragma warning(disable: 4127) // conditional expression is constant  //# pragma warning(disable: 4290) // C++ exception specification ignored except to ... -# pragma warning(disable: 4180) // qualifier applied to function type has no meaning; ignored +//# pragma warning(disable: 4180) // qualifier applied to function type has no meaning; ignored  # pragma warning(disable: 4275) // non dll-interface class ... used as base for dll-interface class ...  //# pragma warning(disable: 4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data  //# pragma warning(disable: 4511) // 'class' : copy constructor could not be generated -# pragma warning(disable: 4250) // 'class' : inherits 'method' via dominance +//# pragma warning(disable: 4250) // 'class' : inherits 'method' via dominance +# pragma warning(disable: 4200) // nonstandard extension used : zero-sized array in struct/union  #endif  // define logical operators diff --git a/host/include/uhd/device.hpp b/host/include/uhd/device.hpp index 2077cae62..992276928 100644 --- a/host/include/uhd/device.hpp +++ b/host/include/uhd/device.hpp @@ -41,9 +41,6 @@ public:      typedef boost::function<device_addrs_t(const device_addr_t &)> find_t;      typedef boost::function<sptr(const device_addr_t &)> make_t; -    //! A reasonable default timeout for receive -    static const size_t default_recv_timeout_ms = 100; -      /*!       * Register a device into the discovery and factory system.       * @@ -112,12 +109,15 @@ public:       *       * This is a blocking call and will not return until the number       * of samples returned have been read out of each buffer. +     * Under a timeout condition, the number of samples returned +     * may be less than the number of samples specified.       *       * \param buffs a vector of read-only memory containing IF data       * \param nsamps_per_buff the number of samples to send, per buffer       * \param metadata data describing the buffer's contents       * \param io_type the type of data loaded in the buffer       * \param send_mode tells send how to unload the buffer +     * \param timeout the timeout in seconds to wait on a packet       * \return the number of samples sent       */      virtual size_t send( @@ -125,7 +125,8 @@ public:          size_t nsamps_per_buff,          const tx_metadata_t &metadata,          const io_type_t &io_type, -        send_mode_t send_mode +        send_mode_t send_mode, +        double timeout = 0.1      ) = 0;      /*! @@ -136,7 +137,8 @@ public:          size_t nsamps_per_buff,          const tx_metadata_t &metadata,          const io_type_t &io_type, -        send_mode_t send_mode +        send_mode_t send_mode, +        double timeout = 0.1      );      /*! @@ -154,7 +156,9 @@ public:       * See the rx metadata fragment flags and offset fields for details.       *       * This is a blocking call and will not return until the number -     * of samples returned have been written into each buffer or timeout. +     * of samples returned have been written into each buffer. +     * Under a timeout condition, the number of samples returned +     * may be less than the number of samples specified.       *       * When using the full buffer recv mode, the metadata only applies       * to the first packet received and written into the recv buffers. @@ -165,7 +169,7 @@ public:       * \param metadata data to fill describing the buffer       * \param io_type the type of data to fill into the buffer       * \param recv_mode tells recv how to load the buffer -     * \param timeout_ms the timeout in milliseconds to wait for a packet +     * \param timeout the timeout in seconds to wait for a packet       * \return the number of samples received or 0 on error       */      virtual size_t recv( @@ -174,7 +178,7 @@ public:          rx_metadata_t &metadata,          const io_type_t &io_type,          recv_mode_t recv_mode, -        size_t timeout_ms = default_recv_timeout_ms +        double timeout = 0.1      ) = 0;      /*! @@ -186,7 +190,7 @@ public:          rx_metadata_t &metadata,          const io_type_t &io_type,          recv_mode_t recv_mode, -        size_t timeout_ms = default_recv_timeout_ms +        double timeout = 0.1      );      /*! @@ -204,12 +208,11 @@ public:      /*!       * Receive and asynchronous message from the device.       * \param async_metadata the metadata to be filled in -     * \param timeout_ms the timeout in milliseconds to wait for a message +     * \param timeout the timeout in seconds to wait for a message       * \return true when the async_metadata is valid, false for timeout       */      virtual bool recv_async_msg( -        async_metadata_t &async_metadata, -        size_t timeout_ms = default_recv_timeout_ms +        async_metadata_t &async_metadata, double timeout = 0.1      ) = 0;  }; diff --git a/host/include/uhd/device.ipp b/host/include/uhd/device.ipp index 60a3f535d..e2e51ecd0 100644 --- a/host/include/uhd/device.ipp +++ b/host/include/uhd/device.ipp @@ -25,12 +25,13 @@ namespace uhd{          size_t nsamps_per_buff,          const tx_metadata_t &metadata,          const io_type_t &io_type, -        send_mode_t send_mode +        send_mode_t send_mode, +        double timeout      ){          return this->send(              std::vector<const void *>(1, buff),              nsamps_per_buff, metadata, -            io_type, send_mode +            io_type, send_mode, timeout          );      } @@ -40,12 +41,12 @@ namespace uhd{          rx_metadata_t &metadata,          const io_type_t &io_type,          recv_mode_t recv_mode, -        size_t timeout_ms +        double timeout      ){          return this->recv(              std::vector<void *>(1, buff),              nsamps_per_buff, metadata, -            io_type, recv_mode, timeout_ms +            io_type, recv_mode, timeout          );      } diff --git a/host/include/uhd/transport/alignment_buffer.hpp b/host/include/uhd/transport/alignment_buffer.hpp index 29ba74efc..f44a037f8 100644 --- a/host/include/uhd/transport/alignment_buffer.hpp +++ b/host/include/uhd/transport/alignment_buffer.hpp @@ -48,20 +48,17 @@ namespace uhd{ namespace transport{           * \return true if the element fit without popping for space           */          virtual bool push_with_pop_on_full( -            const elem_type &elem, -            const seq_type &seq, -            size_t index +            const elem_type &elem, const seq_type &seq, size_t index          ) = 0;          /*!           * Pop an aligned set of elements from this alignment buffer.           * \param elems a collection to store the aligned elements -         * \param time the timeout time +         * \param timeout the timeout in seconds           * \return false when the operation times out           */          virtual bool pop_elems_with_timed_wait( -            std::vector<elem_type> &elems, -            const time_duration_t &time +            std::vector<elem_type> &elems, double timeout          ) = 0;      }; diff --git a/host/include/uhd/transport/alignment_buffer.ipp b/host/include/uhd/transport/alignment_buffer.ipp index 61b3b60f5..833b5d399 100644 --- a/host/include/uhd/transport/alignment_buffer.ipp +++ b/host/include/uhd/transport/alignment_buffer.ipp @@ -41,9 +41,7 @@ namespace uhd{ namespace transport{ namespace{ /*anon*/          }          UHD_INLINE bool push_with_pop_on_full( -            const elem_type &elem, -            const seq_type &seq, -            size_t index +            const elem_type &elem, const seq_type &seq, size_t index          ){              //clear the buffer for this index if the seqs are mis-ordered              if (seq < _last_seqs[index]){ @@ -54,17 +52,16 @@ namespace uhd{ namespace transport{ namespace{ /*anon*/          }          UHD_INLINE bool pop_elems_with_timed_wait( -            std::vector<elem_type> &elems, -            const time_duration_t &time +            std::vector<elem_type> &elems, double timeout          ){ -            boost::system_time exit_time = boost::get_system_time() + time; +            boost::system_time exit_time = boost::get_system_time() + to_time_dur(timeout);              buff_contents_type buff_contents_tmp;              std::list<size_t> indexes_to_do(_all_indexes);              //do an initial pop to load an initial sequence id              size_t index = indexes_to_do.front();              if (not _buffs[index]->pop_with_timed_wait( -                buff_contents_tmp, exit_time - boost::get_system_time() +                buff_contents_tmp, from_time_dur(exit_time - boost::get_system_time())              )) return false;              elems[index] = buff_contents_tmp.first;              seq_type expected_seq_id = buff_contents_tmp.second; @@ -79,7 +76,7 @@ namespace uhd{ namespace transport{ namespace{ /*anon*/                      indexes_to_do = _all_indexes;                      index = indexes_to_do.front();                      if (not _buffs[index]->pop_with_timed_wait( -                        buff_contents_tmp, exit_time - boost::get_system_time() +                        buff_contents_tmp, from_time_dur(exit_time - boost::get_system_time())                      )) return false;                      elems[index] = buff_contents_tmp.first;                      expected_seq_id = buff_contents_tmp.second; @@ -89,7 +86,7 @@ namespace uhd{ namespace transport{ namespace{ /*anon*/                  //pop an element off for this index                  index = indexes_to_do.front();                  if (not _buffs[index]->pop_with_timed_wait( -                    buff_contents_tmp, exit_time - boost::get_system_time() +                    buff_contents_tmp, from_time_dur(exit_time - boost::get_system_time())                  )) return false;                  //if the sequence id matches: diff --git a/host/include/uhd/transport/bounded_buffer.hpp b/host/include/uhd/transport/bounded_buffer.hpp index d1deece96..aca93b071 100644 --- a/host/include/uhd/transport/bounded_buffer.hpp +++ b/host/include/uhd/transport/bounded_buffer.hpp @@ -20,13 +20,9 @@  #include <uhd/config.hpp>  #include <boost/shared_ptr.hpp> -#include <boost/date_time/posix_time/posix_time_types.hpp>  namespace uhd{ namespace transport{ -    //! typedef for the time duration type for wait operations -    typedef boost::posix_time::time_duration time_duration_t; -      /*!       * Implement a templated bounded buffer:       * Used for passing elements between threads in a producer-consumer model. @@ -64,10 +60,10 @@ namespace uhd{ namespace transport{           * Push a new element into the bounded_buffer.           * Wait until the bounded_buffer becomes non-full or timeout.           * \param elem the new element to push -         * \param time the timeout time +         * \param timeout the timeout in seconds           * \return false when the operation times out           */ -        virtual bool push_with_timed_wait(const elem_type &elem, const time_duration_t &time) = 0; +        virtual bool push_with_timed_wait(const elem_type &elem, double timeout) = 0;          /*!           * Pop an element from the bounded_buffer. @@ -80,10 +76,10 @@ namespace uhd{ namespace transport{           * Pop an element from the bounded_buffer.           * Wait until the bounded_buffer becomes non-empty or timeout.           * \param elem the element reference pop to -         * \param time the timeout time +         * \param timeout the timeout in seconds           * \return false when the operation times out           */ -        virtual bool pop_with_timed_wait(elem_type &elem, const time_duration_t &time) = 0; +        virtual bool pop_with_timed_wait(elem_type &elem, double timeout) = 0;          /*!           * Clear all elements from the bounded_buffer. diff --git a/host/include/uhd/transport/bounded_buffer.ipp b/host/include/uhd/transport/bounded_buffer.ipp index e106e229e..edc7faa06 100644 --- a/host/include/uhd/transport/bounded_buffer.ipp +++ b/host/include/uhd/transport/bounded_buffer.ipp @@ -19,17 +19,28 @@  #define INCLUDED_UHD_TRANSPORT_BOUNDED_BUFFER_IPP  #include <boost/bind.hpp> +#include <boost/function.hpp>  #include <boost/circular_buffer.hpp>  #include <boost/thread/condition.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp>  namespace uhd{ namespace transport{ namespace{ /*anon*/ +    static UHD_INLINE boost::posix_time::time_duration to_time_dur(double timeout){ +        return boost::posix_time::microseconds(long(timeout*1e6)); +    } + +    static UHD_INLINE double from_time_dur(const boost::posix_time::time_duration &time_dur){ +        return 1e-6*time_dur.total_microseconds(); +    } +      template <typename elem_type>      class bounded_buffer_impl : public bounded_buffer<elem_type>{      public:          bounded_buffer_impl(size_t capacity) : _buffer(capacity){ -            /* NOP */ +            _not_full_fcn = boost::bind(&bounded_buffer_impl<elem_type>::not_full, this); +            _not_empty_fcn = boost::bind(&bounded_buffer_impl<elem_type>::not_empty, this);          }          UHD_INLINE bool push_with_pop_on_full(const elem_type &elem){ @@ -51,15 +62,17 @@ namespace uhd{ namespace transport{ namespace{ /*anon*/          UHD_INLINE void push_with_wait(const elem_type &elem){              boost::unique_lock<boost::mutex> lock(_mutex); -            _full_cond.wait(lock, boost::bind(&bounded_buffer_impl<elem_type>::not_full, this)); +            _full_cond.wait(lock, _not_full_fcn);              _buffer.push_front(elem);              lock.unlock();              _empty_cond.notify_one();          } -        bool push_with_timed_wait(const elem_type &elem, const time_duration_t &time){ +        UHD_INLINE bool push_with_timed_wait(const elem_type &elem, double timeout){              boost::unique_lock<boost::mutex> lock(_mutex); -            if (not _full_cond.timed_wait(lock, time, boost::bind(&bounded_buffer_impl<elem_type>::not_full, this))) return false; +            if (not _full_cond.timed_wait( +                lock, to_time_dur(timeout), _not_full_fcn +            )) return false;              _buffer.push_front(elem);              lock.unlock();              _empty_cond.notify_one(); @@ -68,16 +81,18 @@ namespace uhd{ namespace transport{ namespace{ /*anon*/          UHD_INLINE void pop_with_wait(elem_type &elem){              boost::unique_lock<boost::mutex> lock(_mutex); -            _empty_cond.wait(lock, boost::bind(&bounded_buffer_impl<elem_type>::not_empty, this)); -            elem = _buffer.back(); _buffer.pop_back(); +            _empty_cond.wait(lock, _not_empty_fcn); +            elem = this->pop_back();              lock.unlock();              _full_cond.notify_one();          } -        bool pop_with_timed_wait(elem_type &elem, const time_duration_t &time){ +        UHD_INLINE bool pop_with_timed_wait(elem_type &elem, double timeout){              boost::unique_lock<boost::mutex> lock(_mutex); -            if (not _empty_cond.timed_wait(lock, time, boost::bind(&bounded_buffer_impl<elem_type>::not_empty, this))) return false; -            elem = _buffer.back(); _buffer.pop_back(); +            if (not _empty_cond.timed_wait( +                lock, to_time_dur(timeout), _not_empty_fcn +            )) return false; +            elem = this->pop_back();              lock.unlock();              _full_cond.notify_one();              return true; @@ -85,7 +100,7 @@ namespace uhd{ namespace transport{ namespace{ /*anon*/          UHD_INLINE void clear(void){              boost::unique_lock<boost::mutex> lock(_mutex); -            while (not_empty()) _buffer.pop_back(); +            while (not_empty()) this->pop_back();              lock.unlock();              _full_cond.notify_one();          } @@ -97,6 +112,21 @@ namespace uhd{ namespace transport{ namespace{ /*anon*/          bool not_full(void) const{return not _buffer.full();}          bool not_empty(void) const{return not _buffer.empty();} + +        boost::function<bool(void)> _not_full_fcn, _not_empty_fcn; + +        /*! +         * Three part operation to pop an element: +         * 1) assign elem to the back element +         * 2) assign the back element to empty +         * 3) pop the back to move the counter +         */ +        UHD_INLINE elem_type pop_back(void){ +            elem_type elem = _buffer.back(); +            _buffer.back() = elem_type(); +            _buffer.pop_back(); +            return elem; +        }      };  }}} //namespace diff --git a/host/include/uhd/transport/udp_zero_copy.hpp b/host/include/uhd/transport/udp_zero_copy.hpp index 818709973..bbba97b21 100644 --- a/host/include/uhd/transport/udp_zero_copy.hpp +++ b/host/include/uhd/transport/udp_zero_copy.hpp @@ -20,6 +20,7 @@  #include <uhd/config.hpp>  #include <uhd/transport/zero_copy.hpp> +#include <uhd/types/device_addr.hpp>  #include <boost/shared_ptr.hpp>  namespace uhd{ namespace transport{ @@ -50,14 +51,12 @@ public:       *       * \param addr a string representing the destination address       * \param port a string representing the destination port -     * \param recv_buff_size size in bytes for the recv buffer, 0 for automatic -     * \param send_buff_size size in bytes for the send buffer, 0 for automatic +     * \param hints optional parameters to pass to the underlying transport       */      static sptr make(          const std::string &addr,          const std::string &port, -        size_t recv_buff_size = 0, -        size_t send_buff_size = 0 +        const device_addr_t &hints = device_addr_t()      );  }; diff --git a/host/include/uhd/transport/usb_control.hpp b/host/include/uhd/transport/usb_control.hpp index 6137ecf84..e6c32f78e 100644 --- a/host/include/uhd/transport/usb_control.hpp +++ b/host/include/uhd/transport/usb_control.hpp @@ -18,7 +18,7 @@  #ifndef INCLUDED_UHD_TRANSPORT_USB_CONTROL_HPP  #define INCLUDED_UHD_TRANSPORT_USB_CONTROL_HPP -#include "usb_device_handle.hpp" +#include <uhd/transport/usb_device_handle.hpp>  namespace uhd { namespace transport { @@ -50,9 +50,9 @@ public:       * \param index        2-byte (wIndex)       * \param buff         buffer to hold send or receive data       * \param length       2-byte (wLength) -     * \return             number of bytes submitted +     * \return             number of bytes submitted or error code       */ -    virtual size_t submit(boost::uint8_t request_type, +    virtual ssize_t submit(boost::uint8_t request_type,                            boost::uint8_t request,                            boost::uint16_t value,                            boost::uint16_t index,  diff --git a/host/include/uhd/transport/usb_device_handle.hpp b/host/include/uhd/transport/usb_device_handle.hpp index 735a3acbe..6f8d868be 100644 --- a/host/include/uhd/transport/usb_device_handle.hpp +++ b/host/include/uhd/transport/usb_device_handle.hpp @@ -38,7 +38,7 @@ namespace uhd { namespace transport {   *       a true descriptor serial number string. This interface returns the   *       actual string descriptor.   */ -class usb_device_handle : boost::noncopyable { +class UHD_API usb_device_handle : boost::noncopyable {  public:      typedef boost::shared_ptr<usb_device_handle> sptr; @@ -61,12 +61,6 @@ public:      virtual boost::uint16_t get_product_id() const = 0;      /*! -     * Return the device's USB address -     * \return a Product ID -     */ -    virtual boost::uint16_t get_device_addr() const = 0; - -    /*!       * Return a vector of USB devices on this host        * \return a vector of USB device handles that match vid and pid       */ diff --git a/host/include/uhd/transport/usb_zero_copy.hpp b/host/include/uhd/transport/usb_zero_copy.hpp index 75232c22a..b39171fba 100644 --- a/host/include/uhd/transport/usb_zero_copy.hpp +++ b/host/include/uhd/transport/usb_zero_copy.hpp @@ -18,8 +18,9 @@  #ifndef INCLUDED_UHD_TRANSPORT_USB_ZERO_COPY_HPP  #define INCLUDED_UHD_TRANSPORT_USB_ZERO_COPY_HPP -#include "usb_device_handle.hpp" +#include <uhd/transport/usb_device_handle.hpp>  #include <uhd/transport/zero_copy.hpp> +#include <uhd/types/device_addr.hpp>  namespace uhd { namespace transport { @@ -45,16 +46,16 @@ public:       * The underlying implementation may be platform specific.       *       * \param handle a device handle that uniquely identifying the device -     * \param rx_endpoint an integer specifiying an IN endpoint number  -     * \param tx_endpoint an integer specifiying an OUT endpoint number -     * \param buff_size total number of bytes of buffer space to allocate  -     * \param block_size number of bytes allocated for each I/O transaction  +     * \param recv_endpoint an integer specifiying an IN endpoint number +     * \param send_endpoint an integer specifiying an OUT endpoint number +     * \param hints optional parameters to pass to the underlying transport       */ -    static sptr make(usb_device_handle::sptr handle, -                     unsigned int rx_endpoint, -                     unsigned int tx_endpoint, -                     size_t buff_size = 0, -                     size_t block_size = 0); +    static sptr make( +        usb_device_handle::sptr handle, +        size_t recv_endpoint, +        size_t send_endpoint, +        const device_addr_t &hints = device_addr_t() +    );  };  }} //namespace diff --git a/host/include/uhd/transport/zero_copy.hpp b/host/include/uhd/transport/zero_copy.hpp index 513291b63..7d8fb4b83 100644 --- a/host/include/uhd/transport/zero_copy.hpp +++ b/host/include/uhd/transport/zero_copy.hpp @@ -19,10 +19,10 @@  #define INCLUDED_UHD_TRANSPORT_ZERO_COPY_HPP  #include <uhd/config.hpp> -#include <uhd/utils/pimpl.hpp>  #include <boost/asio/buffer.hpp>  #include <boost/utility.hpp>  #include <boost/shared_ptr.hpp> +#include <boost/function.hpp>  namespace uhd{ namespace transport{ @@ -34,14 +34,27 @@ namespace uhd{ namespace transport{      class UHD_API managed_recv_buffer : boost::noncopyable{      public:          typedef boost::shared_ptr<managed_recv_buffer> sptr; +        typedef boost::function<void(void)> release_fcn_t; + +        /*! +         * Make a safe managed receive buffer: +         * A safe managed buffer ensures that release is called once, +         * either by the user or automatically upon deconstruction. +         * \param buff a reference to the constant buffer +         * \param release_fcn callback to release the memory +         * \return a new managed receive buffer +         */ +        static sptr make_safe( +            const boost::asio::const_buffer &buff, +            const release_fcn_t &release_fcn +        );          /*! -         * Managed recv buffer destructor:           * Signal to the transport that we are done with the buffer. -         * This should be called to release the buffer to the transport. +         * This should be called to release the buffer to the transport object.           * After calling, the referenced memory should be considered invalid.           */ -        virtual ~managed_recv_buffer(void) = 0; +        virtual void release(void) = 0;          /*!           * Get the size of the underlying buffer. @@ -71,20 +84,34 @@ namespace uhd{ namespace transport{      /*!       * A managed send buffer:       * Contains a reference to transport-managed memory, -     * and a method to release the memory after writing. +     * and a method to commit the memory after writing.       */      class UHD_API managed_send_buffer : boost::noncopyable{      public:          typedef boost::shared_ptr<managed_send_buffer> sptr; +        typedef boost::function<void(size_t)> commit_fcn_t; + +        /*! +         * Make a safe managed send buffer: +         * A safe managed buffer ensures that commit is called once, +         * either by the user or automatically upon deconstruction. +         * In the later case, the deconstructor will call commit(0). +         * \param buff a reference to the mutable buffer +         * \param commit_fcn callback to commit the memory +         * \return a new managed send buffer +         */ +        static sptr make_safe( +            const boost::asio::mutable_buffer &buff, +            const commit_fcn_t &commit_fcn +        );          /*!           * Signal to the transport that we are done with the buffer.           * This should be called to commit the write to the transport object.           * After calling, the referenced memory should be considered invalid.           * \param num_bytes the number of bytes written into the buffer -         * \return the number of bytes written, 0 for timeout, negative for error           */ -        virtual ssize_t commit(size_t num_bytes) = 0; +        virtual void commit(size_t num_bytes) = 0;          /*!           * Get the size of the underlying buffer. @@ -122,100 +149,46 @@ namespace uhd{ namespace transport{          /*!           * Get a new receive buffer from this transport object. +         * \param timeout the timeout to get the buffer in seconds           * \return a managed buffer, or null sptr on timeout/error           */ -        virtual managed_recv_buffer::sptr get_recv_buff(void) = 0; +        virtual managed_recv_buffer::sptr get_recv_buff(double timeout = 0.1) = 0;          /*! -         * Get the maximum number of receive frames: -         *   The maximum number of valid managed recv buffers, -         *   or the maximum number of frames in the ring buffer, -         *   depending upon the underlying implementation. +         * Get the number of receive frames: +         * The number of simultaneous receive buffers in use.           * \return number of frames           */          virtual size_t get_num_recv_frames(void) const = 0;          /*! +         * Get the size of a receive frame: +         * The maximum capacity of a single receive buffer. +         * \return frame size in bytes +         */ +        virtual size_t get_recv_frame_size(void) const = 0; + +        /*!           * Get a new send buffer from this transport object. +         * \param timeout the timeout to get the buffer in seconds           * \return a managed buffer, or null sptr on timeout/error           */ -        virtual managed_send_buffer::sptr get_send_buff(void) = 0; +        virtual managed_send_buffer::sptr get_send_buff(double timeout = 0.1) = 0;          /*! -         * Get the maximum number of send frames: -         *   The maximum number of valid managed send buffers, -         *   or the maximum number of frames in the ring buffer, -         *   depending upon the underlying implementation. +         * Get the number of send frames: +         * The number of simultaneous send buffers in use.           * \return number of frames           */          virtual size_t get_num_send_frames(void) const = 0; -    }; - -    /*! -     * A phony-zero-copy interface for transport objects that -     * provides a zero-copy interface on top of copying transport. -     * This interface implements the get managed recv buffer, -     * the base class must implement the private recv method. -     */ -    class UHD_API phony_zero_copy_recv_if : public virtual zero_copy_if{ -    public: -        /*! -         * Create a phony zero copy recv interface. -         * \param max_buff_size max buffer size in bytes -         */ -        phony_zero_copy_recv_if(size_t max_buff_size); - -        //! destructor -        virtual ~phony_zero_copy_recv_if(void); - -        /*! -         * Get a new receive buffer from this transport object. -         */ -        managed_recv_buffer::sptr get_recv_buff(void); - -    private: -        /*! -         * Perform a private copying recv. -         * \param buff the buffer to write data into -         * \return the number of bytes written to buff, 0 for timeout, negative for error -         */ -        virtual ssize_t recv(const boost::asio::mutable_buffer &buff) = 0; - -        UHD_PIMPL_DECL(impl) _impl; -    }; - -    /*! -     * A phony-zero-copy interface for transport objects that -     * provides a zero-copy interface on top of copying transport. -     * This interface implements the get managed send buffer, -     * the base class must implement the private send method. -     */ -    class UHD_API phony_zero_copy_send_if : public virtual zero_copy_if{ -    public: -        /*! -         * Create a phony zero copy send interface. -         * \param max_buff_size max buffer size in bytes -         */ -        phony_zero_copy_send_if(size_t max_buff_size); - -        //! destructor -        virtual ~phony_zero_copy_send_if(void); - -        /*! -         * Get a new send buffer from this transport object. -         */ -        managed_send_buffer::sptr get_send_buff(void); - -    private:          /*! -         * Perform a private copying send. -         * \param buff the buffer to read data from -         * \return the number of bytes read from buff, 0 for timeout, negative for error +         * Get the size of a send frame: +         * The maximum capacity of a single send buffer. +         * \return frame size in bytes           */ -        virtual ssize_t send(const boost::asio::const_buffer &buff) = 0; +        virtual size_t get_send_frame_size(void) const = 0; -        UHD_PIMPL_DECL(impl) _impl;      };  }} //namespace diff --git a/host/include/uhd/types/CMakeLists.txt b/host/include/uhd/types/CMakeLists.txt index dbce21c98..a96976b5e 100644 --- a/host/include/uhd/types/CMakeLists.txt +++ b/host/include/uhd/types/CMakeLists.txt @@ -19,6 +19,7 @@  INSTALL(FILES      clock_config.hpp      device_addr.hpp +    dict.ipp      dict.hpp      io_type.hpp      mac_addr.hpp @@ -28,6 +29,7 @@ INSTALL(FILES      serial.hpp      stream_cmd.hpp      time_spec.hpp +    tune_request.hpp      tune_result.hpp      DESTINATION ${INCLUDE_DIR}/uhd/types  ) diff --git a/host/include/uhd/types/device_addr.hpp b/host/include/uhd/types/device_addr.hpp index e359d9467..eb3394230 100644 --- a/host/include/uhd/types/device_addr.hpp +++ b/host/include/uhd/types/device_addr.hpp @@ -20,6 +20,8 @@  #include <uhd/config.hpp>  #include <uhd/types/dict.hpp> +#include <boost/lexical_cast.hpp> +#include <stdexcept>  #include <vector>  #include <string> @@ -62,6 +64,24 @@ namespace uhd{           * \return a string with delimiter markup           */          std::string to_string(void) const; + +        /*! +         * Lexically cast a parameter to the specified type, +         * or use the default value if the key is not found. +         * \param key the key as one of the address parameters +         * \param def the value to use when key is not present +         * \return the casted value as type T or the default +         * \throw error when the parameter cannot be casted +         */ +        template <typename T> T cast(const std::string &key, const T &def) const{ +            if (not this->has_key(key)) return def; +            try{ +                return boost::lexical_cast<T>((*this)[key]); +            } +            catch(const boost::bad_lexical_cast &){ +                throw std::runtime_error("cannot cast " + key + " = " + (*this)[key]); +            } +        }      };      //handy typedef for a vector of device addresses diff --git a/host/include/uhd/types/dict.hpp b/host/include/uhd/types/dict.hpp index de96ea768..b14fc5425 100644 --- a/host/include/uhd/types/dict.hpp +++ b/host/include/uhd/types/dict.hpp @@ -19,11 +19,6 @@  #define INCLUDED_UHD_TYPES_DICT_HPP  #include <uhd/config.hpp> -#include <boost/foreach.hpp> -#include <boost/format.hpp> -#include <boost/lexical_cast.hpp> -#include <stdexcept> -#include <typeinfo>  #include <vector>  #include <list> @@ -34,14 +29,10 @@ namespace uhd{       */      template <typename Key, typename Val> class dict{      public: -        typedef std::pair<Key, Val> pair_t; -          /*!           * Create a new empty dictionary.           */ -        dict(void){ -            /* NOP */ -        } +        dict(void);          /*!           * Input iterator constructor: @@ -50,64 +41,34 @@ namespace uhd{           * \param last the end iterator           */          template <typename InputIterator> -        dict(InputIterator first, InputIterator last){ -            for(InputIterator it = first; it != last; it++){ -                _map.push_back(*it); -            } -        } - -        /*! -         * Destroy this dict. -         */ -        ~dict(void){ -            /* NOP */ -        } +        dict(InputIterator first, InputIterator last);          /*!           * Get the number of elements in this dict.           * \return the number of elements           */ -        std::size_t size(void) const{ -            return _map.size(); -        } +        std::size_t size(void) const;          /*!           * Get a list of the keys in this dict.           * Key order depends on insertion precedence.           * \return vector of keys           */ -        const std::vector<Key> keys(void) const{ -            std::vector<Key> keys; -            BOOST_FOREACH(const pair_t &p, _map){ -                keys.push_back(p.first); -            } -            return keys; -        } +        const std::vector<Key> keys(void) const;          /*!           * Get a list of the values in this dict.           * Value order depends on insertion precedence.           * \return vector of values           */ -        const std::vector<Val> vals(void) const{ -            std::vector<Val> vals; -            BOOST_FOREACH(const pair_t &p, _map){ -                vals.push_back(p.second); -            } -            return vals; -        } +        const std::vector<Val> vals(void) const;          /*!           * Does the dictionary contain this key?           * \param key the key to look for           * \return true if found           */ -        bool has_key(const Key &key) const{ -            BOOST_FOREACH(const pair_t &p, _map){ -                if (p.first == key) return true; -            } -            return false; -        } +        bool has_key(const Key &key) const;          /*!           * Get a value for the given key if it exists. @@ -116,15 +77,7 @@ namespace uhd{           * \return the value at the key           * \throw an exception when not found           */ -        const Val &operator[](const Key &key) const{ -            BOOST_FOREACH(const pair_t &p, _map){ -                if (p.first == key) return p.second; -            } -            throw std::invalid_argument(str(boost::format( -                "key \"%s\" not found in dict(%s, %s)" -            ) % boost::lexical_cast<std::string>(key) -            % typeid(Key).name() % typeid(Val).name())); -        } +        const Val &operator[](const Key &key) const;          /*!           * Set a value for the given key, however, in reality @@ -132,13 +85,7 @@ namespace uhd{           * \param key the key to set to           * \return a reference to the value           */ -        Val &operator[](const Key &key){ -            BOOST_FOREACH(pair_t &p, _map){ -                if (p.first == key) return p.second; -            } -            _map.push_back(std::make_pair(key, Val())); -            return _map.back().second; -        } +        Val &operator[](const Key &key);          /*!           * Pop an item out of the dictionary. @@ -146,16 +93,15 @@ namespace uhd{           * \return the value of the item           * \throw an exception when not found           */ -        Val pop(const Key &key){ -            Val val = (*this)[key]; -            _map.remove(pair_t(key, val)); -            return val; -        } +        Val pop(const Key &key);      private: +        typedef std::pair<Key, Val> pair_t;          std::list<pair_t> _map; //private container      };  } //namespace uhd +#include <uhd/types/dict.ipp> +  #endif /* INCLUDED_UHD_TYPES_DICT_HPP */ diff --git a/host/include/uhd/types/dict.ipp b/host/include/uhd/types/dict.ipp new file mode 100644 index 000000000..85071e6fd --- /dev/null +++ b/host/include/uhd/types/dict.ipp @@ -0,0 +1,120 @@ +// +// Copyright 2010 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_TYPES_DICT_IPP +#define INCLUDED_UHD_TYPES_DICT_IPP + +#include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <boost/lexical_cast.hpp> +#include <stdexcept> +#include <typeinfo> + +namespace uhd{ + +    namespace /*anon*/{ +        template<typename Key, typename Val> +        struct UHD_API key_not_found: std::out_of_range{ +            key_not_found(const Key &key): std::out_of_range( +                str(boost::format( +                    "key \"%s\" not found in dict(%s, %s)" +                    ) % boost::lexical_cast<std::string>(key) +                    % typeid(Key).name() % typeid(Val).name() +                ) +            ){ +                /* NOP */ +            } +        }; +    } // namespace /*anon*/ + +    template <typename Key, typename Val> +    dict<Key, Val>::dict(void){ +        /* NOP */ +    } + +    template <typename Key, typename Val> +    template <typename InputIterator> +    dict<Key, Val>::dict(InputIterator first, InputIterator last){ +        for(InputIterator it = first; it != last; it++){ +            _map.push_back(*it); +        } +    } + +    template <typename Key, typename Val> +    std::size_t dict<Key, Val>::size(void) const{ +        return _map.size(); +    } + +    template <typename Key, typename Val> +    const std::vector<Key> dict<Key, Val>::keys(void) const{ +        std::vector<Key> keys; +        BOOST_FOREACH(const pair_t &p, _map){ +            keys.push_back(p.first); +        } +        return keys; +    } + +    template <typename Key, typename Val> +    const std::vector<Val> dict<Key, Val>::vals(void) const{ +        std::vector<Val> vals; +        BOOST_FOREACH(const pair_t &p, _map){ +            vals.push_back(p.second); +        } +        return vals; +    } + +    template <typename Key, typename Val> +    bool dict<Key, Val>::has_key(const Key &key) const{ +        BOOST_FOREACH(const pair_t &p, _map){ +            if (p.first == key) return true; +        } +        return false; +    } + +    template <typename Key, typename Val> +    const Val &dict<Key, Val>::operator[](const Key &key) const{ +        BOOST_FOREACH(const pair_t &p, _map){ +            if (p.first == key) return p.second; +        } +        throw key_not_found<Key, Val>(key); +    } + +    template <typename Key, typename Val> +    Val &dict<Key, Val>::operator[](const Key &key){ +        BOOST_FOREACH(pair_t &p, _map){ +            if (p.first == key) return p.second; +        } +        _map.push_back(std::make_pair(key, Val())); +        return _map.back().second; +    } + +    template <typename Key, typename Val> +    Val dict<Key, Val>::pop(const Key &key){ +        typename std::list<pair_t>::iterator it; +        for (it = _map.begin(); it != _map.end(); it++){ +            if (it->first == key){ +                Val val = it->second; +                _map.erase(it); +                return val; +            } +        } +        throw key_not_found<Key, Val>(key); +    } + +} //namespace uhd + +#endif /* INCLUDED_UHD_TYPES_DICT_IPP */ diff --git a/host/include/uhd/types/metadata.hpp b/host/include/uhd/types/metadata.hpp index 65952941c..3f250d13e 100644 --- a/host/include/uhd/types/metadata.hpp +++ b/host/include/uhd/types/metadata.hpp @@ -19,7 +19,6 @@  #define INCLUDED_UHD_TYPES_METADATA_HPP  #include <uhd/config.hpp> -#include <boost/cstdint.hpp>  #include <uhd/types/time_spec.hpp>  namespace uhd{ @@ -30,58 +29,59 @@ namespace uhd{       * The receive routines will convert IF data headers into metadata.       */      struct UHD_API rx_metadata_t{ -        /*! -         * Time specification: -         * Set from timestamps on incoming data when provided. -         */ +        //! Has time specification?          bool has_time_spec; + +        //! Time of the first sample.          time_spec_t time_spec;          /*! -         * Fragmentation flag and offset: +         * Fragmentation flag:           * Similar to IPv4 fragmentation: http://en.wikipedia.org/wiki/IPv4#Fragmentation_and_reassembly           * More fragments is true when the input buffer has insufficient size to fit           * an entire received packet. More fragments will be false for the last fragment. -         * The fragment offset is the sample number at the start of the receive buffer. -         * For non-fragmented receives, the fragment offset should always be zero.           */          bool more_fragments; -        size_t fragment_offset;          /*! -         * Burst flags: -         * Start of burst will be true for the first packet in the chain. -         * End of burst will be true for the last packet in the chain. +         * Fragmentation offset: +         * The fragment offset is the sample number at the start of the receive buffer. +         * For non-fragmented receives, the fragment offset should always be zero.           */ +        size_t fragment_offset; + +        //! Start of burst will be true for the first packet in the chain.          bool start_of_burst; + +        //! End of burst will be true for the last packet in the chain.          bool end_of_burst;          /*! -         * Error conditions: -         * - none: no error associated with this metadata -         * - timeout: no packet received, underlying code timed-out -         * - late command: a stream command was issued in the past -         * - broken chain: expected another stream command -         * - overflow: an internal receive buffer has filled -         * - bad packet: the buffer was unrecognizable as a vrt packet +         * The error condition on a receive call.           *           * Note: When an overrun occurs in continuous streaming mode,           * the device will continue to send samples to the host.           * For other streaming modes, streaming will discontinue           * until the user issues a new stream command.           * -         * Note: The metadata fields have meaning for the following error codes: +         * The metadata fields have meaning for the following error codes:           * - none           * - late command           * - broken chain           * - overflow           */          enum error_code_t { +            //! No error associated with this metadata.              ERROR_CODE_NONE         = 0x0, +            //! No packet received, implementation timed-out.              ERROR_CODE_TIMEOUT      = 0x1, +            //! A stream command was issued in the past.              ERROR_CODE_LATE_COMMAND = 0x2, +            //! Expected another stream command.              ERROR_CODE_BROKEN_CHAIN = 0x4, +            //! An internal receive buffer has filled.              ERROR_CODE_OVERFLOW     = 0x8, +            //! The packet could not be parsed.              ERROR_CODE_BAD_PACKET   = 0xf          } error_code;      }; @@ -93,19 +93,19 @@ namespace uhd{       */      struct UHD_API tx_metadata_t{          /*! -         * Time specification: -         * Set has time spec to false to perform a send "now". -         * Or, set to true, and fill in time spec for a send "at". +         * Has time specification? +         * - Set false to send immediately. +         * - Set true to send at the time specified by time spec.           */          bool has_time_spec; + +        //! When to send the first sample.          time_spec_t time_spec; -        /*! -         * Burst flags: -         * Set start of burst to true for the first packet in the chain. -         * Set end of burst to true for the last packet in the chain. -         */ +        //! Set start of burst to true for the first packet in the chain.          bool start_of_burst; + +        //! Set end of burst to true for the last packet in the chain.          bool end_of_burst;          /*! @@ -122,27 +122,27 @@ namespace uhd{          //! The channel number in a mimo configuration          size_t channel; -        /*! -         * Time specification: when the async event occurred. -         */ +        //! Has time specification?          bool has_time_spec; + +        //! When the async event occurred.          time_spec_t time_spec;          /*! -         * Event codes: -         * - success: a packet was successfully transmitted -         * - underflow: an internal send buffer has emptied -         * - sequence error: packet loss between host and device -         * - time error: packet had time that was late (or too early) -         * - underflow in packet: underflow occurred inside a packet -         * - sequence error in burst: packet loss within a burst +         * The type of event for a receive async message call.           */          enum event_code_t { +            //! A packet was successfully transmitted.              EVENT_CODE_SUCCESS    = 0x1, +            //! An internal send buffer has emptied.              EVENT_CODE_UNDERFLOW  = 0x2, +            //! Packet loss between host and device.              EVENT_CODE_SEQ_ERROR  = 0x4, +            //! Packet had time that was late (or too early).              EVENT_CODE_TIME_ERROR = 0x8, +            //! Underflow occurred inside a packet.              EVENT_CODE_UNDERFLOW_IN_PACKET = 0x10, +            //! Packet loss within a burst.              EVENT_CODE_SEQ_ERROR_IN_BURST  = 0x20          } event_code;      }; diff --git a/host/include/uhd/types/time_spec.hpp b/host/include/uhd/types/time_spec.hpp index 59b85f4b7..57d002d48 100644 --- a/host/include/uhd/types/time_spec.hpp +++ b/host/include/uhd/types/time_spec.hpp @@ -59,7 +59,7 @@ namespace uhd{           * \param tick_count the fractional seconds tick count           * \param tick_rate the number of ticks per second           */ -        time_spec_t(time_t full_secs, size_t tick_count, double tick_rate); +        time_spec_t(time_t full_secs, long tick_count, double tick_rate);          /*!           * Convert the fractional seconds to clock ticks. @@ -67,7 +67,7 @@ namespace uhd{           * \param tick_rate the number of ticks per second           * \return the fractional seconds tick count           */ -        size_t get_tick_count(double tick_rate) const; +        long get_tick_count(double tick_rate) const;          /*!           * Get the time as a real-valued seconds count. diff --git a/host/include/uhd/types/tune_request.hpp b/host/include/uhd/types/tune_request.hpp new file mode 100644 index 000000000..942b93251 --- /dev/null +++ b/host/include/uhd/types/tune_request.hpp @@ -0,0 +1,95 @@ +// +// Copyright 2010 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_TYPES_TUNE_REQUEST_HPP +#define INCLUDED_UHD_TYPES_TUNE_REQUEST_HPP + +#include <uhd/config.hpp> + +namespace uhd{ + +    /*! +     * A tune request instructs the implementation how to tune the RF chain. +     * The policies can be used to select automatic tuning or +     * fined control over the daughterboard IF and DSP tuning. +     * Not all combinations of policies are applicable. +     * Convenience constructors are supplied for most use cases. +     */ +    struct UHD_API tune_request_t{ +        /*! +         * Make a new tune request for a particular center frequency. +         * Use an automatic policy for the intermediate and DSP frequency +         * to tune the chain as close as possible to the target frequency. +         * \param target_freq the target frequency in Hz +         */ +        tune_request_t(double target_freq = 0); + +        /*! +         * Make a new tune request for a particular center frequency. +         * Use a manual policy for the intermediate frequency, +         * and an automatic policy for the DSP frequency, +         * to tune the chain as close as possible to the target frequency. +         * \param target_freq the target frequency in Hz +         * \param lo_off the LO offset frequency in Hz +         */ +        tune_request_t(double target_freq, double lo_off); + +        //! Policy options for tunable elements in the RF chain. +        enum policy_t { +            //! Do not set this argument, use current setting. +            POLICY_NONE   = 'N', +            //! Automatically determine the argument's value. +            POLICY_AUTO   = 'A', +            //! Use the argument's value for the setting. +            POLICY_MANUAL = 'M' +        }; + +        /*! +         * The target frequency of the overall chain in Hz. +         * Set this even if all policies are set to manual. +         */ +        double target_freq; + +        /*! +         * The policy for the intermediate frequency. +         * Automatic behavior: the target frequency + default LO offset. +         */ +        policy_t inter_freq_policy; + +        /*! +         * The intermediate frequency in Hz. +         * Set when the policy is set to manual. +         */ +        double inter_freq; + +        /*! +         * The policy for the DSP frequency. +         * Automatic behavior: the difference between the target and IF. +         */ +        policy_t dsp_freq_policy; + +        /*! +         * The DSP frequency in Hz. +         * Set when the policy is set to manual. +         */ +        double dsp_freq; + +    }; + +} //namespace uhd + +#endif /* INCLUDED_UHD_TYPES_TUNE_REQUEST_HPP */ diff --git a/host/include/uhd/usrp/CMakeLists.txt b/host/include/uhd/usrp/CMakeLists.txt index f973e401a..abddf3951 100644 --- a/host/include/uhd/usrp/CMakeLists.txt +++ b/host/include/uhd/usrp/CMakeLists.txt @@ -42,6 +42,7 @@ INSTALL(FILES      simple_usrp.hpp      single_usrp.hpp      mimo_usrp.hpp +    multi_usrp.hpp      DESTINATION ${INCLUDE_DIR}/uhd/usrp  ) diff --git a/host/include/uhd/usrp/dboard_id.hpp b/host/include/uhd/usrp/dboard_id.hpp index 4c45e4334..1fda8182e 100644 --- a/host/include/uhd/usrp/dboard_id.hpp +++ b/host/include/uhd/usrp/dboard_id.hpp @@ -67,6 +67,12 @@ namespace uhd{ namespace usrp{          std::string to_string(void) const;          /*! +         * Get the dboard id represented as a canonical name. +         * \return the canonical string representation +         */ +        std::string to_cname(void) const; + +        /*!           * Get the pretty print representation of this dboard id.           * \return a string with the dboard name and id number           */ diff --git a/host/include/uhd/usrp/dboard_iface.hpp b/host/include/uhd/usrp/dboard_iface.hpp index c7db244f2..c430ecd3f 100644 --- a/host/include/uhd/usrp/dboard_iface.hpp +++ b/host/include/uhd/usrp/dboard_iface.hpp @@ -242,6 +242,15 @@ public:       * \param enb true for enabled       */      virtual void set_clock_enabled(unit_t unit, bool enb) = 0; + +    /*! +     * Get the rate of the codec. +     * For rx, this is the rate the ADC feeds the DSP. +     * For tx, this is the rate the DSP feeds the DAC. +     * \param unit which unit rx or tx +     * \return the codec rate in Hz +     */ +    virtual double get_codec_rate(unit_t unit) = 0;  };  }} //namespace diff --git a/host/include/uhd/usrp/mimo_usrp.hpp b/host/include/uhd/usrp/mimo_usrp.hpp index 10a404059..a2092f04f 100644 --- a/host/include/uhd/usrp/mimo_usrp.hpp +++ b/host/include/uhd/usrp/mimo_usrp.hpp @@ -32,12 +32,12 @@  namespace uhd{ namespace usrp{  /*! - * The MIMO USRP device class: + * The MIMO USRP device class (DEPRECATED):   * A mimo usrp facilitates ease-of-use for multi-usrp scenarios.   * The wrapper provides convenience functions to control the group   * of underlying devices as if they consisted of a single device.   */ -class UHD_API mimo_usrp : boost::noncopyable{ +class UHD_API UHD_DEPRECATED mimo_usrp : boost::noncopyable{  public:      typedef boost::shared_ptr<mimo_usrp> sptr; @@ -127,7 +127,7 @@ public:      virtual double get_rx_rate_all(void) = 0;      virtual tune_result_t set_rx_freq(size_t chan, double freq) = 0; -    virtual tune_result_t set_rx_freq(size_t chan, double freq, double lo_off) = 0; +    //virtual tune_result_t set_rx_freq(size_t chan, double freq, double lo_off) = 0;      virtual double get_rx_freq(size_t chan) = 0;      virtual freq_range_t get_rx_freq_range(size_t chan) = 0; @@ -148,6 +148,8 @@ public:       * \return the rssi in dB       */      virtual float read_rssi(size_t chan) = 0; +     +    virtual void set_rx_bandwidth(size_t chan, float bandwidth) = 0;      /*******************************************************************       * TX methods @@ -159,7 +161,7 @@ public:      virtual double get_tx_rate_all(void) = 0;      virtual tune_result_t set_tx_freq(size_t chan, double freq) = 0; -    virtual tune_result_t set_tx_freq(size_t chan, double freq, double lo_off) = 0; +    //virtual tune_result_t set_tx_freq(size_t chan, double freq, double lo_off) = 0;      virtual double get_tx_freq(size_t chan) = 0;      virtual freq_range_t get_tx_freq_range(size_t chan) = 0; @@ -177,4 +179,347 @@ public:  }} +#include <uhd/utils/warning.hpp> +#include <uhd/usrp/tune_helper.hpp> +#include <uhd/utils/assert.hpp> +#include <uhd/utils/gain_group.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/warning.hpp> +#include <uhd/usrp/subdev_props.hpp> +#include <uhd/usrp/mboard_props.hpp> +#include <uhd/usrp/device_props.hpp> +#include <uhd/usrp/dboard_props.hpp> +#include <uhd/usrp/dsp_props.hpp> +#include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <boost/thread.hpp> +#include <stdexcept> +#include <iostream> + +namespace uhd{ namespace usrp{ namespace /*anon*/{ + +static inline freq_range_t add_dsp_shift(const freq_range_t &range, wax::obj dsp){ +    double codec_rate = dsp[DSP_PROP_CODEC_RATE].as<double>(); +    return freq_range_t(range.min - codec_rate/2.0, range.max + codec_rate/2.0); +} + +/*********************************************************************** + * MIMO USRP Implementation + **********************************************************************/ +class mimo_usrp_impl : public mimo_usrp{ +public: +    mimo_usrp_impl(const device_addr_t &addr){ +        _dev = device::make(addr); + +        //set the clock config across all mboards (TODO set through api) +        clock_config_t clock_config; +        clock_config.ref_source = clock_config_t::REF_SMA; +        clock_config.pps_source = clock_config_t::PPS_SMA; +        for (size_t chan = 0; chan < get_num_channels(); chan++){ +            _mboard(chan)[MBOARD_PROP_CLOCK_CONFIG] = clock_config; +        } +    } + +    ~mimo_usrp_impl(void){ +        /* NOP */ +    } + +    device::sptr get_device(void){ +        return _dev; +    } + +    std::string get_pp_string(void){ +        std::string buff = str(boost::format( +            "MIMO USRP:\n" +            "  Device: %s\n" +        ) +            % (*_dev)[DEVICE_PROP_NAME].as<std::string>() +        ); +        for (size_t chan = 0; chan < get_num_channels(); chan++){ +            buff += str(boost::format( +                "  Channel: %u\n" +                "    Mboard: %s\n" +                "    RX DSP: %s\n" +                "    RX Dboard: %s\n" +                "    RX Subdev: %s\n" +                "    TX DSP: %s\n" +                "    TX Dboard: %s\n" +                "    TX Subdev: %s\n" +            ) % chan +                % _mboard(chan)[MBOARD_PROP_NAME].as<std::string>() +                % _rx_dsp(chan)[DSP_PROP_NAME].as<std::string>() +                % _rx_dboard(chan)[DBOARD_PROP_NAME].as<std::string>() +                % _rx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>() +                % _tx_dsp(chan)[DSP_PROP_NAME].as<std::string>() +                % _tx_dboard(chan)[DBOARD_PROP_NAME].as<std::string>() +                % _tx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>() +            ); +        } +        return buff; +    } + +    size_t get_num_channels(void){ +        return (*_dev)[DEVICE_PROP_MBOARD_NAMES].as<prop_names_t>().size(); +    } + +    /******************************************************************* +     * Misc +     ******************************************************************/ +    time_spec_t get_time_now(void){ +        //the time on the first mboard better be the same on all +        return _mboard(0)[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); +    } + +    void set_time_next_pps(const time_spec_t &time_spec){ +        for (size_t chan = 0; chan < get_num_channels(); chan++){ +            _mboard(chan)[MBOARD_PROP_TIME_NEXT_PPS] = time_spec; +        } +    } + +    void set_time_unknown_pps(const time_spec_t &time_spec){ +        std::cout << "Set time with unknown pps edge:" << std::endl; +        std::cout << "    1) set times next pps (race condition)" << std::endl; +        set_time_next_pps(time_spec); +        boost::this_thread::sleep(boost::posix_time::seconds(1)); + +        std::cout << "    2) catch seconds rollover at pps edge" << std::endl; +        time_t last_secs = 0, curr_secs = 0; +        while(curr_secs == last_secs){ +            last_secs = curr_secs; +            curr_secs = get_time_now().get_full_secs(); +        } + +        std::cout << "    3) set times next pps (synchronously)" << std::endl; +        set_time_next_pps(time_spec); +        boost::this_thread::sleep(boost::posix_time::seconds(1)); + +        //verify that the time registers are read to be within a few RTT +        for (size_t chan = 1; chan < get_num_channels(); chan++){ +            time_spec_t time_0 = _mboard(0)[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); +            time_spec_t time_i = _mboard(chan)[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); +            if (time_i < time_0 or (time_i - time_0) > time_spec_t(0.01)){ //10 ms: greater than RTT but not too big +                uhd::warning::post(str(boost::format( +                    "Detected time deviation between board %d and board 0.\n" +                    "Board 0 time is %f seconds.\n" +                    "Board %d time is %f seconds.\n" +                ) % chan % time_0.get_real_secs() % chan % time_i.get_real_secs())); +            } +        } +    } + +    void issue_stream_cmd(const stream_cmd_t &stream_cmd){ +        for (size_t chan = 0; chan < get_num_channels(); chan++){ +            _mboard(chan)[MBOARD_PROP_STREAM_CMD] = stream_cmd; +        } +    } + +    /******************************************************************* +     * RX methods +     ******************************************************************/ +    void set_rx_subdev_spec(size_t chan, const subdev_spec_t &spec){ +        UHD_ASSERT_THROW(spec.size() <= 1); +        _mboard(chan)[MBOARD_PROP_RX_SUBDEV_SPEC] = spec; +    } + +    subdev_spec_t get_rx_subdev_spec(size_t chan){ +        return _mboard(chan)[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>(); +    } + +    void set_rx_rate_all(double rate){ +        std::vector<double> _actual_rates; +        for (size_t chan = 0; chan < get_num_channels(); chan++){ +            _rx_dsp(chan)[DSP_PROP_HOST_RATE] = rate; +            _actual_rates.push_back(_rx_dsp(chan)[DSP_PROP_HOST_RATE].as<double>()); +        } +        _rx_rate = _actual_rates.front(); +        if (std::count(_actual_rates, _rx_rate) != _actual_rates.size()) throw std::runtime_error( +            "MIMO configuratio error: rx rate inconsistent across mboards" +        ); +    } + +    double get_rx_rate_all(void){ +        return _rx_rate; +    } + +    tune_result_t set_rx_freq(size_t chan, double target_freq){ +        return tune_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(chan), 0, target_freq); +    } + +    //tune_result_t set_rx_freq(size_t chan, double target_freq, double lo_off){ +    //    return tune_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(chan), 0, target_freq, lo_off); +    //} + +    double get_rx_freq(size_t chan){ +        return derive_freq_from_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(chan), 0); +    } + +    freq_range_t get_rx_freq_range(size_t chan){ +        return add_dsp_shift(_rx_subdev(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _rx_dsp(chan)); +    } + +    void set_rx_gain(size_t chan, float gain){ +        return _rx_gain_group(chan)->set_value(gain); +    } + +    float get_rx_gain(size_t chan){ +        return _rx_gain_group(chan)->get_value(); +    } + +    gain_range_t get_rx_gain_range(size_t chan){ +        return _rx_gain_group(chan)->get_range(); +    } + +    void set_rx_antenna(size_t chan, const std::string &ant){ +        _rx_subdev(chan)[SUBDEV_PROP_ANTENNA] = ant; +    } + +    std::string get_rx_antenna(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_ANTENNA].as<std::string>(); +    } + +    std::vector<std::string> get_rx_antennas(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_ANTENNA_NAMES].as<prop_names_t>(); +    } + +    bool get_rx_lo_locked(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_LO_LOCKED].as<bool>(); +    } + +    float read_rssi(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_RSSI].as<float>(); +    } +     +    void set_rx_bandwidth(size_t chan, float bandwidth){ +        _rx_subdev(chan)[SUBDEV_PROP_BANDWIDTH] = bandwidth; +    } + +    /******************************************************************* +     * TX methods +     ******************************************************************/ +    void set_tx_subdev_spec(size_t chan, const subdev_spec_t &spec){ +        UHD_ASSERT_THROW(spec.size() <= 1); +        _mboard(chan)[MBOARD_PROP_TX_SUBDEV_SPEC] = spec; +    } + +    subdev_spec_t get_tx_subdev_spec(size_t chan){ +        return _mboard(chan)[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>(); +    } + +    void set_tx_rate_all(double rate){ +        std::vector<double> _actual_rates; +        for (size_t chan = 0; chan < get_num_channels(); chan++){ +            _tx_dsp(chan)[DSP_PROP_HOST_RATE] = rate; +            _actual_rates.push_back(_tx_dsp(chan)[DSP_PROP_HOST_RATE].as<double>()); +        } +        _tx_rate = _actual_rates.front(); +        if (std::count(_actual_rates, _tx_rate) != _actual_rates.size()) throw std::runtime_error( +            "MIMO configuratio error: tx rate inconsistent across mboards" +        ); +    } + +    double get_tx_rate_all(void){ +        return _tx_rate; +    } + +    tune_result_t set_tx_freq(size_t chan, double target_freq){ +        return tune_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(chan), 0, target_freq); +    } + +    //tune_result_t set_tx_freq(size_t chan, double target_freq, double lo_off){ +    //    return tune_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(chan), 0, target_freq, lo_off); +    //} + +    double get_tx_freq(size_t chan){ +        return derive_freq_from_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(chan), 0); +    } + +    freq_range_t get_tx_freq_range(size_t chan){ +        return add_dsp_shift(_tx_subdev(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _tx_dsp(chan)); +    } + +    void set_tx_gain(size_t chan, float gain){ +        return _tx_gain_group(chan)->set_value(gain); +    } + +    float get_tx_gain(size_t chan){ +        return _tx_gain_group(chan)->get_value(); +    } + +    gain_range_t get_tx_gain_range(size_t chan){ +        return _tx_gain_group(chan)->get_range(); +    } + +    void set_tx_antenna(size_t chan, const std::string &ant){ +        _tx_subdev(chan)[SUBDEV_PROP_ANTENNA] = ant; +    } + +    std::string get_tx_antenna(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_ANTENNA].as<std::string>(); +    } + +    std::vector<std::string> get_tx_antennas(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_ANTENNA_NAMES].as<prop_names_t>(); +    } + +    bool get_tx_lo_locked(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_LO_LOCKED].as<bool>(); +    } + +private: +    device::sptr _dev; +    wax::obj _mboard(size_t chan){ +        prop_names_t names = (*_dev)[DEVICE_PROP_MBOARD_NAMES].as<prop_names_t>(); +        return (*_dev)[named_prop_t(DEVICE_PROP_MBOARD, names.at(chan))]; +    } +    wax::obj _rx_dsp(size_t chan){ +        return _mboard(chan)[MBOARD_PROP_RX_DSP]; +    } +    wax::obj _tx_dsp(size_t chan){ +        return _mboard(chan)[MBOARD_PROP_TX_DSP]; +    } +    wax::obj _rx_dboard(size_t chan){ +        std::string db_name = _mboard(chan)[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>().front().db_name; +        return _mboard(chan)[named_prop_t(MBOARD_PROP_RX_DBOARD, db_name)]; +    } +    wax::obj _tx_dboard(size_t chan){ +        std::string db_name = _mboard(chan)[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>().front().db_name; +        return _mboard(chan)[named_prop_t(MBOARD_PROP_TX_DBOARD, db_name)]; +    } +    wax::obj _rx_subdev(size_t chan){ +        std::string sd_name = _mboard(chan)[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>().front().sd_name; +        return _rx_dboard(chan)[named_prop_t(DBOARD_PROP_SUBDEV, sd_name)]; +    } +    wax::obj _tx_subdev(size_t chan){ +        std::string sd_name = _mboard(chan)[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>().front().sd_name; +        return _tx_dboard(chan)[named_prop_t(DBOARD_PROP_SUBDEV, sd_name)]; +    } +    gain_group::sptr _rx_gain_group(size_t chan){ +        std::string sd_name = _mboard(chan)[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>().front().sd_name; +        return _rx_dboard(chan)[named_prop_t(DBOARD_PROP_GAIN_GROUP, sd_name)].as<gain_group::sptr>(); +    } +    gain_group::sptr _tx_gain_group(size_t chan){ +        std::string sd_name = _mboard(chan)[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>().front().sd_name; +        return _tx_dboard(chan)[named_prop_t(DBOARD_PROP_GAIN_GROUP, sd_name)].as<gain_group::sptr>(); +    } + +    //shadows +    double _rx_rate, _tx_rate; +}; +}}} + +namespace uhd{ namespace usrp{ +/*********************************************************************** + * The Make Function + **********************************************************************/ +inline mimo_usrp::sptr mimo_usrp::make(const device_addr_t &dev_addr){ +    uhd::warning::post( +        "The mimo USRP interface has been deprecated.\n" +        "Please switch to the multi USRP interface.\n" +        "#include <uhd/usrp/multi_usrp.hpp>\n" +        "multi_usrp::sptr sdev = multi_usrp::make(args);\n" +    ); +    return sptr(new mimo_usrp_impl(dev_addr)); +} +}} +  #endif /* INCLUDED_UHD_USRP_MIMO_USRP_HPP */ diff --git a/host/include/uhd/usrp/misc_utils.hpp b/host/include/uhd/usrp/misc_utils.hpp index 2af9f5b40..37860a1a5 100644 --- a/host/include/uhd/usrp/misc_utils.hpp +++ b/host/include/uhd/usrp/misc_utils.hpp @@ -20,6 +20,7 @@  #include <uhd/config.hpp>  #include <uhd/wax.hpp> +#include <uhd/usrp/dboard_id.hpp>  #include <uhd/usrp/subdev_spec.hpp>  #include <uhd/utils/gain_group.hpp> @@ -35,11 +36,13 @@ namespace uhd{ namespace usrp{      /*!       * Create a gain group that represents the subdevice and its codec. +     * \param dboard_id the dboard id for this subdevice       * \param subdev the object with subdevice properties       * \param codec the object with codec properties       * \param gain_group_policy the policy to use       */      UHD_API gain_group::sptr make_gain_group( +        const dboard_id_t &dboard_id,          wax::obj subdev, wax::obj codec,          gain_group_policy_t gain_group_policy      ); diff --git a/host/include/uhd/usrp/multi_usrp.hpp b/host/include/uhd/usrp/multi_usrp.hpp new file mode 100644 index 000000000..98ba07fc0 --- /dev/null +++ b/host/include/uhd/usrp/multi_usrp.hpp @@ -0,0 +1,530 @@ +// +// Copyright 2010 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_USRP_MULTI_USRP_HPP +#define INCLUDED_UHD_USRP_MULTI_USRP_HPP + +#include <uhd/config.hpp> +#include <uhd/device.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/stream_cmd.hpp> +#include <uhd/types/clock_config.hpp> +#include <uhd/types/tune_request.hpp> +#include <uhd/types/tune_result.hpp> +#include <uhd/usrp/subdev_spec.hpp> +#include <uhd/usrp/dboard_iface.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <vector> + +namespace uhd{ namespace usrp{ + +/*! + * The multi-USRP device class: + * A multi-USRP facilitates ease-of-use for multiple USRP scenarios. + * The wrapper provides convenience functions to control the group + * of underlying devices as if they consisted of a single device. + * + * A few notes about a multi-USRP configuration: + *  - All boards share a common RX sample rate + *  - All boards share a common TX sample rate + *  - All boards share a common RX subdevice specification size + *  - All boards share a common TX subdevice specification size + *  - All boards must have synchronized times (see the set_time_*() calls) + * + * Example to setup channel mapping: + * <pre> + * + * //create a multi_usrp with two boards in the configuration + * device_addr_t dev_addr; + * dev_addr["addr"] = "192.168.10.2 192.168.10.3"; + * multi_usrp::sptr dev = multi_usrp::make(dev_addr); + * + * //set the board on 10.2 to use the A RX subdevice (RX channel 0) + * dev->set_rx_subdev_spec(":A", 0); + * + * //set the board on 10.3 to use the B RX subdevice (RX channel 1) + * dev->set_rx_subdev_spec(":B", 1); + * + * //set both boards to use the AB TX subdevice (TX channels 0 and 1) + * dev->set_tx_subdev_spec(":AB", multi_usrp::ALL_MBOARDS); + * + * //now that all the channels are mapped, continue with configuration... + * + * </pre> + */ +class UHD_API multi_usrp : boost::noncopyable{ +public: +    typedef boost::shared_ptr<multi_usrp> sptr; + +    //! A wildcard motherboard index +    static const size_t ALL_MBOARDS = size_t(~0); + +    //! A wildcard gain element name +    static const std::string ALL_GAINS; + +    /*! +     * Make a new multi usrp from the device address. +     * \param dev_addr the device address +     * \return a new single usrp object +     */ +    static sptr make(const device_addr_t &dev_addr); + +    /*! +     * Get the underlying device object. +     * This is needed to get access to the streaming API and properties. +     * \return the device object within this single usrp +     */ +    virtual device::sptr get_device(void) = 0; + +    /******************************************************************* +     * Mboard methods +     ******************************************************************/ +    /*! +     * Get a printable summary for this USRP configuration. +     * \return a printable string +     */ +    virtual std::string get_pp_string(void) = 0; + +    /*! +     * Get canonical name for this USRP motherboard. +     * \param mboard which motherboard to query +     * \return a string representing the name +     */ +    virtual std::string get_mboard_name(size_t mboard) = 0; + +    /*! +     * Gets the current time in the usrp time registers. +     * \return a timespec representing current usrp time +     */ +    virtual time_spec_t get_time_now(void) = 0; + +    /*! +     * Set the time registers on the usrp at the next pps tick. +     * The values will not be latched in until the pulse occurs. +     * It is recommended that the user sleep(1) after calling to ensure +     * that the time registers will be in a known state prior to use. +     * +     * Note: Because this call sets the time on the "next" pps, +     * the seconds in the time spec should be current seconds + 1. +     * +     * \param time_spec the time to latch into the usrp device +     */ +    virtual void set_time_next_pps(const time_spec_t &time_spec) = 0; + +    /*! +     * Synchronize the times across all motherboards in this configuration. +     * Use this method to sync the times when the edge of the PPS is unknown. +     * +     * Ex: Host machine is not attached to serial port of GPSDO +     * and can therefore not query the GPSDO for the PPS edge. +     * +     * This is a 3-step process, and will take at most 3 seconds to complete. +     * Upon completion, the times will be synchronized to the time provided. +     * +     * - Step1: set the time at the next pps (potential race condition) +     * - Step2: wait for the seconds to rollover to catch the pps edge +     * - Step3: set the time at the next pps (synchronous for all boards) +     * +     * \param time_spec the time to latch into the usrp device +     */ +    virtual void set_time_unknown_pps(const time_spec_t &time_spec) = 0; + +    /*! +     * Are the times across all motherboards in this configuration synchronized? +     * Checks that all time registers are approximately close but not exact, +     * given that the RTT may varying for a control packet transaction. +     * \return true when all motherboards time registers are in sync +     */ +    virtual bool get_time_synchronized(void) = 0; + +    /*! +     * Issue a stream command to the usrp device. +     * This tells the usrp to send samples into the host. +     * See the documentation for stream_cmd_t for more info. +     * \param stream_cmd the stream command to issue +     */ +    virtual void issue_stream_cmd(const stream_cmd_t &stream_cmd) = 0; + +    /*! +     * Set the clock configuration for the usrp device. +     * This tells the usrp how to get a 10Mhz reference and PPS clock. +     * See the documentation for clock_config_t for more info. +     * \param clock_config the clock configuration to set +     * \param mboard which motherboard to set the config +     */ +    virtual void set_clock_config(const clock_config_t &clock_config, size_t mboard) = 0; + +    /*! +     * Get the number of USRP motherboards in this configuration. +     */ +    virtual size_t get_num_mboards(void) = 0; + +    /******************************************************************* +     * RX methods +     ******************************************************************/ +    /*! +     * Set the RX subdevice specification: +     * The subdev spec maps a physical part of a daughter-board to a channel number. +     * Set the subdev spec before calling into any methods with a channel number. +     * The subdev spec must be the same size across all motherboards. +     * \param spec the new subdevice specification +     * \param mboard the motherboard index 0 to M-1 +     */ +    virtual void set_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec, size_t mboard) = 0; + +    /*! +     * Get the RX subdevice specification. +     * \param mboard the motherboard index 0 to M-1 +     * \return the subdevice specification in use +     */ +    virtual uhd::usrp::subdev_spec_t get_rx_subdev_spec(size_t mboard) = 0; + +    /*! +     * Get the number of RX channels in this configuration. +     * This is the number of USRPs times the number of RX channels per board, +     * where the number of RX channels per board is homogeneous among all USRPs. +     */ +    virtual size_t get_rx_num_channels(void) = 0; + +    /*! +     * Get the name of the RX subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the subdevice name +     */ +    virtual std::string get_rx_subdev_name(size_t chan) = 0; + +    /*! +     * Set the RX sample rate across all channels. +     * \param rate the rate in Sps +     */ +    virtual void set_rx_rate(double rate) = 0; + +    /*! +     * Gets the RX sample rate for all channels. +     * \return the rate in Sps +     */ +    virtual double get_rx_rate(void) = 0; + +    /*! +     * Set the RX center frequency. +     * \param tune_request tune request instructions +     * \param chan the channel index 0 to N-1 +     * \return a tune result object +     */ +    virtual tune_result_t set_rx_freq( +        const tune_request_t &tune_request, size_t chan = 0 +    ) = 0; + +    /*! +     * Get the RX center frequency. +     * \param chan the channel index 0 to N-1 +     * \return the frequency in Hz +     */ +    virtual double get_rx_freq(size_t chan) = 0; + +    /*! +     * Get the RX center frequency range. +     * \param chan the channel index 0 to N-1 +     * \return a frequency range object +     */ +    virtual freq_range_t get_rx_freq_range(size_t chan) = 0; + +    /*! +     * Set the RX gain value for the specified gain element. +     * For an empty name, distribute across all gain elements. +     * \param gain the gain in dB +     * \param name the name of the gain element +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_rx_gain(float gain, const std::string &name, size_t chan) = 0; + +    //! A convenience wrapper for setting overall RX gain +    void set_rx_gain(float gain, size_t chan){ +        return this->set_rx_gain(gain, ALL_GAINS, chan); +    } + +    /*! +     * Get the RX gain value for the specified gain element. +     * For an empty name, sum across all gain elements. +     * \param name the name of the gain element +     * \param chan the channel index 0 to N-1 +     * \return the gain in dB +     */ +    virtual float get_rx_gain(const std::string &name, size_t chan) = 0; + +    //! A convenience wrapper for getting overall RX gain +    float get_rx_gain(size_t chan){ +        return this->get_rx_gain(ALL_GAINS, chan); +    } + +    /*! +     * Get the RX gain range for the specified gain element. +     * For an empty name, calculate the overall gain range. +     * \param name the name of the gain element +     * \param chan the channel index 0 to N-1 +     * \return a gain range object +     */ +    virtual gain_range_t get_rx_gain_range(const std::string &name, size_t chan) = 0; + +    //! A convenience wrapper for getting overall RX gain range +    gain_range_t get_rx_gain_range(size_t chan){ +        return this->get_rx_gain_range(ALL_GAINS, chan); +    } + +    /*! +     * Get the names of the gain elements in the RX chain. +     * Gain elements are ordered from antenna to FPGA. +     * \param chan the channel index 0 to N-1 +     * \return a vector of gain element names +     */ +    virtual std::vector<std::string> get_rx_gain_names(size_t chan) = 0; + +    /*! +     * Select the RX antenna on the subdevice. +     * \param ant the antenna name +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_rx_antenna(const std::string &ant, size_t chan) = 0; + +    /*! +     * Get the selected RX antenna on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the antenna name +     */ +    virtual std::string get_rx_antenna(size_t chan) = 0; + +    /*! +     * Get a list of possible RX antennas on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return a vector of antenna names +     */ +    virtual std::vector<std::string> get_rx_antennas(size_t chan) = 0; + +    /*! +     * Get the locked status of the LO on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return true for locked +     */ +    virtual bool get_rx_lo_locked(size_t chan) = 0; + +    /*! +     * Set the RX bandwidth on the subdevice. +     * \param bandwidth the bandwidth in Hz +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_rx_bandwidth(double bandwidth, size_t chan) = 0; + +    /*! +     * Get the RX bandwidth on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the bandwidth in Hz +     */ +    virtual double get_rx_bandwidth(size_t chan) = 0; + +    /*! +     * Read the RSSI value on the RX subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the rssi in dB +     * \throw exception if RSSI readback not supported +     */ +    virtual float read_rssi(size_t chan) = 0; + +    /*! +     * Get the dboard interface object for the RX subdevice. +     * The dboard interface gives access to GPIOs, SPI, I2C, low-speed ADC and DAC. +     * Use at your own risk! +     * \param chan the channel index 0 to N-1 +     * \return the dboard interface sptr +     */ +    virtual dboard_iface::sptr get_rx_dboard_iface(size_t chan) = 0; + +    /******************************************************************* +     * TX methods +     ******************************************************************/ +    /*! +     * Set the TX subdevice specification: +     * The subdev spec maps a physical part of a daughter-board to a channel number. +     * Set the subdev spec before calling into any methods with a channel number. +     * The subdev spec must be the same size across all motherboards. +     * \param spec the new subdevice specification +     * \param mboard the motherboard index 0 to M-1 +     */ +    virtual void set_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec, size_t mboard) = 0; + +    /*! +     * Get the TX subdevice specification. +     * \param mboard the motherboard index 0 to M-1 +     * \return the subdevice specification in use +     */ +    virtual uhd::usrp::subdev_spec_t get_tx_subdev_spec(size_t mboard) = 0; + +    /*! +     * Get the number of TX channels in this configuration. +     * This is the number of USRPs times the number of TX channels per board, +     * where the number of TX channels per board is homogeneous among all USRPs. +     */ +    virtual size_t get_tx_num_channels(void) = 0; + +    /*! +     * Get the name of the TX subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the subdevice name +     */ +    virtual std::string get_tx_subdev_name(size_t chan) = 0; + +    /*! +     * Set the TX sample rate across all channels. +     * \param rate the rate in Sps +     */ +    virtual void set_tx_rate(double rate) = 0; + +    /*! +     * Gets the TX sample rate for all channels. +     * \return the rate in Sps +     */ +    virtual double get_tx_rate(void) = 0; + +    /*! +     * Set the TX center frequency. +     * \param tune_request tune request instructions +     * \param chan the channel index 0 to N-1 +     * \return a tune result object +     */ +    virtual tune_result_t set_tx_freq( +        const tune_request_t &tune_request, size_t chan = 0 +    ) = 0; + +    /*! +     * Get the TX center frequency. +     * \param chan the channel index 0 to N-1 +     * \return the frequency in Hz +     */ +    virtual double get_tx_freq(size_t chan) = 0; + +    /*! +     * Get the TX center frequency range. +     * \param chan the channel index 0 to N-1 +     * \return a frequency range object +     */ +    virtual freq_range_t get_tx_freq_range(size_t chan) = 0; + +    /*! +     * Set the TX gain value for the specified gain element. +     * For an empty name, distribute across all gain elements. +     * \param gain the gain in dB +     * \param name the name of the gain element +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_tx_gain(float gain, const std::string &name, size_t chan) = 0; + +    //! A convenience wrapper for setting overall TX gain +    void set_tx_gain(float gain, size_t chan){ +        return this->set_tx_gain(gain, ALL_GAINS, chan); +    } + +    /*! +     * Get the TX gain value for the specified gain element. +     * For an empty name, sum across all gain elements. +     * \param name the name of the gain element +     * \param chan the channel index 0 to N-1 +     * \return the gain in dB +     */ +    virtual float get_tx_gain(const std::string &name, size_t chan) = 0; + +    //! A convenience wrapper for getting overall TX gain +    float get_tx_gain(size_t chan){ +        return this->get_tx_gain(ALL_GAINS, chan); +    } + +    /*! +     * Get the TX gain range for the specified gain element. +     * For an empty name, calculate the overall gain range. +     * \param name the name of the gain element +     * \param chan the channel index 0 to N-1 +     * \return a gain range object +     */ +    virtual gain_range_t get_tx_gain_range(const std::string &name, size_t chan) = 0; + +    //! A convenience wrapper for getting overall TX gain range +    gain_range_t get_tx_gain_range(size_t chan){ +        return this->get_tx_gain_range(ALL_GAINS, chan); +    } + +    /*! +     * Get the names of the gain elements in the TX chain. +     * Gain elements are ordered from antenna to FPGA. +     * \param chan the channel index 0 to N-1 +     * \return a vector of gain element names +     */ +    virtual std::vector<std::string> get_tx_gain_names(size_t chan) = 0; + +    /*! +     * Select the TX antenna on the subdevice. +     * \param ant the antenna name +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_tx_antenna(const std::string &ant, size_t chan) = 0; + +    /*! +     * Get the selected TX antenna on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the antenna name +     */ +    virtual std::string get_tx_antenna(size_t chan) = 0; + +    /*! +     * Get a list of possible TX antennas on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return a vector of antenna names +     */ +    virtual std::vector<std::string> get_tx_antennas(size_t chan) = 0; + +    /*! +     * Get the locked status of the LO on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return true for locked +     */ +    virtual bool get_tx_lo_locked(size_t chan) = 0; + +    /*! +     * Set the TX bandwidth on the subdevice. +     * \param bandwidth the bandwidth in Hz +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_tx_bandwidth(double bandwidth, size_t chan) = 0; + +    /*! +     * Get the TX bandwidth on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the bandwidth in Hz +     */ +    virtual double get_tx_bandwidth(size_t chan) = 0; + +    /*! +     * Get the dboard interface object for the TX subdevice. +     * The dboard interface gives access to GPIOs, SPI, I2C, low-speed ADC and DAC. +     * Use at your own risk! +     * \param chan the channel index 0 to N-1 +     * \return the dboard interface sptr +     */ +    virtual dboard_iface::sptr get_tx_dboard_iface(size_t chan) = 0; +}; + +}} + +#endif /* INCLUDED_UHD_USRP_MULTI_USRP_HPP */ diff --git a/host/include/uhd/usrp/simple_usrp.hpp b/host/include/uhd/usrp/simple_usrp.hpp index 6149f739c..77416dbbd 100644 --- a/host/include/uhd/usrp/simple_usrp.hpp +++ b/host/include/uhd/usrp/simple_usrp.hpp @@ -117,7 +117,7 @@ public:      virtual double get_rx_rate(void) = 0;      virtual tune_result_t set_rx_freq(double freq) = 0; -    virtual tune_result_t set_rx_freq(double freq, double lo_off) = 0; +    //virtual tune_result_t set_rx_freq(double freq, double lo_off) = 0;      virtual double get_rx_freq(void) = 0;      virtual freq_range_t get_rx_freq_range(void) = 0; @@ -139,6 +139,8 @@ public:      virtual float read_rssi(void) = 0;      virtual dboard_iface::sptr get_rx_dboard_iface(void) = 0; +     +    virtual void set_rx_bandwidth(float) = 0;      /*******************************************************************       * TX methods @@ -150,7 +152,7 @@ public:      virtual double get_tx_rate(void) = 0;      virtual tune_result_t set_tx_freq(double freq) = 0; -    virtual tune_result_t set_tx_freq(double freq, double lo_off) = 0; +    //virtual tune_result_t set_tx_freq(double freq, double lo_off) = 0;      virtual double get_tx_freq(void) = 0;      virtual freq_range_t get_tx_freq_range(void) = 0; @@ -169,4 +171,218 @@ public:  }} +#include <uhd/usrp/single_usrp.hpp> +#include <uhd/utils/warning.hpp> + +namespace uhd{ namespace usrp{ namespace /*anon*/{ + +/*********************************************************************** + * Simple USRP Implementation + **********************************************************************/ +class simple_usrp_impl : public simple_usrp{ +public: +    simple_usrp_impl(const device_addr_t &addr){ +        _sdev = single_usrp::make(addr); +    } + +    ~simple_usrp_impl(void){ +        /* NOP */ +    } + +    device::sptr get_device(void){ +        return _sdev->get_device(); +    } + +    std::string get_pp_string(void){ +        return _sdev->get_pp_string(); +    } + +    /******************************************************************* +     * Misc +     ******************************************************************/ +    time_spec_t get_time_now(void){ +        return _sdev->get_time_now(); +    } + +    void set_time_now(const time_spec_t &time_spec){ +        return _sdev->set_time_now(time_spec); +    } + +    void set_time_next_pps(const time_spec_t &time_spec){ +        return _sdev->set_time_next_pps(time_spec); +    } + +    void issue_stream_cmd(const stream_cmd_t &stream_cmd){ +        return _sdev->issue_stream_cmd(stream_cmd); +    } + +    void set_clock_config(const clock_config_t &clock_config){ +        return _sdev->set_clock_config(clock_config); +    } + +    /******************************************************************* +     * RX methods +     ******************************************************************/ +    void set_rx_subdev_spec(const subdev_spec_t &spec){ +        return _sdev->set_rx_subdev_spec(spec); +    } + +    subdev_spec_t get_rx_subdev_spec(void){ +        return _sdev->get_rx_subdev_spec(); +    } + +    void set_rx_rate(double rate){ +        return _sdev->set_rx_rate(rate); +    } + +    double get_rx_rate(void){ +        return _sdev->get_rx_rate(); +    } + +    tune_result_t set_rx_freq(double target_freq){ +        return _sdev->set_rx_freq(target_freq); +    } + +    //tune_result_t set_rx_freq(double target_freq, double lo_off){ +    //    return _sdev->set_rx_freq(target_freq, lo_off); +    //} + +    double get_rx_freq(void){ +        return _sdev->get_rx_freq(); +    } + +    freq_range_t get_rx_freq_range(void){ +        return _sdev->get_rx_freq_range(); +    } + +    void set_rx_gain(float gain){ +        return _sdev->set_rx_gain(gain); +    } + +    float get_rx_gain(void){ +        return _sdev->get_rx_gain(); +    } + +    gain_range_t get_rx_gain_range(void){ +        return _sdev->get_rx_gain_range(); +    } + +    void set_rx_antenna(const std::string &ant){ +        return _sdev->set_rx_antenna(ant); +    } + +    std::string get_rx_antenna(void){ +        return _sdev->get_rx_antenna(); +    } + +    std::vector<std::string> get_rx_antennas(void){ +        return _sdev->get_rx_antennas(); +    } + +    bool get_rx_lo_locked(void){ +        return _sdev->get_rx_lo_locked(); +    } + +    float read_rssi(void){ +        return _sdev->read_rssi(); +    } + +    dboard_iface::sptr get_rx_dboard_iface(void){ +        return _sdev->get_rx_dboard_iface(); +    } +     +    void set_rx_bandwidth(float bandwidth) { +        return _sdev->set_rx_bandwidth(bandwidth); +    } + +    /******************************************************************* +     * TX methods +     ******************************************************************/ +    void set_tx_subdev_spec(const subdev_spec_t &spec){ +        return _sdev->set_tx_subdev_spec(spec); +    } + +    subdev_spec_t get_tx_subdev_spec(void){ +        return _sdev->get_tx_subdev_spec(); +    } + +    void set_tx_rate(double rate){ +        return _sdev->set_tx_rate(rate); +    } + +    double get_tx_rate(void){ +        return _sdev->get_tx_rate(); +    } + +    tune_result_t set_tx_freq(double target_freq){ +        return _sdev->set_tx_freq(target_freq); +    } + +    //tune_result_t set_tx_freq(double target_freq, double lo_off){ +    //    return _sdev->set_tx_freq(target_freq, lo_off); +    //} + +    double get_tx_freq(void){ +        return _sdev->get_tx_freq(); +    } + +    freq_range_t get_tx_freq_range(void){ +        return _sdev->get_tx_freq_range(); +    } + +    void set_tx_gain(float gain){ +        return _sdev->set_tx_gain(gain); +    } + +    float get_tx_gain(void){ +        return _sdev->get_tx_gain(); +    } + +    gain_range_t get_tx_gain_range(void){ +        return _sdev->get_tx_gain_range(); +    } + +    void set_tx_antenna(const std::string &ant){ +        return _sdev->set_tx_antenna(ant); +    } + +    std::string get_tx_antenna(void){ +        return _sdev->get_tx_antenna(); +    } + +    std::vector<std::string> get_tx_antennas(void){ +        return _sdev->get_tx_antennas(); +    } + +    bool get_tx_lo_locked(void){ +        return _sdev->get_tx_lo_locked(); +    } + +    dboard_iface::sptr get_tx_dboard_iface(void){ +        return _sdev->get_tx_dboard_iface(); +    } + +private: +    single_usrp::sptr _sdev; +}; + +}}} + +namespace uhd{ namespace usrp{ + +/*********************************************************************** + * The Make Function + **********************************************************************/ +inline simple_usrp::sptr simple_usrp::make(const device_addr_t &dev_addr){ +    uhd::warning::post( +        "The simple USRP interface has been deprecated.\n" +        "Please switch to the single USRP interface.\n" +        "#include <uhd/usrp/single_usrp.hpp>\n" +        "single_usrp::sptr sdev = single_usrp::make(args);\n" +    ); +    return sptr(new simple_usrp_impl(dev_addr)); +} + +}} +  #endif /* INCLUDED_UHD_USRP_SIMPLE_USRP_HPP */ diff --git a/host/include/uhd/usrp/single_usrp.hpp b/host/include/uhd/usrp/single_usrp.hpp index 1b89a3620..26303fe10 100644 --- a/host/include/uhd/usrp/single_usrp.hpp +++ b/host/include/uhd/usrp/single_usrp.hpp @@ -23,6 +23,7 @@  #include <uhd/types/ranges.hpp>  #include <uhd/types/stream_cmd.hpp>  #include <uhd/types/clock_config.hpp> +#include <uhd/types/tune_request.hpp>  #include <uhd/types/tune_result.hpp>  #include <uhd/usrp/subdev_spec.hpp>  #include <uhd/usrp/dboard_iface.hpp> @@ -33,8 +34,8 @@  namespace uhd{ namespace usrp{  /*! - * The single USRP device class: - * A single usrp facilitates ease-of-use for most use-case scenarios. + * The single-USRP device class: + * A single-USRP facilitates ease-of-use for most use-case scenarios.   * The wrapper provides convenience functions to tune the devices   * as well as to set the dboard gains, antennas, and other properties.   * This wrapper supports multi-channel configurations per motherboard. @@ -43,6 +44,9 @@ class UHD_API single_usrp : boost::noncopyable{  public:      typedef boost::shared_ptr<single_usrp> sptr; +    //! A wildcard gain element name +    static const std::string ALL_GAINS; +      /*!       * Make a new single usrp from the device address.       * \param dev_addr the device address @@ -57,15 +61,21 @@ public:       */      virtual device::sptr get_device(void) = 0; +    /******************************************************************* +     * Mboard methods +     ******************************************************************/      /*! -     * Get a printable name for this usrp. +     * Get a printable summary for this USRP configuration.       * \return a printable string       */      virtual std::string get_pp_string(void) = 0; -    /******************************************************************* -     * Misc -     ******************************************************************/ +    /*! +     * Get canonical name for this USRP motherboard. +     * \return a string representing the name +     */ +    virtual std::string get_mboard_name(void) = 0; +      /*!       * Gets the current time in the usrp time registers.       * \return a timespec representing current usrp time @@ -110,60 +120,331 @@ public:      /*******************************************************************       * RX methods       ******************************************************************/ +    /*! +     * Set the RX subdevice specification: +     * The subdev spec maps a physical part of a daughter-board to a channel number. +     * Set the subdev spec before calling into any methods with a channel number. +     * \param spec the new subdevice specification +     */      virtual void set_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec) = 0; + +    /*! +     * Get the RX subdevice specification. +     * \return the subdevice specification in use +     */      virtual uhd::usrp::subdev_spec_t get_rx_subdev_spec(void) = 0; +    /*! +     * Get the name of the RX subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the subdevice name +     */ +    virtual std::string get_rx_subdev_name(size_t chan = 0) = 0; + +    /*! +     * Set the RX sample rate across all channels. +     * \param rate the rate in Sps +     */      virtual void set_rx_rate(double rate) = 0; + +    /*! +     * Gets the RX sample rate for all channels. +     * \return the rate in Sps +     */      virtual double get_rx_rate(void) = 0; -    virtual tune_result_t set_rx_freq(double freq, size_t chan = 0) = 0; -    virtual tune_result_t set_rx_freq(double freq, double lo_off, size_t chan = 0) = 0; +    /*! +     * Set the RX center frequency. +     * \param tune_request tune request instructions +     * \param chan the channel index 0 to N-1 +     * \return a tune result object +     */ +    virtual tune_result_t set_rx_freq( +        const tune_request_t &tune_request, size_t chan = 0 +    ) = 0; + +    /*! +     * Get the RX center frequency. +     * \param chan the channel index 0 to N-1 +     * \return the frequency in Hz +     */      virtual double get_rx_freq(size_t chan = 0) = 0; + +    /*! +     * Get the RX center frequency range. +     * \param chan the channel index 0 to N-1 +     * \return a frequency range object +     */      virtual freq_range_t get_rx_freq_range(size_t chan = 0) = 0; -    virtual void set_rx_gain(float gain, size_t chan = 0) = 0; -    virtual float get_rx_gain(size_t chan = 0) = 0; -    virtual gain_range_t get_rx_gain_range(size_t chan = 0) = 0; +    /*! +     * Set the RX gain value for the specified gain element. +     * For an empty name, distribute across all gain elements. +     * \param gain the gain in dB +     * \param name the name of the gain element +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_rx_gain(float gain, const std::string &name, size_t chan = 0) = 0; + +    //! A convenience wrapper for setting overall RX gain +    void set_rx_gain(float gain, size_t chan = 0){ +        return this->set_rx_gain(gain, ALL_GAINS, chan); +    } + +    /*! +     * Get the RX gain value for the specified gain element. +     * For an empty name, sum across all gain elements. +     * \param name the name of the gain element +     * \param chan the channel index 0 to N-1 +     * \return the gain in dB +     */ +    virtual float get_rx_gain(const std::string &name, size_t chan = 0) = 0; + +    //! A convenience wrapper for getting overall RX gain +    float get_rx_gain(size_t chan = 0){ +        return this->get_rx_gain(ALL_GAINS, chan); +    } + +    /*! +     * Get the RX gain range for the specified gain element. +     * For an empty name, calculate the overall gain range. +     * \param name the name of the gain element +     * \param chan the channel index 0 to N-1 +     * \return a gain range object +     */ +    virtual gain_range_t get_rx_gain_range(const std::string &name, size_t chan = 0) = 0; + +    //! A convenience wrapper for getting overall RX gain range +    gain_range_t get_rx_gain_range(size_t chan = 0){ +        return this->get_rx_gain_range(ALL_GAINS, chan); +    } + +    /*! +     * Get the names of the gain elements in the RX chain. +     * Gain elements are ordered from antenna to FPGA. +     * \param chan the channel index 0 to N-1 +     * \return a vector of gain element names +     */ +    virtual std::vector<std::string> get_rx_gain_names(size_t chan = 0) = 0; +    /*! +     * Select the RX antenna on the subdevice. +     * \param ant the antenna name +     * \param chan the channel index 0 to N-1 +     */      virtual void set_rx_antenna(const std::string &ant, size_t chan = 0) = 0; + +    /*! +     * Get the selected RX antenna on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the antenna name +     */      virtual std::string get_rx_antenna(size_t chan = 0) = 0; + +    /*! +     * Get a list of possible RX antennas on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return a vector of antenna names +     */      virtual std::vector<std::string> get_rx_antennas(size_t chan = 0) = 0; +    /*! +     * Get the locked status of the LO on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return true for locked +     */      virtual bool get_rx_lo_locked(size_t chan = 0) = 0;      /*! -     * Read the RSSI value from a usrp device. -     * Or throw if the dboard does not support an RSSI readback. +     * Set the RX bandwidth on the subdevice. +     * \param bandwidth the bandwidth in Hz +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_rx_bandwidth(double bandwidth, size_t chan = 0) = 0; + +    /*! +     * Get the RX bandwidth on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the bandwidth in Hz +     */ +    virtual double get_rx_bandwidth(size_t chan = 0) = 0; + +    /*! +     * Read the RSSI value on the RX subdevice. +     * \param chan the channel index 0 to N-1       * \return the rssi in dB +     * \throw exception if RSSI readback not supported       */      virtual float read_rssi(size_t chan = 0) = 0; +    /*! +     * Get the dboard interface object for the RX subdevice. +     * The dboard interface gives access to GPIOs, SPI, I2C, low-speed ADC and DAC. +     * Use at your own risk! +     * \param chan the channel index 0 to N-1 +     * \return the dboard interface sptr +     */      virtual dboard_iface::sptr get_rx_dboard_iface(size_t chan = 0) = 0;      /*******************************************************************       * TX methods       ******************************************************************/ +    /*! +     * Set the TX subdevice specification: +     * The subdev spec maps a physical part of a daughter-board to a channel number. +     * Set the subdev spec before calling into any methods with a channel number. +     * \param spec the new subdevice specification +     */      virtual void set_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec) = 0; + +    /*! +     * Get the TX subdevice specification. +     * \return the subdevice specification in use +     */      virtual uhd::usrp::subdev_spec_t get_tx_subdev_spec(void) = 0; +    /*! +     * Get the name of the TX subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the subdevice name +     */ +    virtual std::string get_tx_subdev_name(size_t chan = 0) = 0; + +    /*! +     * Set the TX sample rate across all channels. +     * \param rate the rate in Sps +     */      virtual void set_tx_rate(double rate) = 0; + +    /*! +     * Gets the TX sample rate for all channels. +     * \return the rate in Sps +     */      virtual double get_tx_rate(void) = 0; -    virtual tune_result_t set_tx_freq(double freq, size_t chan = 0) = 0; -    virtual tune_result_t set_tx_freq(double freq, double lo_off, size_t chan = 0) = 0; +    /*! +     * Set the TX center frequency. +     * \param tune_request tune request instructions +     * \param chan the channel index 0 to N-1 +     * \return a tune result object +     */ +    virtual tune_result_t set_tx_freq( +        const tune_request_t &tune_request, size_t chan = 0 +    ) = 0; + +    /*! +     * Get the TX center frequency. +     * \param chan the channel index 0 to N-1 +     * \return the frequency in Hz +     */      virtual double get_tx_freq(size_t chan = 0) = 0; + +    /*! +     * Get the TX center frequency range. +     * \param chan the channel index 0 to N-1 +     * \return a frequency range object +     */      virtual freq_range_t get_tx_freq_range(size_t chan = 0) = 0; -    virtual void set_tx_gain(float gain, size_t chan = 0) = 0; -    virtual float get_tx_gain(size_t chan = 0) = 0; -    virtual gain_range_t get_tx_gain_range(size_t chan = 0) = 0; +    /*! +     * Set the TX gain value for the specified gain element. +     * For an empty name, distribute across all gain elements. +     * \param gain the gain in dB +     * \param name the name of the gain element +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_tx_gain(float gain, const std::string &name, size_t chan = 0) = 0; + +    //! A convenience wrapper for setting overall TX gain +    void set_tx_gain(float gain, size_t chan = 0){ +        return this->set_tx_gain(gain, ALL_GAINS, chan); +    } +    /*! +     * Get the TX gain value for the specified gain element. +     * For an empty name, sum across all gain elements. +     * \param name the name of the gain element +     * \param chan the channel index 0 to N-1 +     * \return the gain in dB +     */ +    virtual float get_tx_gain(const std::string &name, size_t chan = 0) = 0; + +    //! A convenience wrapper for getting overall TX gain +    float get_tx_gain(size_t chan = 0){ +        return this->get_tx_gain(ALL_GAINS, chan); +    } + +    /*! +     * Get the TX gain range for the specified gain element. +     * For an empty name, calculate the overall gain range. +     * \param name the name of the gain element +     * \param chan the channel index 0 to N-1 +     * \return a gain range object +     */ +    virtual gain_range_t get_tx_gain_range(const std::string &name, size_t chan = 0) = 0; + +    //! A convenience wrapper for getting overall TX gain range +    gain_range_t get_tx_gain_range(size_t chan = 0){ +        return this->get_tx_gain_range(ALL_GAINS, chan); +    } + +    /*! +     * Get the names of the gain elements in the TX chain. +     * Gain elements are ordered from antenna to FPGA. +     * \param chan the channel index 0 to N-1 +     * \return a vector of gain element names +     */ +    virtual std::vector<std::string> get_tx_gain_names(size_t chan = 0) = 0; + +    /*! +     * Select the TX antenna on the subdevice. +     * \param ant the antenna name +     * \param chan the channel index 0 to N-1 +     */      virtual void set_tx_antenna(const std::string &ant, size_t chan = 0) = 0; + +    /*! +     * Get the selected TX antenna on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the antenna name +     */      virtual std::string get_tx_antenna(size_t chan = 0) = 0; + +    /*! +     * Get a list of possible TX antennas on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return a vector of antenna names +     */      virtual std::vector<std::string> get_tx_antennas(size_t chan = 0) = 0; +    /*! +     * Get the locked status of the LO on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return true for locked +     */      virtual bool get_tx_lo_locked(size_t chan = 0) = 0; +    /*! +     * Set the TX bandwidth on the subdevice. +     * \param bandwidth the bandwidth in Hz +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_tx_bandwidth(double bandwidth, size_t chan = 0) = 0; + +    /*! +     * Get the TX bandwidth on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the bandwidth in Hz +     */ +    virtual double get_tx_bandwidth(size_t chan = 0) = 0; + +    /*! +     * Get the dboard interface object for the TX subdevice. +     * The dboard interface gives access to GPIOs, SPI, I2C, low-speed ADC and DAC. +     * Use at your own risk! +     * \param chan the channel index 0 to N-1 +     * \return the dboard interface sptr +     */      virtual dboard_iface::sptr get_tx_dboard_iface(size_t chan = 0) = 0;  }; diff --git a/host/include/uhd/usrp/subdev_props.hpp b/host/include/uhd/usrp/subdev_props.hpp index cd07cb7a8..8f096ffe4 100644 --- a/host/include/uhd/usrp/subdev_props.hpp +++ b/host/include/uhd/usrp/subdev_props.hpp @@ -53,6 +53,7 @@ namespace uhd{ namespace usrp{          SUBDEV_PROP_ANTENNA_NAMES     = 'A', //ro, prop_names_t          SUBDEV_PROP_LO_LOCKED         = 'L', //ro, bool          SUBDEV_PROP_CONNECTION        = 'c', //ro, subdev_conn_t +        SUBDEV_PROP_ENABLED           = 'e', //rw, bool          SUBDEV_PROP_USE_LO_OFFSET     = 'l', //ro, bool          SUBDEV_PROP_RSSI              = 'R', //ro, float          SUBDEV_PROP_BANDWIDTH         = 'B'  //rw, double diff --git a/host/include/uhd/usrp/subdev_spec.hpp b/host/include/uhd/usrp/subdev_spec.hpp index 2f32509b9..b189724c9 100644 --- a/host/include/uhd/usrp/subdev_spec.hpp +++ b/host/include/uhd/usrp/subdev_spec.hpp @@ -19,16 +19,17 @@  #define INCLUDED_UHD_USRP_SUBDEV_SPEC_HPP  #include <uhd/config.hpp> +#include <boost/operators.hpp>  #include <vector>  #include <string>  namespace uhd{ namespace usrp{      /*! -     * A subdevice specification (daughterboard, subdevice) name pairing. +     * A subdevice specification (daughterboard slot, subdevice) name pairing.       */ -    struct UHD_API subdev_spec_pair_t{ -        //! The daughterboard name +    struct UHD_API subdev_spec_pair_t : boost::equality_comparable<subdev_spec_pair_t>{ +        //! The daughterboard slot name          std::string db_name;          //! The subdevice name @@ -45,8 +46,11 @@ namespace uhd{ namespace usrp{          );      }; +    //! overloaded comparison operator for subdev_spec_pair_t +    UHD_API bool operator==(const subdev_spec_pair_t &, const subdev_spec_pair_t &); +      /*! -     * A list of (daughterboard name, subdevice name) pairs: +     * A list of (daughterboard slot name, subdevice name) pairs:       *       * A subdevice specification represents a list of subdevices on a motherboard.       * The subdevices specified may span across multiple daughterboards; @@ -58,6 +62,11 @@ namespace uhd{ namespace usrp{       * The markup-string is a whitespace separated list of dboard:subdev pairs.       * The first pair represents the subdevice for channel zero,       * the second pair represents the subdevice for channel one, and so on. +     * +     * Special handling for empty conditions: +     * - An empty subdevice specification means: select the first subdevice found in the configuration +     * - An empty daughterboard name means: select the only daughterboard slot or error if multiple exist +     * - An empty subdevice name means: select the only subdevice on that board or error if multiple exist       */      class UHD_API subdev_spec_t : public std::vector<subdev_spec_pair_t>{      public: diff --git a/host/include/uhd/usrp/tune_helper.hpp b/host/include/uhd/usrp/tune_helper.hpp index ec133fa08..db12241c1 100644 --- a/host/include/uhd/usrp/tune_helper.hpp +++ b/host/include/uhd/usrp/tune_helper.hpp @@ -20,6 +20,7 @@  #include <uhd/config.hpp>  #include <uhd/wax.hpp> +#include <uhd/types/tune_request.hpp>  #include <uhd/types/tune_result.hpp>  namespace uhd{ namespace usrp{ @@ -32,23 +33,12 @@ namespace uhd{ namespace usrp{       * \param subdev the dboard subdevice object with properties       * \param ddc the mboard dsp object with properties       * \param chan the channel of the dsp to tune -     * \param target_freq the desired center frequency -     * \param lo_offset an offset for the subdevice IF from center +     * \param tune_request tune request instructions       * \return a tune result struct       */      UHD_API tune_result_t tune_rx_subdev_and_dsp(          wax::obj subdev, wax::obj ddc, size_t chan, -        double target_freq, double lo_offset -    ); - -    /*! -     * Tune a rx chain to the desired frequency: -     * Same as the above, except the LO offset -     * is calculated based on the subdevice and BW. -     */ -    UHD_API tune_result_t tune_rx_subdev_and_dsp( -        wax::obj subdev, wax::obj ddc, -        size_t chan, double target_freq +        const tune_request_t &tune_request      );      /*! @@ -70,23 +60,12 @@ namespace uhd{ namespace usrp{       * \param subdev the dboard subdevice object with properties       * \param duc the mboard dsp object with properties       * \param chan the channel of the dsp to tune -     * \param target_freq the desired center frequency -     * \param lo_offset an offset for the subdevice IF from center +     * \param tune_request tune request instructions       * \return a tune result struct       */      UHD_API tune_result_t tune_tx_subdev_and_dsp(          wax::obj subdev, wax::obj duc, size_t chan, -        double target_freq, double lo_offset -    ); - -    /*! -     * Tune a tx chain to the desired frequency: -     * Same as the above, except the LO offset -     * is calculated based on the subdevice and BW. -     */ -    UHD_API tune_result_t tune_tx_subdev_and_dsp( -        wax::obj subdev, wax::obj duc, -        size_t chan, double target_freq +        const tune_request_t &tune_request      );      /*! diff --git a/host/include/uhd/utils/gain_group.hpp b/host/include/uhd/utils/gain_group.hpp index 3955dfa9a..c863248ce 100644 --- a/host/include/uhd/utils/gain_group.hpp +++ b/host/include/uhd/utils/gain_group.hpp @@ -23,6 +23,8 @@  #include <boost/shared_ptr.hpp>  #include <boost/function.hpp>  #include <boost/utility.hpp> +#include <vector> +#include <string>  namespace uhd{ @@ -40,36 +42,57 @@ public:      typedef boost::shared_ptr<gain_group> sptr;      /*! -     * Get the overall gain range for this group. +     * Get the gain range for the gain element specified by name. +     * For an empty name, get the overall gain range for this group.       * Overall step is defined as the minimum step size. +     * \param name name of the gain element (optional)       * \return a gain range with overall min, max, step       */ -    virtual gain_range_t get_range(void) = 0; +    virtual gain_range_t get_range(const std::string &name = "") = 0;      /*! -     * Get the overall gain value for this group. -     * \return a summation of all the gain values +     * Get the gain value for the gain element specified by name. +     * For an empty name, get the overall gain value for this group. +     * \param name name of the gain element (optional) +     * \return a gain value of the element or all elements       */ -    virtual float get_value(void) = 0; +    virtual float get_value(const std::string &name = "") = 0;      /*! -     * Set the overall gain value for this group. +     * Set the gain value for the gain element specified by name. +     * For an empty name, set the overall gain value for this group.       * The power will be distributed across individual gain elements.       * The semantics of how to do this are determined by the priority. -     * \param gain the gain to set across the group +     * \param gain the gain to set for the lement or across the group +     * \param name name of the gain element (optional)       */ -    virtual void set_value(float gain) = 0; +    virtual void set_value(float gain, const std::string &name = "") = 0;      /*! -     * Register a set of gain functions into this group. +     * Get a list of names of registered gain elements. +     * The names are in the order that they were registered. +     * \return a vector of gain name strings +     */ +    virtual const std::vector<std::string> get_names(void) = 0; + +    /*! +     * Register a set of gain functions into this group: +     * +     * The name should be a unique and non-empty name. +     * Othwerwise, the implementation will rename it. +     *       * Priority determines how power will be distributed       * with higher priorities getting the power first,       * and lower priorities getting the remainder power. +     * +     * \param name the name of the gain element       * \param gain_fcns the set of gain functions       * \param priority the priority of the gain element       */      virtual void register_fcns( -        const gain_fcns_t &gain_fcns, size_t priority = 0 +        const std::string &name, +        const gain_fcns_t &gain_fcns, +        size_t priority = 0      ) = 0;      /*! diff --git a/host/include/uhd/utils/warning.hpp b/host/include/uhd/utils/warning.hpp index 91d8400ab..a1e3f0d1e 100644 --- a/host/include/uhd/utils/warning.hpp +++ b/host/include/uhd/utils/warning.hpp @@ -19,16 +19,44 @@  #define INCLUDED_UHD_UTILS_WARNING_HPP  #include <uhd/config.hpp> +#include <boost/function.hpp> +#include <vector>  #include <string> -namespace uhd{ +namespace uhd{ namespace warning{ + +    //! Callback function type for a message handler +    typedef boost::function<void(std::string)> handler_t;      /*! -     * Print a formatted warning string to stderr. +     * Post a warning message to all registered handlers.       * \param msg the multiline warning message       */ -    UHD_API void print_warning(const std::string &msg); +    UHD_API void post(const std::string &msg); + +    /*! +     * Register a new handler with this name. +     * If the name was already registered for this name, +     * the old registered handler will be replaced. +     * \param name a unique name for this handler +     * \param handler the callback handler function +     */ +    UHD_API void register_handler(const std::string &name, const handler_t &handler); + +    /*! +     * Unregister a handler for this name. +     * \param name a unique name for a registered handler +     * \return the handler that was registered +     * \throw error when the name was not found in the registry +     */ +    UHD_API handler_t unregister_handler(const std::string &name); + +    /*! +     * Get a list of registered handler names. +     * \return a vector of unique string names +     */ +    UHD_API const std::vector<std::string> registry_names(void); -} //namespace uhd +}} //namespace uhd::warning  #endif /* INCLUDED_UHD_UTILS_WARNING_HPP */ diff --git a/host/lib/device.cpp b/host/lib/device.cpp index d575ebaab..386588a08 100644 --- a/host/lib/device.cpp +++ b/host/lib/device.cpp @@ -26,6 +26,7 @@  #include <boost/functional/hash.hpp>  #include <boost/tuple/tuple.hpp>  #include <stdexcept> +#include <iostream>  using namespace uhd; @@ -73,12 +74,17 @@ device_addrs_t device::find(const device_addr_t &hint){      device_addrs_t device_addrs;      BOOST_FOREACH(const dev_fcn_reg_t &fcn, get_dev_fcn_regs()){ -        device_addrs_t discovered_addrs = fcn.get<0>()(hint); -        device_addrs.insert( -            device_addrs.begin(), -            discovered_addrs.begin(), -            discovered_addrs.end() -        ); +        try{ +            device_addrs_t discovered_addrs = fcn.get<0>()(hint); +            device_addrs.insert( +                device_addrs.begin(), +                discovered_addrs.begin(), +                discovered_addrs.end() +            ); +        } +        catch(const std::exception &e){ +            std::cerr << "Device discovery error: " << e.what() << std::endl; +        }      }      return device_addrs; diff --git a/host/lib/ic_reg_maps/CMakeLists.txt b/host/lib/ic_reg_maps/CMakeLists.txt index f8e15c13d..25f34a280 100644 --- a/host/lib/ic_reg_maps/CMakeLists.txt +++ b/host/lib/ic_reg_maps/CMakeLists.txt @@ -68,3 +68,8 @@ LIBUHD_PYTHON_GEN_SOURCE(      ${CMAKE_SOURCE_DIR}/lib/ic_reg_maps/gen_ad9522_regs.py      ${CMAKE_BINARY_DIR}/lib/ic_reg_maps/ad9522_regs.hpp  ) + +LIBUHD_PYTHON_GEN_SOURCE( +    ${CMAKE_SOURCE_DIR}/lib/ic_reg_maps/gen_tuner_4937di5_regs.py +    ${CMAKE_BINARY_DIR}/lib/ic_reg_maps/tuner_4937di5_regs.hpp +) diff --git a/host/lib/ic_reg_maps/gen_max2118_regs.py b/host/lib/ic_reg_maps/gen_max2118_regs.py index 506fbaec8..506fbaec8 100644..100755 --- a/host/lib/ic_reg_maps/gen_max2118_regs.py +++ b/host/lib/ic_reg_maps/gen_max2118_regs.py diff --git a/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py b/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py new file mode 100644 index 000000000..73f7aa3db --- /dev/null +++ b/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# +# Copyright 2010 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/>. +# + +######################################################################## +# Template for raw text data describing registers +# name addr[bit range inclusive] default optional enums +######################################################################## +REGS_TMPL="""\ +######################################################################## +## Note: offsets given from perspective of data bits (excludes address) +######################################################################## +## Divider byte 1 +######################################################################## +db1                   0[0:6]        0x00 +######################################################################## +## Divider byte 2 +######################################################################## +db2                   1[0:7]        0x00 +######################################################################## +## Control byte 1 +######################################################################## +cb7                   2[7]          0x01 +cp                    2[6]          0x00     low,high +os                    2[0]          0x00     on,off +rs                    2[1:2]        0x00     d512=3,d640=0,d1024=1 +test                  2[3:5]        0x01     normal=0x01,cpoff=0x02,cpsink=0x06,cpsrc=0x07,cptest1=0x04,cptest2=0x05 +######################################################################## +## Control byte 2 +######################################################################## +bandsel               3[4:7]        0x03     uhf=0x03,vhfhi=0x09,vhflo=0x0a +power                 3[3]          0x00     on,off +""" + +######################################################################## +# Template for methods in the body of the struct +######################################################################## +BODY_TMPL="""\ +boost::uint8_t get_reg(boost::uint8_t addr){ +    boost::uint8_t reg = 0; +    switch(addr){ +    #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) +    case $addr: +        #for $reg in filter(lambda r: r.get_addr() == addr, $regs) +        reg |= (boost::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); +        #end for +        break; +    #end for +    } +    return boost::uint8_t(reg); +} + +""" + +if __name__ == '__main__': +    import common; common.generate( +        name='tuner_4937di5_regs', +        regs_tmpl=REGS_TMPL, +        body_tmpl=BODY_TMPL, +        file=__file__, +    ) diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index 62c4f62b1..b95d46381 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -31,12 +31,15 @@ IF(LIBUSB_FOUND)          ${CMAKE_SOURCE_DIR}/lib/transport/libusb1_zero_copy.cpp          ${CMAKE_SOURCE_DIR}/lib/transport/libusb1_base.cpp          ${CMAKE_SOURCE_DIR}/lib/transport/libusb1_base.hpp -        ${CMAKE_SOURCE_DIR}/lib/transport/libusb1_device_handle.cpp      ) -    IF(WIN32) #include our custom stdint for libusb -	INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/transport/include) -    ENDIF(WIN32) +    IF(MSVC) #include our custom stdint for libusb +        INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/transport/msvc) +    ENDIF(MSVC)      SET(HAVE_USB_SUPPORT TRUE) +ELSE(LIBUSB_FOUND) +    LIBUHD_APPEND_SOURCES( +        ${CMAKE_SOURCE_DIR}/lib/transport/usb_dummy_impl.cpp +    )  ENDIF(LIBUSB_FOUND)  IF(HAVE_USB_SUPPORT) diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp index 1f816c6e2..cfa77d9ca 100644 --- a/host/lib/transport/libusb1_base.cpp +++ b/host/lib/transport/libusb1_base.cpp @@ -17,110 +17,249 @@  #include "libusb1_base.hpp"  #include <uhd/utils/assert.hpp> +#include <uhd/types/dict.hpp> +#include <boost/weak_ptr.hpp> +#include <boost/foreach.hpp>  #include <iostream> +using namespace uhd;  using namespace uhd::transport; -/********************************************************** - * Helper Methods - **********************************************************/ +/*********************************************************************** + * libusb session + **********************************************************************/ +class libusb_session_impl : public libusb::session{ +public: +    libusb_session_impl(void){ +        UHD_ASSERT_THROW(libusb_init(&_context) == 0); +        libusb_set_debug(_context, debug_level); +    } + +    ~libusb_session_impl(void){ +        libusb_exit(_context); +    } + +    libusb_context *get_context(void) const{ +        return _context; +    } + +private: +    libusb_context *_context; +}; + +libusb::session::sptr libusb::session::get_global_session(void){ +    static boost::weak_ptr<session> global_session; -/********************************************************** - * libusb namespace  - **********************************************************/ -void libusb::init(libusb_context **ctx, int debug_level) -{ -    if (libusb_init(ctx) < 0) -        std::cerr << "error: libusb_init" << std::endl; +    //not expired -> get existing session +    if (not global_session.expired()) return global_session.lock(); -    libusb_set_debug(*ctx, debug_level); +    //create a new global session +    sptr new_global_session(new libusb_session_impl()); +    global_session = new_global_session; +    return new_global_session;  } -libusb_device_handle *libusb::open_device(libusb_context *ctx, -                                          usb_device_handle::sptr handle) -{ -    libusb_device_handle *dev_handle = NULL; -    libusb_device **libusb_dev_list; -    size_t dev_cnt = libusb_get_device_list(ctx, &libusb_dev_list); - -    //find and open the USB device  -    for (size_t i = 0; i < dev_cnt; i++) { -        libusb_device *dev = libusb_dev_list[i]; - -        if (compare_device(dev, handle)) { -            libusb_open(dev, &dev_handle); -            libusb_unref_device(dev); -            break; -        } -             -        libusb_unref_device(dev); +/*********************************************************************** + * libusb device + **********************************************************************/ +class libusb_device_impl : public libusb::device{ +public: +    libusb_device_impl(libusb_device *dev){ +        _session = libusb::session::get_global_session(); +        _dev = dev;      } -    return dev_handle; -} +    ~libusb_device_impl(void){ +        libusb_unref_device(this->get()); +    } + +    libusb_device *get(void) const{ +        return _dev; +    } + +private: +    libusb::session::sptr _session; //always keep a reference to session +    libusb_device *_dev; +}; + +/*********************************************************************** + * libusb device list + **********************************************************************/ +class libusb_device_list_impl : public libusb::device_list{ +public: +    libusb_device_list_impl(void){ +        libusb::session::sptr sess = libusb::session::get_global_session(); -//note: changed order of checks so it only tries to get_serial and get_device_address if vid and pid match -//doing this so it doesn't try to open the device if it's not ours -bool libusb::compare_device(libusb_device *dev, -                            usb_device_handle::sptr handle) -{ -    std::string serial         = handle->get_serial(); -    boost::uint16_t vendor_id  = handle->get_vendor_id(); -    boost::uint16_t product_id = handle->get_product_id(); -    boost::uint16_t device_addr = handle->get_device_addr(); - -    libusb_device_descriptor libusb_desc; -    if (libusb_get_device_descriptor(dev, &libusb_desc) < 0) -        return false; -    if (vendor_id != libusb_desc.idVendor) -        return false; -    if (product_id != libusb_desc.idProduct) -        return false;  -    if (serial != get_serial(dev)) -        return false; -    if (device_addr != libusb_get_device_address(dev)) -        return false; - -    return true; +        //allocate a new list of devices +        libusb_device** dev_list; +        ssize_t ret = libusb_get_device_list(sess->get_context(), &dev_list); +        if (ret < 0) throw std::runtime_error("cannot enumerate usb devices"); + +        //fill the vector of device references +        for (size_t i = 0; i < size_t(ret); i++) _devs.push_back( +            libusb::device::sptr(new libusb_device_impl(dev_list[i])) +        ); + +        //free the device list but dont unref (done in ~device) +        libusb_free_device_list(dev_list, false/*dont unref*/); +    } + +    size_t size(void) const{ +        return _devs.size(); +    } + +    libusb::device::sptr at(size_t i) const{ +        return _devs.at(i); +    } + +private: +    std::vector<libusb::device::sptr> _devs; +}; + +libusb::device_list::sptr libusb::device_list::make(void){ +    return sptr(new libusb_device_list_impl());  } +/*********************************************************************** + * libusb device descriptor + **********************************************************************/ +class libusb_device_descriptor_impl : public libusb::device_descriptor{ +public: +    libusb_device_descriptor_impl(libusb::device::sptr dev){ +        _dev = dev; +        UHD_ASSERT_THROW(libusb_get_device_descriptor(_dev->get(), &_desc) == 0); +    } -bool libusb::open_interface(libusb_device_handle *dev_handle, -                            int interface) -{ -    int ret = libusb_claim_interface(dev_handle, interface); -    if (ret < 0) { -        std::cerr << "error: libusb_claim_interface() " << ret << std::endl; -        return false; +    const libusb_device_descriptor &get(void) const{ +        return _desc;      } -    else { -        return true; + +    std::string get_ascii_serial(void) const{ +        if (this->get().iSerialNumber == 0) return ""; + +        libusb::device_handle::sptr handle( +            libusb::device_handle::get_cached_handle(_dev) +        ); + +        unsigned char buff[512]; +        ssize_t ret = libusb_get_string_descriptor_ascii( +            handle->get(), this->get().iSerialNumber, buff, sizeof(buff) +        ); +        if (ret < 0) return ""; //on error, just return empty string + +        return std::string((char *)buff, ret);      } + +private: +    libusb::device::sptr _dev; //always keep a reference to device +    libusb_device_descriptor _desc; +}; + +libusb::device_descriptor::sptr libusb::device_descriptor::make(device::sptr dev){ +    return sptr(new libusb_device_descriptor_impl(dev));  } +/*********************************************************************** + * libusb device handle + **********************************************************************/ +class libusb_device_handle_impl : public libusb::device_handle{ +public: +    libusb_device_handle_impl(libusb::device::sptr dev){ +        _dev = dev; +        UHD_ASSERT_THROW(libusb_open(_dev->get(), &_handle) == 0); +    } + +    ~libusb_device_handle_impl(void){ +        //release all claimed interfaces +        for (size_t i = 0; i < _claimed.size(); i++){ +            libusb_release_interface(this->get(), _claimed[i]); +        } +        libusb_close(_handle); +    } + +    libusb_device_handle *get(void) const{ +        return _handle; +    } + +    void claim_interface(int interface){ +        UHD_ASSERT_THROW(libusb_claim_interface(this->get(), interface) == 0); +        _claimed.push_back(interface); +    } + +private: +    libusb::device::sptr _dev; //always keep a reference to device +    libusb_device_handle *_handle; +    std::vector<int> _claimed; +}; + +libusb::device_handle::sptr libusb::device_handle::get_cached_handle(device::sptr dev){ +    static uhd::dict<libusb_device *, boost::weak_ptr<device_handle> > handles; + +    //not expired -> get existing handle +    if (handles.has_key(dev->get()) and not handles[dev->get()].expired()){ +        return handles[dev->get()].lock(); +    } + +    //create a new cached handle +    try{ +        sptr new_handle(new libusb_device_handle_impl(dev)); +        handles[dev->get()] = new_handle; +        return new_handle; +    } +    catch(const std::exception &e){ +        std::cerr << "USB open failed: see the application notes for your device." << std::endl; +        throw std::runtime_error(e.what()); +    } +} -std::string libusb::get_serial(libusb_device *dev) -{ -    unsigned char buff[32]; +/*********************************************************************** + * libusb special handle + **********************************************************************/ +class libusb_special_handle_impl : public libusb::special_handle{ +public: +    libusb_special_handle_impl(libusb::device::sptr dev){ +        _dev = dev; +    } -    libusb_device_descriptor desc; -    if (libusb_get_device_descriptor(dev, &desc) < 0) -        return ""; +    libusb::device::sptr get_device(void) const{ +        return _dev; +    } -    if (desc.iSerialNumber == 0) -        return ""; +    std::string get_serial(void) const{ +        return libusb::device_descriptor::make(this->get_device())->get_ascii_serial(); +    } -    //open the device because we have to -    libusb_device_handle *dev_handle; -    if (libusb_open(dev, &dev_handle) < 0) -        return ""; +    boost::uint16_t get_vendor_id(void) const{ +        return libusb::device_descriptor::make(this->get_device())->get().idVendor; +    } -    if (libusb_get_string_descriptor_ascii(dev_handle, desc.iSerialNumber, -                                           buff, sizeof(buff)) < 0) { -        return ""; +    boost::uint16_t get_product_id(void) const{ +        return libusb::device_descriptor::make(this->get_device())->get().idProduct;      } -    libusb_close(dev_handle); +private: +    libusb::device::sptr _dev; //always keep a reference to device +}; + +libusb::special_handle::sptr libusb::special_handle::make(device::sptr dev){ +    return sptr(new libusb_special_handle_impl(dev)); +} + +/*********************************************************************** + * list device handles implementations + **********************************************************************/ +std::vector<usb_device_handle::sptr> usb_device_handle::get_device_list( +    boost::uint16_t vid, boost::uint16_t pid +){ +    std::vector<usb_device_handle::sptr> handles; + +    libusb::device_list::sptr dev_list = libusb::device_list::make(); +    for (size_t i = 0; i < dev_list->size(); i++){ +        usb_device_handle::sptr handle = libusb::special_handle::make(dev_list->at(i)); +        if (handle->get_vendor_id() == vid and handle->get_product_id() == pid){ +            handles.push_back(handle); +        } +    } -    return (char*) buff; +    return handles;  } diff --git a/host/lib/transport/libusb1_base.hpp b/host/lib/transport/libusb1_base.hpp index 484bcf3d9..04c1d6574 100644 --- a/host/lib/transport/libusb1_base.hpp +++ b/host/lib/transport/libusb1_base.hpp @@ -19,74 +19,129 @@  #define INCLUDED_LIBUHD_TRANSPORT_LIBUSB_HPP  #include <uhd/config.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp>  #include <uhd/transport/usb_device_handle.hpp> -#include <libusb-1.0/libusb.h> +#include <libusb.h> +/*********************************************************************** + * Libusb object oriented smart pointer wrappers: + * The following wrappers provide allocation and automatic deallocation + * for various libusb data types and handles. The construction routines + * also store tables of already allocated structures to avoid multiple + * occurrences of opened handles (for example). + **********************************************************************/  namespace uhd { namespace transport {  namespace libusb { -    /* -     * Initialize libusb and set debug level -     * Takes a pointer to context pointer because that's -     * how libusb rolls. Debug levels. -     *      -     *   Level 0: no messages ever printed by the library (default) -     *   Level 1: error messages are printed to stderr -     *   Level 2: warning and error messages are printed to stderr -     *   Level 3: informational messages are printed to stdout, warning -     *            and error messages are printed to stderr -     * -     * \param ctx pointer to context pointer -     * \param debug_level + +    /*! +     * This session class holds a global libusb context for this process. +     * The get global session call will create a new context if none exists. +     * When all references to session are destroyed, the context will be freed.       */ -    void init(libusb_context **ctx, int debug_level); - -    /* -     * Open the device specified by a generic handle -     * Find the libusb_device cooresponding to the generic handle -     * and open it for I/O, which returns a libusb_device_handle -     * ready for an interface -     * \param ctx the libusb context used for init -     * \return a libusb_device_handle ready for action  +    class session : boost::noncopyable { +    public: +        typedef boost::shared_ptr<session> sptr; + +        /*! +         *   Level 0: no messages ever printed by the library (default) +         *   Level 1: error messages are printed to stderr +         *   Level 2: warning and error messages are printed to stderr +         *   Level 3: informational messages are printed to stdout, warning +         *            and error messages are printed to stderr +         */ +        static const int debug_level = 0; + +        //! get a shared pointer to the global session +        static sptr get_global_session(void); + +        //! get the underlying libusb context pointer +        virtual libusb_context *get_context(void) const = 0; +    }; + +    /*! +     * Holds a device pointer with a reference to the session.       */ -    libusb_device_handle *open_device(libusb_context *ctx, -                                      usb_device_handle::sptr handle); - -    /* -     * Compare a libusb device with a generic handle  -     * Check the descriptors and open the device to check the -     * serial number string. Compare values against the given -     * handle. The libusb context is already implied in the -     * libusb_device. -     * \param dev a libusb_device pointer -     * \param handle a generic handle specifier -     * \return true if handle and device match, false otherwise +    class device : boost::noncopyable { +    public: +        typedef boost::shared_ptr<device> sptr; + +        //! get the underlying device pointer +        virtual libusb_device *get(void) const = 0; +    }; + +    /*! +     * This device list class holds a device list that will be +     * automatically freed when the last reference is destroyed.       */ -    bool compare_device(libusb_device *dev, usb_device_handle::sptr handle); - -    /* -     * Open an interface to the device -     * This is a logical operation for operating system housekeeping as -     * nothing is sent over the bus. The interface much correspond -     * to the USB device descriptors. -     * \param dev_handle libusb handle to an opened device -     * \param interface integer of the interface to use -     * \return true on success, false on error +    class device_list : boost::noncopyable { +    public: +        typedef boost::shared_ptr<device_list> sptr; + +        //! make a new device list +        static sptr make(void); + +        //! the number of devices in this list +        virtual size_t size() const = 0; + +        //! get the device pointer at a particular index +        virtual device::sptr at(size_t index) const = 0; +    }; + +    /*! +     * Holds a device descriptor and a reference to the device.       */ -    bool open_interface(libusb_device_handle *dev_handle, int interface); - -    /* -     * Get serial number  -     * The standard USB device descriptor contains an index to an -     * actual serial number string descriptor. The index is readily -     * readble, but the string descriptor requires probing the device. -     * Because this call attempts to open the device, it may not -     * succeed because not all USB devices are readily opened. -     * The default language is used for the request (English). -     * \param dev a libusb_device pointer -     * \return string serial number or 0 on error or unavailablity +    class device_descriptor : boost::noncopyable { +    public: +        typedef boost::shared_ptr<device_descriptor> sptr; + +        //! make a new descriptor from a device reference +        static sptr make(device::sptr); + +        //! get the underlying device descriptor +        virtual const libusb_device_descriptor &get(void) const = 0; + +        virtual std::string get_ascii_serial(void) const = 0; +    }; + +    /*! +     * Holds a device handle and a reference to the device.       */ -    std::string get_serial(libusb_device *dev); +    class device_handle : boost::noncopyable { +    public: +        typedef boost::shared_ptr<device_handle> sptr; + +        //! get a cached handle or make a new one given the device +        static sptr get_cached_handle(device::sptr); + +        //! get the underlying device handle +        virtual libusb_device_handle *get(void) const = 0; + +        /*! +         * Open USB interfaces for control using magic value +         * IN interface:      2 +         * OUT interface:     1 +         * Control interface: 0 +         */ +        virtual void claim_interface(int) = 0; +    }; + +    /*! +     * The special handle is our internal implementation of the +     * usb device handle which is used publicly to identify a device. +     */ +    class special_handle : public usb_device_handle { +    public: +        typedef boost::shared_ptr<special_handle> sptr; + +        //! make a new special handle from device +        static sptr make(device::sptr); + +        //! get the underlying device reference +        virtual device::sptr get_device(void) const = 0; +    }; +  }  }} //namespace diff --git a/host/lib/transport/libusb1_control.cpp b/host/lib/transport/libusb1_control.cpp index 3531128b2..f903907d0 100644 --- a/host/lib/transport/libusb1_control.cpp +++ b/host/lib/transport/libusb1_control.cpp @@ -20,7 +20,6 @@  using namespace uhd::transport; -const int libusb_debug_level = 0;  const int libusb_timeout = 0;  /*********************************************************************** @@ -28,68 +27,38 @@ const int libusb_timeout = 0;   **********************************************************************/  class libusb_control_impl : public usb_control {  public: -    libusb_control_impl(usb_device_handle::sptr handle); -    ~libusb_control_impl(); +    libusb_control_impl(libusb::device_handle::sptr handle): +        _handle(handle) +    { +        _handle->claim_interface(0 /* control interface */); +    } -    size_t submit(boost::uint8_t request_type, +    ssize_t submit(boost::uint8_t request_type,                    boost::uint8_t request,                    boost::uint16_t value,                    boost::uint16_t index,                    unsigned char *buff, -                  boost::uint16_t length);  +                  boost::uint16_t length +    ){ +        return libusb_control_transfer(_handle->get(), +                                       request_type, +                                       request, +                                       value, +                                       index, +                                       buff, +                                       length, +                                       libusb_timeout); +    }  private: -    libusb_context       *_ctx; -    libusb_device_handle *_dev_handle; +    libusb::device_handle::sptr _handle;  }; - -libusb_control_impl::libusb_control_impl(usb_device_handle::sptr handle) -{ -    libusb::init(&_ctx, libusb_debug_level); - -    // Find and open the libusb_device corresponding to the -    // given handle and return the libusb_device_handle -    // that can be used for I/O purposes. -    _dev_handle = libusb::open_device(_ctx, handle); - -    // Open USB interfaces for control using magic value -    // IN interface:      2 -    // OUT interface:     1 -    // Control interface: 0 -    libusb::open_interface(_dev_handle, 0); -} - - -libusb_control_impl::~libusb_control_impl() -{ -    libusb_close(_dev_handle); -    libusb_exit(_ctx); -} - - -size_t libusb_control_impl::submit(boost::uint8_t request_type, -                                   boost::uint8_t request, -                                   boost::uint16_t value, -                                   boost::uint16_t index,  -                                   unsigned char *buff, -                                   boost::uint16_t length)  -{ -    return libusb_control_transfer(_dev_handle, -                                   request_type, -                                   request, -                                   value, -                                   index, -                                   buff,  -                                   length,  -                                   libusb_timeout); -} - -  /***********************************************************************   * USB control public make functions   **********************************************************************/ -usb_control::sptr usb_control::make(usb_device_handle::sptr handle) -{ -    return sptr(new libusb_control_impl(handle)); +usb_control::sptr usb_control::make(usb_device_handle::sptr handle){ +    return sptr(new libusb_control_impl(libusb::device_handle::get_cached_handle( +        boost::static_pointer_cast<libusb::special_handle>(handle)->get_device() +    )));  } diff --git a/host/lib/transport/libusb1_device_handle.cpp b/host/lib/transport/libusb1_device_handle.cpp deleted file mode 100644 index 7efddd410..000000000 --- a/host/lib/transport/libusb1_device_handle.cpp +++ /dev/null @@ -1,117 +0,0 @@ -// -// Copyright 2010 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 "libusb1_base.hpp" -#include <uhd/utils/assert.hpp> -#include <iostream> - -using namespace uhd::transport; - -const int libusb_debug_level = 0; - -/**************************************************************** - * libusb USB device handle implementation class - ***************************************************************/ -class libusb1_device_handle_impl : public usb_device_handle { -public: -    libusb1_device_handle_impl(std::string serial, -                               boost::uint16_t product_id, -                               boost::uint16_t vendor_id, -                               boost::uint16_t device_addr) -      : _serial(serial), _product_id(product_id),  -        _vendor_id(vendor_id), _device_addr(device_addr) -    { -        /* NOP */ -    } - -    ~libusb1_device_handle_impl() -    { -        /* NOP */ -    } - -    std::string get_serial() const -    { -        return _serial; -    } - -    boost::uint16_t get_vendor_id() const -    { -        return _vendor_id; -    } - - -    boost::uint16_t get_product_id() const -    { -        return _product_id; -    } - -    boost::uint16_t get_device_addr() const -    { -        return _device_addr; -    } - -private: -    std::string     _serial; -    boost::uint16_t _product_id; -    boost::uint16_t _vendor_id; -    boost::uint16_t _device_addr; -}; - - -usb_device_handle::sptr make_usb_device_handle(libusb_device *dev) -{ -    libusb_device_descriptor desc; - -    if (libusb_get_device_descriptor(dev, &desc) < 0) { -        UHD_ASSERT_THROW("USB: failed to get device descriptor"); -    } - -    std::string     serial      = libusb::get_serial(dev); -    boost::uint16_t product_id  = desc.idProduct; -    boost::uint16_t vendor_id   = desc.idVendor; -    boost::uint16_t device_addr = libusb_get_device_address(dev); - -    return usb_device_handle::sptr(new libusb1_device_handle_impl( -        serial, -        product_id, -        vendor_id, -        device_addr)); -} - -std::vector<usb_device_handle::sptr> usb_device_handle::get_device_list(boost::uint16_t vid, boost::uint16_t pid) -{ -    libusb_context *ctx = NULL; -    libusb_device** libusb_device_list; -    std::vector<usb_device_handle::sptr> device_handle_list; -    libusb_device_descriptor desc; - -    libusb::init(&ctx, libusb_debug_level); - -    size_t dev_size = libusb_get_device_list(ctx, &libusb_device_list); -    for (size_t i = 0; i < dev_size; i++) { -        libusb_device *dev = libusb_device_list[i]; -        if(libusb_get_device_descriptor(dev, &desc) < 0) { -          UHD_ASSERT_THROW("USB: failed to get device descriptor"); -        } -        if(desc.idVendor == vid && desc.idProduct == pid) { -          device_handle_list.push_back(make_usb_device_handle(dev)); -        } -    } - -    libusb_exit(ctx); -    return device_handle_list;  -} diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp index b3a160462..f589d7c77 100644 --- a/host/lib/transport/libusb1_zero_copy.cpp +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -17,15 +17,22 @@  #include "libusb1_base.hpp"  #include <uhd/transport/usb_zero_copy.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <uhd/utils/thread_priority.hpp>  #include <uhd/utils/assert.hpp> -#include <boost/format.hpp> +#include <boost/shared_array.hpp> +#include <boost/foreach.hpp> +#include <boost/thread.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <vector>  #include <iostream> -#include <iomanip> +using namespace uhd;  using namespace uhd::transport; -const int libusb_debug_level = 0; -const int libusb_timeout = 0; +static const double CLEANUP_TIMEOUT   = 0.2;    //seconds +static const size_t DEFAULT_NUM_XFERS = 16;     //num xfers +static const size_t DEFAULT_XFER_SIZE = 32*512; //bytes  /***********************************************************************   * Helper functions @@ -53,56 +60,57 @@ void pp_transfer(libusb_transfer *lut)   *   create a bidirectional interface. It is a zero copy implementation   *   with respect to libusb, however, each send and recv requires a copy   *   operation from kernel to userspace; this is due to the usbfs - *   interface provided by the kernel.  + *   interface provided by the kernel.   **********************************************************************/  class usb_endpoint { -private: -    libusb_device_handle *_dev_handle; -    libusb_context *_ctx; -    int  _endpoint; -    bool _input; - -    size_t _transfer_size; -    size_t _num_transfers; - -    // Transfer state lists (transfers are free, pending, or completed) -    std::list<libusb_transfer *>  _free_list; -    std::list<libusb_transfer *>  _pending_list; -    std::list<libusb_transfer *>  _completed_list; - -    // Calls for processing asynchronous I/O  -    libusb_transfer *allocate_transfer(int buff_len); -    bool cancel(libusb_transfer *lut); -    bool cancel_all(); -    bool reap_pending_list(); -    bool reap_pending_list_timeout(); -    bool reap_completed_list(); - -    // Transfer state manipulators  -    void free_list_add(libusb_transfer *lut); -    void pending_list_add(libusb_transfer *lut); -    void completed_list_add(libusb_transfer *lut); -    libusb_transfer *free_list_get(); -    libusb_transfer *completed_list_get(); -    bool pending_list_remove(libusb_transfer *lut); - -    // Debug use -    void print_transfer_status(libusb_transfer *lut); -  public: -    usb_endpoint(libusb_device_handle *dev_handle, -                 libusb_context *ctx, int endpoint, bool input, -                 size_t transfer_size, size_t num_transfers); +    typedef boost::shared_ptr<usb_endpoint> sptr; -    ~usb_endpoint(); +    usb_endpoint( +        libusb::device_handle::sptr handle, +        int endpoint, +        bool input, +        size_t transfer_size, +        size_t num_transfers +    ); + +    ~usb_endpoint(void);      // Exposed interface for submitting / retrieving transfer buffers -    bool submit(libusb_transfer *lut); -    libusb_transfer *get_completed_transfer(); -    libusb_transfer *get_free_transfer(); + +    //! Submit a new transfer that was presumably just filled or emptied. +    void submit(libusb_transfer *lut); + +    /*! +     * Get an available transfer: +     * For inputs, this is a just filled transfer. +     * For outputs, this is a just emptied transfer. +     * \param timeout the timeout to wait for a lut +     * \return the transfer pointer or NULL if timeout +     */ +    libusb_transfer *get_lut_with_wait(double timeout);      //Callback use only      void callback_handle_transfer(libusb_transfer *lut); + +private: +    libusb::device_handle::sptr _handle; +    int  _endpoint; +    bool _input; + +    //! hold a bounded buffer of completed transfers +    typedef bounded_buffer<libusb_transfer *> lut_buff_type; +    lut_buff_type::sptr _completed_list; + +    //! a list of all transfer structs we allocated +    std::vector<libusb_transfer *> _all_luts; + +    //! a block of memory for the transfer buffers +    boost::shared_array<char> _buffer; + +    // Calls for processing asynchronous I/O +    libusb_transfer *allocate_transfer(void *mem, size_t len); +    void print_transfer_status(libusb_transfer *lut);  }; @@ -115,9 +123,8 @@ public:   * it from the pending to completed status list.   * \param lut pointer to libusb_transfer   */ -static void callback(libusb_transfer *lut) -{ -    usb_endpoint *endpoint = (usb_endpoint *) lut->user_data;  +static void callback(libusb_transfer *lut){ +    usb_endpoint *endpoint = (usb_endpoint *) lut->user_data;      endpoint->callback_handle_transfer(lut);  } @@ -126,14 +133,9 @@ static void callback(libusb_transfer *lut)   * Accessor call to allow list access from callback space   * \param pointer to libusb_transfer   */ -void usb_endpoint::callback_handle_transfer(libusb_transfer *lut) -{ -    if (!pending_list_remove(lut)) { -        std::cerr << "USB: pending remove failed" << std::endl; -        return; -    } - -    completed_list_add(lut);     +void usb_endpoint::callback_handle_transfer(libusb_transfer *lut){ +    boost::this_thread::disable_interruption di; //disable because the wait can throw +    _completed_list->push_with_wait(lut);  } @@ -141,21 +143,28 @@ void usb_endpoint::callback_handle_transfer(libusb_transfer *lut)   * Constructor   * Allocate libusb transfers and mark as free.  For IN endpoints,   * submit the transfers so that they're ready to return when - * data is available.  + * data is available.   */ -usb_endpoint::usb_endpoint(libusb_device_handle *dev_handle, -                          libusb_context *ctx, int endpoint, bool input, -                          size_t transfer_size, size_t num_transfers) -    : _dev_handle(dev_handle), -      _ctx(ctx), _endpoint(endpoint), _input(input), -      _transfer_size(transfer_size), _num_transfers(num_transfers) +usb_endpoint::usb_endpoint( +    libusb::device_handle::sptr handle, +    int endpoint, +    bool input, +    size_t transfer_size, +    size_t num_transfers +): +    _handle(handle), +    _endpoint(endpoint), +    _input(input)  { -    unsigned int i; -    for (i = 0; i < _num_transfers; i++) { -        free_list_add(allocate_transfer(_transfer_size)); +    _completed_list = lut_buff_type::make(num_transfers); +    _buffer = boost::shared_array<char>(new char[num_transfers*transfer_size]); +    for (size_t i = 0; i < num_transfers; i++){ +        _all_luts.push_back(allocate_transfer(_buffer.get() + i*transfer_size, transfer_size)); -        if (_input) -            submit(free_list_get()); +        //input luts are immediately submitted to be filled +        //output luts go into the completed list as free buffers +        if (_input) this->submit(_all_luts.back()); +        else _completed_list->push_with_wait(_all_luts.back());      }  } @@ -167,47 +176,44 @@ usb_endpoint::usb_endpoint(libusb_device_handle *dev_handle,   * the transfers. Libusb will deallocate the data buffer held by   * each transfer.   */ -usb_endpoint::~usb_endpoint() -{ -    cancel_all(); - -    while (!_pending_list.empty()) { -        if (!reap_pending_list()) -            std::cerr << "error: destructor failed to reap" << std::endl; +usb_endpoint::~usb_endpoint(void){ +    //cancel all transfers +    BOOST_FOREACH(libusb_transfer *lut, _all_luts){ +        libusb_cancel_transfer(lut);      } -    while (!_completed_list.empty()) { -        if (!reap_completed_list()) -            std::cerr << "error: destructor failed to reap" << std::endl; -    } +    //collect canceled transfers (drain the queue) +    while (this->get_lut_with_wait(CLEANUP_TIMEOUT) != NULL){}; -    while (!_free_list.empty()) { -        libusb_free_transfer(free_list_get()); +    //free all transfers +    BOOST_FOREACH(libusb_transfer *lut, _all_luts){ +        libusb_free_transfer(lut);      }  }  /* - * Allocate a libusb transfer  + * Allocate a libusb transfer   * The allocated transfer - and buffer it contains - is repeatedly   * submitted, reaped, and reused and should not be freed until shutdown. - * \param buff_len size of the individual buffer held by each transfer + * \param mem a pointer to the buffer memory + * \param len size of the individual buffer   * \return pointer to an allocated libusb_transfer   */ -libusb_transfer *usb_endpoint::allocate_transfer(int buff_len) -{ +libusb_transfer *usb_endpoint::allocate_transfer(void *mem, size_t len){      libusb_transfer *lut = libusb_alloc_transfer(0); - -    unsigned char *buff = new unsigned char[buff_len]; +    UHD_ASSERT_THROW(lut != NULL);      unsigned int endpoint = ((_endpoint & 0x7f) | (_input ? 0x80 : 0)); +    unsigned char *buff = reinterpret_cast<unsigned char *>(mem); +    libusb_transfer_cb_fn lut_callback = libusb_transfer_cb_fn(&callback);      libusb_fill_bulk_transfer(lut,                // transfer -                              _dev_handle,        // dev_handle +                              _handle->get(),     // dev_handle                                endpoint,           // endpoint                                buff,               // buffer -                              buff_len,           // length -       libusb_transfer_cb_fn(callback),           // callback +                              len,                // length +                              lut_callback,       // callback                                this,               // user_data                                0);                 // timeout      return lut; @@ -218,97 +224,18 @@ libusb_transfer *usb_endpoint::allocate_transfer(int buff_len)   * Asynchonous transfer submission   * Submit a libusb transfer to libusb add pending status   * \param lut pointer to libusb_transfer - * \return true on success or false on error  - */ -bool usb_endpoint::submit(libusb_transfer *lut) -{ -    int retval; -    if ((retval = libusb_submit_transfer(lut)) < 0) { -        std::cerr << "error: libusb_submit_transfer: " << retval << std::endl; -        return false; -    } - -    pending_list_add(lut); -    return true; -} - - -/* - * Cancel a pending transfer  - * Search the pending list for the transfer and cancel if found. - * \param lut pointer to libusb_transfer to cancel - * \return true on success or false if transfer is not found - * - * Note: success only indicates submission of cancelation request. - * Sucessful cancelation is not known until the callback occurs. + * \return true on success or false on error   */ -bool usb_endpoint::cancel(libusb_transfer *lut) -{ -    std::list<libusb_transfer*>::iterator iter; -    for (iter = _pending_list.begin(); iter != _pending_list.end(); iter++) { -        if (*iter == lut) {  -            libusb_cancel_transfer(lut);  -            return true; -        } -    } -    return false; +void usb_endpoint::submit(libusb_transfer *lut){ +    UHD_ASSERT_THROW(libusb_submit_transfer(lut) == 0);  } - -/* - * Cancel all pending transfers  - * \return bool true if cancelation request is submitted - * - * Note: success only indicates submission of cancelation request. - * Sucessful cancelation is not known until the callback occurs. - */ -bool usb_endpoint::cancel_all() -{ -    std::list<libusb_transfer*>::iterator iter; - -    for (iter = _pending_list.begin(); iter != _pending_list.end(); iter++) { -        if (libusb_cancel_transfer(*iter) < 0) { -            std::cerr << "error: libusb_cancal_transfer() failed" << std::endl; -            return false; -        } -    } - -    return true; -} - - -/* - * Reap completed transfers - * return true if at least one transfer was reaped, false otherwise.  - * Check completed transfers for errors and mark as free. This is a - * blocking call.  - * \return bool true if a libusb transfer is reaped, false otherwise - */ -bool usb_endpoint::reap_completed_list() -{ -    libusb_transfer *lut; - -    if (_completed_list.empty()) { -        if (!reap_pending_list_timeout()) -            return false; -    } - -    while (!_completed_list.empty()) { -        lut = completed_list_get(); -        print_transfer_status(lut); -        free_list_add(lut); -    } - -    return true; -} - -  /*   * Print status errors of a completed transfer   * \param lut pointer to an libusb_transfer   */ -void usb_endpoint::print_transfer_status(libusb_transfer *lut) -{ +void usb_endpoint::print_transfer_status(libusb_transfer *lut){ +    std::cout << "here " << lut->status << std::endl;      switch (lut->status) {      case LIBUSB_TRANSFER_COMPLETED:          if (lut->actual_length < lut->length) { @@ -344,382 +271,136 @@ void usb_endpoint::print_transfer_status(libusb_transfer *lut)      }  } - -/* - * Reap pending transfers without timeout  - * This is a blocking call. Reaping submitted transfers is - * handled by libusb and the assigned callback function. - * Block until at least one transfer is reaped. - * \return true true if a transfer was reaped or false otherwise - */ -bool usb_endpoint::reap_pending_list() -{ -    int retval; - -    if ((retval = libusb_handle_events(_ctx)) < 0) { -        std::cerr << "error: libusb_handle_events: " << retval << std::endl; -        return false; -    } - -    return true; -} - - -/* - * Reap pending transfers with timeout  - * This call blocks until a transfer is reaped or timeout. - * Reaping submitted transfers is handled by libusb and the - * assigned callback function. Block until at least one - * transfer is reaped or timeout occurs. - * \return true if a transfer was reaped or false otherwise - */ -bool usb_endpoint::reap_pending_list_timeout() -{ -    int retval; -    timeval tv; - -    tv.tv_sec = 0; -    tv.tv_usec = 100000; //100ms - -    size_t pending_list_size = _pending_list.size(); - -    if ((retval = libusb_handle_events_timeout(_ctx, &tv)) < 0) { -        std::cerr << "error: libusb_handle_events: " << retval << std::endl; -        return false; -    } - -    if (_pending_list.size() < pending_list_size) { -        return true; -    } -    else { -        return false; -    } -} - - -/* - * Get a free transfer - * The transfer has an empty data bufer for OUT requests  - * \return pointer to a libusb_transfer - */ -libusb_transfer *usb_endpoint::get_free_transfer() -{ -    if (_free_list.empty()) { -        if (!reap_completed_list()) -            return NULL;  -    } - -    return free_list_get(); -} - - -/* - * Get a completed transfer  - * The transfer has a full data buffer for IN requests - * \return pointer to libusb_transfer - */ -libusb_transfer *usb_endpoint::get_completed_transfer() -{ -    if (_completed_list.empty()) { -        if (!reap_pending_list_timeout()) -            return NULL;  -    } - -    return completed_list_get(); -} - -/* - * List operations  - */ -void usb_endpoint::free_list_add(libusb_transfer *lut) -{ -    _free_list.push_back(lut); -} - -void usb_endpoint::pending_list_add(libusb_transfer *lut) -{ -    _pending_list.push_back(lut); -} - -void usb_endpoint::completed_list_add(libusb_transfer *lut) -{ -    _completed_list.push_back(lut); -} - - -/* - * Free and completed lists don't have ordered content  - * Pop transfers from the front as needed - */ -libusb_transfer *usb_endpoint::free_list_get() -{ +libusb_transfer *usb_endpoint::get_lut_with_wait(double timeout){ +    boost::this_thread::disable_interruption di; //disable because the wait can throw      libusb_transfer *lut; - -    if (_free_list.size() == 0) { -        return NULL;  -    } -    else {  -        lut = _free_list.front(); -        _free_list.pop_front(); -        return lut; -    } +    if (_completed_list->pop_with_timed_wait(lut, timeout)) return lut; +    return NULL;  } - -/* - * Free and completed lists don't have ordered content  - * Pop transfers from the front as needed - */ -libusb_transfer *usb_endpoint::completed_list_get() -{ -    libusb_transfer *lut; - -    if (_completed_list.empty()) { -        return NULL; -    } -    else {  -        lut = _completed_list.front(); -        _completed_list.pop_front(); -        return lut; -    } -} - - -/* - * Search and remove transfer from pending list - * Assuming that the callbacks occur in order, the front element - * should yield the correct transfer. If not, then something else - * is going on. If no transfers match, then something went wrong. - */ -bool usb_endpoint::pending_list_remove(libusb_transfer *lut) -{ -    std::list<libusb_transfer*>::iterator iter; -    for (iter = _pending_list.begin(); iter != _pending_list.end(); iter++) { -        if (*iter == lut) {  -            _pending_list.erase(iter); -            return true; -        } -    } -    return false; -} - -  /*********************************************************************** - * Managed buffers  + * USB zero_copy device class   **********************************************************************/ -/* - * Libusb managed receive buffer - * Construct a recv buffer from a libusb transfer. The memory held by - * the libusb transfer is exposed through the managed buffer interface. - * Upon destruction, the transfer and buffer are resubmitted to the - * endpoint for further use.  - */ -class libusb_managed_recv_buffer_impl : public managed_recv_buffer { +class libusb_zero_copy_impl : public usb_zero_copy, public boost::enable_shared_from_this<libusb_zero_copy_impl> {  public: -    libusb_managed_recv_buffer_impl(libusb_transfer *lut, -                                    usb_endpoint *endpoint) -        : _buff(lut->buffer, lut->length) -    { -        _lut = lut; -        _endpoint = endpoint; -    } -    ~libusb_managed_recv_buffer_impl() -    { -       if (!_endpoint->submit(_lut)) -           std::cerr << "USB: failed to submit IN transfer" << std::endl; -    } +    libusb_zero_copy_impl( +        libusb::device_handle::sptr handle, +        size_t recv_endpoint, +        size_t send_endpoint, +        const device_addr_t &hints +    ); -private: -    const boost::asio::const_buffer &get() const -    { -        return _buff;  +    ~libusb_zero_copy_impl(void){ +        _threads_running = false; +        _thread_group.join_all();      } -    libusb_transfer *_lut; -    usb_endpoint *_endpoint; -    const boost::asio::const_buffer _buff; -}; - -/* - * Libusb managed send buffer - * Construct a send buffer from a libusb transfer. The memory held by - * the libusb transfer is exposed through the managed buffer interface. - * Committing the buffer will set the data length and submit the buffer - * to the endpoint. Submitting a buffer multiple times or destroying - * the buffer before committing is an error. For the latter, the transfer - * is returned to the endpoint with no data for reuse. - */ -class libusb_managed_send_buffer_impl : public managed_send_buffer { -public: -    libusb_managed_send_buffer_impl(libusb_transfer *lut, -                                    usb_endpoint *endpoint, -                                    size_t buff_size) -        : _buff(lut->buffer, buff_size), _committed(false) -    { -        _lut = lut; -        _endpoint = endpoint; -    } +    managed_recv_buffer::sptr get_recv_buff(double); +    managed_send_buffer::sptr get_send_buff(double); -    ~libusb_managed_send_buffer_impl() -    { -        if (!_committed) { -            _lut->length = 0; -            _lut->actual_length = 0; -            _endpoint->submit(_lut); -        } -    } +    size_t get_num_recv_frames(void) const { return _num_recv_frames; } +    size_t get_num_send_frames(void) const { return _num_send_frames; } -    ssize_t commit(size_t num_bytes) -    { -        if (_committed) { -            std::cerr << "UHD: send buffer already committed" << std::endl; -            return 0; -        } -         -        UHD_ASSERT_THROW(num_bytes <= boost::asio::buffer_size(_buff)); +    size_t get_recv_frame_size(void) const { return _recv_frame_size; } +    size_t get_send_frame_size(void) const { return _send_frame_size; } -        _lut->length = num_bytes; -        _lut->actual_length = 0; +private: +    void release(libusb_transfer *lut){ +        _recv_ep->submit(lut); +    } -        if (_endpoint->submit(_lut)) { -            _committed = true; -            return num_bytes; +    void commit(libusb_transfer *lut, size_t num_bytes){ +        lut->length = num_bytes; +        try{ +            _send_ep->submit(lut);          } -        else { -            return 0; +        catch(const std::exception &e){ +            std::cerr << "Error in commit: " << e.what() << std::endl;          }      } -private: -    const boost::asio::mutable_buffer &get() const -    { -        return _buff;  +    libusb::device_handle::sptr _handle; +    const size_t _recv_frame_size, _num_recv_frames; +    const size_t _send_frame_size, _num_send_frames; +    usb_endpoint::sptr _recv_ep, _send_ep; + +    //event handler threads +    boost::thread_group _thread_group; +    bool _threads_running; + +    void run_event_loop(void){ +        set_thread_priority_safe(); +        libusb::session::sptr session = libusb::session::get_global_session(); +        _threads_running = true; +        while(_threads_running){ +            timeval tv; +            tv.tv_sec = 0; +            tv.tv_usec = 100000; //100ms +            libusb_handle_events_timeout(session->get_context(), &tv); +        }      } - -    libusb_transfer *_lut; -    usb_endpoint *_endpoint; -    const boost::asio::mutable_buffer _buff; -    bool _committed; -}; - - -/*********************************************************************** - * USB zero_copy device class - **********************************************************************/ -class libusb_zero_copy_impl : public usb_zero_copy -{ -private: -    usb_endpoint          *_rx_ep; -    usb_endpoint          *_tx_ep; - -    // Maintain libusb values -    libusb_context       *_rx_ctx; -    libusb_context       *_tx_ctx; -    libusb_device_handle *_rx_dev_handle; -    libusb_device_handle *_tx_dev_handle; - -    size_t _recv_buff_size; -    size_t _send_buff_size; -    size_t _num_frames; - -public: -    typedef boost::shared_ptr<libusb_zero_copy_impl> sptr; - -    libusb_zero_copy_impl(usb_device_handle::sptr handle, -                          unsigned int rx_endpoint, -                          unsigned int tx_endpoint, -                          size_t recv_buff_size, -                          size_t send_buff_size); -     -    ~libusb_zero_copy_impl(); - -    managed_recv_buffer::sptr get_recv_buff(void); -    managed_send_buffer::sptr get_send_buff(void); - -    size_t get_num_recv_frames(void) const { return _num_frames; } -    size_t get_num_send_frames(void) const { return _num_frames; }  };  /*   * Constructor   * Initializes libusb, opens devices, and sets up interfaces for I/O. - * Finally, creates endpoints for asynchronous I/O.  + * Finally, creates endpoints for asynchronous I/O.   */ -libusb_zero_copy_impl::libusb_zero_copy_impl(usb_device_handle::sptr handle, -                                             unsigned int rx_endpoint, -                                             unsigned int tx_endpoint, -                                             size_t buff_size, -                                             size_t block_size) - : _rx_ctx(NULL), _tx_ctx(NULL), _rx_dev_handle(NULL), _tx_dev_handle(NULL), -   _recv_buff_size(block_size), _send_buff_size(block_size), -   _num_frames(buff_size / block_size) +libusb_zero_copy_impl::libusb_zero_copy_impl( +    libusb::device_handle::sptr handle, +    size_t recv_endpoint, +    size_t send_endpoint, +    const device_addr_t &hints +): +    _handle(handle), +    _recv_frame_size(size_t(hints.cast<double>("recv_frame_size", DEFAULT_XFER_SIZE))), +    _num_recv_frames(size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_XFERS))), +    _send_frame_size(size_t(hints.cast<double>("send_frame_size", DEFAULT_XFER_SIZE))), +    _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_XFERS)))  { -    // Initialize libusb with separate contexts to allow -    // thread safe operation of transmit and receive  -    libusb::init(&_rx_ctx, libusb_debug_level); -    libusb::init(&_tx_ctx, libusb_debug_level); - -    UHD_ASSERT_THROW((_rx_ctx != NULL) && (_tx_ctx != NULL)); - -    // Find and open the libusb_device corresponding to the -    // given handle and return the libusb_device_handle -    // that can be used for I/O purposes. -    _rx_dev_handle = libusb::open_device(_rx_ctx, handle); -    _tx_dev_handle = libusb::open_device(_tx_ctx, handle); - -    // Open USB interfaces for tx/rx using magic values. -    // IN interface:      2 -    // OUT interface:     1 -    // Control interface: 0 -    libusb::open_interface(_rx_dev_handle, 2); -    libusb::open_interface(_tx_dev_handle, 1); - -    _rx_ep = new usb_endpoint(_rx_dev_handle,  // libusb device_handle -                              _rx_ctx,         // libusb context -                              rx_endpoint,     // USB endpoint number +    _handle->claim_interface(2 /*in interface*/); +    _handle->claim_interface(1 /*out interface*/); + +    _recv_ep = usb_endpoint::sptr(new usb_endpoint( +                              _handle,         // libusb device_handle +                              recv_endpoint,   // USB endpoint number                                true,            // IN endpoint -                              _recv_buff_size, // buffer size per transfer  -                              _num_frames);    // number of libusb transfers +                              this->get_recv_frame_size(),  // buffer size per transfer +                              this->get_num_recv_frames()   // number of libusb transfers +    )); -    _tx_ep = new usb_endpoint(_tx_dev_handle,  // libusb device_handle -                              _tx_ctx,         // libusb context -                              tx_endpoint,     // USB endpoint number +    _send_ep = usb_endpoint::sptr(new usb_endpoint( +                              _handle,         // libusb device_handle +                              send_endpoint,   // USB endpoint number                                false,           // OUT endpoint -                              _send_buff_size, // buffer size per transfer -                              _num_frames);    // number of libusb transfers +                              this->get_send_frame_size(),  // buffer size per transfer +                              this->get_num_send_frames()   // number of libusb transfers +    )); + +    //spawn the event handler threads +    size_t concurrency = hints.cast<size_t>("concurrency_hint", 1); +    for (size_t i = 0; i < concurrency; i++) _thread_group.create_thread( +        boost::bind(&libusb_zero_copy_impl::run_event_loop, this) +    );  } - -libusb_zero_copy_impl::~libusb_zero_copy_impl() -{ -    delete _rx_ep; -    delete _tx_ep;  - -    libusb_close(_rx_dev_handle); -    libusb_close(_tx_dev_handle); - -    libusb_exit(_rx_ctx); -    libusb_exit(_tx_ctx); -} - -  /*   * Construct a managed receive buffer from a completed libusb transfer   * (happy with buffer full of data) obtained from the receive endpoint.   * Return empty pointer if no transfer is available (timeout or error). - * \return pointer to a managed receive buffer  + * \return pointer to a managed receive buffer   */ -managed_recv_buffer::sptr libusb_zero_copy_impl::get_recv_buff() -{ -    libusb_transfer *lut = _rx_ep->get_completed_transfer(); +managed_recv_buffer::sptr libusb_zero_copy_impl::get_recv_buff(double timeout){ +    libusb_transfer *lut = _recv_ep->get_lut_with_wait(timeout);      if (lut == NULL) {          return managed_recv_buffer::sptr();      }      else { -        return managed_recv_buffer::sptr( -            new libusb_managed_recv_buffer_impl(lut, -                                                _rx_ep)); +        return managed_recv_buffer::make_safe( +            boost::asio::const_buffer(lut->buffer, lut->actual_length), +            boost::bind(&libusb_zero_copy_impl::release, shared_from_this(), lut) +        );      }  } @@ -728,39 +409,34 @@ managed_recv_buffer::sptr libusb_zero_copy_impl::get_recv_buff()   * Construct a managed send buffer from a free libusb transfer (with   * empty buffer). Return empty pointer of no transfer is available   * (timeout or error). - * \return pointer to a managed send buffer  + * \return pointer to a managed send buffer   */ -managed_send_buffer::sptr libusb_zero_copy_impl::get_send_buff() -{ -    libusb_transfer *lut = _tx_ep->get_free_transfer(); +managed_send_buffer::sptr libusb_zero_copy_impl::get_send_buff(double timeout){ +    libusb_transfer *lut = _send_ep->get_lut_with_wait(timeout);      if (lut == NULL) {          return managed_send_buffer::sptr();      }      else { -        return managed_send_buffer::sptr( -            new libusb_managed_send_buffer_impl(lut, -                                                _tx_ep, -                                                _send_buff_size)); +        return managed_send_buffer::make_safe( +            boost::asio::mutable_buffer(lut->buffer, this->get_send_frame_size()), +            boost::bind(&libusb_zero_copy_impl::commit, shared_from_this(), lut, _1) +        );      }  } -  /***********************************************************************   * USB zero_copy make functions   **********************************************************************/ -usb_zero_copy::sptr usb_zero_copy::make(usb_device_handle::sptr handle, -                                        unsigned int rx_endpoint, -                                        unsigned int tx_endpoint, -                                        size_t buff_size, -                                        size_t block_size) - -{ -    return sptr(new libusb_zero_copy_impl(handle, -                                          rx_endpoint, -                                          tx_endpoint, -                                          buff_size,  -                                          block_size)); +usb_zero_copy::sptr usb_zero_copy::make( +    usb_device_handle::sptr handle, +    size_t recv_endpoint, +    size_t send_endpoint, +    const device_addr_t &hints +){ +    libusb::device_handle::sptr dev_handle(libusb::device_handle::get_cached_handle( +        boost::static_pointer_cast<libusb::special_handle>(handle)->get_device() +    )); +    return sptr(new libusb_zero_copy_impl( +        dev_handle, recv_endpoint, send_endpoint, hints +    ));  } - - - diff --git a/host/lib/transport/include/stdint.h b/host/lib/transport/msvc/stdint.h index b3eb61aae..b3eb61aae 100644 --- a/host/lib/transport/include/stdint.h +++ b/host/lib/transport/msvc/stdint.h diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp index ee989ee2b..ed29864e9 100644 --- a/host/lib/transport/udp_zero_copy_asio.cpp +++ b/host/lib/transport/udp_zero_copy_asio.cpp @@ -17,25 +17,58 @@  #include <uhd/transport/udp_zero_copy.hpp>  #include <uhd/transport/udp_simple.hpp> //mtu +#include <uhd/transport/bounded_buffer.hpp> +#include <uhd/utils/thread_priority.hpp>  #include <uhd/utils/assert.hpp>  #include <uhd/utils/warning.hpp> -#include <boost/cstdint.hpp> +#include <boost/shared_array.hpp>  #include <boost/asio.hpp>  #include <boost/format.hpp> +#include <boost/thread.hpp> +#include <boost/enable_shared_from_this.hpp>  #include <iostream> +using namespace uhd;  using namespace uhd::transport; +namespace asio = boost::asio;  /***********************************************************************   * Constants   **********************************************************************/ +//Define this to the the boost async io calls to perform receive. +//Otherwise, get_recv_buff uses a blocking receive with timeout. +//#define USE_ASIO_ASYNC_RECV + +//Define this to the the boost async io calls to perform send. +//Otherwise, the commit callback uses a blocking send. +//#define USE_ASIO_ASYNC_SEND +  //enough buffering for half a second of samples at full rate on usrp2 -static const size_t MIN_RECV_SOCK_BUFF_SIZE = size_t(sizeof(boost::uint32_t) * 25e6 * 0.5); +static const size_t MIN_RECV_SOCK_BUFF_SIZE = size_t(4 * 25e6 * 0.5); +  //Large buffers cause more underflow at high rates.  //Perhaps this is due to the kernel scheduling,  //but may change with host-based flow control.  static const size_t MIN_SEND_SOCK_BUFF_SIZE = size_t(10e3); -static const double RECV_TIMEOUT = 0.1; //100 ms + +//The number of async frames to allocate for each send and recv: +//The non-async recv can have a very large number of recv frames +//because the CPU overhead is independent of the number of frames. +#ifdef USE_ASIO_ASYNC_RECV +static const size_t DEFAULT_NUM_RECV_FRAMES = 32; +#else +static const size_t DEFAULT_NUM_RECV_FRAMES = MIN_RECV_SOCK_BUFF_SIZE/udp_simple::mtu; +#endif +//The non-async send only ever requires a single frame +//because the buffer will be committed before a new get. +#ifdef USE_ASIO_ASYNC_SEND +static const size_t DEFAULT_NUM_SEND_FRAMES = 32; +#else +static const size_t DEFAULT_NUM_SEND_FRAMES = MIN_SEND_SOCK_BUFF_SIZE/udp_simple::mtu;; +#endif + +//a single concurrent thread for io_service seems to be the fastest +static const size_t CONCURRENCY_HINT = 1;  /***********************************************************************   * Zero Copy UDP implementation with ASIO: @@ -44,39 +77,68 @@ static const double RECV_TIMEOUT = 0.1; //100 ms   *   However, it is not a true zero copy implementation as each   *   send and recv requires a copy operation to/from userspace.   **********************************************************************/ -class udp_zero_copy_impl: -    public phony_zero_copy_recv_if, -    public phony_zero_copy_send_if, -    public udp_zero_copy -{ +class udp_zero_copy_asio_impl : public udp_zero_copy, public boost::enable_shared_from_this<udp_zero_copy_asio_impl> {  public: -    typedef boost::shared_ptr<udp_zero_copy_impl> sptr; +    typedef boost::shared_ptr<udp_zero_copy_asio_impl> sptr; -    udp_zero_copy_impl( +    udp_zero_copy_asio_impl(          const std::string &addr, -        const std::string &port +        const std::string &port, +        const device_addr_t &hints      ): -        phony_zero_copy_recv_if(udp_simple::mtu), -        phony_zero_copy_send_if(udp_simple::mtu) +        _io_service(hints.cast<size_t>("concurrency_hint", CONCURRENCY_HINT)), +        _recv_frame_size(size_t(hints.cast<double>("recv_frame_size", udp_simple::mtu))), +        _num_recv_frames(size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_RECV_FRAMES))), +        _send_frame_size(size_t(hints.cast<double>("send_frame_size", udp_simple::mtu))), +        _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_SEND_FRAMES)))      {          //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; -        // resolve the address -        boost::asio::ip::udp::resolver resolver(_io_service); -        boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); -        boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); +        //resolve the address +        asio::ip::udp::resolver resolver(_io_service); +        asio::ip::udp::resolver::query query(asio::ip::udp::v4(), addr, port); +        asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); -        // create, open, and connect the socket -        _socket = new boost::asio::ip::udp::socket(_io_service); -        _socket->open(boost::asio::ip::udp::v4()); +        //create, open, and connect the socket +        _socket = new asio::ip::udp::socket(_io_service); +        _socket->open(asio::ip::udp::v4());          _socket->connect(receiver_endpoint);          _sock_fd = _socket->native();      } -    ~udp_zero_copy_impl(void){ +    ~udp_zero_copy_asio_impl(void){ +        delete _work; //allow io_service run to complete +        _thread_group.join_all(); //wait for service threads to exit          delete _socket;      } +    void init(void){ +        //allocate all recv frames and release them to begin xfers +        _pending_recv_buffs = pending_buffs_type::make(_num_recv_frames); +        _recv_buffer = boost::shared_array<char>(new char[_num_recv_frames*_recv_frame_size]); +        for (size_t i = 0; i < _num_recv_frames; i++){ +            release(_recv_buffer.get() + i*_recv_frame_size); +        } + +        //allocate all send frames and push them into the fifo +        _pending_send_buffs = pending_buffs_type::make(_num_send_frames); +        _send_buffer = boost::shared_array<char>(new char[_num_send_frames*_send_frame_size]); +        for (size_t i = 0; i < _num_send_frames; i++){ +            handle_send(_send_buffer.get() + i*_send_frame_size); +        } + +        //spawn the service threads that will run the io service +        _work = new asio::io_service::work(_io_service); //new work to delete later +        for (size_t i = 0; i < CONCURRENCY_HINT; i++) _thread_group.create_thread( +            boost::bind(&udp_zero_copy_asio_impl::service, this) +        ); +    } + +    void service(void){ +        set_thread_priority_safe(); +        _io_service.run(); +    } +      //get size for internal socket buffer      template <typename Opt> size_t get_buff_size(void) const{          Opt option; @@ -91,60 +153,165 @@ public:          return get_buff_size<Opt>();      } +    //! handle a recv callback -> push the filled memory into the fifo +    UHD_INLINE void handle_recv(void *mem, size_t len){ +        boost::this_thread::disable_interruption di; //disable because the wait can throw +        _pending_recv_buffs->push_with_wait(boost::asio::buffer(mem, len)); +    } -    //The number of frames is approximately the buffer size divided by the max datagram size. -    //In reality, this is a phony zero-copy interface and the number of frames is infinite. -    //However, its sensible to advertise a frame count that is approximate to buffer size. -    //This way, the transport caller will have an idea about how much buffering to create. - -    size_t get_num_recv_frames(void) const{ -        return this->get_buff_size<boost::asio::socket_base::receive_buffer_size>()/udp_simple::mtu; +    //////////////////////////////////////////////////////////////////// +    #ifdef USE_ASIO_ASYNC_RECV +    //////////////////////////////////////////////////////////////////// +    //! pop a filled recv buffer off of the fifo and bind with the release callback +    managed_recv_buffer::sptr get_recv_buff(double timeout){ +        boost::this_thread::disable_interruption di; //disable because the wait can throw +        asio::mutable_buffer buff; +        if (_pending_recv_buffs->pop_with_timed_wait(buff, timeout)){ +            return managed_recv_buffer::make_safe( +                buff, boost::bind( +                    &udp_zero_copy_asio_impl::release, +                    shared_from_this(), +                    asio::buffer_cast<void*>(buff) +                ) +            ); +        } +        return managed_recv_buffer::sptr();      } -    size_t get_num_send_frames(void) const{ -        return this->get_buff_size<boost::asio::socket_base::send_buffer_size>()/udp_simple::mtu; +    //! release a recv buffer -> start an async recv on the buffer +    void release(void *mem){ +        _socket->async_receive( +            boost::asio::buffer(mem, this->get_recv_frame_size()), +            boost::bind( +                &udp_zero_copy_asio_impl::handle_recv, +                shared_from_this(), mem, +                asio::placeholders::bytes_transferred +            ) +        );      } -private: -    boost::asio::ip::udp::socket   *_socket; -    boost::asio::io_service        _io_service; -    int                            _sock_fd; +    //////////////////////////////////////////////////////////////////// +    #else /*USE_ASIO_ASYNC_RECV*/ +    //////////////////////////////////////////////////////////////////// +    managed_recv_buffer::sptr get_recv_buff(double timeout){ +        boost::this_thread::disable_interruption di; //disable because the wait can throw +        asio::mutable_buffer buff; -    ssize_t recv(const boost::asio::mutable_buffer &buff){          //setup timeval for timeout          timeval tv;          tv.tv_sec = 0; -        tv.tv_usec = int(RECV_TIMEOUT*1e6); +        tv.tv_usec = long(timeout*1e6);          //setup rset for timeout          fd_set rset;          FD_ZERO(&rset);          FD_SET(_sock_fd, &rset); -        //call select to perform timed wait -        if (::select(_sock_fd+1, &rset, NULL, NULL, &tv) <= 0) return 0; +        //call select to perform timed wait and grab an available buffer with wait +        //if the condition is true, call receive and return the managed buffer +        if ( +            ::select(_sock_fd+1, &rset, NULL, NULL, &tv) > 0 and +            _pending_recv_buffs->pop_with_timed_wait(buff, timeout) +        ){ +            return managed_recv_buffer::make_safe( +                asio::buffer( +                    boost::asio::buffer_cast<void *>(buff), +                    _socket->receive(asio::buffer(buff)) +                ), +                boost::bind( +                    &udp_zero_copy_asio_impl::release, +                    shared_from_this(), +                    asio::buffer_cast<void*>(buff) +                ) +            ); +        } +        return managed_recv_buffer::sptr(); +    } + +    void release(void *mem){ +        boost::this_thread::disable_interruption di; //disable because the wait can throw +        handle_recv(mem, this->get_recv_frame_size()); +    } -        return ::recv( -            _sock_fd, -            boost::asio::buffer_cast<char *>(buff), -            boost::asio::buffer_size(buff), 0 -        ); +    //////////////////////////////////////////////////////////////////// +    #endif /*USE_ASIO_ASYNC_RECV*/ +    //////////////////////////////////////////////////////////////////// + +    size_t get_num_recv_frames(void) const {return _num_recv_frames;} +    size_t get_recv_frame_size(void) const {return _recv_frame_size;} + +    //! handle a send callback -> push the emptied memory into the fifo +    UHD_INLINE void handle_send(void *mem){ +        boost::this_thread::disable_interruption di; //disable because the wait can throw +        _pending_send_buffs->push_with_wait(boost::asio::buffer(mem, this->get_send_frame_size()));      } -    ssize_t send(const boost::asio::const_buffer &buff){ -        return ::send( -            _sock_fd, -            boost::asio::buffer_cast<const char *>(buff), -            boost::asio::buffer_size(buff), 0 +    //! pop an empty send buffer off of the fifo and bind with the commit callback +    managed_send_buffer::sptr get_send_buff(double timeout){ +        boost::this_thread::disable_interruption di; //disable because the wait can throw +        asio::mutable_buffer buff; +        if (_pending_send_buffs->pop_with_timed_wait(buff, timeout)){ +            return managed_send_buffer::make_safe( +                buff, boost::bind( +                    &udp_zero_copy_asio_impl::commit, +                    shared_from_this(), +                    asio::buffer_cast<void*>(buff), _1 +                ) +            ); +        } +        return managed_send_buffer::sptr(); +    } + +    //////////////////////////////////////////////////////////////////// +    #ifdef USE_ASIO_ASYNC_SEND +    //////////////////////////////////////////////////////////////////// +    //! commit a send buffer -> start an async send on the buffer +    void commit(void *mem, size_t len){ +        _socket->async_send( +            boost::asio::buffer(mem, len), +            boost::bind( +                &udp_zero_copy_asio_impl::handle_send, +                shared_from_this(), mem +            )          );      } + +    //////////////////////////////////////////////////////////////////// +    #else /*USE_ASIO_ASYNC_SEND*/ +    //////////////////////////////////////////////////////////////////// +    void commit(void *mem, size_t len){ +        _socket->send(asio::buffer(mem, len)); +        handle_send(mem); +    } + +    //////////////////////////////////////////////////////////////////// +    #endif /*USE_ASIO_ASYNC_SEND*/ +    //////////////////////////////////////////////////////////////////// + +    size_t get_num_send_frames(void) const {return _num_send_frames;} +    size_t get_send_frame_size(void) const {return _send_frame_size;} + +private: +    //asio guts -> socket and service +    asio::ip::udp::socket   *_socket; +    asio::io_service        _io_service; +    asio::io_service::work  *_work; +    int                     _sock_fd; + +    //memory management -> buffers and fifos +    boost::thread_group _thread_group; +    boost::shared_array<char> _send_buffer, _recv_buffer; +    typedef bounded_buffer<asio::mutable_buffer> pending_buffs_type; +    pending_buffs_type::sptr _pending_recv_buffs, _pending_send_buffs; +    const size_t _recv_frame_size, _num_recv_frames; +    const size_t _send_frame_size, _num_send_frames;  };  /***********************************************************************   * UDP zero copy make function   **********************************************************************/  template<typename Opt> static void resize_buff_helper( -    udp_zero_copy_impl::sptr udp_trans, +    udp_zero_copy_asio_impl::sptr udp_trans,      size_t target_size,      const std::string &name  ){ @@ -152,6 +319,13 @@ template<typename Opt> static void resize_buff_helper(      if (name == "recv") min_sock_buff_size = MIN_RECV_SOCK_BUFF_SIZE;      if (name == "send") min_sock_buff_size = MIN_SEND_SOCK_BUFF_SIZE; +    std::string help_message; +    #if defined(UHD_PLATFORM_LINUX) +        help_message = str(boost::format( +            "Please run: sudo sysctl -w net.core.%smem_max=%d\n" +        ) % ((name == "recv")?"r":"w") % min_sock_buff_size); +    #endif /*defined(UHD_PLATFORM_LINUX)*/ +      //resize the buffer if size was provided      if (target_size > 0){          size_t actual_size = udp_trans->resize_buff<Opt>(target_size); @@ -162,11 +336,11 @@ template<typename Opt> static void resize_buff_helper(          else std::cout << boost::format(              "Current %s sock buff size: %d bytes"          ) % name % actual_size << std::endl; -        if (actual_size < target_size) uhd::print_warning(str(boost::format( +        if (actual_size < target_size) uhd::warning::post(str(boost::format(              "The %s buffer is smaller than the requested size.\n"              "The minimum recommended buffer size is %d bytes.\n" -            "See the USRP2 application notes on buffer resizing.\n" -        ) % name % min_sock_buff_size)); +            "See the transport application notes on buffer resizing.\n%s" +        ) % name % min_sock_buff_size % help_message));      }      //only enable on platforms that are happy with the large buffer resize @@ -181,14 +355,21 @@ template<typename Opt> static void resize_buff_helper(  udp_zero_copy::sptr udp_zero_copy::make(      const std::string &addr,      const std::string &port, -    size_t recv_buff_size, -    size_t send_buff_size +    const device_addr_t &hints  ){ -    udp_zero_copy_impl::sptr udp_trans(new udp_zero_copy_impl(addr, port)); +    udp_zero_copy_asio_impl::sptr udp_trans( +        new udp_zero_copy_asio_impl(addr, port, hints) +    ); + +    //extract buffer size hints from the device addr +    size_t recv_buff_size = size_t(hints.cast<double>("recv_buff_size", 0.0)); +    size_t send_buff_size = size_t(hints.cast<double>("send_buff_size", 0.0));      //call the helper to resize send and recv buffers -    resize_buff_helper<boost::asio::socket_base::receive_buffer_size>(udp_trans, recv_buff_size, "recv"); -    resize_buff_helper<boost::asio::socket_base::send_buffer_size>   (udp_trans, send_buff_size, "send"); +    resize_buff_helper<asio::socket_base::receive_buffer_size>(udp_trans, recv_buff_size, "recv"); +    resize_buff_helper<asio::socket_base::send_buffer_size>   (udp_trans, send_buff_size, "send"); + +    udp_trans->init(); //buffers resized -> call init() to use      return udp_trans;  } diff --git a/host/lib/transport/usb_dummy_impl.cpp b/host/lib/transport/usb_dummy_impl.cpp new file mode 100644 index 000000000..8a9772e7f --- /dev/null +++ b/host/lib/transport/usb_dummy_impl.cpp @@ -0,0 +1,39 @@ +// +// Copyright 2010 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 <uhd/transport/usb_device_handle.hpp> +#include <uhd/transport/usb_control.hpp> +#include <uhd/transport/usb_zero_copy.hpp> +#include <uhd/utils/exception.hpp> + +using namespace uhd; +using namespace uhd::transport; + +std::vector<usb_device_handle::sptr> usb_device_handle::get_device_list(boost::uint16_t, boost::uint16_t){ +    return std::vector<usb_device_handle::sptr>(); //empty list +} + +usb_control::sptr usb_control::make(usb_device_handle::sptr){ +    throw std::runtime_error("no usb support -> usb_control::make not implemented"); +} + +usb_zero_copy::sptr usb_zero_copy::make( +    usb_device_handle::sptr, +    size_t, size_t, const device_addr_t & +){ +    throw std::runtime_error("no usb support -> usb_zero_copy::make not implemented"); +} diff --git a/host/lib/transport/vrt_packet_handler.hpp b/host/lib/transport/vrt_packet_handler.hpp index b603f1371..939517411 100644 --- a/host/lib/transport/vrt_packet_handler.hpp +++ b/host/lib/transport/vrt_packet_handler.hpp @@ -303,18 +303,18 @@ template <typename T> UHD_INLINE T get_context_code(       * Pack a vrt header, copy-convert the data, and send it.       *  - helper function for vrt_packet_handler::send       ******************************************************************/ -    static UHD_INLINE void _send1( +    static UHD_INLINE size_t _send1(          send_state &state,          const std::vector<const void *> &buffs, -        size_t offset_bytes, -        size_t num_samps, +        const size_t offset_bytes, +        const size_t num_samps,          uhd::transport::vrt::if_packet_info_t &if_packet_info,          const uhd::io_type_t &io_type,          const uhd::otw_type_t &otw_type,          const vrt_packer_t &vrt_packer,          const get_send_buffs_t &get_send_buffs, -        size_t vrt_header_offset_words32, -        size_t chans_per_otw_buff +        const size_t vrt_header_offset_words32, +        const size_t chans_per_otw_buff      ){          //load the rest of the if_packet_info in here          if_packet_info.num_payload_words32 = (num_samps*chans_per_otw_buff*otw_type.get_sample_size())/sizeof(boost::uint32_t); @@ -322,7 +322,7 @@ template <typename T> UHD_INLINE T get_context_code(          //get send buffers for each channel          managed_send_buffs_t send_buffs(buffs.size()/chans_per_otw_buff); -        UHD_ASSERT_THROW(get_send_buffs(send_buffs)); +        if (not get_send_buffs(send_buffs)) return 0;          std::vector<const void *> io_buffs(chans_per_otw_buff);          for (size_t i = 0; i < buffs.size(); i+=chans_per_otw_buff){ @@ -343,10 +343,9 @@ template <typename T> UHD_INLINE T get_context_code(              //commit the samples to the zero-copy interface              size_t num_bytes_total = (vrt_header_offset_words32+if_packet_info.num_packet_words32)*sizeof(boost::uint32_t); -            if (send_buffs[i]->commit(num_bytes_total) < ssize_t(num_bytes_total)){ -                std::cerr << "commit to send buffer returned less than commit size" << std::endl; -            } +            send_buffs[i]->commit(num_bytes_total);          } +        return num_samps;      }      /******************************************************************* @@ -381,7 +380,6 @@ template <typename T> UHD_INLINE T get_context_code(          ////////////////////////////////////////////////////////////////          case uhd::device::SEND_MODE_ONE_PACKET:{          //////////////////////////////////////////////////////////////// -            size_t num_samps = std::min(total_num_samps, max_samples_per_packet);              //fill in parts of the packet info overwrote in full buff mode              if_packet_info.has_tsi = metadata.has_time_spec; @@ -389,10 +387,10 @@ template <typename T> UHD_INLINE T get_context_code(              if_packet_info.sob = metadata.start_of_burst;              if_packet_info.eob = metadata.end_of_burst; -            _send1( +            return _send1(                  state,                  buffs, 0, -                num_samps, +                std::min(total_num_samps, max_samples_per_packet),                  if_packet_info,                  io_type, otw_type,                  vrt_packer, @@ -400,31 +398,32 @@ template <typename T> UHD_INLINE T get_context_code(                  vrt_header_offset_words32,                  chans_per_otw_buff              ); -            return num_samps;          }          ////////////////////////////////////////////////////////////////          case uhd::device::SEND_MODE_FULL_BUFF:{          //////////////////////////////////////////////////////////////// -            //calculate constants for fragmentation -            const size_t num_fragments = (total_num_samps+max_samples_per_packet-1)/max_samples_per_packet; -            static const size_t first_fragment_index = 0; -            const size_t final_fragment_index = num_fragments-1; +            size_t total_num_samps_sent = 0;              //loop through the following fragment indexes -            for (size_t n = first_fragment_index; n <= final_fragment_index; n++){ +            while(total_num_samps_sent < total_num_samps){ + +                //calculate per-loop-iteration variables +                const size_t total_num_samps_unsent = total_num_samps - total_num_samps_sent; +                const bool first_fragment = (total_num_samps_sent == 0); +                const bool final_fragment = (total_num_samps_unsent <= max_samples_per_packet);                  //calculate new flags for the fragments -                if_packet_info.has_tsi = metadata.has_time_spec  and (n == first_fragment_index); -                if_packet_info.has_tsf = metadata.has_time_spec  and (n == first_fragment_index); -                if_packet_info.sob     = metadata.start_of_burst and (n == first_fragment_index); -                if_packet_info.eob     = metadata.end_of_burst   and (n == final_fragment_index); +                if_packet_info.has_tsi = metadata.has_time_spec  and first_fragment; +                if_packet_info.has_tsf = if_packet_info.has_tsi; +                if_packet_info.sob     = metadata.start_of_burst and first_fragment; +                if_packet_info.eob     = metadata.end_of_burst   and final_fragment;                  //send the fragment with the helper function -                _send1( +                const size_t num_samps_sent = _send1(                      state, -                    buffs, n*max_samples_per_packet*io_type.size, -                    (n == final_fragment_index)?(total_num_samps%max_samples_per_packet):max_samples_per_packet, +                    buffs, total_num_samps_sent*io_type.size, +                    std::min(total_num_samps_unsent, max_samples_per_packet),                      if_packet_info,                      io_type, otw_type,                      vrt_packer, @@ -432,8 +431,10 @@ template <typename T> UHD_INLINE T get_context_code(                      vrt_header_offset_words32,                      chans_per_otw_buff                  ); +                total_num_samps_sent += num_samps_sent; +                if (num_samps_sent == 0) return total_num_samps_sent;              } -            return total_num_samps; +            return total_num_samps_sent;          }          default: throw std::runtime_error("unknown send mode"); diff --git a/host/lib/transport/zero_copy.cpp b/host/lib/transport/zero_copy.cpp index 8a1cde694..a5a864a04 100644 --- a/host/lib/transport/zero_copy.cpp +++ b/host/lib/transport/zero_copy.cpp @@ -16,32 +16,35 @@  //  #include <uhd/transport/zero_copy.hpp> -#include <boost/cstdint.hpp> -#include <boost/function.hpp> -#include <boost/bind.hpp>  using namespace uhd::transport;  /*********************************************************************** - * The pure-virtual deconstructor needs an implementation to be happy + * Safe managed receive buffer   **********************************************************************/ -managed_recv_buffer::~managed_recv_buffer(void){ +static void release_nop(void){      /* NOP */  } -/*********************************************************************** - * Phony zero-copy recv interface implementation - **********************************************************************/ - -//! phony zero-copy recv buffer implementation -class managed_recv_buffer_impl : public managed_recv_buffer{ +class safe_managed_receive_buffer : public managed_recv_buffer{  public: -    managed_recv_buffer_impl(const boost::asio::const_buffer &buff) : _buff(buff){ +    safe_managed_receive_buffer( +        const boost::asio::const_buffer &buff, +        const release_fcn_t &release_fcn +    ): +        _buff(buff), _release_fcn(release_fcn) +    {          /* NOP */      } -    ~managed_recv_buffer_impl(void){ -        delete [] this->cast<const boost::uint8_t *>(); +    ~safe_managed_receive_buffer(void){ +        _release_fcn(); +    } + +    void release(void){ +        release_fcn_t release_fcn = _release_fcn; +        _release_fcn = &release_nop; +        return release_fcn();      }  private: @@ -50,64 +53,42 @@ private:      }      const boost::asio::const_buffer _buff; +    release_fcn_t _release_fcn;  }; -//! phony zero-copy recv interface implementation -struct phony_zero_copy_recv_if::impl{ -    impl(size_t max_buff_size) : max_buff_size(max_buff_size){ -        /* NOP */ -    } -    size_t max_buff_size; -}; - -phony_zero_copy_recv_if::phony_zero_copy_recv_if(size_t max_buff_size){ -    _impl = UHD_PIMPL_MAKE(impl, (max_buff_size)); -} - -phony_zero_copy_recv_if::~phony_zero_copy_recv_if(void){ -    /* NOP */ -} - -managed_recv_buffer::sptr phony_zero_copy_recv_if::get_recv_buff(void){ -    //allocate memory -    boost::uint8_t *recv_mem = new boost::uint8_t[_impl->max_buff_size]; - -    //call recv() with timeout option -    ssize_t num_bytes = this->recv(boost::asio::buffer(recv_mem, _impl->max_buff_size)); - -    if (num_bytes <= 0) return managed_recv_buffer::sptr(); //NULL sptr - -    //create a new managed buffer to house the data -    return managed_recv_buffer::sptr( -        new managed_recv_buffer_impl(boost::asio::buffer(recv_mem, num_bytes)) -    ); +managed_recv_buffer::sptr managed_recv_buffer::make_safe( +    const boost::asio::const_buffer &buff, +    const release_fcn_t &release_fcn +){ +    return sptr(new safe_managed_receive_buffer(buff, release_fcn));  }  /*********************************************************************** - * Phony zero-copy send interface implementation + * Safe managed send buffer   **********************************************************************/ +static void commit_nop(size_t){ +    /* NOP */ +} -//! phony zero-copy send buffer implementation -class managed_send_buffer_impl : public managed_send_buffer{ +class safe_managed_send_buffer : public managed_send_buffer{  public: -    typedef boost::function<ssize_t(const boost::asio::const_buffer &)> send_fcn_t; - -    managed_send_buffer_impl( +    safe_managed_send_buffer(          const boost::asio::mutable_buffer &buff, -        const send_fcn_t &send_fcn +        const commit_fcn_t &commit_fcn      ): -        _buff(buff), -        _send_fcn(send_fcn) +        _buff(buff), _commit_fcn(commit_fcn)      {          /* NOP */      } -    ~managed_send_buffer_impl(void){ -        /* NOP */ +    ~safe_managed_send_buffer(void){ +        _commit_fcn(0);      } -    ssize_t commit(size_t num_bytes){ -        return _send_fcn(boost::asio::buffer(_buff, num_bytes)); +    void commit(size_t num_bytes){ +        commit_fcn_t commit_fcn = _commit_fcn; +        _commit_fcn = &commit_nop; +        return commit_fcn(num_bytes);      }  private: @@ -116,28 +97,12 @@ private:      }      const boost::asio::mutable_buffer _buff; -    const send_fcn_t                  _send_fcn; -}; - -//! phony zero-copy send interface implementation -struct phony_zero_copy_send_if::impl{ -    boost::uint8_t *send_mem; -    managed_send_buffer::sptr send_buff; +    commit_fcn_t _commit_fcn;  }; -phony_zero_copy_send_if::phony_zero_copy_send_if(size_t max_buff_size){ -    _impl = UHD_PIMPL_MAKE(impl, ()); -    _impl->send_mem = new boost::uint8_t[max_buff_size]; -    _impl->send_buff = managed_send_buffer::sptr(new managed_send_buffer_impl( -        boost::asio::buffer(_impl->send_mem, max_buff_size), -        boost::bind(&phony_zero_copy_send_if::send, this, _1) -    )); -} - -phony_zero_copy_send_if::~phony_zero_copy_send_if(void){ -    delete [] _impl->send_mem; -} - -managed_send_buffer::sptr phony_zero_copy_send_if::get_send_buff(void){ -    return _impl->send_buff; //FIXME there is only ever one send buff, we assume that the caller doesnt hang onto these +safe_managed_send_buffer::sptr managed_send_buffer::make_safe( +    const boost::asio::mutable_buffer &buff, +    const commit_fcn_t &commit_fcn +){ +    return sptr(new safe_managed_send_buffer(buff, commit_fcn));  } diff --git a/host/lib/types.cpp b/host/lib/types.cpp index f957cd83f..4188568aa 100644 --- a/host/lib/types.cpp +++ b/host/lib/types.cpp @@ -17,6 +17,7 @@  #include <uhd/utils/assert.hpp>  #include <uhd/types/ranges.hpp> +#include <uhd/types/tune_request.hpp>  #include <uhd/types/tune_result.hpp>  #include <uhd/types/clock_config.hpp>  #include <uhd/types/stream_cmd.hpp> @@ -58,6 +59,26 @@ freq_range_t::freq_range_t(double min, double max):  }  /*********************************************************************** + * tune request + **********************************************************************/ +tune_request_t::tune_request_t(double target_freq): +    target_freq(target_freq), +    inter_freq_policy(POLICY_AUTO), +    dsp_freq_policy(POLICY_AUTO) +{ +    /* NOP */ +} + +tune_request_t::tune_request_t(double target_freq, double lo_off): +    target_freq(target_freq), +    inter_freq_policy(POLICY_MANUAL), +    inter_freq(target_freq + lo_off), +    dsp_freq_policy(POLICY_AUTO) +{ +    /* NOP */ +} + +/***********************************************************************   * tune result   **********************************************************************/  std::string tune_result_t::to_pp_string(void) const{ @@ -124,14 +145,14 @@ time_spec_t::time_spec_t(time_t full_secs, double frac_secs):      /* NOP */  } -time_spec_t::time_spec_t(time_t full_secs, size_t tick_count, double tick_rate): +time_spec_t::time_spec_t(time_t full_secs, long tick_count, double tick_rate):      _full_secs(full_secs),      _frac_secs(double(tick_count)/tick_rate)  {      /* NOP */  } -size_t time_spec_t::get_tick_count(double tick_rate) const{ +long time_spec_t::get_tick_count(double tick_rate) const{      return boost::math::iround(this->get_frac_secs()*tick_rate);  } @@ -140,7 +161,9 @@ double time_spec_t::get_real_secs(void) const{  }  time_t time_spec_t::get_full_secs(void) const{ -    return this->_full_secs + time_t(std::floor(this->_frac_secs)); +    double intpart; +    std::modf(this->_frac_secs, &intpart); +    return this->_full_secs + time_t(intpart);  }  double time_spec_t::get_frac_secs(void) const{ @@ -160,13 +183,18 @@ time_spec_t &time_spec_t::operator-=(const time_spec_t &rhs){  }  bool uhd::operator==(const time_spec_t &lhs, const time_spec_t &rhs){ -    return lhs.get_full_secs() == rhs.get_full_secs() and lhs.get_frac_secs() == rhs.get_frac_secs(); +    return +        lhs.get_full_secs() == rhs.get_full_secs() and +        lhs.get_frac_secs() == rhs.get_frac_secs() +    ;  }  bool uhd::operator<(const time_spec_t &lhs, const time_spec_t &rhs){ -    if (lhs.get_full_secs() < rhs.get_full_secs()) return true; -    if (lhs.get_full_secs() > rhs.get_full_secs()) return false; -    return lhs.get_frac_secs() < rhs.get_frac_secs(); +    return ( +        (lhs.get_full_secs() < rhs.get_full_secs()) or ( +        (lhs.get_full_secs() == rhs.get_full_secs()) and +        (lhs.get_frac_secs() < rhs.get_frac_secs()) +    ));  }  /*********************************************************************** diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt index 73197bca4..c264252e1 100644 --- a/host/lib/usrp/CMakeLists.txt +++ b/host/lib/usrp/CMakeLists.txt @@ -23,12 +23,12 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_SOURCE_DIR}/lib/usrp/dboard_id.cpp      ${CMAKE_SOURCE_DIR}/lib/usrp/dboard_manager.cpp      ${CMAKE_SOURCE_DIR}/lib/usrp/dsp_utils.cpp -    ${CMAKE_SOURCE_DIR}/lib/usrp/mimo_usrp.cpp      ${CMAKE_SOURCE_DIR}/lib/usrp/misc_utils.cpp -    ${CMAKE_SOURCE_DIR}/lib/usrp/simple_usrp.cpp +    ${CMAKE_SOURCE_DIR}/lib/usrp/multi_usrp.cpp      ${CMAKE_SOURCE_DIR}/lib/usrp/single_usrp.cpp      ${CMAKE_SOURCE_DIR}/lib/usrp/subdev_spec.cpp      ${CMAKE_SOURCE_DIR}/lib/usrp/tune_helper.cpp +    ${CMAKE_SOURCE_DIR}/lib/usrp/wrapper_utils.hpp  )  INCLUDE(${CMAKE_SOURCE_DIR}/lib/usrp/dboard/CMakeLists.txt) diff --git a/host/lib/usrp/dboard/CMakeLists.txt b/host/lib/usrp/dboard/CMakeLists.txt index 3e995009e..8d3d11530 100644 --- a/host/lib/usrp/dboard/CMakeLists.txt +++ b/host/lib/usrp/dboard/CMakeLists.txt @@ -24,5 +24,6 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_wbx.cpp      ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_dbsrx.cpp      ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_unknown.cpp +    ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_tvrx.cpp  ) diff --git a/host/lib/usrp/dboard/db_basic_and_lf.cpp b/host/lib/usrp/dboard/db_basic_and_lf.cpp index 4b0d8bf27..4c49b3bff 100644 --- a/host/lib/usrp/dboard/db_basic_and_lf.cpp +++ b/host/lib/usrp/dboard/db_basic_and_lf.cpp @@ -20,6 +20,7 @@  #include <uhd/types/ranges.hpp>  #include <uhd/utils/assert.hpp>  #include <uhd/utils/static.hpp> +#include <uhd/utils/warning.hpp>  #include <uhd/usrp/dboard_base.hpp>  #include <uhd/usrp/dboard_manager.hpp>  #include <boost/assign/list_of.hpp> @@ -68,11 +69,11 @@ static const uhd::dict<std::string, subdev_conn_t> sd_name_to_conn = map_list_of   * Register the basic and LF dboards   **********************************************************************/  static dboard_base::sptr make_basic_rx(dboard_base::ctor_args_t args){ -    return dboard_base::sptr(new basic_rx(args, 90e9)); +    return dboard_base::sptr(new basic_rx(args, 250e6));  }  static dboard_base::sptr make_basic_tx(dboard_base::ctor_args_t args){ -    return dboard_base::sptr(new basic_tx(args, 90e9)); +    return dboard_base::sptr(new basic_tx(args, 250e6));  }  static dboard_base::sptr make_lf_rx(dboard_base::ctor_args_t args){ @@ -149,6 +150,10 @@ void basic_rx::rx_get(const wax::obj &key_, wax::obj &val){          val = sd_name_to_conn[get_subdev_name()];          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = false;          return; @@ -157,6 +162,10 @@ void basic_rx::rx_get(const wax::obj &key_, wax::obj &val){          val = true; //there is no LO, so it must be true!          return; +    case SUBDEV_PROP_BANDWIDTH: +        val = 2*_max_freq; //we want complex double-sided +        return; +      default: UHD_THROW_PROP_GET_ERROR();      }  } @@ -178,6 +187,17 @@ void basic_rx::rx_set(const wax::obj &key_, const wax::obj &val){      case SUBDEV_PROP_FREQ:          return; // it wont do you much good, but you can set it +    case SUBDEV_PROP_ENABLED: +        return; //always enabled + +    case SUBDEV_PROP_BANDWIDTH: +        uhd::warning::post( +            str(boost::format("%s: No tunable bandwidth, fixed filtered to %0.2fMHz") +                % get_rx_id().to_pp_string() % _max_freq +            ) +        ); +        return; +      default: UHD_THROW_PROP_SET_ERROR();      }  } @@ -241,6 +261,10 @@ void basic_tx::tx_get(const wax::obj &key_, wax::obj &val){          val = sd_name_to_conn[get_subdev_name()];          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = false;          return; @@ -249,6 +273,10 @@ void basic_tx::tx_get(const wax::obj &key_, wax::obj &val){          val = true; //there is no LO, so it must be true!          return; +    case SUBDEV_PROP_BANDWIDTH: +        val = 2*_max_freq; //we want complex double-sided +        return; +      default: UHD_THROW_PROP_GET_ERROR();      }  } @@ -270,6 +298,17 @@ void basic_tx::tx_set(const wax::obj &key_, const wax::obj &val){      case SUBDEV_PROP_FREQ:          return; // it wont do you much good, but you can set it +    case SUBDEV_PROP_ENABLED: +        return; //always enabled + +    case SUBDEV_PROP_BANDWIDTH: +        uhd::warning::post( +            str(boost::format("%s: No tunable bandwidth, fixed filtered to %0.2fMHz") +                % get_tx_id().to_pp_string() % _max_freq +            ) +        ); +        return; +      default: UHD_THROW_PROP_SET_ERROR();      }  } diff --git a/host/lib/usrp/dboard/db_dbsrx.cpp b/host/lib/usrp/dboard/db_dbsrx.cpp index 81434f054..85251bdf9 100644 --- a/host/lib/usrp/dboard/db_dbsrx.cpp +++ b/host/lib/usrp/dboard/db_dbsrx.cpp @@ -69,7 +69,7 @@ public:  private:      double _lo_freq; -    float _bandwidth; +    double _bandwidth;      uhd::dict<std::string, float> _gains;      max2118_write_regs_t _max2118_write_regs;      max2118_read_regs_t _max2118_read_regs; @@ -79,7 +79,7 @@ private:      void set_lo_freq(double target_freq);      void set_gain(float gain, const std::string &name); -    void set_bandwidth(float bandwidth); +    void set_bandwidth(double bandwidth);      void send_reg(boost::uint8_t start_reg, boost::uint8_t stop_reg){          start_reg = boost::uint8_t(std::clip(int(start_reg), 0x0, 0x5)); @@ -162,15 +162,10 @@ static dboard_base::sptr make_dbsrx(dboard_base::ctor_args_t args){      return dboard_base::sptr(new dbsrx(args));  } -//dbid for USRP2 version  UHD_STATIC_BLOCK(reg_dbsrx_dboard){ -    //register the factory function for the rx dbid +    //register the factory function for the rx dbid (others version)      dboard_manager::register_dboard(0x000D, &make_dbsrx, "DBSRX"); -} - -//dbid for USRP1 version -UHD_STATIC_BLOCK(reg_dbsrx_on_usrp1_dboard){ -    //register the factory function for the rx dbid +    //register the factory function for the rx dbid (USRP1 version)      dboard_manager::register_dboard(0x0002, &make_dbsrx, "DBSRX");  } @@ -180,7 +175,7 @@ UHD_STATIC_BLOCK(reg_dbsrx_on_usrp1_dboard){  dbsrx::dbsrx(ctor_args_t args) : rx_dboard_base(args){      //warn user about incorrect DBID on USRP1, requires R193 populated      if (this->get_iface()->get_special_props().soft_clock_divider and this->get_rx_id() == 0x000D) -        uhd::print_warning( +        uhd::warning::post(              str(boost::format(                  "DBSRX: incorrect dbid\n"                  "Expected dbid 0x0002 and R193\n" @@ -191,7 +186,7 @@ dbsrx::dbsrx(ctor_args_t args) : rx_dboard_base(args){      //warn user about incorrect DBID on non-USRP1, requires R194 populated      if (not this->get_iface()->get_special_props().soft_clock_divider and this->get_rx_id() == 0x0002) -        uhd::print_warning( +        uhd::warning::post(              str(boost::format(                  "DBSRX: incorrect dbid\n"                  "Expected dbid 0x000D and R194\n" @@ -239,11 +234,12 @@ void dbsrx::set_lo_freq(double target_freq){      double actual_freq=0.0, pfd_freq=0.0, ref_clock=0.0;      int R=0, N=0, r=0, m=0;      bool update_filter_settings = false; -      //choose refclock      std::vector<double> clock_rates = this->get_iface()->get_clock_rates(dboard_iface::UNIT_RX); +    const double max_clock_rate = std::sorted(clock_rates).back();      BOOST_FOREACH(ref_clock, std::reversed(std::sorted(clock_rates))){          if (ref_clock > 27.0e6) continue; +        if (size_t(max_clock_rate/ref_clock)%2 == 1) continue; //reject asymmetric clocks (odd divisors)          //choose m_divider such that filter tuning constraint is met          m = 31; @@ -251,7 +247,7 @@ void dbsrx::set_lo_freq(double target_freq){          if(dbsrx_debug) std::cerr << boost::format(              "DBSRX: trying ref_clock %f and m_divider %d" -        ) % (this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX)) % m << std::endl; +        ) % (ref_clock) % m << std::endl;          if (m >= 32) continue; @@ -277,15 +273,17 @@ void dbsrx::set_lo_freq(double target_freq){          }      }  -    //Assert because we failed to find a suitable combination of ref_clock, R and N  -    UHD_ASSERT_THROW(ref_clock/(1 << m) < 1e6 or ref_clock/(1 << m) > 2.5e6); -    UHD_ASSERT_THROW((pfd_freq < dbsrx_pfd_freq_range.min) or (pfd_freq > dbsrx_pfd_freq_range.max)); -    UHD_ASSERT_THROW((N < 256) or (N > 32768));      done_loop: +    //Assert because we failed to find a suitable combination of ref_clock, R and N  +    UHD_ASSERT_THROW(ref_clock <= 27.0e6 and ref_clock >= 0.0); +    UHD_ASSERT_THROW(ref_clock/m >= 1e6 and ref_clock/m <= 2.5e6); +    UHD_ASSERT_THROW((pfd_freq >= dbsrx_pfd_freq_range.min) and (pfd_freq <= dbsrx_pfd_freq_range.max)); +    UHD_ASSERT_THROW((N >= 256) and (N <= 32768)); +      if(dbsrx_debug) std::cerr << boost::format( -        "DBSRX: choose ref_clock %f and m_divider %d" -    ) % (this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX)) % m << std::endl; +        "DBSRX: choose ref_clock (current: %f, new: %f) and m_divider %d" +    ) % (this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX)) % ref_clock % m << std::endl;      //if ref_clock or m divider changed, we need to update the filter settings      if (ref_clock != this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX) or m != _max2118_write_regs.m_divider) update_filter_settings = true; @@ -344,12 +342,12 @@ void dbsrx::set_lo_freq(double target_freq){          //vtune is too low, try lower frequency vco          if (_max2118_read_regs.adc == 0){              if (_max2118_write_regs.osc_band == 0){ -                uhd::print_warning( +                uhd::warning::post(                      str(boost::format(                          "DBSRX: Tuning exceeded vco range, _max2118_write_regs.osc_band == %d\n"                           ) % int(_max2118_write_regs.osc_band))                  ); -                UHD_ASSERT_THROW(_max2118_read_regs.adc == 0); +                UHD_ASSERT_THROW(_max2118_read_regs.adc != 0); //just to cause a throw              }              if (_max2118_write_regs.osc_band <= 0) break;              _max2118_write_regs.osc_band -= 1; @@ -358,12 +356,12 @@ void dbsrx::set_lo_freq(double target_freq){          //vtune is too high, try higher frequency vco          if (_max2118_read_regs.adc == 7){              if (_max2118_write_regs.osc_band == 7){ -                uhd::print_warning( +                uhd::warning::post(                      str(boost::format(                          "DBSRX: Tuning exceeded vco range, _max2118_write_regs.osc_band == %d\n"                           ) % int(_max2118_write_regs.osc_band))                  ); -                UHD_ASSERT_THROW(_max2118_read_regs.adc == 0); +                UHD_ASSERT_THROW(_max2118_read_regs.adc != 7); //just to cause a throw              }              if (_max2118_write_regs.osc_band >= 7) break;              _max2118_write_regs.osc_band += 1; @@ -401,6 +399,7 @@ void dbsrx::set_lo_freq(double target_freq){          << boost::format("    Ref    Freq=%fMHz\n") % (ref_clock/1e6)          << boost::format("    Target Freq=%fMHz\n") % (target_freq/1e6)          << boost::format("    Actual Freq=%fMHz\n") % (_lo_freq/1e6) +        << boost::format("    VCO    Freq=%fMHz\n") % (vco_freq/1e6)          << std::endl;      if (update_filter_settings) set_bandwidth(_bandwidth); @@ -480,9 +479,9 @@ void dbsrx::set_gain(float gain, const std::string &name){  /***********************************************************************   * Bandwidth Handling   **********************************************************************/ -void dbsrx::set_bandwidth(float bandwidth){ +void dbsrx::set_bandwidth(double bandwidth){      //clip the input -    bandwidth = std::clip<float>(bandwidth, 4e6, 33e6); +    bandwidth = std::clip<double>(bandwidth, 4e6, 33e6);      double ref_clock = this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX); @@ -492,7 +491,7 @@ void dbsrx::set_bandwidth(float bandwidth){      _max2118_write_regs.f_dac = std::clip<int>(int((((bandwidth*_max2118_write_regs.m_divider)/ref_clock) - 4)/0.145),0,127);      //determine actual bandwidth -    _bandwidth = float((ref_clock/(_max2118_write_regs.m_divider))*(4+0.145*_max2118_write_regs.f_dac)); +    _bandwidth = double((ref_clock/(_max2118_write_regs.m_divider))*(4+0.145*_max2118_write_regs.f_dac));      if (dbsrx_debug) std::cerr << boost::format(          "DBSRX Filter Bandwidth: %f MHz, m: %d, f_dac: %d\n" @@ -551,6 +550,10 @@ void dbsrx::rx_get(const wax::obj &key_, wax::obj &val){          val = SUBDEV_CONN_COMPLEX_IQ;          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = false;          return; @@ -559,14 +562,8 @@ void dbsrx::rx_get(const wax::obj &key_, wax::obj &val){          val = this->get_locked();          return; -/* -    case SUBDEV_PROP_RSSI: -        val = this->get_rssi(); -        return; -*/ -      case SUBDEV_PROP_BANDWIDTH: -        val = _bandwidth; +        val = 2*_bandwidth; //_bandwidth is low-pass, we want complex double-sided          return;      default: UHD_THROW_PROP_GET_ERROR(); @@ -587,8 +584,11 @@ void dbsrx::rx_set(const wax::obj &key_, const wax::obj &val){          this->set_gain(val.as<float>(), key.name);          return; +    case SUBDEV_PROP_ENABLED: +        return; //always enabled +      case SUBDEV_PROP_BANDWIDTH: -        this->set_bandwidth(val.as<float>()); +        this->set_bandwidth(val.as<double>()/2.0); //complex double-sided, we want low-pass          return;      default: UHD_THROW_PROP_SET_ERROR(); diff --git a/host/lib/usrp/dboard/db_rfx.cpp b/host/lib/usrp/dboard/db_rfx.cpp index c3ab96e59..12e458d8c 100644 --- a/host/lib/usrp/dboard/db_rfx.cpp +++ b/host/lib/usrp/dboard/db_rfx.cpp @@ -43,6 +43,7 @@  #include <uhd/utils/assert.hpp>  #include <uhd/utils/static.hpp>  #include <uhd/utils/algorithm.hpp> +#include <uhd/utils/warning.hpp>  #include <uhd/usrp/dboard_id.hpp>  #include <uhd/usrp/dboard_base.hpp>  #include <uhd/usrp/dboard_manager.hpp> @@ -152,12 +153,12 @@ static dboard_base::sptr make_rfx_flex2400(dboard_base::ctor_args_t args){  }  UHD_STATIC_BLOCK(reg_rfx_dboards){ -    dboard_manager::register_dboard(0x0024, 0x0028, &make_rfx_flex400,  "Flex 400 MIMO B"); -    dboard_manager::register_dboard(0x0025, 0x0029, &make_rfx_flex900,  "Flex 900 MIMO B"); -    dboard_manager::register_dboard(0x0034, 0x0035, &make_rfx_flex1800, "Flex 1800 MIMO B"); -    dboard_manager::register_dboard(0x0026, 0x002a, &make_rfx_flex1200, "Flex 1200 MIMO B"); -    dboard_manager::register_dboard(0x002c, 0x002d, &make_rfx_flex2200, "Flex 2200 MIMO B"); -    dboard_manager::register_dboard(0x0027, 0x002b, &make_rfx_flex2400, "Flex 2400 MIMO B"); +    dboard_manager::register_dboard(0x0024, 0x0028, &make_rfx_flex400,  "RFX400"); +    dboard_manager::register_dboard(0x0025, 0x0029, &make_rfx_flex900,  "RFX900"); +    dboard_manager::register_dboard(0x0034, 0x0035, &make_rfx_flex1800, "RFX1800"); +    dboard_manager::register_dboard(0x0026, 0x002a, &make_rfx_flex1200, "RFX1200"); +    dboard_manager::register_dboard(0x002c, 0x002d, &make_rfx_flex2200, "RFX2200"); +    dboard_manager::register_dboard(0x0027, 0x002b, &make_rfx_flex2400, "RFX2400");  }  /*********************************************************************** @@ -444,6 +445,10 @@ void rfx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){          val = SUBDEV_CONN_COMPLEX_QI;          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = false;          return; @@ -452,6 +457,10 @@ void rfx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){          val = this->get_locked(dboard_iface::UNIT_RX);          return; +    case SUBDEV_PROP_BANDWIDTH: +        val = 2*20.0e6; //30MHz low-pass, we want complex double-sided +        return; +      default: UHD_THROW_PROP_GET_ERROR();      }  } @@ -474,6 +483,15 @@ void rfx_xcvr::rx_set(const wax::obj &key_, const wax::obj &val){          this->set_rx_ant(val.as<std::string>());          return; +    case SUBDEV_PROP_ENABLED: +        return; //always enabled + +    case SUBDEV_PROP_BANDWIDTH: +        uhd::warning::post( +            str(boost::format("RFX: No tunable bandwidth, fixed filtered to 40MHz")) +        ); +        return; +      default: UHD_THROW_PROP_SET_ERROR();      }  } @@ -524,6 +542,10 @@ void rfx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){          val = SUBDEV_CONN_COMPLEX_IQ;          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = true;          return; @@ -532,6 +554,10 @@ void rfx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){          val = this->get_locked(dboard_iface::UNIT_TX);          return; +    case SUBDEV_PROP_BANDWIDTH: +        val = 2*20.0e6; //30MHz low-pass, we want complex double-sided +        return; +      default: UHD_THROW_PROP_GET_ERROR();      }  } @@ -554,6 +580,15 @@ void rfx_xcvr::tx_set(const wax::obj &key_, const wax::obj &val){          this->set_tx_ant(val.as<std::string>());          return; +    case SUBDEV_PROP_ENABLED: +        return; //always enabled + +    case SUBDEV_PROP_BANDWIDTH: +        uhd::warning::post( +            str(boost::format("RFX: No tunable bandwidth, fixed filtered to 40MHz")) +        ); +        return; +      default: UHD_THROW_PROP_SET_ERROR();      }  } diff --git a/host/lib/usrp/dboard/db_tvrx.cpp b/host/lib/usrp/dboard/db_tvrx.cpp new file mode 100644 index 000000000..1f3c76556 --- /dev/null +++ b/host/lib/usrp/dboard/db_tvrx.cpp @@ -0,0 +1,495 @@ +// +// Copyright 2010 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/>. +// + +// No RX IO Pins Used + +// RX IO Functions + +//ADC/DAC functions: +//DAC 1: RF AGC +//DAC 2: IF AGC + +//min freq: 50e6 +//max freq: 860e6 +//gain range: [0:1dB:115dB] + +#include <uhd/utils/static.hpp> +#include <uhd/utils/assert.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/warning.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/usrp/subdev_props.hpp> +#include <uhd/usrp/dboard_base.hpp> +#include <uhd/usrp/dboard_manager.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/format.hpp> +#include <boost/thread.hpp> +#include <boost/array.hpp> +#include <boost/math/special_functions/round.hpp> +#include <utility> +#include <cmath> +#include <cfloat> +#include <limits> +#include <tuner_4937di5_regs.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + +/*********************************************************************** + * The tvrx constants + **********************************************************************/ +static const bool tvrx_debug = false; + +static const freq_range_t tvrx_freq_range(50e6, 860e6); + +static const prop_names_t tvrx_antennas = list_of("RX"); + +static const uhd::dict<std::string, freq_range_t> tvrx_freq_ranges = map_list_of +    ("VHFLO", freq_range_t(50e6, 158e6)) +    ("VHFHI", freq_range_t(158e6, 454e6)) +    ("UHF"  , freq_range_t(454e6, 860e6)) +; + +static const boost::array<double, 17> vhflo_gains_db = +    {{-6.00000, -6.00000, -6.00000, -4.00000, 0.00000, +     5.00000, 10.00000, 17.40000, 26.30000, 36.00000, +     43.00000, 48.00000, 49.50000, 50.10000, 50.30000, +     50.30000, 50.30000}}; + +static const boost::array<double, 17> vhfhi_gains_db = +    {{-13.3000,  -13.3000,  -13.3000,   -1.0000,    7.7000, +    11.0000,   14.7000,   19.3000,   26.1000,   36.0000, +    42.7000,   46.0000,   47.0000,   47.8000,   48.2000, +    48.2000,   48.2000}}; + +static const boost::array<double, 17> uhf_gains_db = +    {{-8.0000,   -8.0000,   -7.0000,    4.0000,   10.2000, +     14.5000,   17.5000,   20.0000,   24.5000,   30.8000, +     37.0000,   39.8000,   40.7000,   41.6000,   42.6000, +     43.2000,   43.8000}}; + +static const boost::array<double, 17> tvrx_if_gains_db = +    {{-1.50000,   -1.50000,   -1.50000,   -1.00000,    0.20000, +     2.10000,    4.30000,    6.40000,    9.00000,   12.00000, +     14.80000,   18.20000,   26.10000,   32.50000,  32.50000, +     32.50000,   32.50000}}; + +//gain linearization data +//this is from the datasheet and is dB vs. volts (below) +//i tried to curve fit this, but it's really just so nonlinear that you'd +//need dang near as many coefficients as to just map it like this and interp. +//these numbers are culled from the 4937DI5 datasheet and are probably totally inaccurate +//but if it's better than the old linear fit i'm happy +static const uhd::dict<std::string, boost::array<double, 17> > tvrx_rf_gains_db = map_list_of +    ("VHFLO", vhflo_gains_db) +    ("VHFHI", vhfhi_gains_db) +    ("UHF"  , uhf_gains_db) +; + +//sample voltages for the above points +static const boost::array<double, 17> tvrx_gains_volts = +    {{0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0}}; + +static uhd::dict<std::string, gain_range_t> get_tvrx_gain_ranges(void) { +    double rfmax = 0.0, rfmin = FLT_MAX; +    BOOST_FOREACH(const std::string range, tvrx_rf_gains_db.keys()) { +        double my_max = tvrx_rf_gains_db[range].back(); //we're assuming it's monotonic +        double my_min = tvrx_rf_gains_db[range].front(); //if it's not this is wrong wrong wrong +        if(my_max > rfmax) rfmax = my_max; +        if(my_min < rfmin) rfmin = my_min; +    } + +    double ifmin = tvrx_if_gains_db.front(); +    double ifmax = tvrx_if_gains_db.back(); + +    return map_list_of +        ("RF", gain_range_t(float(rfmin), float(rfmax), float((rfmax-rfmin)/4096.0))) +        ("IF", gain_range_t(float(ifmin), float(ifmax), float((ifmax-ifmin)/4096.0))) +    ; +} + +static const double opamp_gain = 1.22; //onboard DAC opamp gain +static const double tvrx_if_freq = 43.75e6; //IF freq of TVRX module +static const boost::uint16_t reference_divider = 640; //clock reference divider to use +static const double reference_freq = 4.0e6; + +/*********************************************************************** + * The tvrx dboard class + **********************************************************************/ +class tvrx : public rx_dboard_base{ +public: +    tvrx(ctor_args_t args); +    ~tvrx(void); + +    void rx_get(const wax::obj &key, wax::obj &val); +    void rx_set(const wax::obj &key, const wax::obj &val); + +private: +    uhd::dict<std::string, float> _gains; +    double _lo_freq; +    tuner_4937di5_regs_t _tuner_4937di5_regs; +    boost::uint8_t _tuner_4937di5_addr(void){ +        return (this->get_iface()->get_special_props().mangle_i2c_addrs)? 0x61 : 0x60; //ok really? we could rename that call +    }; + +    void set_gain(float gain, const std::string &name); +    void set_freq(double freq); + +    void update_regs(void){ +        byte_vector_t regs_vector(4); + +        //get the register data +        for(int i=0; i<4; i++){ +            regs_vector[i] = _tuner_4937di5_regs.get_reg(i); +            if(tvrx_debug) std::cerr << boost::format( +                "tvrx: send reg 0x%02x, value 0x%04x" +            ) % int(i) % int(regs_vector[i]) << std::endl; +        } + +        //send the data +        this->get_iface()->write_i2c( +            _tuner_4937di5_addr(), regs_vector +        ); +    } + +}; + +/*********************************************************************** + * Register the tvrx dboard + **********************************************************************/ +static dboard_base::sptr make_tvrx(dboard_base::ctor_args_t args){ +    return dboard_base::sptr(new tvrx(args)); +} + +UHD_STATIC_BLOCK(reg_tvrx_dboard){ +    //register the factory function for the rx dbid +    dboard_manager::register_dboard(0x0040, &make_tvrx, "TVRX"); +} + +/*********************************************************************** + * Structors + **********************************************************************/ +tvrx::tvrx(ctor_args_t args) : rx_dboard_base(args){ +    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); + +    //set the gpio directions and atr controls (identically) +    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0x0); // All unused in atr +    if (this->get_iface()->get_special_props().soft_clock_divider){ +        this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x1); // GPIO0 is clock +    } +    else{ +        this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x0); // All Inputs +    } + +    //send initial register settings if necessary + +    //set default freq +    _lo_freq = tvrx_freq_range.min + tvrx_if_freq; //init _lo_freq to a sane default +    set_freq(tvrx_freq_range.min); + +    //set default gains +    BOOST_FOREACH(const std::string &name, get_tvrx_gain_ranges().keys()){ +        set_gain(get_tvrx_gain_ranges()[name].min, name); +    } +} + +tvrx::~tvrx(void){ +} + +/*! Return a string corresponding to the relevant band + * \param freq the frequency of interest + * \return a string corresponding to the band + */ + +static std::string get_band(double freq) { +    BOOST_FOREACH(const std::string &band, tvrx_freq_ranges.keys()) { +        if(freq >= tvrx_freq_ranges[band].min && freq <= tvrx_freq_ranges[band].max){ +            if(tvrx_debug) std::cout << "Band: " << band << std::endl; +            return band; +        } +    } +    UHD_THROW_INVALID_CODE_PATH(); +} + +/*********************************************************************** + * Gain Handling + **********************************************************************/ +/*! + * Execute a linear interpolation to find the voltage corresponding to a desired gain + * \param gain the desired gain in dB + * \param db_vector the vector of dB readings + * \param volts_vector the corresponding vector of voltages db_vector was sampled at + * \return a voltage to feed the TVRX analog gain + */ + +static double gain_interp(double gain, boost::array<double, 17> db_vector, boost::array<double, 17> volts_vector) { +    double volts; +    gain = std::clip<double>(gain, db_vector.front(), db_vector.back()); //let's not get carried away here + +    boost::uint8_t gain_step = 0; +    //find which bin we're in +    for(size_t i = 0; i < db_vector.size()-1; i++) { +        if(gain >= db_vector[i] && gain <= db_vector[i+1]) gain_step = i; +    } + +    //find the current slope for linear interpolation +    double slope = (volts_vector[gain_step + 1] - volts_vector[gain_step]) +                / (db_vector[gain_step + 1] - db_vector[gain_step]); + +    //the problem here is that for gains approaching the maximum, the voltage slope becomes infinite +    //i.e., a small change in gain requires an infinite change in voltage +    //to cope, we limit the slope + +    if(slope == std::numeric_limits<double>::infinity()) +        return volts_vector[gain_step]; + +    //use the volts per dB slope to find the final interpolated voltage +    volts = volts_vector[gain_step] + (slope * (gain - db_vector[gain_step])); + +    if(tvrx_debug) +        std::cout << "Gain interp: gain: " << gain << ", gain_step: " << int(gain_step) << ", slope: " << slope << ", volts: " << volts << std::endl; + +    return volts; +} + +/*! + * Convert a requested gain for the RF gain into a DAC voltage. + * The gain passed into the function will be set to the actual value. + * \param gain the requested gain in dB + * \return dac voltage value + */ + +static float rf_gain_to_voltage(float gain, double lo_freq){ +    //clip the input +    gain = std::clip<float>(gain, get_tvrx_gain_ranges()["RF"].min, get_tvrx_gain_ranges()["RF"].max); + +    //first we need to find out what band we're in, because gains are different across different bands +    std::string band = get_band(lo_freq + tvrx_if_freq); + +    //this is the voltage at the TVRX gain input +    double gain_volts = gain_interp(gain, tvrx_rf_gains_db[band], tvrx_gains_volts); +    //this is the voltage at the USRP DAC output +    double dac_volts = gain_volts / opamp_gain; + +    dac_volts = std::clip<double>(dac_volts, 0.0, 3.3); + +    if (tvrx_debug) std::cerr << boost::format( +        "tvrx RF AGC gain: %f dB, dac_volts: %f V" +    ) % gain % dac_volts << std::endl; + +    return float(dac_volts); +} + +/*! + * Convert a requested gain for the IF gain into a DAC voltage. + * The gain passed into the function will be set to the actual value. + * \param gain the requested gain in dB + * \return dac voltage value + */ + +static float if_gain_to_voltage(float gain){ +    //clip the input +    gain = std::clip<float>(gain, get_tvrx_gain_ranges()["IF"].min, get_tvrx_gain_ranges()["IF"].max); + +    double gain_volts = gain_interp(gain, tvrx_if_gains_db, tvrx_gains_volts); +    double dac_volts = gain_volts / opamp_gain; + +    dac_volts = std::clip<double>(dac_volts, 0.0, 3.3); + +    if (tvrx_debug) std::cerr << boost::format( +        "tvrx IF AGC gain: %f dB, dac_volts: %f V" +    ) % gain % dac_volts << std::endl; + +    return float(dac_volts); +} + +void tvrx::set_gain(float gain, const std::string &name){ +    assert_has(get_tvrx_gain_ranges().keys(), name, "tvrx gain name"); +    if (name == "RF"){ +        this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_B, rf_gain_to_voltage(gain, _lo_freq)); +    } +    else if(name == "IF"){ +        this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_A, if_gain_to_voltage(gain)); +    } +    else UHD_THROW_INVALID_CODE_PATH(); +    _gains[name] = gain; +} + +/*! + * Set the tuner to center the desired frequency at 43.75MHz + * \param freq the requested frequency + */ + +void tvrx::set_freq(double freq) { +    freq = std::clip<double>(freq, tvrx_freq_range.min, tvrx_freq_range.max); +    std::string prev_band = get_band(_lo_freq - tvrx_if_freq); +    std::string new_band = get_band(freq); + +    double target_lo_freq = freq + tvrx_if_freq; //the desired LO freq for high-side mixing +    double f_ref = reference_freq / double(reference_divider); //your tuning step size + +    int divisor = int((target_lo_freq + (f_ref * 4.0)) / (f_ref * 8)); //the divisor we'll use +    double actual_lo_freq = (f_ref * 8 * divisor); //the LO freq we'll actually get + +    if((divisor & ~0x7fff)) UHD_THROW_INVALID_CODE_PATH(); + +    //now we update the registers +    _tuner_4937di5_regs.db1 = (divisor >> 8) & 0xff; +    _tuner_4937di5_regs.db2 = divisor & 0xff; + +    if(new_band == "VHFLO") _tuner_4937di5_regs.bandsel = tuner_4937di5_regs_t::BANDSEL_VHFLO; +    else if(new_band == "VHFHI") _tuner_4937di5_regs.bandsel = tuner_4937di5_regs_t::BANDSEL_VHFHI; +    else if(new_band == "UHF") _tuner_4937di5_regs.bandsel = tuner_4937di5_regs_t::BANDSEL_UHF; +    else UHD_THROW_INVALID_CODE_PATH(); + +    _tuner_4937di5_regs.power = tuner_4937di5_regs_t::POWER_OFF; +    update_regs(); + +    //ok don't forget to reset RF gain here if the new band != the old band +    //we do this because the gains are different for different band settings +    //not FAR off, but we do this to be consistent +    if(prev_band != new_band) set_gain(_gains["RF"], "RF"); + +    if(tvrx_debug) +        std::cout << boost::format("set_freq: target LO: %f f_ref: %f divisor: %i actual LO: %f") % target_lo_freq % f_ref % divisor % actual_lo_freq << std::endl; + +    _lo_freq = actual_lo_freq; //for rx props +} + +/*********************************************************************** + * Get the alias frequency of frequency freq when sampled at fs. + * \param freq the frequency of interest + * \param fs the sample rate + * \return the alias frequency + **********************************************************************/ + +static double get_alias(double freq, double fs) { +    double alias; +    freq = fmod(freq, fs); +    if(freq >= (fs/2)) { +        alias = fs - freq; +    } else { +        alias = freq; +    } +    return alias; +} + +/*********************************************************************** + * RX Get and Set + **********************************************************************/ +void tvrx::rx_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); +    double codec_rate; + +    //handle the get request conditioned on the key +    switch(key.as<subdev_prop_t>()){ +    case SUBDEV_PROP_NAME: +        val = get_rx_id().to_pp_string(); +        return; + +    case SUBDEV_PROP_OTHERS: +        val = prop_names_t(); //empty +        return; + +    case SUBDEV_PROP_GAIN: +        assert_has(_gains.keys(), key.name, "tvrx gain name"); +        val = _gains[key.name]; +        return; + +    case SUBDEV_PROP_GAIN_RANGE: +        assert_has(get_tvrx_gain_ranges().keys(), key.name, "tvrx gain name"); +        val = get_tvrx_gain_ranges()[key.name]; +        return; + +    case SUBDEV_PROP_GAIN_NAMES: +        val = prop_names_t(get_tvrx_gain_ranges().keys()); +        return; + +    case SUBDEV_PROP_FREQ: +    /* +     * so here we have to do some magic. because the TVRX uses a relatively high IF, +     * we have to watch the sample rate to see if the IF will be aliased +     * or if it will fall within Nyquist. +     */ +        codec_rate = this->get_iface()->get_codec_rate(dboard_iface::UNIT_RX); +        val = (_lo_freq - tvrx_if_freq) + get_alias(tvrx_if_freq, codec_rate); +        return; + +    case SUBDEV_PROP_FREQ_RANGE: +        val = tvrx_freq_range; +        return; + +    case SUBDEV_PROP_ANTENNA: +        val = tvrx_antennas.front(); //there's only one +        return; + +    case SUBDEV_PROP_ANTENNA_NAMES: +        val = tvrx_antennas; +        return; + +    case SUBDEV_PROP_CONNECTION: +        val = SUBDEV_CONN_COMPLEX_IQ; +        return; + +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; + +    case SUBDEV_PROP_USE_LO_OFFSET: +        val = false; +        return; + +    case SUBDEV_PROP_LO_LOCKED: +        val = true; +        return; + +    case SUBDEV_PROP_BANDWIDTH: +        val = 6.0e6; //30MHz low-pass, we want complex double-sided +        return; + +    default: UHD_THROW_PROP_GET_ERROR(); +    } +} + +void tvrx::rx_set(const wax::obj &key_, const wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); + +    //handle the get request conditioned on the key +    switch(key.as<subdev_prop_t>()){ +    case SUBDEV_PROP_GAIN: +        this->set_gain(val.as<float>(), key.name); +        return; + +    case SUBDEV_PROP_FREQ: +        this->set_freq(val.as<double>()); +        return; + +    case SUBDEV_PROP_ENABLED: +        return; //always enabled + +    case SUBDEV_PROP_BANDWIDTH: +        uhd::warning::post( +            str(boost::format("TVRX: No tunable bandwidth, fixed filtered to 6MHz")) +        ); +        return; + +    default: UHD_THROW_PROP_SET_ERROR(); +    } +} + diff --git a/host/lib/usrp/dboard/db_unknown.cpp b/host/lib/usrp/dboard/db_unknown.cpp index f6f4f4a61..a342471c4 100644 --- a/host/lib/usrp/dboard/db_unknown.cpp +++ b/host/lib/usrp/dboard/db_unknown.cpp @@ -19,6 +19,7 @@  #include <uhd/types/ranges.hpp>  #include <uhd/utils/assert.hpp>  #include <uhd/utils/static.hpp> +#include <uhd/utils/warning.hpp>  #include <uhd/usrp/dboard_base.hpp>  #include <uhd/usrp/dboard_manager.hpp>  #include <boost/assign/list_of.hpp> @@ -122,6 +123,10 @@ void unknown_rx::rx_get(const wax::obj &key_, wax::obj &val){          val = SUBDEV_CONN_COMPLEX_IQ;          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = false;          return; @@ -130,6 +135,10 @@ void unknown_rx::rx_get(const wax::obj &key_, wax::obj &val){          val = true; //there is no LO, so it must be true!          return; +    case SUBDEV_PROP_BANDWIDTH: +        val = 0.0; +        return; +      default: UHD_THROW_PROP_GET_ERROR();      }  } @@ -151,12 +160,21 @@ void unknown_rx::rx_set(const wax::obj &key_, const wax::obj &val){      case SUBDEV_PROP_FREQ:          return; // it wont do you much good, but you can set it +    case SUBDEV_PROP_ENABLED: +        return; //always enabled + +    case SUBDEV_PROP_BANDWIDTH: +        uhd::warning::post( +            str(boost::format("Unknown Daughterboard: No tunable bandwidth, fixed filtered to 0.0MHz")) +        ); +        return; +      default: UHD_THROW_PROP_SET_ERROR();      }  }  /*********************************************************************** - * Basic and LF TX dboard + * Unknown TX dboard   **********************************************************************/  unknown_tx::unknown_tx(ctor_args_t args) : tx_dboard_base(args){      /* NOP */ @@ -211,6 +229,10 @@ void unknown_tx::tx_get(const wax::obj &key_, wax::obj &val){          val = SUBDEV_CONN_COMPLEX_IQ;          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = false;          return; @@ -219,6 +241,10 @@ void unknown_tx::tx_get(const wax::obj &key_, wax::obj &val){          val = true; //there is no LO, so it must be true!          return; +    case SUBDEV_PROP_BANDWIDTH: +        val = 0.0; +        return; +      default: UHD_THROW_PROP_GET_ERROR();      }  } @@ -240,6 +266,15 @@ void unknown_tx::tx_set(const wax::obj &key_, const wax::obj &val){      case SUBDEV_PROP_FREQ:          return; // it wont do you much good, but you can set it +    case SUBDEV_PROP_ENABLED: +        return; //always enabled + +    case SUBDEV_PROP_BANDWIDTH: +        uhd::warning::post( +            str(boost::format("Unknown Daughterboard: No tunable bandwidth, fixed filtered to 0.0MHz")) +        ); +        return; +      default: UHD_THROW_PROP_SET_ERROR();      }  } diff --git a/host/lib/usrp/dboard/db_wbx.cpp b/host/lib/usrp/dboard/db_wbx.cpp index ccd897674..647f1b975 100644 --- a/host/lib/usrp/dboard/db_wbx.cpp +++ b/host/lib/usrp/dboard/db_wbx.cpp @@ -71,6 +71,7 @@  #include <uhd/utils/assert.hpp>  #include <uhd/utils/static.hpp>  #include <uhd/utils/algorithm.hpp> +#include <uhd/utils/warning.hpp>  #include <uhd/usrp/dboard_base.hpp>  #include <uhd/usrp/dboard_manager.hpp>  #include <boost/assign/list_of.hpp> @@ -154,7 +155,7 @@ static dboard_base::sptr make_wbx(dboard_base::ctor_args_t args){  }  UHD_STATIC_BLOCK(reg_wbx_dboards){ -    dboard_manager::register_dboard(0x0052, 0x0053, &make_wbx, "WBX"); +    dboard_manager::register_dboard(0x0053, 0x0052, &make_wbx, "WBX");  }  /*********************************************************************** @@ -513,6 +514,10 @@ void wbx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){          val = SUBDEV_CONN_COMPLEX_IQ;          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = false;          return; @@ -521,6 +526,10 @@ void wbx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){          val = this->get_locked(dboard_iface::UNIT_RX);          return; +    case SUBDEV_PROP_BANDWIDTH: +        val = 2*30.0e6; //20MHz low-pass, we want complex double-sided +        return; +      default: UHD_THROW_PROP_GET_ERROR();      }  } @@ -543,6 +552,15 @@ void wbx_xcvr::rx_set(const wax::obj &key_, const wax::obj &val){          this->set_rx_ant(val.as<std::string>());          return; +    case SUBDEV_PROP_ENABLED: +        return; //always enabled + +    case SUBDEV_PROP_BANDWIDTH: +        uhd::warning::post( +            str(boost::format("WBX: No tunable bandwidth, fixed filtered to 40MHz")) +        ); +        return; +      default: UHD_THROW_PROP_SET_ERROR();      }  } @@ -597,6 +615,10 @@ void wbx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){          val = SUBDEV_CONN_COMPLEX_IQ;          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = false;          return; @@ -605,6 +627,10 @@ void wbx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){          val = this->get_locked(dboard_iface::UNIT_TX);          return; +    case SUBDEV_PROP_BANDWIDTH: +        val = 2*30.0e6; //20MHz low-pass, we want complex double-sided +        return; +      default: UHD_THROW_PROP_GET_ERROR();      }  } @@ -627,6 +653,15 @@ void wbx_xcvr::tx_set(const wax::obj &key_, const wax::obj &val){          this->set_tx_ant(val.as<std::string>());          return; +    case SUBDEV_PROP_ENABLED: +        return; //always enabled + +    case SUBDEV_PROP_BANDWIDTH: +        uhd::warning::post( +            str(boost::format("WBX: No tunable bandwidth, fixed filtered to 40MHz")) +        ); +        return; +      default: UHD_THROW_PROP_SET_ERROR();      }  } diff --git a/host/lib/usrp/dboard/db_xcvr2450.cpp b/host/lib/usrp/dboard/db_xcvr2450.cpp index 798ff74a3..be0e42b92 100644 --- a/host/lib/usrp/dboard/db_xcvr2450.cpp +++ b/host/lib/usrp/dboard/db_xcvr2450.cpp @@ -51,6 +51,7 @@  #include <uhd/utils/static.hpp>  #include <uhd/utils/assert.hpp>  #include <uhd/utils/algorithm.hpp> +#include <uhd/utils/warning.hpp>  #include <uhd/types/ranges.hpp>  #include <uhd/types/dict.hpp>  #include <uhd/usrp/subdev_props.hpp> @@ -72,6 +73,7 @@ using namespace boost::assign;  static const bool xcvr2450_debug = false;  static const freq_range_t xcvr_freq_range(2.4e9, 6.0e9); +static const freq_range_t xcvr_freq_band_seperation(2.5e9, 4.9e9);  static const prop_names_t xcvr_antennas = list_of("J1")("J2"); @@ -100,6 +102,7 @@ public:  private:      double _lo_freq; +    double _rx_bandwidth, _tx_bandwidth;      uhd::dict<std::string, float> _tx_gains, _rx_gains;      std::string _tx_ant, _rx_ant;      int _ad9515div; @@ -110,6 +113,8 @@ private:      void set_rx_ant(const std::string &ant);      void set_tx_gain(float gain, const std::string &name);      void set_rx_gain(float gain, const std::string &name); +    void set_rx_bandwidth(double bandwidth); +    void set_tx_bandwidth(double bandwidth);      void update_atr(void);      void spi_reset(void); @@ -176,6 +181,9 @@ xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){      spi_reset(); //prepare the spi +    _rx_bandwidth = 9.5e6; +    _tx_bandwidth = 12.0e6; +      //setup the misc max2829 registers      _max2829_regs.mimo_select         = max2829_regs_t::MIMO_SELECT_MIMO;      _max2829_regs.band_sel_mimo       = max2829_regs_t::BAND_SEL_MIMO_MIMO; @@ -183,7 +191,7 @@ xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){      _max2829_regs.rssi_high_bw        = max2829_regs_t::RSSI_HIGH_BW_6MHZ;      _max2829_regs.tx_lpf_coarse_adj   = max2829_regs_t::TX_LPF_COARSE_ADJ_12MHZ;      _max2829_regs.rx_lpf_coarse_adj   = max2829_regs_t::RX_LPF_COARSE_ADJ_9_5MHZ; -    _max2829_regs.rx_lpf_fine_adj     = max2829_regs_t::RX_LPF_FINE_ADJ_95; +    _max2829_regs.rx_lpf_fine_adj     = max2829_regs_t::RX_LPF_FINE_ADJ_100;      _max2829_regs.rx_vga_gain_spi     = max2829_regs_t::RX_VGA_GAIN_SPI_SPI;      _max2829_regs.rssi_output_range   = max2829_regs_t::RSSI_OUTPUT_RANGE_HIGH;      _max2829_regs.rssi_op_mode        = max2829_regs_t::RSSI_OP_MODE_ENABLED; @@ -244,15 +252,24 @@ void xcvr2450::update_atr(void){      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE,        POWER_UP_RXIO | RX_DIS_RXIO);      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY,     POWER_UP_RXIO | RX_ENB_RXIO);      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     POWER_UP_RXIO | RX_DIS_RXIO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, POWER_UP_RXIO | RX_ENB_RXIO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, POWER_UP_RXIO | RX_DIS_RXIO);  }  /***********************************************************************   * Tuning   **********************************************************************/  void xcvr2450::set_lo_freq(double target_freq){ +    //clip for highband and lowband +    if((target_freq > xcvr_freq_band_seperation.min) and (target_freq < xcvr_freq_band_seperation.max)){ +        if(target_freq - xcvr_freq_band_seperation.min < xcvr_freq_band_seperation.max - target_freq){ +            target_freq = xcvr_freq_band_seperation.min; +        }else{ +            target_freq = xcvr_freq_band_seperation.max; +        } +    } + +    //clip for max and min      target_freq = std::clip(target_freq, xcvr_freq_range.min, xcvr_freq_range.max); -    //TODO: clip for highband and lowband      //variables used in the calculation below      double scaler = xcvr2450::is_highband(target_freq)? (4.0/5.0) : (4.0/3.0); @@ -434,6 +451,114 @@ void xcvr2450::set_rx_gain(float gain, const std::string &name){      _rx_gains[name] = gain;  } + +/*********************************************************************** + * Bandwidth Handling + **********************************************************************/ +static max2829_regs_t::tx_lpf_coarse_adj_t bandwidth_to_tx_lpf_coarse_reg(double &bandwidth){ +    int reg = std::clip(boost::math::iround((bandwidth-6.0e6)/6.0e6), 1, 3); + +    switch(reg){ +    case 1: // bandwidth < 15MHz +        bandwidth = 12e6; +        return max2829_regs_t::TX_LPF_COARSE_ADJ_12MHZ; +    case 2: // 15MHz < bandwidth < 21MHz +        bandwidth = 18e6; +        return max2829_regs_t::TX_LPF_COARSE_ADJ_18MHZ; +    case 3: // bandwidth > 21MHz +        bandwidth = 24e6; +        return max2829_regs_t::TX_LPF_COARSE_ADJ_24MHZ; +    } +    UHD_THROW_INVALID_CODE_PATH(); +} + +static max2829_regs_t::rx_lpf_fine_adj_t bandwidth_to_rx_lpf_fine_reg(double &bandwidth, double requested_bandwidth){ +    int reg = std::clip(boost::math::iround((requested_bandwidth/bandwidth)/0.05), 18, 22); + +    switch(reg){ +    case 18: // requested_bandwidth < 92.5% +        bandwidth = 0.9 * bandwidth; +        return max2829_regs_t::RX_LPF_FINE_ADJ_90; +    case 19: // 92.5% < requested_bandwidth < 97.5% +        bandwidth = 0.95 * bandwidth; +        return max2829_regs_t::RX_LPF_FINE_ADJ_95; +    case 20: // 97.5% < requested_bandwidth < 102.5% +        bandwidth = 1.0 * bandwidth; +        return max2829_regs_t::RX_LPF_FINE_ADJ_100; +    case 21: // 102.5% < requested_bandwidth < 107.5% +        bandwidth = 1.05 * bandwidth; +        return max2829_regs_t::RX_LPF_FINE_ADJ_105; +    case 22: // 107.5% < requested_bandwidth +        bandwidth = 1.1 * bandwidth; +        return max2829_regs_t::RX_LPF_FINE_ADJ_110; +    } +    UHD_THROW_INVALID_CODE_PATH(); +} + +static max2829_regs_t::rx_lpf_coarse_adj_t bandwidth_to_rx_lpf_coarse_reg(double &bandwidth){ +    int reg = std::clip(boost::math::iround((bandwidth-7.0e6)/1.0e6), 0, 11); + +    switch(reg){ +    case 0: // bandwidth < 7.5MHz +    case 1: // 7.5MHz < bandwidth < 8.5MHz +        bandwidth = 7.5e6; +        return max2829_regs_t::RX_LPF_COARSE_ADJ_7_5MHZ; +    case 2: // 8.5MHz < bandwidth < 9.5MHz +    case 3: // 9.5MHz < bandwidth < 10.5MHz +    case 4: // 10.5MHz < bandwidth < 11.5MHz +        bandwidth = 9.5e6; +        return max2829_regs_t::RX_LPF_COARSE_ADJ_9_5MHZ; +    case 5: // 11.5MHz < bandwidth < 12.5MHz +    case 6: // 12.5MHz < bandwidth < 13.5MHz +    case 7: // 13.5MHz < bandwidth < 14.5MHz +    case 8: // 14.5MHz < bandwidth < 15.5MHz +        bandwidth = 14e6; +        return max2829_regs_t::RX_LPF_COARSE_ADJ_14MHZ; +    case 9: // 15.5MHz < bandwidth < 16.5MHz +    case 10: // 16.5MHz < bandwidth < 17.5MHz +    case 11: // 17.5MHz < bandwidth +        bandwidth = 18e6; +        return max2829_regs_t::RX_LPF_COARSE_ADJ_18MHZ; +    } +    UHD_THROW_INVALID_CODE_PATH(); +} + +void xcvr2450::set_rx_bandwidth(double bandwidth){ +    double requested_bandwidth = bandwidth; + +    //compute coarse low pass cutoff frequency setting +    _max2829_regs.rx_lpf_coarse_adj = bandwidth_to_rx_lpf_coarse_reg(bandwidth); + +    //compute fine low pass cutoff frequency setting +    _max2829_regs.rx_lpf_fine_adj = bandwidth_to_rx_lpf_fine_reg(bandwidth, requested_bandwidth); + +    //shadow bandwidth setting +    _rx_bandwidth = bandwidth; + +    //update register +    send_reg(0x7); + +    if (xcvr2450_debug) std::cerr << boost::format( +        "XCVR2450 RX Bandwidth (lp_fc): %f Hz, coarse reg: %d, fine reg: %d" +    ) % _rx_bandwidth % (int(_max2829_regs.rx_lpf_coarse_adj)) % (int(_max2829_regs.rx_lpf_fine_adj)) << std::endl; +} + +void xcvr2450::set_tx_bandwidth(double bandwidth){ +    //compute coarse low pass cutoff frequency setting +    _max2829_regs.tx_lpf_coarse_adj = bandwidth_to_tx_lpf_coarse_reg(bandwidth); + +    //shadow bandwidth setting +    _tx_bandwidth = bandwidth; + +    //update register +    send_reg(0x7); + +    if (xcvr2450_debug) std::cerr << boost::format( +        "XCVR2450 TX Bandwidth (lp_fc): %f Hz, coarse reg: %d" +    ) % _tx_bandwidth % (int(_max2829_regs.tx_lpf_coarse_adj)) << std::endl; +} + +  /***********************************************************************   * RX Get and Set   **********************************************************************/ @@ -484,6 +609,10 @@ void xcvr2450::rx_get(const wax::obj &key_, wax::obj &val){          val = SUBDEV_CONN_COMPLEX_IQ;          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = false;          return; @@ -496,6 +625,10 @@ void xcvr2450::rx_get(const wax::obj &key_, wax::obj &val){          val = this->get_rssi();          return; +    case SUBDEV_PROP_BANDWIDTH: +        val = 2*_rx_bandwidth; //_tx_bandwidth is low-pass, we want complex double-sided +        return; +      default: UHD_THROW_PROP_GET_ERROR();      }  } @@ -518,6 +651,13 @@ void xcvr2450::rx_set(const wax::obj &key_, const wax::obj &val){          this->set_rx_ant(val.as<std::string>());          return; +    case SUBDEV_PROP_BANDWIDTH: +        this->set_rx_bandwidth(val.as<double>()/2.0); //complex double-sided, we want low-pass +        return; + +    case SUBDEV_PROP_ENABLED: +        return; //always enabled +      default: UHD_THROW_PROP_SET_ERROR();      }  } @@ -572,6 +712,10 @@ void xcvr2450::tx_get(const wax::obj &key_, wax::obj &val){          val = SUBDEV_CONN_COMPLEX_QI;          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = false;          return; @@ -580,6 +724,10 @@ void xcvr2450::tx_get(const wax::obj &key_, wax::obj &val){          val = this->get_locked();          return; +    case SUBDEV_PROP_BANDWIDTH: +        val = 2*_tx_bandwidth; //_tx_bandwidth is low-pass, we want complex double-sided +        return; +      default: UHD_THROW_PROP_GET_ERROR();      }  } @@ -598,10 +746,17 @@ void xcvr2450::tx_set(const wax::obj &key_, const wax::obj &val){          this->set_tx_gain(val.as<float>(), key.name);          return; +    case SUBDEV_PROP_BANDWIDTH: +        this->set_tx_bandwidth(val.as<double>()/2.0); //complex double-sided, we want low-pass +        return; +      case SUBDEV_PROP_ANTENNA:          this->set_tx_ant(val.as<std::string>());          return; +    case SUBDEV_PROP_ENABLED: +        return; //always enabled +      default: UHD_THROW_PROP_SET_ERROR();      }  } diff --git a/host/lib/usrp/dboard_manager.cpp b/host/lib/usrp/dboard_manager.cpp index ab80875f5..5a98bb8eb 100644 --- a/host/lib/usrp/dboard_manager.cpp +++ b/host/lib/usrp/dboard_manager.cpp @@ -18,6 +18,7 @@  #include "dboard_ctor_args.hpp"  #include <uhd/usrp/dboard_manager.hpp>  #include <uhd/usrp/subdev_props.hpp> +#include <uhd/utils/warning.hpp>  #include <uhd/utils/static.hpp>  #include <uhd/utils/assert.hpp>  #include <uhd/types/dict.hpp> @@ -68,24 +69,25 @@ void dboard_manager::register_dboard(      const prop_names_t &subdev_names  ){      //regular registration for ids -    register_dboard(rx_dboard_id, dboard_ctor, name + " RX", subdev_names); -    register_dboard(tx_dboard_id, dboard_ctor, name + " TX", subdev_names); +    register_dboard(rx_dboard_id, dboard_ctor, name, subdev_names); +    register_dboard(tx_dboard_id, dboard_ctor, name, subdev_names);      //register xcvr mapping for ids      get_xcvr_id_to_id_map()[rx_dboard_id] = tx_dboard_id;      get_xcvr_id_to_id_map()[tx_dboard_id] = rx_dboard_id;  } +std::string dboard_id_t::to_cname(void) const{ +    if (not get_id_to_args_map().has_key(*this)) return "Unknown"; +    return get_id_to_args_map()[*this].get<1>(); +} +  std::string dboard_id_t::to_pp_string(void) const{ -    std::string name = "unknown"; -    if (get_id_to_args_map().has_key(*this)){ -        name = get_id_to_args_map()[*this].get<1>(); -    } -    return str(boost::format("%s (%s)") % name % this->to_string()); +    return str(boost::format("%s (%s)") % this->to_cname() % this->to_string());  }  /*********************************************************************** - * internal helper classe + * internal helper classes   **********************************************************************/  /*!   * A special wax proxy object that forwards calls to a subdev. @@ -176,10 +178,6 @@ static args_t get_dboard_args(  ){      //special case, the none id was provided, use the following ids      if (dboard_id == dboard_id_t::none() or force_to_unknown){ -        std::cerr << boost::format( -            "Warning: unknown dboard-id or dboard-id combination: %s\n" -            "    -> defaulting to the unknown board type" -        ) % dboard_id.to_pp_string() << std::endl;          UHD_ASSERT_THROW(get_id_to_args_map().has_key(0xfff1));          UHD_ASSERT_THROW(get_id_to_args_map().has_key(0xfff0));          switch(unit){ @@ -191,6 +189,9 @@ static args_t get_dboard_args(      //verify that there is a registered constructor for this id      if (not get_id_to_args_map().has_key(dboard_id)){ +        uhd::warning::post(str(boost::format( +            "Unknown dboard ID: %s.\n" +        ) % dboard_id.to_pp_string()));          return get_dboard_args(unit, dboard_id, true);      } @@ -214,12 +215,25 @@ dboard_manager_impl::dboard_manager_impl(          (get_xcvr_id_to_id_map()[tx_dboard_id] == rx_dboard_id)      ); +    //warn for invalid dboard id xcvr combinations +    if (rx_dboard_is_xcvr != this_dboard_is_xcvr or tx_dboard_is_xcvr != this_dboard_is_xcvr){ +        uhd::warning::post(str(boost::format( +            "Unknown transceiver board ID combination...\n" +            "RX dboard ID: %s\n" +            "TX dboard ID: %s\n" +        ) % rx_dboard_id.to_pp_string() % tx_dboard_id.to_pp_string())); +    } +      //extract dboard constructor and settings (force to unknown for messed up xcvr status)      dboard_ctor_t rx_dboard_ctor; std::string rx_name; prop_names_t rx_subdevs; -    boost::tie(rx_dboard_ctor, rx_name, rx_subdevs) = get_dboard_args(dboard_iface::UNIT_RX, rx_dboard_id, rx_dboard_is_xcvr != this_dboard_is_xcvr); +    boost::tie(rx_dboard_ctor, rx_name, rx_subdevs) = get_dboard_args( +        dboard_iface::UNIT_RX, rx_dboard_id, rx_dboard_is_xcvr != this_dboard_is_xcvr +    );      dboard_ctor_t tx_dboard_ctor; std::string tx_name; prop_names_t tx_subdevs; -    boost::tie(tx_dboard_ctor, tx_name, tx_subdevs) = get_dboard_args(dboard_iface::UNIT_TX, tx_dboard_id, tx_dboard_is_xcvr != this_dboard_is_xcvr); +    boost::tie(tx_dboard_ctor, tx_name, tx_subdevs) = get_dboard_args( +        dboard_iface::UNIT_TX, tx_dboard_id, tx_dboard_is_xcvr != this_dboard_is_xcvr +    );      //initialize the gpio pins before creating subdevs      set_nice_dboard_if(); @@ -317,4 +331,14 @@ void dboard_manager_impl::set_nice_dboard_if(void){          _iface->set_pin_ctrl(unit, 0x0000); //all gpio          _iface->set_clock_enabled(unit, false); //clock off      } + +    //disable all rx subdevices +    BOOST_FOREACH(const std::string &sd_name, this->get_rx_subdev_names()){ +        this->get_rx_subdev(sd_name)[SUBDEV_PROP_ENABLED] = false; +    } + +    //disable all tx subdevices +    BOOST_FOREACH(const std::string &sd_name, this->get_tx_subdev_names()){ +        this->get_tx_subdev(sd_name)[SUBDEV_PROP_ENABLED] = false; +    }  } diff --git a/host/lib/usrp/mimo_usrp.cpp b/host/lib/usrp/mimo_usrp.cpp deleted file mode 100644 index 9331c7fbb..000000000 --- a/host/lib/usrp/mimo_usrp.cpp +++ /dev/null @@ -1,347 +0,0 @@ -// -// Copyright 2010 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 <uhd/usrp/mimo_usrp.hpp> -#include <uhd/usrp/tune_helper.hpp> -#include <uhd/utils/assert.hpp> -#include <uhd/utils/gain_group.hpp> -#include <uhd/utils/algorithm.hpp> -#include <uhd/utils/warning.hpp> -#include <uhd/usrp/subdev_props.hpp> -#include <uhd/usrp/mboard_props.hpp> -#include <uhd/usrp/device_props.hpp> -#include <uhd/usrp/dboard_props.hpp> -#include <uhd/usrp/dsp_props.hpp> -#include <boost/foreach.hpp> -#include <boost/format.hpp> -#include <boost/thread.hpp> -#include <stdexcept> -#include <iostream> - -using namespace uhd; -using namespace uhd::usrp; - -static inline freq_range_t add_dsp_shift(const freq_range_t &range, wax::obj dsp){ -    double codec_rate = dsp[DSP_PROP_CODEC_RATE].as<double>(); -    return freq_range_t(range.min - codec_rate/2.0, range.max + codec_rate/2.0); -} - -/*********************************************************************** - * MIMO USRP Implementation - **********************************************************************/ -class mimo_usrp_impl : public mimo_usrp{ -public: -    mimo_usrp_impl(const device_addr_t &addr){ -        _dev = device::make(addr); - -        //set the clock config across all mboards (TODO set through api) -        clock_config_t clock_config; -        clock_config.ref_source = clock_config_t::REF_SMA; -        clock_config.pps_source = clock_config_t::PPS_SMA; -        for (size_t chan = 0; chan < get_num_channels(); chan++){ -            _mboard(chan)[MBOARD_PROP_CLOCK_CONFIG] = clock_config; -        } -    } - -    ~mimo_usrp_impl(void){ -        /* NOP */ -    } - -    device::sptr get_device(void){ -        return _dev; -    } - -    std::string get_pp_string(void){ -        std::string buff = str(boost::format( -            "MIMO USRP:\n" -            "  Device: %s\n" -        ) -            % (*_dev)[DEVICE_PROP_NAME].as<std::string>() -        ); -        for (size_t chan = 0; chan < get_num_channels(); chan++){ -            buff += str(boost::format( -                "  Channel: %u\n" -                "    Mboard: %s\n" -                "    RX DSP: %s\n" -                "    RX Dboard: %s\n" -                "    RX Subdev: %s\n" -                "    TX DSP: %s\n" -                "    TX Dboard: %s\n" -                "    TX Subdev: %s\n" -            ) % chan -                % _mboard(chan)[MBOARD_PROP_NAME].as<std::string>() -                % _rx_dsp(chan)[DSP_PROP_NAME].as<std::string>() -                % _rx_dboard(chan)[DBOARD_PROP_NAME].as<std::string>() -                % _rx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>() -                % _tx_dsp(chan)[DSP_PROP_NAME].as<std::string>() -                % _tx_dboard(chan)[DBOARD_PROP_NAME].as<std::string>() -                % _tx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>() -            ); -        } -        return buff; -    } - -    size_t get_num_channels(void){ -        return (*_dev)[DEVICE_PROP_MBOARD_NAMES].as<prop_names_t>().size(); -    } - -    /******************************************************************* -     * Misc -     ******************************************************************/ -    time_spec_t get_time_now(void){ -        //the time on the first mboard better be the same on all -        return _mboard(0)[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); -    } - -    void set_time_next_pps(const time_spec_t &time_spec){ -        for (size_t chan = 0; chan < get_num_channels(); chan++){ -            _mboard(chan)[MBOARD_PROP_TIME_NEXT_PPS] = time_spec; -        } -    } - -    void set_time_unknown_pps(const time_spec_t &time_spec){ -        std::cout << "Set time with unknown pps edge:" << std::endl; -        std::cout << "    1) set times next pps (race condition)" << std::endl; -        set_time_next_pps(time_spec); -        boost::this_thread::sleep(boost::posix_time::seconds(1)); - -        std::cout << "    2) catch seconds rollover at pps edge" << std::endl; -        time_t last_secs = 0, curr_secs = 0; -        while(curr_secs == last_secs){ -            last_secs = curr_secs; -            curr_secs = get_time_now().get_full_secs(); -        } - -        std::cout << "    3) set times next pps (synchronously)" << std::endl; -        set_time_next_pps(time_spec); -        boost::this_thread::sleep(boost::posix_time::seconds(1)); - -        //verify that the time registers are read to be within a few RTT -        for (size_t chan = 1; chan < get_num_channels(); chan++){ -            time_spec_t time_0 = _mboard(0)[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); -            time_spec_t time_i = _mboard(chan)[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); -            if (time_i < time_0 or (time_i - time_0) > time_spec_t(0.01)){ //10 ms: greater than RTT but not too big -                uhd::print_warning(str(boost::format( -                    "Detected time deviation between board %d and board 0.\n" -                    "Board 0 time is %f seconds.\n" -                    "Board %d time is %f seconds.\n" -                ) % chan % time_0.get_real_secs() % chan % time_i.get_real_secs())); -            } -        } -    } - -    void issue_stream_cmd(const stream_cmd_t &stream_cmd){ -        for (size_t chan = 0; chan < get_num_channels(); chan++){ -            _mboard(chan)[MBOARD_PROP_STREAM_CMD] = stream_cmd; -        } -    } - -    /******************************************************************* -     * RX methods -     ******************************************************************/ -    void set_rx_subdev_spec(size_t chan, const subdev_spec_t &spec){ -        UHD_ASSERT_THROW(spec.size() <= 1); -        _mboard(chan)[MBOARD_PROP_RX_SUBDEV_SPEC] = spec; -    } - -    subdev_spec_t get_rx_subdev_spec(size_t chan){ -        return _mboard(chan)[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>(); -    } - -    void set_rx_rate_all(double rate){ -        std::vector<double> _actual_rates; -        for (size_t chan = 0; chan < get_num_channels(); chan++){ -            _rx_dsp(chan)[DSP_PROP_HOST_RATE] = rate; -            _actual_rates.push_back(_rx_dsp(chan)[DSP_PROP_HOST_RATE].as<double>()); -        } -        _rx_rate = _actual_rates.front(); -        if (std::count(_actual_rates, _rx_rate) != _actual_rates.size()) throw std::runtime_error( -            "MIMO configuratio error: rx rate inconsistent across mboards" -        ); -    } - -    double get_rx_rate_all(void){ -        return _rx_rate; -    } - -    tune_result_t set_rx_freq(size_t chan, double target_freq){ -        return tune_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(chan), 0, target_freq); -    } - -    tune_result_t set_rx_freq(size_t chan, double target_freq, double lo_off){ -        return tune_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(chan), 0, target_freq, lo_off); -    } - -    double get_rx_freq(size_t chan){ -        return derive_freq_from_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(chan), 0); -    } - -    freq_range_t get_rx_freq_range(size_t chan){ -        return add_dsp_shift(_rx_subdev(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _rx_dsp(chan)); -    } - -    void set_rx_gain(size_t chan, float gain){ -        return _rx_gain_group(chan)->set_value(gain); -    } - -    float get_rx_gain(size_t chan){ -        return _rx_gain_group(chan)->get_value(); -    } - -    gain_range_t get_rx_gain_range(size_t chan){ -        return _rx_gain_group(chan)->get_range(); -    } - -    void set_rx_antenna(size_t chan, const std::string &ant){ -        _rx_subdev(chan)[SUBDEV_PROP_ANTENNA] = ant; -    } - -    std::string get_rx_antenna(size_t chan){ -        return _rx_subdev(chan)[SUBDEV_PROP_ANTENNA].as<std::string>(); -    } - -    std::vector<std::string> get_rx_antennas(size_t chan){ -        return _rx_subdev(chan)[SUBDEV_PROP_ANTENNA_NAMES].as<prop_names_t>(); -    } - -    bool get_rx_lo_locked(size_t chan){ -        return _rx_subdev(chan)[SUBDEV_PROP_LO_LOCKED].as<bool>(); -    } - -    float read_rssi(size_t chan){ -        return _rx_subdev(chan)[SUBDEV_PROP_RSSI].as<float>(); -    } - -    /******************************************************************* -     * TX methods -     ******************************************************************/ -    void set_tx_subdev_spec(size_t chan, const subdev_spec_t &spec){ -        UHD_ASSERT_THROW(spec.size() <= 1); -        _mboard(chan)[MBOARD_PROP_TX_SUBDEV_SPEC] = spec; -    } - -    subdev_spec_t get_tx_subdev_spec(size_t chan){ -        return _mboard(chan)[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>(); -    } - -    void set_tx_rate_all(double rate){ -        std::vector<double> _actual_rates; -        for (size_t chan = 0; chan < get_num_channels(); chan++){ -            _tx_dsp(chan)[DSP_PROP_HOST_RATE] = rate; -            _actual_rates.push_back(_tx_dsp(chan)[DSP_PROP_HOST_RATE].as<double>()); -        } -        _tx_rate = _actual_rates.front(); -        if (std::count(_actual_rates, _tx_rate) != _actual_rates.size()) throw std::runtime_error( -            "MIMO configuratio error: tx rate inconsistent across mboards" -        ); -    } - -    double get_tx_rate_all(void){ -        return _tx_rate; -    } - -    tune_result_t set_tx_freq(size_t chan, double target_freq){ -        return tune_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(chan), 0, target_freq); -    } - -    tune_result_t set_tx_freq(size_t chan, double target_freq, double lo_off){ -        return tune_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(chan), 0, target_freq, lo_off); -    } - -    double get_tx_freq(size_t chan){ -        return derive_freq_from_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(chan), 0); -    } - -    freq_range_t get_tx_freq_range(size_t chan){ -        return add_dsp_shift(_tx_subdev(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _tx_dsp(chan)); -    } - -    void set_tx_gain(size_t chan, float gain){ -        return _tx_gain_group(chan)->set_value(gain); -    } - -    float get_tx_gain(size_t chan){ -        return _tx_gain_group(chan)->get_value(); -    } - -    gain_range_t get_tx_gain_range(size_t chan){ -        return _tx_gain_group(chan)->get_range(); -    } - -    void set_tx_antenna(size_t chan, const std::string &ant){ -        _tx_subdev(chan)[SUBDEV_PROP_ANTENNA] = ant; -    } - -    std::string get_tx_antenna(size_t chan){ -        return _tx_subdev(chan)[SUBDEV_PROP_ANTENNA].as<std::string>(); -    } - -    std::vector<std::string> get_tx_antennas(size_t chan){ -        return _tx_subdev(chan)[SUBDEV_PROP_ANTENNA_NAMES].as<prop_names_t>(); -    } - -    bool get_tx_lo_locked(size_t chan){ -        return _tx_subdev(chan)[SUBDEV_PROP_LO_LOCKED].as<bool>(); -    } - -private: -    device::sptr _dev; -    wax::obj _mboard(size_t chan){ -        prop_names_t names = (*_dev)[DEVICE_PROP_MBOARD_NAMES].as<prop_names_t>(); -        return (*_dev)[named_prop_t(DEVICE_PROP_MBOARD, names.at(chan))]; -    } -    wax::obj _rx_dsp(size_t chan){ -        return _mboard(chan)[MBOARD_PROP_RX_DSP]; -    } -    wax::obj _tx_dsp(size_t chan){ -        return _mboard(chan)[MBOARD_PROP_TX_DSP]; -    } -    wax::obj _rx_dboard(size_t chan){ -        std::string db_name = _mboard(chan)[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>().front().db_name; -        return _mboard(chan)[named_prop_t(MBOARD_PROP_RX_DBOARD, db_name)]; -    } -    wax::obj _tx_dboard(size_t chan){ -        std::string db_name = _mboard(chan)[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>().front().db_name; -        return _mboard(chan)[named_prop_t(MBOARD_PROP_TX_DBOARD, db_name)]; -    } -    wax::obj _rx_subdev(size_t chan){ -        std::string sd_name = _mboard(chan)[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>().front().sd_name; -        return _rx_dboard(chan)[named_prop_t(DBOARD_PROP_SUBDEV, sd_name)]; -    } -    wax::obj _tx_subdev(size_t chan){ -        std::string sd_name = _mboard(chan)[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>().front().sd_name; -        return _tx_dboard(chan)[named_prop_t(DBOARD_PROP_SUBDEV, sd_name)]; -    } -    gain_group::sptr _rx_gain_group(size_t chan){ -        std::string sd_name = _mboard(chan)[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>().front().sd_name; -        return _rx_dboard(chan)[named_prop_t(DBOARD_PROP_GAIN_GROUP, sd_name)].as<gain_group::sptr>(); -    } -    gain_group::sptr _tx_gain_group(size_t chan){ -        std::string sd_name = _mboard(chan)[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>().front().sd_name; -        return _tx_dboard(chan)[named_prop_t(DBOARD_PROP_GAIN_GROUP, sd_name)].as<gain_group::sptr>(); -    } - -    //shadows -    double _rx_rate, _tx_rate; -}; - -/*********************************************************************** - * The Make Function - **********************************************************************/ -mimo_usrp::sptr mimo_usrp::make(const device_addr_t &dev_addr){ -    return sptr(new mimo_usrp_impl(dev_addr)); -} diff --git a/host/lib/usrp/misc_utils.cpp b/host/lib/usrp/misc_utils.cpp index 5cfcdc8d3..5856d706f 100644 --- a/host/lib/usrp/misc_utils.cpp +++ b/host/lib/usrp/misc_utils.cpp @@ -17,6 +17,7 @@  #include <uhd/usrp/misc_utils.hpp>  #include <uhd/utils/assert.hpp> +#include <uhd/utils/algorithm.hpp>  #include <uhd/utils/gain_group.hpp>  #include <uhd/usrp/dboard_id.hpp>  #include <uhd/usrp/subdev_props.hpp> @@ -79,6 +80,7 @@ static void set_subdev_gain(wax::obj subdev, const std::string &name, float gain   * gain group factory function for usrp   **********************************************************************/  gain_group::sptr usrp::make_gain_group( +    const dboard_id_t &dboard_id,      wax::obj subdev, wax::obj codec,      gain_group_policy_t gain_group_policy  ){ @@ -86,6 +88,8 @@ gain_group::sptr usrp::make_gain_group(      const size_t codec_gain_priority = (gain_group_policy == GAIN_GROUP_POLICY_RX)?          (subdev_gain_priority - 1): //RX policy, codec gains fill last (lower priority)          (subdev_gain_priority + 1); //TX policy, codec gains fill first (higher priority) +    const std::string subdev_prefix = dboard_id.to_cname() + "-"; +    const std::string codec_prefix = (gain_group_policy == GAIN_GROUP_POLICY_RX)? "ADC-" : "DAC-";      gain_group::sptr gg = gain_group::make();      gain_fcns_t fcns; @@ -94,7 +98,7 @@ gain_group::sptr usrp::make_gain_group(          fcns.get_range = boost::bind(&get_subdev_gain_range, subdev, name);          fcns.get_value = boost::bind(&get_subdev_gain, subdev, name);          fcns.set_value = boost::bind(&set_subdev_gain, subdev, name, _1); -        gg->register_fcns(fcns, subdev_gain_priority); +        gg->register_fcns(subdev_prefix+name, fcns, subdev_gain_priority);      }      //add all the codec gains last (antenna to dsp order)      BOOST_FOREACH(const std::string &name, codec[CODEC_PROP_GAIN_NAMES].as<prop_names_t>()){ @@ -118,7 +122,7 @@ gain_group::sptr usrp::make_gain_group(              fcns.set_value = boost::bind(&set_codec_gain_q, codec, name, _1);              break;          } -        gg->register_fcns(fcns, codec_gain_priority); +        gg->register_fcns(codec_prefix+name, fcns, codec_gain_priority);      }      return gg;  } @@ -160,13 +164,13 @@ static void verify_xx_subdev_spec(          }          //sanity check that the dboard/subdevice names exist for this mboard -        BOOST_FOREACH(const subdev_spec_pair_t &pair, subdev_spec){ +        BOOST_FOREACH(subdev_spec_pair_t &pair, subdev_spec){              //empty db name means select dboard automatically              if (pair.db_name.empty()){                  if (dboard_names.size() != 1) throw std::runtime_error(                      "A daughterboard name must be provided for multi-slot motherboards: " + subdev_spec.to_string()                  ); -                pair.db_name == dboard_names.front(); +                pair.db_name = dboard_names.front();              }              uhd::assert_has(dboard_names, pair.db_name, xx_type + " dboard name");              wax::obj dboard = mboard[named_prop_t(dboard_prop, pair.db_name)]; @@ -177,7 +181,7 @@ static void verify_xx_subdev_spec(                  if (subdev_names.size() != 1) throw std::runtime_error(                      "A subdevice name must be provided for multi-subdev daughterboards: " + subdev_spec.to_string()                  ); -                pair.sd_name == subdev_names.front(); +                pair.sd_name = subdev_names.front();              }              uhd::assert_has(subdev_names, pair.sd_name, xx_type + " subdev name");          } @@ -186,6 +190,22 @@ static void verify_xx_subdev_spec(              "Validate %s subdev spec failed: %s\n    %s"          ) % xx_type % subdev_spec.to_string() % e.what()));      } + +    //now use the subdev spec to enable the subdevices in use and vice-versa +    BOOST_FOREACH(const std::string &db_name, mboard[dboard_names_prop].as<prop_names_t>()){ +        wax::obj dboard = mboard[named_prop_t(dboard_prop, db_name)]; +        BOOST_FOREACH(const std::string &sd_name, dboard[DBOARD_PROP_SUBDEV_NAMES].as<prop_names_t>()){ +            try{ +                bool enable = std::has(subdev_spec, subdev_spec_pair_t(db_name, sd_name)); +                dboard[named_prop_t(DBOARD_PROP_SUBDEV, sd_name)][SUBDEV_PROP_ENABLED] = enable; +            } +            catch(const std::exception &e){ +                throw std::runtime_error(str(boost::format( +                    "Cannot set enabled property on subdevice %s:%s\n    %s" +                ) % db_name % sd_name % e.what())); +            } +        } +    }  }  void usrp::verify_rx_subdev_spec(subdev_spec_t &subdev_spec, wax::obj mboard){ diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp new file mode 100644 index 000000000..876f1a3fc --- /dev/null +++ b/host/lib/usrp/multi_usrp.cpp @@ -0,0 +1,442 @@ +// +// Copyright 2010 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 "wrapper_utils.hpp" +#include <uhd/usrp/multi_usrp.hpp> +#include <uhd/usrp/tune_helper.hpp> +#include <uhd/utils/assert.hpp> +#include <uhd/utils/warning.hpp> +#include <uhd/utils/gain_group.hpp> +#include <uhd/usrp/subdev_props.hpp> +#include <uhd/usrp/mboard_props.hpp> +#include <uhd/usrp/device_props.hpp> +#include <uhd/usrp/dboard_props.hpp> +#include <uhd/usrp/dsp_props.hpp> +#include <boost/thread.hpp> +#include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <stdexcept> +#include <iostream> + +using namespace uhd; +using namespace uhd::usrp; + +const std::string multi_usrp::ALL_GAINS = ""; + +/*********************************************************************** + * Simple USRP Implementation + **********************************************************************/ +class multi_usrp_impl : public multi_usrp{ +public: +    multi_usrp_impl(const device_addr_t &addr){ +        _dev = device::make(addr); +    } + +    device::sptr get_device(void){ +        return _dev; +    } + +    /******************************************************************* +     * Mboard methods +     ******************************************************************/ +    std::string get_pp_string(void){ +        std::string buff = str(boost::format( +            "Multi USRP:\n" +            "  Device: %s\n" +        ) +            % (*_dev)[DEVICE_PROP_NAME].as<std::string>() +        ); +        for (size_t m = 0; m < get_num_mboards(); m++){ +            buff += str(boost::format( +                "  Mboard %d: %s\n" +            ) % m +                % _mboard(m)[MBOARD_PROP_NAME].as<std::string>() +            ); +        } + +        //----------- rx side of life ---------------------------------- +        for (size_t m = 0, chan = 0; m < get_num_mboards(); m++){ +            buff += str(boost::format( +                "  RX DSP %d: %s\n" +            ) % m +                % _rx_dsp(m)[DSP_PROP_NAME].as<std::string>() +            ); +            for (; chan < (m + 1)*get_rx_subdev_spec(m).size(); chan++){ +                buff += str(boost::format( +                    "  RX Channel: %u\n" +                    "    RX Dboard: %s\n" +                    "    RX Subdev: %s\n" +                ) % chan +                    % _rx_dboard(chan)[DBOARD_PROP_NAME].as<std::string>() +                    % _rx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>() +                ); +            } +        } + +        //----------- tx side of life ---------------------------------- +        for (size_t m = 0, chan = 0; m < get_num_mboards(); m++){ +            buff += str(boost::format( +                "  TX DSP %d: %s\n" +            ) % m +                % _tx_dsp(m)[DSP_PROP_NAME].as<std::string>() +            ); +            for (; chan < (m + 1)*get_tx_subdev_spec(m).size(); chan++){ +                buff += str(boost::format( +                    "  TX Channel: %u\n" +                    "    TX Dboard: %s\n" +                    "    TX Subdev: %s\n" +                ) % chan +                    % _tx_dboard(chan)[DBOARD_PROP_NAME].as<std::string>() +                    % _tx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>() +                ); +            } +        } + +        return buff; +    } + +    std::string get_mboard_name(size_t mboard){ +        return _mboard(mboard)[MBOARD_PROP_NAME].as<std::string>(); +    } + +    time_spec_t get_time_now(void){ +        return _mboard(0)[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); +    } + +    void set_time_next_pps(const time_spec_t &time_spec){ +        for (size_t m = 0; m < get_num_mboards(); m++){ +            _mboard(m)[MBOARD_PROP_TIME_NEXT_PPS] = time_spec; +        } +    } + +    void set_time_unknown_pps(const time_spec_t &time_spec){ +        std::cout << "Set time with unknown pps edge:" << std::endl; +        std::cout << "    1) set times next pps (race condition)" << std::endl; +        set_time_next_pps(time_spec); +        boost::this_thread::sleep(boost::posix_time::seconds(1)); + +        std::cout << "    2) catch seconds rollover at pps edge" << std::endl; +        time_t last_secs = 0, curr_secs = 0; +        while(curr_secs == last_secs){ +            last_secs = curr_secs; +            curr_secs = get_time_now().get_full_secs(); +        } + +        std::cout << "    3) set times next pps (synchronously)" << std::endl; +        set_time_next_pps(time_spec); +        boost::this_thread::sleep(boost::posix_time::seconds(1)); + +        //verify that the time registers are read to be within a few RTT +        for (size_t m = 1; m < get_num_mboards(); m++){ +            time_spec_t time_0 = _mboard(0)[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); +            time_spec_t time_i = _mboard(m)[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); +            if (time_i < time_0 or (time_i - time_0) > time_spec_t(0.01)){ //10 ms: greater than RTT but not too big +                uhd::warning::post(str(boost::format( +                    "Detected time deviation between board %d and board 0.\n" +                    "Board 0 time is %f seconds.\n" +                    "Board %d time is %f seconds.\n" +                ) % m % time_0.get_real_secs() % m % time_i.get_real_secs())); +            } +        } +    } + +    bool get_time_synchronized(void){ +        for (size_t m = 1; m < get_num_mboards(); m++){ +            time_spec_t time_0 = _mboard(0)[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); +            time_spec_t time_i = _mboard(m)[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); +            if (time_i < time_0 or (time_i - time_0) > time_spec_t(0.01)) return false; +        } +        return true; +    } + +    void issue_stream_cmd(const stream_cmd_t &stream_cmd){ +        for (size_t m = 0; m < get_num_mboards(); m++){ +            _mboard(m)[MBOARD_PROP_STREAM_CMD] = stream_cmd; +        } +    } + +    void set_clock_config(const clock_config_t &clock_config, size_t mboard){ +        if (mboard != ALL_MBOARDS){ +            _mboard(mboard)[MBOARD_PROP_CLOCK_CONFIG] = clock_config; +            return; +        } +        for (size_t m = 0; m < get_num_mboards(); m++){ +            set_clock_config(clock_config, m); +        } +    } + +    size_t get_num_mboards(void){ +        return (*_dev)[DEVICE_PROP_MBOARD_NAMES].as<prop_names_t>().size(); +    } + +    /******************************************************************* +     * RX methods +     ******************************************************************/ +    void set_rx_subdev_spec(const subdev_spec_t &spec, size_t mboard){ +        if (mboard != ALL_MBOARDS){ +            _mboard(mboard)[MBOARD_PROP_RX_SUBDEV_SPEC] = spec; +            return; +        } +        for (size_t m = 0; m < get_num_mboards(); m++){ +            set_rx_subdev_spec(spec, m); +        } +    } + +    subdev_spec_t get_rx_subdev_spec(size_t mboard){ +        return _mboard(mboard)[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>(); +    } + +    size_t get_rx_num_channels(void){ +        return rx_cpm()*get_num_mboards(); //total num channels +    } + +    std::string get_rx_subdev_name(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>(); +    } + +    void set_rx_rate(double rate){ +        for (size_t m = 0; m < get_num_mboards(); m++){ +            _rx_dsp(m)[DSP_PROP_HOST_RATE] = rate; +        } +        do_samp_rate_warning_message(rate, get_rx_rate(), "RX"); +    } + +    double get_rx_rate(void){ +        return _rx_dsp(0)[DSP_PROP_HOST_RATE].as<double>(); +    } + +    tune_result_t set_rx_freq(const tune_request_t &tune_request, size_t chan){ +        tune_result_t r = tune_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(chan/rx_cpm()), chan%rx_cpm(), tune_request); +        do_tune_freq_warning_message(tune_request.target_freq, get_rx_freq(chan), "RX"); +        return r; +    } + +    double get_rx_freq(size_t chan){ +        return derive_freq_from_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(chan/rx_cpm()), chan%rx_cpm()); +    } + +    freq_range_t get_rx_freq_range(size_t chan){ +        return add_dsp_shift(_rx_subdev(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _rx_dsp(chan/rx_cpm())); +    } + +    void set_rx_gain(float gain, const std::string &name, size_t chan){ +        return _rx_gain_group(chan)->set_value(gain, name); +    } + +    float get_rx_gain(const std::string &name, size_t chan){ +        return _rx_gain_group(chan)->get_value(name); +    } + +    gain_range_t get_rx_gain_range(const std::string &name, size_t chan){ +        return _rx_gain_group(chan)->get_range(name); +    } + +    std::vector<std::string> get_rx_gain_names(size_t chan){ +        return _rx_gain_group(chan)->get_names(); +    } + +    void set_rx_antenna(const std::string &ant, size_t chan){ +        _rx_subdev(chan)[SUBDEV_PROP_ANTENNA] = ant; +    } + +    std::string get_rx_antenna(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_ANTENNA].as<std::string>(); +    } + +    std::vector<std::string> get_rx_antennas(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_ANTENNA_NAMES].as<prop_names_t>(); +    } + +    bool get_rx_lo_locked(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_LO_LOCKED].as<bool>(); +    } + +    void set_rx_bandwidth(double bandwidth, size_t chan){ +        _rx_subdev(chan)[SUBDEV_PROP_BANDWIDTH] = bandwidth; +    } + +    double get_rx_bandwidth(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_BANDWIDTH].as<double>(); +    } + +    float read_rssi(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_RSSI].as<float>(); +    } + +    dboard_iface::sptr get_rx_dboard_iface(size_t chan){ +        return _rx_dboard(chan)[DBOARD_PROP_DBOARD_IFACE].as<dboard_iface::sptr>(); +    } + +    /******************************************************************* +     * TX methods +     ******************************************************************/ +    void set_tx_subdev_spec(const subdev_spec_t &spec, size_t mboard){ +        if (mboard != ALL_MBOARDS){ +            _mboard(mboard)[MBOARD_PROP_TX_SUBDEV_SPEC] = spec; +            return; +        } +        for (size_t m = 0; m < get_num_mboards(); m++){ +            set_tx_subdev_spec(spec, m); +        } +    } + +    subdev_spec_t get_tx_subdev_spec(size_t mboard){ +        return _mboard(mboard)[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>(); +    } + +    std::string get_tx_subdev_name(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>(); +    } + +    size_t get_tx_num_channels(void){ +        return tx_cpm()*get_num_mboards(); //total num channels +    } + +    void set_tx_rate(double rate){ +        for (size_t m = 0; m < get_num_mboards(); m++){ +            _tx_dsp(m)[DSP_PROP_HOST_RATE] = rate; +        } +        do_samp_rate_warning_message(rate, get_tx_rate(), "TX"); +    } + +    double get_tx_rate(void){ +        return _tx_dsp(0)[DSP_PROP_HOST_RATE].as<double>(); +    } + +    tune_result_t set_tx_freq(const tune_request_t &tune_request, size_t chan){ +        tune_result_t r = tune_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(chan/tx_cpm()), chan%tx_cpm(), tune_request); +        do_tune_freq_warning_message(tune_request.target_freq, get_tx_freq(chan), "TX"); +        return r; +    } + +    double get_tx_freq(size_t chan){ +        return derive_freq_from_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(chan/tx_cpm()), chan%tx_cpm()); +    } + +    freq_range_t get_tx_freq_range(size_t chan){ +        return add_dsp_shift(_tx_subdev(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _tx_dsp(chan/tx_cpm())); +    } + +    void set_tx_gain(float gain, const std::string &name, size_t chan){ +        return _tx_gain_group(chan)->set_value(gain, name); +    } + +    float get_tx_gain(const std::string &name, size_t chan){ +        return _tx_gain_group(chan)->get_value(name); +    } + +    gain_range_t get_tx_gain_range(const std::string &name, size_t chan){ +        return _tx_gain_group(chan)->get_range(name); +    } + +    std::vector<std::string> get_tx_gain_names(size_t chan){ +        return _tx_gain_group(chan)->get_names(); +    } + +    void set_tx_antenna(const std::string &ant, size_t chan){ +        _tx_subdev(chan)[SUBDEV_PROP_ANTENNA] = ant; +    } + +    std::string get_tx_antenna(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_ANTENNA].as<std::string>(); +    } + +    std::vector<std::string> get_tx_antennas(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_ANTENNA_NAMES].as<prop_names_t>(); +    } + +    bool get_tx_lo_locked(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_LO_LOCKED].as<bool>(); +    } + +    void set_tx_bandwidth(double bandwidth, size_t chan){ +        _tx_subdev(chan)[SUBDEV_PROP_BANDWIDTH] = bandwidth; +    } + +    double get_tx_bandwidth(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_BANDWIDTH].as<double>(); +    } + +    dboard_iface::sptr get_tx_dboard_iface(size_t chan){ +        return _tx_dboard(chan)[DBOARD_PROP_DBOARD_IFACE].as<dboard_iface::sptr>(); +    } + +private: +    device::sptr _dev; + +    size_t rx_cpm(void){ //channels per mboard +        size_t nchan = get_rx_subdev_spec(0).size(); +        for (size_t m = 1; m < get_num_mboards(); m++){ +            if (nchan != get_rx_subdev_spec(m).size()){ +                throw std::runtime_error("rx subdev spec size inconsistent across all mboards"); +            } +        } +        return nchan; +    } + +    size_t tx_cpm(void){ //channels per mboard +        size_t nchan = get_tx_subdev_spec(0).size(); +        for (size_t m = 1; m < get_num_mboards(); m++){ +            if (nchan != get_tx_subdev_spec(m).size()){ +                throw std::runtime_error("tx subdev spec size inconsistent across all mboards"); +            } +        } +        return nchan; +    } + +    wax::obj _mboard(size_t mboard){ +        std::string mb_name = (*_dev)[DEVICE_PROP_MBOARD_NAMES].as<prop_names_t>().at(mboard); +        return (*_dev)[named_prop_t(DEVICE_PROP_MBOARD, mb_name)]; +    } +    wax::obj _rx_dsp(size_t mboard){ +        return _mboard(mboard)[MBOARD_PROP_RX_DSP]; +    } +    wax::obj _tx_dsp(size_t mboard){ +        return _mboard(mboard)[MBOARD_PROP_TX_DSP]; +    } +    wax::obj _rx_dboard(size_t chan){ +        std::string db_name = get_rx_subdev_spec(chan/rx_cpm()).at(chan%rx_cpm()).db_name; +        return _mboard(chan/rx_cpm())[named_prop_t(MBOARD_PROP_RX_DBOARD, db_name)]; +    } +    wax::obj _tx_dboard(size_t chan){ +        std::string db_name = get_tx_subdev_spec(chan/tx_cpm()).at(chan%tx_cpm()).db_name; +        return _mboard(chan/tx_cpm())[named_prop_t(MBOARD_PROP_TX_DBOARD, db_name)]; +    } +    wax::obj _rx_subdev(size_t chan){ +        std::string sd_name = get_rx_subdev_spec(chan/rx_cpm()).at(chan%rx_cpm()).sd_name; +        return _rx_dboard(chan)[named_prop_t(DBOARD_PROP_SUBDEV, sd_name)]; +    } +    wax::obj _tx_subdev(size_t chan){ +        std::string sd_name = get_tx_subdev_spec(chan/tx_cpm()).at(chan%tx_cpm()).sd_name; +        return _tx_dboard(chan)[named_prop_t(DBOARD_PROP_SUBDEV, sd_name)]; +    } +    gain_group::sptr _rx_gain_group(size_t chan){ +        std::string sd_name = get_rx_subdev_spec(chan/rx_cpm()).at(chan%rx_cpm()).sd_name; +        return _rx_dboard(chan)[named_prop_t(DBOARD_PROP_GAIN_GROUP, sd_name)].as<gain_group::sptr>(); +    } +    gain_group::sptr _tx_gain_group(size_t chan){ +        std::string sd_name = get_tx_subdev_spec(chan/tx_cpm()).at(chan%tx_cpm()).sd_name; +        return _tx_dboard(chan)[named_prop_t(DBOARD_PROP_GAIN_GROUP, sd_name)].as<gain_group::sptr>(); +    } +}; + +/*********************************************************************** + * The Make Function + **********************************************************************/ +multi_usrp::sptr multi_usrp::make(const device_addr_t &dev_addr){ +    return sptr(new multi_usrp_impl(dev_addr)); +} diff --git a/host/lib/usrp/simple_usrp.cpp b/host/lib/usrp/simple_usrp.cpp deleted file mode 100644 index b89b76eed..000000000 --- a/host/lib/usrp/simple_usrp.cpp +++ /dev/null @@ -1,222 +0,0 @@ -// -// Copyright 2010 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 <uhd/usrp/single_usrp.hpp> -#include <uhd/usrp/simple_usrp.hpp> -#include <uhd/utils/warning.hpp> - -using namespace uhd; -using namespace uhd::usrp; - -/*********************************************************************** - * Simple USRP Implementation - **********************************************************************/ -class simple_usrp_impl : public simple_usrp{ -public: -    simple_usrp_impl(const device_addr_t &addr){ -        _sdev = single_usrp::make(addr); -    } - -    ~simple_usrp_impl(void){ -        /* NOP */ -    } - -    device::sptr get_device(void){ -        return _sdev->get_device(); -    } - -    std::string get_pp_string(void){ -        return _sdev->get_pp_string(); -    } - -    /******************************************************************* -     * Misc -     ******************************************************************/ -    time_spec_t get_time_now(void){ -        return _sdev->get_time_now(); -    } - -    void set_time_now(const time_spec_t &time_spec){ -        return _sdev->set_time_now(time_spec); -    } - -    void set_time_next_pps(const time_spec_t &time_spec){ -        return _sdev->set_time_next_pps(time_spec); -    } - -    void issue_stream_cmd(const stream_cmd_t &stream_cmd){ -        return _sdev->issue_stream_cmd(stream_cmd); -    } - -    void set_clock_config(const clock_config_t &clock_config){ -        return _sdev->set_clock_config(clock_config); -    } - -    /******************************************************************* -     * RX methods -     ******************************************************************/ -    void set_rx_subdev_spec(const subdev_spec_t &spec){ -        return _sdev->set_rx_subdev_spec(spec); -    } - -    subdev_spec_t get_rx_subdev_spec(void){ -        return _sdev->get_rx_subdev_spec(); -    } - -    void set_rx_rate(double rate){ -        return _sdev->set_rx_rate(rate); -    } - -    double get_rx_rate(void){ -        return _sdev->get_rx_rate(); -    } - -    tune_result_t set_rx_freq(double target_freq){ -        return _sdev->set_rx_freq(target_freq); -    } - -    tune_result_t set_rx_freq(double target_freq, double lo_off){ -        return _sdev->set_rx_freq(target_freq, lo_off); -    } - -    double get_rx_freq(void){ -        return _sdev->get_rx_freq(); -    } - -    freq_range_t get_rx_freq_range(void){ -        return _sdev->get_rx_freq_range(); -    } - -    void set_rx_gain(float gain){ -        return _sdev->set_rx_gain(gain); -    } - -    float get_rx_gain(void){ -        return _sdev->get_rx_gain(); -    } - -    gain_range_t get_rx_gain_range(void){ -        return _sdev->get_rx_gain_range(); -    } - -    void set_rx_antenna(const std::string &ant){ -        return _sdev->set_rx_antenna(ant); -    } - -    std::string get_rx_antenna(void){ -        return _sdev->get_rx_antenna(); -    } - -    std::vector<std::string> get_rx_antennas(void){ -        return _sdev->get_rx_antennas(); -    } - -    bool get_rx_lo_locked(void){ -        return _sdev->get_rx_lo_locked(); -    } - -    float read_rssi(void){ -        return _sdev->read_rssi(); -    } - -    dboard_iface::sptr get_rx_dboard_iface(void){ -        return _sdev->get_rx_dboard_iface(); -    } - -    /******************************************************************* -     * TX methods -     ******************************************************************/ -    void set_tx_subdev_spec(const subdev_spec_t &spec){ -        return _sdev->set_tx_subdev_spec(spec); -    } - -    subdev_spec_t get_tx_subdev_spec(void){ -        return _sdev->get_tx_subdev_spec(); -    } - -    void set_tx_rate(double rate){ -        return _sdev->set_tx_rate(rate); -    } - -    double get_tx_rate(void){ -        return _sdev->get_tx_rate(); -    } - -    tune_result_t set_tx_freq(double target_freq){ -        return _sdev->set_tx_freq(target_freq); -    } - -    tune_result_t set_tx_freq(double target_freq, double lo_off){ -        return _sdev->set_tx_freq(target_freq, lo_off); -    } - -    double get_tx_freq(void){ -        return _sdev->get_tx_freq(); -    } - -    freq_range_t get_tx_freq_range(void){ -        return _sdev->get_tx_freq_range(); -    } - -    void set_tx_gain(float gain){ -        return _sdev->set_tx_gain(gain); -    } - -    float get_tx_gain(void){ -        return _sdev->get_tx_gain(); -    } - -    gain_range_t get_tx_gain_range(void){ -        return _sdev->get_tx_gain_range(); -    } - -    void set_tx_antenna(const std::string &ant){ -        return _sdev->set_tx_antenna(ant); -    } - -    std::string get_tx_antenna(void){ -        return _sdev->get_tx_antenna(); -    } - -    std::vector<std::string> get_tx_antennas(void){ -        return _sdev->get_tx_antennas(); -    } - -    bool get_tx_lo_locked(void){ -        return _sdev->get_tx_lo_locked(); -    } - -    dboard_iface::sptr get_tx_dboard_iface(void){ -        return _sdev->get_tx_dboard_iface(); -    } - -private: -    single_usrp::sptr _sdev; -}; - -/*********************************************************************** - * The Make Function - **********************************************************************/ -simple_usrp::sptr simple_usrp::make(const device_addr_t &dev_addr){ -    uhd::print_warning( -        "The simple USRP interface has been deprecated.\n" -        "Please switch to the single USRP interface.\n" -        "#include <uhd/usrp/single_usrp.hpp>\n" -        "single_usrp::sptr sdev = single_usrp::make(args);\n" -    ); -    return sptr(new simple_usrp_impl(dev_addr)); -} diff --git a/host/lib/usrp/single_usrp.cpp b/host/lib/usrp/single_usrp.cpp index bb4af44b8..a0456d1f0 100644 --- a/host/lib/usrp/single_usrp.cpp +++ b/host/lib/usrp/single_usrp.cpp @@ -15,6 +15,7 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // +#include "wrapper_utils.hpp"  #include <uhd/usrp/single_usrp.hpp>  #include <uhd/usrp/tune_helper.hpp>  #include <uhd/utils/assert.hpp> @@ -32,10 +33,7 @@  using namespace uhd;  using namespace uhd::usrp; -static inline freq_range_t add_dsp_shift(const freq_range_t &range, wax::obj dsp){ -    double codec_rate = dsp[DSP_PROP_CODEC_RATE].as<double>(); -    return freq_range_t(range.min - codec_rate/2.0, range.max + codec_rate/2.0); -} +const std::string single_usrp::ALL_GAINS = "";  /***********************************************************************   * Simple USRP Implementation @@ -46,14 +44,13 @@ public:          _dev = device::make(addr);      } -    ~single_usrp_impl(void){ -        /* NOP */ -    } -      device::sptr get_device(void){          return _dev;      } +    /******************************************************************* +     * Mboard methods +     ******************************************************************/      std::string get_pp_string(void){          std::string buff = str(boost::format(              "Single USRP:\n" @@ -101,9 +98,10 @@ public:          return buff;      } -    /******************************************************************* -     * Misc -     ******************************************************************/ +    std::string get_mboard_name(void){ +        return _mboard()[MBOARD_PROP_NAME].as<std::string>(); +    } +      time_spec_t get_time_now(void){          return _mboard()[MBOARD_PROP_TIME_NOW].as<time_spec_t>();      } @@ -135,20 +133,23 @@ public:          return _mboard()[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>();      } +    std::string get_rx_subdev_name(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>(); +    } +      void set_rx_rate(double rate){          _rx_dsp()[DSP_PROP_HOST_RATE] = rate; +        do_samp_rate_warning_message(rate, get_rx_rate(), "RX");      }      double get_rx_rate(void){          return _rx_dsp()[DSP_PROP_HOST_RATE].as<double>();      } -    tune_result_t set_rx_freq(double target_freq, size_t chan){ -        return tune_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(), chan, target_freq); -    } - -    tune_result_t set_rx_freq(double target_freq, double lo_off, size_t chan){ -        return tune_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(), chan, target_freq, lo_off); +    tune_result_t set_rx_freq(const tune_request_t &tune_request, size_t chan){ +        tune_result_t r = tune_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(), chan, tune_request); +        do_tune_freq_warning_message(tune_request.target_freq, get_rx_freq(chan), "RX"); +        return r;      }      double get_rx_freq(size_t chan){ @@ -159,16 +160,20 @@ public:          return add_dsp_shift(_rx_subdev(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _rx_dsp());      } -    void set_rx_gain(float gain, size_t chan){ -        return _rx_gain_group(chan)->set_value(gain); +    void set_rx_gain(float gain, const std::string &name, size_t chan){ +        return _rx_gain_group(chan)->set_value(gain, name); +    } + +    float get_rx_gain(const std::string &name, size_t chan){ +        return _rx_gain_group(chan)->get_value(name);      } -    float get_rx_gain(size_t chan){ -        return _rx_gain_group(chan)->get_value(); +    gain_range_t get_rx_gain_range(const std::string &name, size_t chan){ +        return _rx_gain_group(chan)->get_range(name);      } -    gain_range_t get_rx_gain_range(size_t chan){ -        return _rx_gain_group(chan)->get_range(); +    std::vector<std::string> get_rx_gain_names(size_t chan){ +        return _rx_gain_group(chan)->get_names();      }      void set_rx_antenna(const std::string &ant, size_t chan){ @@ -187,6 +192,14 @@ public:          return _rx_subdev(chan)[SUBDEV_PROP_LO_LOCKED].as<bool>();      } +    void set_rx_bandwidth(double bandwidth, size_t chan){ +        _rx_subdev(chan)[SUBDEV_PROP_BANDWIDTH] = bandwidth; +    } + +    double get_rx_bandwidth(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_BANDWIDTH].as<double>(); +    } +      float read_rssi(size_t chan){          return _rx_subdev(chan)[SUBDEV_PROP_RSSI].as<float>();      } @@ -206,20 +219,23 @@ public:          return _mboard()[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>();      } +    std::string get_tx_subdev_name(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>(); +    } +      void set_tx_rate(double rate){          _tx_dsp()[DSP_PROP_HOST_RATE] = rate; +        do_samp_rate_warning_message(rate, get_tx_rate(), "TX");      }      double get_tx_rate(void){          return _tx_dsp()[DSP_PROP_HOST_RATE].as<double>();      } -    tune_result_t set_tx_freq(double target_freq, size_t chan){ -        return tune_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(), chan, target_freq); -    } - -    tune_result_t set_tx_freq(double target_freq, double lo_off, size_t chan){ -        return tune_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(), chan, target_freq, lo_off); +    tune_result_t set_tx_freq(const tune_request_t &tune_request, size_t chan){ +        tune_result_t r = tune_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(), chan, tune_request); +        do_tune_freq_warning_message(tune_request.target_freq, get_tx_freq(chan), "TX"); +        return r;      }      double get_tx_freq(size_t chan){ @@ -230,16 +246,20 @@ public:          return add_dsp_shift(_tx_subdev(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _tx_dsp());      } -    void set_tx_gain(float gain, size_t chan){ -        return _tx_gain_group(chan)->set_value(gain); +    void set_tx_gain(float gain, const std::string &name, size_t chan){ +        return _tx_gain_group(chan)->set_value(gain, name); +    } + +    float get_tx_gain(const std::string &name, size_t chan){ +        return _tx_gain_group(chan)->get_value(name);      } -    float get_tx_gain(size_t chan){ -        return _tx_gain_group(chan)->get_value(); +    gain_range_t get_tx_gain_range(const std::string &name, size_t chan){ +        return _tx_gain_group(chan)->get_range(name);      } -    gain_range_t get_tx_gain_range(size_t chan){ -        return _tx_gain_group(chan)->get_range(); +    std::vector<std::string> get_tx_gain_names(size_t chan){ +        return _tx_gain_group(chan)->get_names();      }      void set_tx_antenna(const std::string &ant, size_t chan){ @@ -258,6 +278,14 @@ public:          return _tx_subdev(chan)[SUBDEV_PROP_LO_LOCKED].as<bool>();      } +    void set_tx_bandwidth(double bandwidth, size_t chan){ +        _tx_subdev(chan)[SUBDEV_PROP_BANDWIDTH] = bandwidth; +    } + +    double get_tx_bandwidth(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_BANDWIDTH].as<double>(); +    } +      dboard_iface::sptr get_tx_dboard_iface(size_t chan){          return _tx_dboard(chan)[DBOARD_PROP_DBOARD_IFACE].as<dboard_iface::sptr>();      } diff --git a/host/lib/usrp/subdev_spec.cpp b/host/lib/usrp/subdev_spec.cpp index 7a3e72867..95d2cbb12 100644 --- a/host/lib/usrp/subdev_spec.cpp +++ b/host/lib/usrp/subdev_spec.cpp @@ -34,6 +34,10 @@ subdev_spec_pair_t::subdev_spec_pair_t(      /* NOP */  } +bool usrp::operator==(const subdev_spec_pair_t &lhs, const subdev_spec_pair_t &rhs){ +    return (lhs.db_name == rhs.db_name) and (lhs.sd_name == rhs.sd_name); +} +  subdev_spec_t::subdev_spec_t(const std::string &markup){      BOOST_FOREACH(const std::string &pair, std::split_string(markup)){          if (pair == "") continue; diff --git a/host/lib/usrp/tune_helper.cpp b/host/lib/usrp/tune_helper.cpp index 7633c67f2..fa40a8a26 100644 --- a/host/lib/usrp/tune_helper.cpp +++ b/host/lib/usrp/tune_helper.cpp @@ -19,6 +19,7 @@  #include <uhd/usrp/subdev_props.hpp>  #include <uhd/usrp/dsp_props.hpp>  #include <uhd/usrp/dboard_iface.hpp> //unit_t +#include <uhd/utils/algorithm.hpp>  #include <boost/math/special_functions/sign.hpp>  #include <cmath> @@ -28,55 +29,99 @@ using namespace uhd::usrp;  /***********************************************************************   * Tune Helper Functions   **********************************************************************/ -static tune_result_t tune_xx_subdev_and_dxc( +static tune_result_t tune_xx_subdev_and_dsp(      dboard_iface::unit_t unit, -    wax::obj subdev, wax::obj dxc, size_t chan, -    double target_freq, double lo_offset +    wax::obj subdev, wax::obj dsp, size_t chan, +    const tune_request_t &tune_request  ){      wax::obj subdev_freq_proxy = subdev[SUBDEV_PROP_FREQ]; -    std::string freq_name = dxc[DSP_PROP_FREQ_SHIFT_NAMES].as<prop_names_t>().at(chan); -    wax::obj dxc_freq_proxy = dxc[named_prop_t(DSP_PROP_FREQ_SHIFT, freq_name)]; -    double dxc_sample_rate = dxc[DSP_PROP_CODEC_RATE].as<double>(); +    std::string freq_name = dsp[DSP_PROP_FREQ_SHIFT_NAMES].as<prop_names_t>().at(chan); +    wax::obj dsp_freq_proxy = dsp[named_prop_t(DSP_PROP_FREQ_SHIFT, freq_name)]; +    double dsp_sample_rate = dsp[DSP_PROP_CODEC_RATE].as<double>(); -    // Ask the d'board to tune as closely as it can to target_freq+lo_offset -    double target_inter_freq = target_freq + lo_offset; -    subdev_freq_proxy = target_inter_freq; -    double actual_inter_freq = subdev_freq_proxy.as<double>(); - -    //perform the correction correction for dxc rates outside of nyquist -    double delta_freq = std::fmod(target_freq - actual_inter_freq, dxc_sample_rate); -    bool outside_of_nyquist = std::abs(delta_freq) > dxc_sample_rate/2.0; -    double target_dxc_freq = (outside_of_nyquist)? -        boost::math::sign(delta_freq)*dxc_sample_rate - delta_freq : -delta_freq; +    //------------------------------------------------------------------ +    //-- calculate the LO offset, only used with automatic policy +    //------------------------------------------------------------------ +    double lo_offset = 0.0; +    if (subdev[SUBDEV_PROP_USE_LO_OFFSET].as<bool>()){ +        //If the local oscillator will be in the passband, use an offset. +        //But constrain the LO offset by the width of the filter bandwidth. +        double rate = dsp[DSP_PROP_HOST_RATE].as<double>(); +        double bw = subdev[SUBDEV_PROP_BANDWIDTH].as<double>(); +        if (bw > rate) lo_offset = std::min((bw - rate)/2, rate/2); +    } -    //invert the sign on the dxc freq given the following conditions -    if (unit == dboard_iface::UNIT_TX) target_dxc_freq *= -1.0; +    //------------------------------------------------------------------ +    //-- set the intermediate frequency depending upon the IF policy +    //------------------------------------------------------------------ +    double target_inter_freq = 0.0; +    switch (tune_request.inter_freq_policy){ +    case tune_request_t::POLICY_AUTO: +        target_inter_freq = tune_request.target_freq + lo_offset; +        subdev_freq_proxy = target_inter_freq; +        break; + +    case tune_request_t::POLICY_MANUAL: +        target_inter_freq = tune_request.inter_freq; +        subdev_freq_proxy = target_inter_freq; +        break; + +    case tune_request_t::POLICY_NONE: break; //does not set +    } +    double actual_inter_freq = subdev_freq_proxy.as<double>(); -    dxc_freq_proxy = target_dxc_freq; -    double actual_dxc_freq = dxc_freq_proxy.as<double>(); +    //------------------------------------------------------------------ +    //-- calculate the dsp freq, only used with automatic policy +    //------------------------------------------------------------------ +    double delta_freq = std::fmod(tune_request.target_freq - actual_inter_freq, dsp_sample_rate); +    bool outside_of_nyquist = std::abs(delta_freq) > dsp_sample_rate/2.0; +    double target_dsp_freq = (outside_of_nyquist)? +        boost::math::sign(delta_freq)*dsp_sample_rate - delta_freq : -delta_freq; + +    //invert the sign on the dsp freq given the following conditions +    if (unit == dboard_iface::UNIT_TX) target_dsp_freq *= -1.0; + +    //------------------------------------------------------------------ +    //-- set the dsp frequency depending upon the dsp frequency policy +    //------------------------------------------------------------------ +    switch (tune_request.dsp_freq_policy){ +    case tune_request_t::POLICY_AUTO: +        dsp_freq_proxy = target_dsp_freq; +        break; + +    case tune_request_t::POLICY_MANUAL: +        target_dsp_freq = tune_request.dsp_freq; +        dsp_freq_proxy = target_dsp_freq; +        break; + +    case tune_request_t::POLICY_NONE: break; //does not set +    } +    double actual_dsp_freq = dsp_freq_proxy.as<double>(); -    //load and return the tune result +    //------------------------------------------------------------------ +    //-- load and return the tune result +    //------------------------------------------------------------------      tune_result_t tune_result;      tune_result.target_inter_freq = target_inter_freq;      tune_result.actual_inter_freq = actual_inter_freq; -    tune_result.target_dsp_freq = target_dxc_freq; -    tune_result.actual_dsp_freq = actual_dxc_freq; +    tune_result.target_dsp_freq = target_dsp_freq; +    tune_result.actual_dsp_freq = actual_dsp_freq;      return tune_result;  } -static double derive_freq_from_xx_subdev_and_dxc( +static double derive_freq_from_xx_subdev_and_dsp(      dboard_iface::unit_t unit, -    wax::obj subdev, wax::obj dxc, size_t chan +    wax::obj subdev, wax::obj dsp, size_t chan  ){      //extract actual dsp and IF frequencies      double actual_inter_freq = subdev[SUBDEV_PROP_FREQ].as<double>(); -    std::string freq_name = dxc[DSP_PROP_FREQ_SHIFT_NAMES].as<prop_names_t>().at(chan); -    double actual_dxc_freq = dxc[named_prop_t(DSP_PROP_FREQ_SHIFT, freq_name)].as<double>(); +    std::string freq_name = dsp[DSP_PROP_FREQ_SHIFT_NAMES].as<prop_names_t>().at(chan); +    double actual_dsp_freq = dsp[named_prop_t(DSP_PROP_FREQ_SHIFT, freq_name)].as<double>(); -    //invert the sign on the dxc freq given the following conditions -    if (unit == dboard_iface::UNIT_TX) actual_dxc_freq *= -1.0; +    //invert the sign on the dsp freq given the following conditions +    if (unit == dboard_iface::UNIT_TX) actual_dsp_freq *= -1.0; -    return actual_inter_freq - actual_dxc_freq; +    return actual_inter_freq - actual_dsp_freq;  }  /*********************************************************************** @@ -84,27 +129,15 @@ static double derive_freq_from_xx_subdev_and_dxc(   **********************************************************************/  tune_result_t usrp::tune_rx_subdev_and_dsp(      wax::obj subdev, wax::obj ddc, size_t chan, -    double target_freq, double lo_offset -){ -    return tune_xx_subdev_and_dxc(dboard_iface::UNIT_RX, subdev, ddc, chan, target_freq, lo_offset); -} - -tune_result_t usrp::tune_rx_subdev_and_dsp( -    wax::obj subdev, wax::obj ddc, -    size_t chan, double target_freq +    const tune_request_t &tune_request  ){ -    double lo_offset = 0.0; -    //if the local oscillator will be in the passband, use an offset -    if (subdev[SUBDEV_PROP_USE_LO_OFFSET].as<bool>()){ -        lo_offset = 2.0*ddc[DSP_PROP_HOST_RATE].as<double>(); -    } -    return tune_rx_subdev_and_dsp(subdev, ddc, chan, target_freq, lo_offset); +    return tune_xx_subdev_and_dsp(dboard_iface::UNIT_RX, subdev, ddc, chan, tune_request);  }  double usrp::derive_freq_from_rx_subdev_and_dsp(      wax::obj subdev, wax::obj ddc, size_t chan  ){ -    return derive_freq_from_xx_subdev_and_dxc(dboard_iface::UNIT_RX, subdev, ddc, chan); +    return derive_freq_from_xx_subdev_and_dsp(dboard_iface::UNIT_RX, subdev, ddc, chan);  }  /*********************************************************************** @@ -112,25 +145,13 @@ double usrp::derive_freq_from_rx_subdev_and_dsp(   **********************************************************************/  tune_result_t usrp::tune_tx_subdev_and_dsp(      wax::obj subdev, wax::obj duc, size_t chan, -    double target_freq, double lo_offset +    const tune_request_t &tune_request  ){ -    return tune_xx_subdev_and_dxc(dboard_iface::UNIT_TX, subdev, duc, chan, target_freq, lo_offset); -} - -tune_result_t usrp::tune_tx_subdev_and_dsp( -    wax::obj subdev, wax::obj duc, -    size_t chan, double target_freq -){ -    double lo_offset = 0.0; -    //if the local oscillator will be in the passband, use an offset -    if (subdev[SUBDEV_PROP_USE_LO_OFFSET].as<bool>()){ -        lo_offset = 2.0*duc[DSP_PROP_HOST_RATE].as<double>(); -    } -    return tune_tx_subdev_and_dsp(subdev, duc, chan, target_freq, lo_offset); +    return tune_xx_subdev_and_dsp(dboard_iface::UNIT_TX, subdev, duc, chan, tune_request);  }  double usrp::derive_freq_from_tx_subdev_and_dsp(      wax::obj subdev, wax::obj duc, size_t chan  ){ -    return derive_freq_from_xx_subdev_and_dxc(dboard_iface::UNIT_TX, subdev, duc, chan); +    return derive_freq_from_xx_subdev_and_dsp(dboard_iface::UNIT_TX, subdev, duc, chan);  } diff --git a/host/lib/usrp/usrp1/codec_ctrl.cpp b/host/lib/usrp/usrp1/codec_ctrl.cpp index ad16f6b3a..4aa730573 100644 --- a/host/lib/usrp/usrp1/codec_ctrl.cpp +++ b/host/lib/usrp/usrp1/codec_ctrl.cpp @@ -61,6 +61,9 @@ public:      float get_tx_pga_gain(void);      void set_rx_pga_gain(float, char);      float get_rx_pga_gain(char); +     +    //rx adc buffer control +    void bypass_adc_buffers(bool bypass);  private:      usrp1_iface::sptr _iface; @@ -419,6 +422,17 @@ void usrp1_codec_ctrl_impl::set_duc_freq(double freq)  }  /*********************************************************************** + * Codec Control ADC buffer bypass + * Disable this for AC-coupled daughterboards (TVRX) + * By default it is initialized TRUE. + **********************************************************************/ +void usrp1_codec_ctrl_impl::bypass_adc_buffers(bool bypass) { +    _ad9862_regs.byp_buffer_a = bypass; +    _ad9862_regs.byp_buffer_b = bypass; +    this->send_reg(2); +} + +/***********************************************************************   * Codec Control Make   **********************************************************************/  usrp1_codec_ctrl::sptr usrp1_codec_ctrl::make(usrp1_iface::sptr iface, diff --git a/host/lib/usrp/usrp1/codec_ctrl.hpp b/host/lib/usrp/usrp1/codec_ctrl.hpp index 259d10ef4..e2e8a010d 100644 --- a/host/lib/usrp/usrp1/codec_ctrl.hpp +++ b/host/lib/usrp/usrp1/codec_ctrl.hpp @@ -92,6 +92,9 @@ public:      //! Set the TX modulator frequency      virtual void set_duc_freq(double freq) = 0; +     +    //! Enable or disable ADC buffer bypass +    virtual void bypass_adc_buffers(bool bypass) = 0;  };  #endif /* INCLUDED_USRP1_CODEC_CTRL_HPP */ diff --git a/host/lib/usrp/usrp1/codec_impl.cpp b/host/lib/usrp/usrp1/codec_impl.cpp index 1756c1ed4..db53be53e 100644 --- a/host/lib/usrp/usrp1/codec_impl.cpp +++ b/host/lib/usrp/usrp1/codec_impl.cpp @@ -45,7 +45,7 @@ void usrp1_impl::codec_init(void)  /***********************************************************************   * RX Codec Properties   **********************************************************************/ -static const std::string ad9862_pga_gain_name = "ad9862 pga"; +static const std::string adc_pga_gain_name = "PGA";  void usrp1_impl::rx_codec_get(const wax::obj &key_, wax::obj &val, dboard_slot_t dboard_slot)  { @@ -62,21 +62,21 @@ void usrp1_impl::rx_codec_get(const wax::obj &key_, wax::obj &val, dboard_slot_t          return;      case CODEC_PROP_GAIN_NAMES: -        val = prop_names_t(1, ad9862_pga_gain_name); +        val = prop_names_t(1, adc_pga_gain_name);          return;      case CODEC_PROP_GAIN_RANGE: -        UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); +        UHD_ASSERT_THROW(key.name == adc_pga_gain_name);          val = usrp1_codec_ctrl::rx_pga_gain_range;          return;      case CODEC_PROP_GAIN_I: -        UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); +        UHD_ASSERT_THROW(key.name == adc_pga_gain_name);          val = _codec_ctrls[dboard_slot]->get_rx_pga_gain('A');          return;      case CODEC_PROP_GAIN_Q: -        UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); +        UHD_ASSERT_THROW(key.name == adc_pga_gain_name);          val = _codec_ctrls[dboard_slot]->get_rx_pga_gain('B');          return; @@ -91,12 +91,12 @@ void usrp1_impl::rx_codec_set(const wax::obj &key_, const wax::obj &val, dboard_      //handle the set request conditioned on the key      switch(key.as<codec_prop_t>()) {      case CODEC_PROP_GAIN_I: -        UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); +        UHD_ASSERT_THROW(key.name == adc_pga_gain_name);          _codec_ctrls[dboard_slot]->set_rx_pga_gain(val.as<float>(), 'A');          return;      case CODEC_PROP_GAIN_Q: -        UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); +        UHD_ASSERT_THROW(key.name == adc_pga_gain_name);          _codec_ctrls[dboard_slot]->set_rx_pga_gain(val.as<float>(), 'B');          return; @@ -107,6 +107,8 @@ void usrp1_impl::rx_codec_set(const wax::obj &key_, const wax::obj &val, dboard_  /***********************************************************************   * TX Codec Properties   **********************************************************************/ +static const std::string dac_pga_gain_name = "PGA"; +  void usrp1_impl::tx_codec_get(const wax::obj &key_, wax::obj &val, dboard_slot_t dboard_slot)  {      named_prop_t key = named_prop_t::extract(key_); @@ -122,17 +124,17 @@ void usrp1_impl::tx_codec_get(const wax::obj &key_, wax::obj &val, dboard_slot_t          return;      case CODEC_PROP_GAIN_NAMES: -        val = prop_names_t(1, ad9862_pga_gain_name); +        val = prop_names_t(1, dac_pga_gain_name);          return;      case CODEC_PROP_GAIN_RANGE: -        UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); +        UHD_ASSERT_THROW(key.name == dac_pga_gain_name);          val = usrp1_codec_ctrl::tx_pga_gain_range;          return;      case CODEC_PROP_GAIN_I: //only one gain for I and Q      case CODEC_PROP_GAIN_Q: -        UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); +        UHD_ASSERT_THROW(key.name == dac_pga_gain_name);          val = _codec_ctrls[dboard_slot]->get_tx_pga_gain();          return; @@ -148,7 +150,7 @@ void usrp1_impl::tx_codec_set(const wax::obj &key_, const wax::obj &val, dboard_      switch(key.as<codec_prop_t>()){      case CODEC_PROP_GAIN_I: //only one gain for I and Q      case CODEC_PROP_GAIN_Q: -        UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); +        UHD_ASSERT_THROW(key.name == dac_pga_gain_name);          _codec_ctrls[dboard_slot]->set_tx_pga_gain(val.as<float>());          return; diff --git a/host/lib/usrp/usrp1/dboard_iface.cpp b/host/lib/usrp/usrp1/dboard_iface.cpp index 4791b55ce..1ac15a46a 100644 --- a/host/lib/usrp/usrp1/dboard_iface.cpp +++ b/host/lib/usrp/usrp1/dboard_iface.cpp @@ -32,6 +32,8 @@ using namespace uhd;  using namespace uhd::usrp;  using namespace boost::assign; +static const dboard_id_t tvrx_id(0x0040); +  class usrp1_dboard_iface : public dboard_iface {  public: @@ -51,6 +53,10 @@ public:          //init the clock rate shadows          this->set_clock_rate(UNIT_RX, this->get_clock_rates(UNIT_RX).front());          this->set_clock_rate(UNIT_TX, this->get_clock_rates(UNIT_TX).front()); +         +        //yes this is evil but it's necessary for TVRX to work on USRP1 +        if(_rx_dboard_id == tvrx_id) _codec->bypass_adc_buffers(false); +        //else _codec->bypass_adc_buffers(false); //don't think this is necessary      }      ~usrp1_dboard_iface() @@ -93,6 +99,7 @@ public:      std::vector<double> get_clock_rates(unit_t);      double get_clock_rate(unit_t);      void set_clock_enabled(unit_t, bool); +    double get_codec_rate(unit_t);  private:      usrp1_iface::sptr _iface; @@ -170,6 +177,10 @@ void usrp1_dboard_iface::set_clock_enabled(unit_t, bool)      //TODO we can only enable for special case anyway...  } +double usrp1_dboard_iface::get_codec_rate(unit_t){ +    return _clock->get_master_clock_freq(); +} +  /***********************************************************************   * GPIO   **********************************************************************/ diff --git a/host/lib/usrp/usrp1/dboard_impl.cpp b/host/lib/usrp/usrp1/dboard_impl.cpp index 3a8480e1b..2a2762a82 100644 --- a/host/lib/usrp/usrp1/dboard_impl.cpp +++ b/host/lib/usrp/usrp1/dboard_impl.cpp @@ -124,6 +124,7 @@ void usrp1_impl::rx_dboard_get(const wax::obj &key_, wax::obj &val, dboard_slot_      case DBOARD_PROP_GAIN_GROUP:          val = make_gain_group( +            _rx_db_eeproms[dboard_slot].id,              _dboard_managers[dboard_slot]->get_rx_subdev(key.name),              _rx_codec_proxies[dboard_slot]->get_link(),              GAIN_GROUP_POLICY_RX @@ -188,6 +189,7 @@ void usrp1_impl::tx_dboard_get(const wax::obj &key_, wax::obj &val, dboard_slot_      case DBOARD_PROP_GAIN_GROUP:          val = make_gain_group( +            _tx_db_eeproms[dboard_slot].id,              _dboard_managers[dboard_slot]->get_tx_subdev(key.name),              _tx_codec_proxies[dboard_slot]->get_link(),              GAIN_GROUP_POLICY_TX diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp index 73974f2d6..6728d9b15 100644 --- a/host/lib/usrp/usrp1/io_impl.cpp +++ b/host/lib/usrp/usrp1/io_impl.cpp @@ -33,33 +33,26 @@ using namespace uhd::usrp;  using namespace uhd::transport;  namespace asio = boost::asio; +static const size_t alignment_padding = 512; +  /*********************************************************************** - * Pseudo send buffer implementation + * Helper struct to associate an offset with a buffer   **********************************************************************/ -class pseudo_managed_send_buffer : public managed_send_buffer{ +class offset_send_buffer{  public: +    typedef boost::shared_ptr<offset_send_buffer> sptr; -    pseudo_managed_send_buffer( -        const boost::asio::mutable_buffer &buff, -        const boost::function<ssize_t(size_t)> &commit -    ): -        _buff(buff), -        _commit(commit) -    { -        /* NOP */ +    static sptr make(managed_send_buffer::sptr buff, size_t offset = 0){ +        return sptr(new offset_send_buffer(buff, offset));      } -    ssize_t commit(size_t num_bytes){ -        return _commit(num_bytes); -    } +    //member variables +    managed_send_buffer::sptr buff; +    size_t offset; /* in bytes */  private: -    const boost::asio::mutable_buffer &get(void) const{ -        return _buff; -    } - -    const boost::asio::mutable_buffer      _buff; -    const boost::function<ssize_t(size_t)> _commit; +    offset_send_buffer(managed_send_buffer::sptr buff, size_t offset): +        buff(buff), offset(offset){/* NOP */}  };  /*********************************************************************** @@ -70,8 +63,8 @@ struct usrp1_impl::io_impl{          data_transport(data_transport),          underflow_poll_samp_count(0),          overflow_poll_samp_count(0), -        send_buff(data_transport->get_send_buff()), -        num_bytes_committed(0) +        curr_buff_committed(true), +        curr_buff(offset_send_buffer::make(data_transport->get_send_buff()))      {          /* NOP */      } @@ -91,100 +84,91 @@ struct usrp1_impl::io_impl{      size_t overflow_poll_samp_count;      //wrapper around the actual send buffer interface -    //all of this to ensure only full buffers are committed -    managed_send_buffer::sptr send_buff; -    size_t num_bytes_committed; -    boost::uint8_t pseudo_buff[BYTES_PER_PACKET]; -    ssize_t phony_commit_pseudo_buff(size_t num_bytes); -    ssize_t phony_commit_send_buff(size_t num_bytes); -    ssize_t commit_send_buff(void); +    //all of this to ensure only aligned lengths are committed +    //NOTE: you must commit before getting a new buffer +    //since the vrt packet handler obeys this, we are ok +    bool curr_buff_committed; +    offset_send_buffer::sptr curr_buff; +    void commit_send_buff(offset_send_buffer::sptr, offset_send_buffer::sptr, size_t);      void flush_send_buff(void); -    bool get_send_buffs(vrt_packet_handler::managed_send_buffs_t &); - -    //helpers to get at the send buffer + offset -    inline void *get_send_mem_ptr(void){ -        return send_buff->cast<boost::uint8_t *>() + num_bytes_committed; -    } -    inline size_t get_send_mem_size(void){ -        return send_buff->size() - num_bytes_committed; -    } +    bool get_send_buffs(vrt_packet_handler::managed_send_buffs_t &, double);  };  /*! - * Accept a commit of num bytes to the pseudo buffer. - * Memcpy the entire contents of pseudo buffer into send buffers. - * - * Under most conditions: - *   The first loop iteration will fill the remainder of the send buffer. - *   The second loop iteration will empty the pseudo buffer remainder. + * Perform an actual commit on the send buffer: + * Copy the remainder of alignment to the next buffer. + * Commit the current buffer at multiples of alignment.   */ -ssize_t usrp1_impl::io_impl::phony_commit_pseudo_buff(size_t num_bytes){ -    size_t bytes_to_copy = num_bytes, bytes_copied = 0; -    while(bytes_to_copy){ -        size_t bytes_copied_here = std::min(bytes_to_copy, get_send_mem_size()); -        std::memcpy(get_send_mem_ptr(), pseudo_buff + bytes_copied, bytes_copied_here); -        ssize_t ret = phony_commit_send_buff(bytes_copied_here); -        if (ret < 0) return ret; -        bytes_to_copy -= ret; -        bytes_copied += ret; -    } -    return bytes_copied; -} +void usrp1_impl::io_impl::commit_send_buff( +    offset_send_buffer::sptr curr, +    offset_send_buffer::sptr next, +    size_t num_bytes +){ +    //total number of bytes now in the current buffer +    size_t bytes_in_curr_buffer = curr->offset + num_bytes; + +    //calculate how many to commit and remainder +    size_t num_bytes_remaining = bytes_in_curr_buffer % alignment_padding; +    size_t num_bytes_to_commit = bytes_in_curr_buffer - num_bytes_remaining; + +    //copy the remainder into the next buffer +    std::memcpy( +        next->buff->cast<char *>() + next->offset, +        curr->buff->cast<char *>() + num_bytes_to_commit, +        num_bytes_remaining +    ); -/*! - * Accept a commit of num bytes to the send buffer. - * Conditionally commit the send buffer if full. - */ -ssize_t usrp1_impl::io_impl::phony_commit_send_buff(size_t num_bytes){ -    num_bytes_committed += num_bytes; -    if (num_bytes_committed != send_buff->size()) return num_bytes; -    ssize_t ret = commit_send_buff(); -    return (ret < 0)? ret : num_bytes; +    //update the offset into the next buffer +    next->offset += num_bytes_remaining; + +    //commit the current buffer +    curr->buff->commit(num_bytes_to_commit); +    curr_buff_committed = true;  }  /*! - * Flush the send buffer: - * Zero-pad the send buffer to the nearest 512 byte boundary and commit. + * Flush the current buffer by padding out to alignment and committing.   */  void usrp1_impl::io_impl::flush_send_buff(void){ -    size_t bytes_to_pad = (-1*num_bytes_committed)%512; -    std::memset(get_send_mem_ptr(), 0, bytes_to_pad); -    num_bytes_committed += bytes_to_pad; -    commit_send_buff(); +    //calculate the number of bytes to alignment +    size_t bytes_to_pad = (-1*curr_buff->offset)%alignment_padding; + +    //get the buffer, clear, and commit (really current buffer) +    vrt_packet_handler::managed_send_buffs_t buffs(1); +    if (this->get_send_buffs(buffs, 0.1)){ +        std::memset(buffs[0]->cast<void *>(), 0, bytes_to_pad); +        buffs[0]->commit(bytes_to_pad); +    }  }  /*! - * Perform an actual commit on the send buffer: - * Commit the contents of the send buffer and request a new buffer. + * Get a managed send buffer with the alignment padding: + * Always grab the next send buffer so we can timeout here.   */ -ssize_t usrp1_impl::io_impl::commit_send_buff(void){ -    ssize_t ret = send_buff->commit(num_bytes_committed); -    send_buff = data_transport->get_send_buff(); -    num_bytes_committed = 0; -    return ret; -} -  bool usrp1_impl::io_impl::get_send_buffs( -    vrt_packet_handler::managed_send_buffs_t &buffs +    vrt_packet_handler::managed_send_buffs_t &buffs, double timeout  ){ -    UHD_ASSERT_THROW(buffs.size() == 1); +    UHD_ASSERT_THROW(curr_buff_committed and buffs.size() == 1); + +    //try to get a new managed buffer with timeout +    offset_send_buffer::sptr next_buff(offset_send_buffer::make(data_transport->get_send_buff(timeout))); +    if (not next_buff->buff.get()) return false; /* propagate timeout here */ + +    //calculate the buffer pointer and size given the offset +    //references to the buffers are held in the bound function +    buffs[0] = managed_send_buffer::make_safe( +        boost::asio::buffer( +            curr_buff->buff->cast<char *>() + curr_buff->offset, +            curr_buff->buff->size()         - curr_buff->offset +        ), +        boost::bind(&usrp1_impl::io_impl::commit_send_buff, this, curr_buff, next_buff, _1) +    ); -    //not enough bytes free -> use the pseudo buffer -    if (get_send_mem_size() < BYTES_PER_PACKET){ -        buffs[0] = managed_send_buffer::sptr(new pseudo_managed_send_buffer( -            boost::asio::buffer(pseudo_buff), -            boost::bind(&usrp1_impl::io_impl::phony_commit_pseudo_buff, this, _1) -        )); -    } -    //otherwise use the send buffer offset by the bytes written -    else{ -        buffs[0] = managed_send_buffer::sptr(new pseudo_managed_send_buffer( -            boost::asio::buffer(get_send_mem_ptr(), get_send_mem_size()), -            boost::bind(&usrp1_impl::io_impl::phony_commit_send_buff, this, _1) -        )); -    } +    //store the next buffer for the next call +    curr_buff = next_buff; +    curr_buff_committed = false; -    return buffs[0].get() != NULL; +    return true;  }  /*********************************************************************** @@ -213,10 +197,17 @@ static void usrp1_bs_vrt_packer(      if_packet_info.num_packet_words32 = if_packet_info.num_payload_words32;  } +size_t usrp1_impl::get_max_send_samps_per_packet(void) const { +    return (_data_transport->get_send_frame_size() - alignment_padding) +        / _tx_otw_type.get_sample_size() +        / _tx_subdev_spec.size() +    ; +} +  size_t usrp1_impl::send(      const std::vector<const void *> &buffs, size_t num_samps,      const tx_metadata_t &metadata, const io_type_t &io_type, -    send_mode_t send_mode +    send_mode_t send_mode, double timeout  ){      size_t num_samps_sent = vrt_packet_handler::send(          _io_impl->packet_handler_send_state,       //last state of the send handler @@ -225,7 +216,7 @@ size_t usrp1_impl::send(          io_type, _tx_otw_type,                     //input and output types to convert          _clock_ctrl->get_master_clock_freq(),      //master clock tick rate          &usrp1_bs_vrt_packer, -        boost::bind(&usrp1_impl::io_impl::get_send_buffs, _io_impl.get(), _1), +        boost::bind(&usrp1_impl::io_impl::get_send_buffs, _io_impl.get(), _1, timeout),          get_max_send_samps_per_packet(),          0,                                         //vrt header offset          _tx_subdev_spec.size()                     //num channels @@ -272,18 +263,25 @@ static void usrp1_bs_vrt_unpacker(  }  static bool get_recv_buffs( -    zero_copy_if::sptr zc_if, +    zero_copy_if::sptr zc_if, double timeout,      vrt_packet_handler::managed_recv_buffs_t &buffs  ){      UHD_ASSERT_THROW(buffs.size() == 1); -    buffs[0] = zc_if->get_recv_buff(); +    buffs[0] = zc_if->get_recv_buff(timeout);      return buffs[0].get() != NULL;  } +size_t usrp1_impl::get_max_recv_samps_per_packet(void) const { +    return _data_transport->get_recv_frame_size() +        / _rx_otw_type.get_sample_size() +        / _rx_subdev_spec.size() +    ; +} +  size_t usrp1_impl::recv(      const std::vector<void *> &buffs, size_t num_samps,      rx_metadata_t &metadata, const io_type_t &io_type, -    recv_mode_t recv_mode, size_t /*timeout_ms TODO*/ +    recv_mode_t recv_mode, double timeout  ){      size_t num_samps_recvd = vrt_packet_handler::recv(          _io_impl->packet_handler_recv_state,       //last state of the recv handler @@ -292,7 +290,7 @@ size_t usrp1_impl::recv(          io_type, _rx_otw_type,                     //input and output types to convert          _clock_ctrl->get_master_clock_freq(),      //master clock tick rate          &usrp1_bs_vrt_unpacker, -        boost::bind(&get_recv_buffs, _data_transport, _1), +        boost::bind(&get_recv_buffs, _data_transport, timeout, _1),          &vrt_packet_handler::handle_overflow_nop,          0,                                         //vrt header offset          _rx_subdev_spec.size()                     //num channels diff --git a/host/lib/usrp/usrp1/mboard_impl.cpp b/host/lib/usrp/usrp1/mboard_impl.cpp index fe3774eb4..669b20efa 100644 --- a/host/lib/usrp/usrp1/mboard_impl.cpp +++ b/host/lib/usrp/usrp1/mboard_impl.cpp @@ -98,7 +98,7 @@ static boost::uint32_t calc_rx_mux(      //    for all quadrature sources: Z = 0      //    for mixed sources: warning + Z = 0      int Z = (num_quads > 0)? 0 : 1; -    if (num_quads != 0 and num_reals != 0) uhd::print_warning( +    if (num_quads != 0 and num_reals != 0) uhd::warning::post(          "Mixing real and quadrature rx subdevices is not supported.\n"          "The Q input to the real source(s) will be non-zero.\n"      ); diff --git a/host/lib/usrp/usrp1/usrp1_ctrl.cpp b/host/lib/usrp/usrp1/usrp1_ctrl.cpp index 1dc6e6e25..5043aed7d 100644 --- a/host/lib/usrp/usrp1/usrp1_ctrl.cpp +++ b/host/lib/usrp/usrp1/usrp1_ctrl.cpp @@ -38,6 +38,8 @@ enum firmware_code {  #define FX2_FIRMWARE_LOAD 0xa0 +static const bool load_img_msg = true; +  /***********************************************************************   * Helper Functions   **********************************************************************/ @@ -178,6 +180,7 @@ public:          unsigned char reset_n = 0;          //hit the reset line +        if (load_img_msg) std::cout << "Loading firmware image: " << filestring << "..." << std::flush;          usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0,                             &reset_y, 1); @@ -206,14 +209,14 @@ public:              }                //type 0x01 is end               else if (type == 0x01) { +                usrp_set_firmware_hash(hash); //set hash before reset                  usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0,                                     &reset_n, 1); -                usrp_set_firmware_hash(hash);                  file.close();                  //wait for things to settle                  boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); - +                if (load_img_msg) std::cout << " done" << std::endl;                  return USRP_FIRMWARE_LOAD_SUCCESS;               }              //type anything else is unhandled @@ -249,6 +252,7 @@ public:          unsigned char buf[ep0_size];          int ret; +        if (load_img_msg) std::cout << "Loading FPGA image: " << filestring << "..." << std::flush;          std::ifstream file;          file.open(filename, std::ios::in | std::ios::binary);          if (not file.good()) { @@ -263,11 +267,12 @@ public:              return -1;          } -        ssize_t n; -        while ((n = file.readsome((char *)buf, sizeof(buf))) > 0) { +        while (not file.eof()) { +            file.read((char *)buf, sizeof(buf)); +            size_t n = file.gcount();              ret = usrp_control_write(VRQ_FPGA_LOAD, 0, FL_XFER,                                       buf, n); -            if (ret != n) { +            if (ret < 0 or size_t(ret) != n) {                  std::cerr << "fpga load error " << ret << std::endl;                  file.close();                  return -1; @@ -282,6 +287,7 @@ public:          usrp_set_fpga_hash(hash);          file.close(); +        if (load_img_msg) std::cout << " done" << std::endl;          return 0;       } diff --git a/host/lib/usrp/usrp1/usrp1_iface.cpp b/host/lib/usrp/usrp1/usrp1_iface.cpp index 5fd3987d5..64ced2905 100644 --- a/host/lib/usrp/usrp1/usrp1_iface.cpp +++ b/host/lib/usrp/usrp1/usrp1_iface.cpp @@ -49,7 +49,7 @@ public:       ******************************************************************/      void poke32(boost::uint32_t addr, boost::uint32_t value)      { -        boost::uint32_t swapped = byteswap(value); +        boost::uint32_t swapped = uhd::htonx(value);          if (iface_debug) {              std::cout.fill('0'); @@ -72,12 +72,6 @@ public:              std::cerr << "USRP: failed memory write: " << ret << std::endl;      } -    void poke16(boost::uint32_t, boost::uint16_t) -    { -        //fpga only handles 32 bit writes -        std::cerr << "USRP: unsupported operation: poke16()" << std::endl; -    } -      boost::uint32_t peek32(boost::uint32_t addr)      {          boost::uint32_t value_out; @@ -95,13 +89,7 @@ public:          if (ret < 0)              std::cerr << "USRP: failed memory read: " << ret << std::endl; -        return byteswap(value_out); -    } - -    boost::uint16_t peek16(boost::uint32_t addr) -    { -        boost::uint32_t val = peek32(addr); -        return boost::uint16_t(val & 0xff); +        return uhd::ntohx(value_out);      }      /******************************************************************* diff --git a/host/lib/usrp/usrp1/usrp1_iface.hpp b/host/lib/usrp/usrp1/usrp1_iface.hpp index 9a3fdd6bc..3f608584a 100644 --- a/host/lib/usrp/usrp1/usrp1_iface.hpp +++ b/host/lib/usrp/usrp1/usrp1_iface.hpp @@ -54,20 +54,6 @@ public:      virtual boost::uint32_t peek32(boost::uint32_t addr) = 0;      /*! -     * Write a register (16 bits) -     * \param addr the address -     * \param data the 16bit data -     */ -    virtual void poke16(boost::uint32_t addr, boost::uint16_t data) = 0; - -    /*! -     * read a register (16 bits) -     * \param addr the address -     * \return the 16bit data -     */ -    virtual boost::uint16_t peek16(boost::uint32_t addr) = 0; - -    /*!       * Perform an spi transaction.       * \param which_slave the slave device number       * \param config spi config args diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index 627180b11..314384e72 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -30,6 +30,7 @@  #include <boost/assign/list_of.hpp>  #include <boost/filesystem.hpp>  #include <boost/thread/thread.hpp> +#include <boost/lexical_cast.hpp>  #include <iostream>  using namespace uhd; @@ -63,7 +64,7 @@ static device_addrs_t usrp1_find(const device_addr_t &hint)          );      }      catch(...){ -        uhd::print_warning( +        uhd::warning::post(              "Could not locate USRP1 firmware.\n"              "Please install the images package.\n"          ); @@ -74,29 +75,29 @@ static device_addrs_t usrp1_find(const device_addr_t &hint)      boost::uint16_t vid = hint.has_key("uninit") ? FX2_VENDOR_ID : USRP1_VENDOR_ID;      boost::uint16_t pid = hint.has_key("uninit") ? FX2_PRODUCT_ID : USRP1_PRODUCT_ID; -    //see what we got on the USB bus -    std::vector<usb_device_handle::sptr> device_list = -        usb_device_handle::get_device_list(vid, pid); - -    if(device_list.size() == 0) return usrp1_addrs; //return nothing if no USRPs found +    // Important note: +    // The get device list calls are nested inside the for loop. +    // This allows the usb guts to decontruct when not in use, +    // so that re-enumeration after fw load can occur successfully. +    // This requirement is a courtesy of libusb1.0 on windows.      //find the usrps and load firmware -    BOOST_FOREACH(usb_device_handle::sptr handle, device_list) { -            usb_control::sptr ctrl_transport = usb_control::make(handle); -            usrp_ctrl::sptr usrp_ctrl = usrp_ctrl::make(ctrl_transport); -            usrp_ctrl->usrp_load_firmware(usrp1_fw_image); +    BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { +        usrp_ctrl::make(usb_control::make(handle))->usrp_load_firmware(usrp1_fw_image);      }      //get descriptors again with serial number, but using the initialized VID/PID now since we have firmware      vid = USRP1_VENDOR_ID;      pid = USRP1_PRODUCT_ID; -    device_list = usb_device_handle::get_device_list(vid, pid); -    BOOST_FOREACH(usb_device_handle::sptr handle, device_list) { -            device_addr_t new_addr; -            new_addr["type"] = "usrp1"; -            new_addr["serial"] = handle->get_serial(); +    BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { +        device_addr_t new_addr; +        new_addr["type"] = "usrp1"; +        new_addr["serial"] = handle->get_serial(); +        //this is a found usrp1 when a hint serial is not specified or it matches +        if (not hint.has_key("serial") or hint["serial"] == new_addr["serial"]){              usrp1_addrs.push_back(new_addr); +        }      }      return usrp1_addrs; @@ -105,8 +106,8 @@ static device_addrs_t usrp1_find(const device_addr_t &hint)  /***********************************************************************   * Make   **********************************************************************/ -static device::sptr usrp1_make(const device_addr_t &device_addr) -{ +static device::sptr usrp1_make(const device_addr_t &device_addr){ +      //extract the FPGA path for the USRP1      std::string usrp1_fpga_image = find_image_path(          device_addr.has_key("fpga")? device_addr["fpga"] : "usrp1_fpga.rbf" @@ -117,25 +118,26 @@ static device::sptr usrp1_make(const device_addr_t &device_addr)      std::vector<usb_device_handle::sptr> device_list =          usb_device_handle::get_device_list(USRP1_VENDOR_ID, USRP1_PRODUCT_ID); -    //create data and control transports -    usb_zero_copy::sptr data_transport; -    usrp_ctrl::sptr usrp_ctrl; - - -    BOOST_FOREACH(usb_device_handle::sptr handle, device_list) { -        if (handle->get_serial() == device_addr["serial"]) { -            usb_control::sptr ctrl_transport = usb_control::make(handle); -            usrp_ctrl = usrp_ctrl::make(ctrl_transport); -            usrp_ctrl->usrp_load_fpga(usrp1_fpga_image); - -            data_transport = usb_zero_copy::make(handle,        // identifier -                                                 6,             // IN endpoint -                                                 2,             // OUT endpoint -                                                 2 * (1 << 20), // buffer size -                                                 16384);        // transfer size +    //locate the matching handle in the device list +    usb_device_handle::sptr handle; +    BOOST_FOREACH(usb_device_handle::sptr dev_handle, device_list) { +        if (dev_handle->get_serial() == device_addr["serial"]){ +            handle = dev_handle;              break;          }      } +    UHD_ASSERT_THROW(handle.get() != NULL); //better be found + +    //create control objects and a data transport +    usb_control::sptr ctrl_transport = usb_control::make(handle); +    usrp_ctrl::sptr usrp_ctrl = usrp_ctrl::make(ctrl_transport); +    usrp_ctrl->usrp_load_fpga(usrp1_fpga_image); +    usb_zero_copy::sptr data_transport = usb_zero_copy::make( +        handle,        // identifier +        6,             // IN endpoint +        2,             // OUT endpoint +        device_addr    // param hints +    );      //create the usrp1 implementation guts      return device::sptr(new usrp1_impl(data_transport, usrp_ctrl)); @@ -171,7 +173,7 @@ usrp1_impl::usrp1_impl(uhd::transport::usb_zero_copy::sptr data_transport,      //initialize the mboard      mboard_init(); -    //initialize the dboards  +    //initialize the dboards      dboard_init();      //initialize the dsps @@ -195,9 +197,9 @@ usrp1_impl::~usrp1_impl(void){      /* NOP */  } -bool usrp1_impl::recv_async_msg(uhd::async_metadata_t &, size_t timeout_ms){ +bool usrp1_impl::recv_async_msg(uhd::async_metadata_t &, double timeout){      //dummy fill-in for the recv_async_msg -    boost::this_thread::sleep(boost::posix_time::milliseconds(timeout_ms)); +    boost::this_thread::sleep(boost::posix_time::microseconds(long(timeout*1e6)));      return false;  } diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp index 20ae3c02a..ff4d40762 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.hpp +++ b/host/lib/usrp/usrp1/usrp1_impl.hpp @@ -83,25 +83,18 @@ public:                  size_t,                  const uhd::tx_metadata_t &,                  const uhd::io_type_t &, -                send_mode_t); +                send_mode_t, double);      size_t recv(const std::vector<void *> &,                  size_t, uhd::rx_metadata_t &,                  const uhd::io_type_t &, -                recv_mode_t, -                size_t timeout); +                recv_mode_t, double); -    static const size_t BYTES_PER_PACKET = 512*4; //under the transfer size +    size_t get_max_send_samps_per_packet(void) const; -    size_t get_max_send_samps_per_packet(void) const { -        return BYTES_PER_PACKET/_tx_otw_type.get_sample_size()/_tx_subdev_spec.size(); -    } - -    size_t get_max_recv_samps_per_packet(void) const { -        return BYTES_PER_PACKET/_rx_otw_type.get_sample_size()/_rx_subdev_spec.size(); -    } +    size_t get_max_recv_samps_per_packet(void) const; -    bool recv_async_msg(uhd::async_metadata_t &, size_t); +    bool recv_async_msg(uhd::async_metadata_t &, double);  private:      /*! diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp index f6d2b718a..fdfbf0d17 100644 --- a/host/lib/usrp/usrp2/dboard_iface.cpp +++ b/host/lib/usrp/usrp2/dboard_iface.cpp @@ -61,6 +61,7 @@ public:      double get_clock_rate(unit_t);      std::vector<double> get_clock_rates(unit_t);      void set_clock_enabled(unit_t, bool); +    double get_codec_rate(unit_t);      void write_spi(          unit_t unit, @@ -158,6 +159,9 @@ void usrp2_dboard_iface::set_clock_enabled(unit_t unit, bool enb){      }  } +double usrp2_dboard_iface::get_codec_rate(unit_t){ +    return _clock_ctrl->get_master_clock_rate(); +}  /***********************************************************************   * GPIO   **********************************************************************/ diff --git a/host/lib/usrp/usrp2/dboard_impl.cpp b/host/lib/usrp/usrp2/dboard_impl.cpp index a462b93c2..540c9fefb 100644 --- a/host/lib/usrp/usrp2/dboard_impl.cpp +++ b/host/lib/usrp/usrp2/dboard_impl.cpp @@ -89,6 +89,7 @@ void usrp2_mboard_impl::rx_dboard_get(const wax::obj &key_, wax::obj &val){      case DBOARD_PROP_GAIN_GROUP:          val = make_gain_group( +            _rx_db_eeprom.id,              _dboard_manager->get_rx_subdev(key.name),              _rx_codec_proxy->get_link(),              GAIN_GROUP_POLICY_RX @@ -145,6 +146,7 @@ void usrp2_mboard_impl::tx_dboard_get(const wax::obj &key_, wax::obj &val){      case DBOARD_PROP_GAIN_GROUP:          val = make_gain_group( +            _tx_db_eeprom.id,              _dboard_manager->get_tx_subdev(key.name),              _tx_codec_proxy->get_link(),              GAIN_GROUP_POLICY_TX diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index 91a1b2344..bbe9c273f 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -46,7 +46,7 @@ struct usrp2_impl::io_impl{      io_impl(size_t num_frames, size_t width):          packet_handler_recv_state(width), -        recv_pirate_booty(alignment_buffer_type::make(num_frames, width)), +        recv_pirate_booty(alignment_buffer_type::make(num_frames-3, width)),          async_msg_fifo(bounded_buffer<async_metadata_t>::make(100/*messages deep*/))      {          /* NOP */ @@ -58,9 +58,9 @@ struct usrp2_impl::io_impl{          recv_pirate_crew.join_all();      } -    bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs, size_t timeout_ms){ +    bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs, double timeout){          boost::this_thread::disable_interruption di; //disable because the wait can throw -        return recv_pirate_booty->pop_elems_with_timed_wait(buffs, boost::posix_time::milliseconds(timeout_ms)); +        return recv_pirate_booty->pop_elems_with_timed_wait(buffs, timeout);      }      //state management for the vrt packet handler code @@ -150,7 +150,7 @@ void usrp2_impl::io_init(void){          std::memcpy(send_buff->cast<void*>(), &data, sizeof(data));          send_buff->commit(sizeof(data));          //drain the recv buffers (may have junk) -        while (data_transport->get_recv_buff().get()); +        while (data_transport->get_recv_buff().get()){};      }      //the number of recv frames is the number for the first transport @@ -168,22 +168,16 @@ void usrp2_impl::io_init(void){              _mboards.at(i), i          ));      } - -    std::cout << "RX samples per packet: " << get_max_recv_samps_per_packet() << std::endl; -    std::cout << "TX samples per packet: " << get_max_send_samps_per_packet() << std::endl; -    std::cout << "Recv pirate num frames: " << num_frames << std::endl;  }  /***********************************************************************   * Async Data   **********************************************************************/  bool usrp2_impl::recv_async_msg( -    async_metadata_t &async_metadata, size_t timeout_ms +    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, boost::posix_time::milliseconds(timeout_ms) -    ); +    return _io_impl->async_msg_fifo->pop_with_timed_wait(async_metadata, timeout);  }  /*********************************************************************** @@ -191,28 +185,40 @@ bool usrp2_impl::recv_async_msg(   **********************************************************************/  static bool get_send_buffs(      const std::vector<udp_zero_copy::sptr> &trans, -    vrt_packet_handler::managed_send_buffs_t &buffs +    vrt_packet_handler::managed_send_buffs_t &buffs, +    double timeout  ){      UHD_ASSERT_THROW(trans.size() == buffs.size()); +    bool good = true;      for (size_t i = 0; i < buffs.size(); i++){ -        buffs[i] = trans[i]->get_send_buff(); +        buffs[i] = trans[i]->get_send_buff(timeout); +        good = good and (buffs[i].get() != NULL);      } -    return true; +    return good; +} + +size_t usrp2_impl::get_max_send_samps_per_packet(void) const{ +    static const size_t hdr_size = 0 +        + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) +        - sizeof(vrt::if_packet_info_t().cid) //no class id ever used +    ; +    const size_t bpp = _data_transports.front()->get_send_frame_size() - hdr_size; +    return bpp/_tx_otw_type.get_sample_size();  }  size_t usrp2_impl::send(      const std::vector<const void *> &buffs, size_t num_samps,      const tx_metadata_t &metadata, const io_type_t &io_type, -    send_mode_t send_mode +    send_mode_t send_mode, double timeout  ){      return vrt_packet_handler::send(          _io_impl->packet_handler_send_state,       //last state of the send handler          buffs, num_samps,                          //buffer to fill          metadata, send_mode,                       //samples metadata -        io_type, _io_helper.get_tx_otw_type(),     //input and output types to convert +        io_type, _tx_otw_type,                     //input and output types to convert          _mboards.front()->get_master_clock_freq(), //master clock tick rate          uhd::transport::vrt::if_hdr_pack_be, -        boost::bind(&get_send_buffs, _data_transports, _1), +        boost::bind(&get_send_buffs, _data_transports, _1, timeout),          get_max_send_samps_per_packet()      );  } @@ -220,18 +226,28 @@ size_t usrp2_impl::send(  /***********************************************************************   * Receive Data   **********************************************************************/ +size_t usrp2_impl::get_max_recv_samps_per_packet(void) const{ +    static const size_t hdr_size = 0 +        + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) +        + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer +        - sizeof(vrt::if_packet_info_t().cid) //no class id ever used +    ; +    const size_t bpp = _data_transports.front()->get_recv_frame_size() - hdr_size; +    return bpp/_rx_otw_type.get_sample_size(); +} +  size_t usrp2_impl::recv(      const std::vector<void *> &buffs, size_t num_samps,      rx_metadata_t &metadata, const io_type_t &io_type, -    recv_mode_t recv_mode, size_t timeout_ms +    recv_mode_t recv_mode, double timeout  ){      return vrt_packet_handler::recv(          _io_impl->packet_handler_recv_state,       //last state of the recv handler          buffs, num_samps,                          //buffer to fill          metadata, recv_mode,                       //samples metadata -        io_type, _io_helper.get_rx_otw_type(),     //input and output types to convert +        io_type, _rx_otw_type,                     //input and output types to convert          _mboards.front()->get_master_clock_freq(), //master clock tick rate          uhd::transport::vrt::if_hdr_unpack_be, -        boost::bind(&usrp2_impl::io_impl::get_recv_buffs, _io_impl.get(), _1, timeout_ms) +        boost::bind(&usrp2_impl::io_impl::get_recv_buffs, _io_impl.get(), _1, timeout)      );  } diff --git a/host/lib/usrp/usrp2/mboard_impl.cpp b/host/lib/usrp/usrp2/mboard_impl.cpp index 0b9f8ee83..a0e6adfad 100644 --- a/host/lib/usrp/usrp2/mboard_impl.cpp +++ b/host/lib/usrp/usrp2/mboard_impl.cpp @@ -38,10 +38,10 @@ using namespace uhd::usrp;  usrp2_mboard_impl::usrp2_mboard_impl(      size_t index,      transport::udp_simple::sptr ctrl_transport, -    const usrp2_io_helper &io_helper +    size_t recv_frame_size  ):      _index(index), -    _io_helper(io_helper) +    _recv_frame_size(recv_frame_size)  {      //make a new interface for usrp2 stuff      _iface = usrp2_iface::make(ctrl_transport); @@ -75,7 +75,7 @@ usrp2_mboard_impl::usrp2_mboard_impl(      this->issue_ddc_stream_cmd(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);      //init the rx control registers -    _iface->poke32(U2_REG_RX_CTRL_NSAMPS_PER_PKT, _io_helper.get_max_recv_samps_per_packet()); +    _iface->poke32(U2_REG_RX_CTRL_NSAMPS_PER_PKT, _recv_frame_size);      _iface->poke32(U2_REG_RX_CTRL_NCHANNELS, 1);      _iface->poke32(U2_REG_RX_CTRL_CLEAR_OVERRUN, 1); //reset      _iface->poke32(U2_REG_RX_CTRL_VRT_HEADER, 0 @@ -178,7 +178,7 @@ void usrp2_mboard_impl::set_time_spec(const time_spec_t &time_spec, bool now){  void usrp2_mboard_impl::issue_ddc_stream_cmd(const stream_cmd_t &stream_cmd){      _iface->poke32(U2_REG_RX_CTRL_STREAM_CMD, dsp_type1::calc_stream_cmd_word( -        stream_cmd, _io_helper.get_max_recv_samps_per_packet() +        stream_cmd, _recv_frame_size      ));      _iface->poke32(U2_REG_RX_CTRL_TIME_SECS,  boost::uint32_t(stream_cmd.time_spec.get_full_secs()));      _iface->poke32(U2_REG_RX_CTRL_TIME_TICKS, stream_cmd.time_spec.get_tick_count(get_master_clock_freq())); diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 568c87a22..a680708ad 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -124,26 +124,7 @@ static uhd::device_addrs_t usrp2_find(const device_addr_t &hint){  /***********************************************************************   * Make   **********************************************************************/ -template <typename out_type, typename in_type> -out_type lexical_cast(const in_type &in){ -    try{ -        return boost::lexical_cast<out_type>(in); -    }catch(...){ -        throw std::runtime_error(str(boost::format( -            "failed to cast \"%s\" to type \"%s\"" -        ) % boost::lexical_cast<std::string>(in) % typeid(out_type).name())); -    } -} -  static device::sptr usrp2_make(const device_addr_t &device_addr){ -    //extract the receive and send buffer sizes -    size_t recv_buff_size = 0, send_buff_size= 0 ; -    if (device_addr.has_key("recv_buff_size")){ -        recv_buff_size = size_t(lexical_cast<double>(device_addr["recv_buff_size"])); -    } -    if (device_addr.has_key("send_buff_size")){ -        send_buff_size = size_t(lexical_cast<double>(device_addr["send_buff_size"])); -    }      //create a ctrl and data transport for each address      std::vector<udp_simple::sptr> ctrl_transports; @@ -154,8 +135,7 @@ static device::sptr usrp2_make(const device_addr_t &device_addr){              addr, num2str(USRP2_UDP_CTRL_PORT)          ));          data_transports.push_back(udp_zero_copy::make( -            addr, num2str(USRP2_UDP_DATA_PORT), -            recv_buff_size, send_buff_size +            addr, num2str(USRP2_UDP_DATA_PORT), device_addr          ));      } @@ -178,11 +158,23 @@ usrp2_impl::usrp2_impl(  ):      _data_transports(data_transports)  { +    //setup rx otw type +    _rx_otw_type.width = 16; +    _rx_otw_type.shift = 0; +    _rx_otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; + +    //setup tx otw type +    _tx_otw_type.width = 16; +    _tx_otw_type.shift = 0; +    _tx_otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; + +    //!!!!! set the otw type here before continuing, its used below +      //create a new mboard handler for each control transport      for(size_t i = 0; i < ctrl_transports.size(); i++){ -        _mboards.push_back(usrp2_mboard_impl::sptr( -            new usrp2_mboard_impl(i, ctrl_transports[i], _io_helper) -        )); +        _mboards.push_back(usrp2_mboard_impl::sptr(new usrp2_mboard_impl( +            i, ctrl_transports[i], this->get_max_recv_samps_per_packet() +        )));          //use an empty name when there is only one mboard          std::string name = (ctrl_transports.size() > 1)? boost::lexical_cast<std::string>(i) : "";          _mboard_dict[name] = _mboards.back(); diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 157d17057..558726a2b 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -72,54 +72,6 @@ private:  };  /*! - * The io helper class encapculates the max packet sizes and otw types. - * The otw types are read-only for now, this will be reimplemented - * when it becomes possible to change the otw type in the usrp2. - */ -class usrp2_io_helper{ -public: -    usrp2_io_helper(void){ -        //setup rx otw type -        _rx_otw_type.width = 16; -        _rx_otw_type.shift = 0; -        _rx_otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; - -        //setup tx otw type -        _tx_otw_type.width = 16; -        _tx_otw_type.shift = 0; -        _tx_otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; -    } - -    inline size_t get_max_send_samps_per_packet(void) const{ -        return _max_tx_bytes_per_packet/_tx_otw_type.get_sample_size(); -    } - -    inline size_t get_max_recv_samps_per_packet(void) const{ -        return _max_rx_bytes_per_packet/_rx_otw_type.get_sample_size(); -    } - -    inline const uhd::otw_type_t &get_rx_otw_type(void) const{ -        return _rx_otw_type; -    } - -    inline const uhd::otw_type_t &get_tx_otw_type(void) const{ -        return _tx_otw_type; -    } - -private: -    uhd::otw_type_t _rx_otw_type, _tx_otw_type; -    static const size_t _max_rx_bytes_per_packet = uhd::transport::udp_simple::mtu -        - uhd::transport::vrt::max_if_hdr_words32*sizeof(boost::uint32_t) -        - sizeof(uhd::transport::vrt::if_packet_info_t().tlr) //forced to have trailer -        + sizeof(uhd::transport::vrt::if_packet_info_t().cid) //no class id ever used -    ; -    static const size_t _max_tx_bytes_per_packet = uhd::transport::udp_simple::mtu -        - uhd::transport::vrt::max_if_hdr_words32*sizeof(boost::uint32_t) -        + sizeof(uhd::transport::vrt::if_packet_info_t().cid) //no class id ever used -    ; -}; - -/*!   * USRP2 mboard implementation guts:   * The implementation details are encapsulated here.   * Handles properties on the mboard, dboard, dsps... @@ -129,7 +81,11 @@ public:      typedef boost::shared_ptr<usrp2_mboard_impl> sptr;      //structors -    usrp2_mboard_impl(size_t index, uhd::transport::udp_simple::sptr, const usrp2_io_helper &); +    usrp2_mboard_impl( +        size_t index, +        uhd::transport::udp_simple::sptr, +        size_t recv_frame_size +    );      ~usrp2_mboard_impl(void);      inline double get_master_clock_freq(void){ @@ -139,7 +95,7 @@ public:  private:      size_t _index;      int _rev_hi, _rev_lo; -    const usrp2_io_helper &_io_helper; +    const size_t _recv_frame_size;      //properties for this mboard      void get(const wax::obj &, wax::obj &); @@ -228,23 +184,19 @@ public:      ~usrp2_impl(void);      //the io interface -    size_t get_max_send_samps_per_packet(void) const{ -        return _io_helper.get_max_send_samps_per_packet(); -    }      size_t send(          const std::vector<const void *> &, size_t,          const uhd::tx_metadata_t &, const uhd::io_type_t &, -        uhd::device::send_mode_t +        uhd::device::send_mode_t, double      ); -    size_t get_max_recv_samps_per_packet(void) const{ -        return _io_helper.get_max_recv_samps_per_packet(); -    }      size_t recv(          const std::vector<void *> &, size_t,          uhd::rx_metadata_t &, const uhd::io_type_t &, -        uhd::device::recv_mode_t, size_t +        uhd::device::recv_mode_t, double      ); -    bool recv_async_msg(uhd::async_metadata_t &, size_t); +    size_t get_max_send_samps_per_packet(void) const; +    size_t get_max_recv_samps_per_packet(void) const; +    bool recv_async_msg(uhd::async_metadata_t &, double);  private:      //device properties interface @@ -257,7 +209,7 @@ private:      //io impl methods and members      std::vector<uhd::transport::udp_zero_copy::sptr> _data_transports; -    const usrp2_io_helper _io_helper; +    uhd::otw_type_t _rx_otw_type, _tx_otw_type;      UHD_PIMPL_DECL(io_impl) _io_impl;      void io_init(void);  }; diff --git a/host/lib/usrp/usrp_e/CMakeLists.txt b/host/lib/usrp/usrp_e/CMakeLists.txt index fbcd19276..6c5d281dd 100644 --- a/host/lib/usrp/usrp_e/CMakeLists.txt +++ b/host/lib/usrp/usrp_e/CMakeLists.txt @@ -56,6 +56,7 @@ IF(ENABLE_USRP_E)          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_e/usrp_e_impl.hpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_e/usrp_e_iface.cpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_e/usrp_e_iface.hpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_e/usrp_e_mmap_zero_copy.cpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_e/usrp_e_regs.hpp      )  ELSE(ENABLE_USRP_E) diff --git a/host/lib/usrp/usrp_e/dboard_iface.cpp b/host/lib/usrp/usrp_e/dboard_iface.cpp index 1bd177f60..6898df8df 100644 --- a/host/lib/usrp/usrp_e/dboard_iface.cpp +++ b/host/lib/usrp/usrp_e/dboard_iface.cpp @@ -91,6 +91,7 @@ public:      std::vector<double> get_clock_rates(unit_t);      double get_clock_rate(unit_t);      void set_clock_enabled(unit_t, bool); +    double get_codec_rate(unit_t);  private:      usrp_e_iface::sptr _iface; @@ -140,6 +141,10 @@ void usrp_e_dboard_iface::set_clock_enabled(unit_t unit, bool enb){      }  } +double usrp_e_dboard_iface::get_codec_rate(unit_t){ +    return _clock->get_fpga_clock_rate(); +} +  /***********************************************************************   * GPIO   **********************************************************************/ diff --git a/host/lib/usrp/usrp_e/dboard_impl.cpp b/host/lib/usrp/usrp_e/dboard_impl.cpp index d4a27cb72..f2840dcfc 100644 --- a/host/lib/usrp/usrp_e/dboard_impl.cpp +++ b/host/lib/usrp/usrp_e/dboard_impl.cpp @@ -87,6 +87,7 @@ void usrp_e_impl::rx_dboard_get(const wax::obj &key_, wax::obj &val){      case DBOARD_PROP_GAIN_GROUP:          val = make_gain_group( +            _rx_db_eeprom.id,              _dboard_manager->get_rx_subdev(key.name),              _rx_codec_proxy->get_link(),              GAIN_GROUP_POLICY_RX @@ -145,6 +146,7 @@ void usrp_e_impl::tx_dboard_get(const wax::obj &key_, wax::obj &val){      case DBOARD_PROP_GAIN_GROUP:          val = make_gain_group( +            _tx_db_eeprom.id,              _dboard_manager->get_tx_subdev(key.name),              _tx_codec_proxy->get_link(),              GAIN_GROUP_POLICY_TX diff --git a/host/lib/usrp/usrp_e/dsp_impl.cpp b/host/lib/usrp/usrp_e/dsp_impl.cpp index 9312bb603..97f173c1a 100644 --- a/host/lib/usrp/usrp_e/dsp_impl.cpp +++ b/host/lib/usrp/usrp_e/dsp_impl.cpp @@ -65,11 +65,11 @@ void usrp_e_impl::rx_ddc_get(const wax::obj &key_, wax::obj &val){          return;      case DSP_PROP_CODEC_RATE: -        val = MASTER_CLOCK_RATE; +        val = _clock_ctrl->get_fpga_clock_rate();          return;      case DSP_PROP_HOST_RATE: -        val = MASTER_CLOCK_RATE/_ddc_decim; +        val = _clock_ctrl->get_fpga_clock_rate()/_ddc_decim;          return;      default: UHD_THROW_PROP_GET_ERROR(); @@ -87,7 +87,7 @@ void usrp_e_impl::rx_ddc_set(const wax::obj &key_, const wax::obj &val){      case DSP_PROP_FREQ_SHIFT:{              double new_freq = val.as<double>();              _iface->poke32(UE_REG_DSP_RX_FREQ, -                dsp_type1::calc_cordic_word_and_update(new_freq, MASTER_CLOCK_RATE) +                dsp_type1::calc_cordic_word_and_update(new_freq, _clock_ctrl->get_fpga_clock_rate())              );              _ddc_freq = new_freq; //shadow          } @@ -95,7 +95,7 @@ void usrp_e_impl::rx_ddc_set(const wax::obj &key_, const wax::obj &val){      case DSP_PROP_HOST_RATE:{              //set the decimation -            _ddc_decim = rint(MASTER_CLOCK_RATE/val.as<double>()); +            _ddc_decim = rint(_clock_ctrl->get_fpga_clock_rate()/val.as<double>());              _iface->poke32(UE_REG_DSP_RX_DECIM_RATE, dsp_type1::calc_cic_filter_word(_ddc_decim));              //set the scaling @@ -148,11 +148,11 @@ void usrp_e_impl::tx_duc_get(const wax::obj &key_, wax::obj &val){          return;      case DSP_PROP_CODEC_RATE: -        val = MASTER_CLOCK_RATE; +        val = _clock_ctrl->get_fpga_clock_rate();          return;      case DSP_PROP_HOST_RATE: -        val = MASTER_CLOCK_RATE/_duc_interp; +        val = _clock_ctrl->get_fpga_clock_rate()/_duc_interp;          return;      default: UHD_THROW_PROP_GET_ERROR(); @@ -170,14 +170,14 @@ void usrp_e_impl::tx_duc_set(const wax::obj &key_, const wax::obj &val){      case DSP_PROP_FREQ_SHIFT:{              double new_freq = val.as<double>();              _iface->poke32(UE_REG_DSP_TX_FREQ, -                dsp_type1::calc_cordic_word_and_update(new_freq, MASTER_CLOCK_RATE) +                dsp_type1::calc_cordic_word_and_update(new_freq, _clock_ctrl->get_fpga_clock_rate())              );              _duc_freq = new_freq; //shadow          }          return;      case DSP_PROP_HOST_RATE:{ -            _duc_interp = rint(MASTER_CLOCK_RATE/val.as<double>()); +            _duc_interp = rint(_clock_ctrl->get_fpga_clock_rate()/val.as<double>());              //set the interpolation              _iface->poke32(UE_REG_DSP_TX_INTERP_RATE, dsp_type1::calc_cic_filter_word(_duc_interp)); diff --git a/host/lib/usrp/usrp_e/fpga-downloader.cc b/host/lib/usrp/usrp_e/fpga-downloader.cc index ff8671e98..b0d56e856 100644 --- a/host/lib/usrp/usrp_e/fpga-downloader.cc +++ b/host/lib/usrp/usrp_e/fpga-downloader.cc @@ -15,11 +15,14 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // +#include <uhd/config.hpp> +  #include <iostream>  #include <sstream>  #include <fstream>  #include <string>  #include <cstdlib> +#include <stdexcept>  #include <fcntl.h>  #include <sys/types.h> @@ -82,8 +85,9 @@ gpio::gpio(unsigned int gpio_num, gpio_direction pin_direction)  	std::fstream export_file;  	export_file.open("/sys/class/gpio/export", std::ios::out); -	if (!export_file.is_open())  ///\todo Poor error handling -		std::cout << "Failed to open gpio export file." << std::endl; +	if (not export_file.is_open()) throw std::runtime_error( +		"Failed to open gpio export file." +	);  	export_file << gpio_num << std::endl; @@ -92,15 +96,17 @@ gpio::gpio(unsigned int gpio_num, gpio_direction pin_direction)  	std::fstream direction_file;  	std::string direction_file_name; -	direction_file_name = base_path.str() + "/direction"; +	if (gpio_num != 114) { +		direction_file_name = base_path.str() + "/direction"; -	direction_file.open(direction_file_name.c_str());  -	if (!direction_file.is_open()) -		std::cout << "Failed to open direction file." << std::endl; -	if (pin_direction == OUT) -		direction_file << "out" << std::endl; -	else -		direction_file << "in" << std::endl; +		direction_file.open(direction_file_name.c_str()); +		if (!direction_file.is_open()) +			std::cout << "Failed to open direction file." << std::endl; +		if (pin_direction == OUT) +			direction_file << "out" << std::endl; +		else +			direction_file << "in" << std::endl; +	}  	std::string value_file_name; @@ -251,11 +257,11 @@ void usrp_e_load_fpga(const std::string &bin_file){  	gpio gpio_init_b(INIT_B, IN);  	gpio gpio_done  (DONE,   IN); -	std::cout << "FPGA config file: " << bin_file << std::endl; +	std::cout << "Loading FPGA image: " << bin_file << "... " << std::flush;  	prepare_fpga_for_configuration(gpio_prog_b, gpio_init_b); -	std::cout << "Done = " << gpio_done.get_value() << std::endl; +	std::cout << "done = " << gpio_done.get_value() << std::endl;  	send_file_to_fpga(bin_file, gpio_init_b, gpio_done);  } diff --git a/host/lib/usrp/usrp_e/io_impl.cpp b/host/lib/usrp/usrp_e/io_impl.cpp index 31ea4c6c0..9996e7172 100644 --- a/host/lib/usrp/usrp_e/io_impl.cpp +++ b/host/lib/usrp/usrp_e/io_impl.cpp @@ -22,8 +22,6 @@  #include <uhd/transport/bounded_buffer.hpp>  #include "../../transport/vrt_packet_handler.hpp"  #include <boost/bind.hpp> -#include <fcntl.h> //read, write -#include <poll.h>  #include <boost/format.hpp>  #include <boost/thread.hpp>  #include <iostream> @@ -32,88 +30,14 @@ using namespace uhd;  using namespace uhd::usrp;  using namespace uhd::transport; +zero_copy_if::sptr usrp_e_make_mmap_zero_copy(usrp_e_iface::sptr iface); +  /***********************************************************************   * Constants   **********************************************************************/ -static const size_t MAX_BUFF_SIZE = 2048; -static const bool usrp_e_io_impl_verbose = false;  static const size_t tx_async_report_sid = 1;  static const int underflow_flags = async_metadata_t::EVENT_CODE_UNDERFLOW | async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET; - -/*********************************************************************** - * Data Transport (phony zero-copy with read/write) - **********************************************************************/ -class data_transport: -    public transport::phony_zero_copy_recv_if, -    public transport::phony_zero_copy_send_if -{ -public: -    data_transport(int fd): -        transport::phony_zero_copy_recv_if(MAX_BUFF_SIZE), -        transport::phony_zero_copy_send_if(MAX_BUFF_SIZE), -        _fd(fd) -    { -        /* NOP */ -    } - -    size_t get_num_recv_frames(void) const{ -        return 100; //FIXME no idea! -        //this will be an important number when packet ring gets implemented -    } - -    size_t get_num_send_frames(void) const{ -        return 100; //FIXME no idea! -        //this will be an important number when packet ring gets implemented -    } - -private: -    int _fd; -    ssize_t send(const boost::asio::const_buffer &buff){ -        return write(_fd, -            boost::asio::buffer_cast<const void *>(buff), -            boost::asio::buffer_size(buff) -        ); -    } -    ssize_t recv(const boost::asio::mutable_buffer &buff){ -        //std::cout << boost::format( -        //    "calling read on fd %d, buff size is %d" -        //) % _fd % boost::asio::buffer_size(buff) << std::endl; - -        //setup and call poll on the file descriptor -        //return 0 and do not read when poll times out -        pollfd pfd; -        pfd.fd = _fd; -        pfd.events = POLLIN; -        ssize_t poll_ret = poll(&pfd, 1, 100/*ms*/); -        if (poll_ret <= 0){ -            if (usrp_e_io_impl_verbose) std::cerr << boost::format( -                "usrp-e io impl recv(): poll() returned non-positive value: %d\n" -                "    -> return 0 for timeout" -            ) % poll_ret << std::endl; -            return 0; //timeout -        } - -        //perform the blocking read(...) -        ssize_t read_ret = read(_fd, -            boost::asio::buffer_cast<void *>(buff), -            boost::asio::buffer_size(buff) -        ); -        if (read_ret < 0){ -            if (usrp_e_io_impl_verbose) std::cerr << boost::format( -                "usrp-e io impl recv(): read() returned small value: %d\n" -                "    -> return -1 for error" -            ) % read_ret << std::endl; -            return -1; -        } - -        //std::cout << "len " << int(read_ret) << std::endl; -        //for (size_t i = 0; i < 9; i++){ -        //    std::cout << boost::format("    0x%08x") % boost::asio::buffer_cast<boost::uint32_t *>(buff)[i] << std::endl; -        //} - -        return read_ret; -    } -}; +static const bool recv_debug = false;  /***********************************************************************   * io impl details (internal to this file) @@ -126,11 +50,11 @@ struct usrp_e_impl::io_impl{      //state management for the vrt packet handler code      vrt_packet_handler::recv_state packet_handler_recv_state;      vrt_packet_handler::send_state packet_handler_send_state; -    data_transport transport; +    zero_copy_if::sptr data_xport;      bool continuous_streaming; -    io_impl(int fd): -        transport(fd), -        recv_pirate_booty(recv_booty_type::make(transport.get_num_recv_frames())), +    io_impl(usrp_e_iface::sptr iface): +        data_xport(usrp_e_make_mmap_zero_copy(iface)), +        recv_pirate_booty(recv_booty_type::make(data_xport->get_num_recv_frames())),          async_msg_fifo(bounded_buffer<async_metadata_t>::make(100/*messages deep*/))      {          /* NOP */ @@ -142,14 +66,14 @@ struct usrp_e_impl::io_impl{          recv_pirate_crew.join_all();      } -    bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs, size_t timeout_ms){ +    bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs, double timeout){          UHD_ASSERT_THROW(buffs.size() == 1);          boost::this_thread::disable_interruption di; //disable because the wait can throw -        return recv_pirate_booty->pop_with_timed_wait(buffs.front(), boost::posix_time::milliseconds(timeout_ms)); +        return recv_pirate_booty->pop_with_timed_wait(buffs.front(), timeout);      }      //a pirate's life is the life for me! -    void recv_pirate_loop(); +    void recv_pirate_loop(usrp_e_clock_ctrl::sptr);      typedef bounded_buffer<managed_recv_buffer::sptr> recv_booty_type;      recv_booty_type::sptr recv_pirate_booty;      bounded_buffer<async_metadata_t>::sptr async_msg_fifo; @@ -162,17 +86,23 @@ struct usrp_e_impl::io_impl{   * - while raiding, loot for recv buffers   * - put booty into the alignment buffer   **********************************************************************/ -void usrp_e_impl::io_impl::recv_pirate_loop( - -){ +void usrp_e_impl::io_impl::recv_pirate_loop(usrp_e_clock_ctrl::sptr clock_ctrl) +{      set_thread_priority_safe();      recv_pirate_crew_raiding = true; -    //size_t next_packet_seq = 0;      while(recv_pirate_crew_raiding){ -        managed_recv_buffer::sptr buff = this->transport.get_recv_buff(); +        managed_recv_buffer::sptr buff = this->data_xport->get_recv_buff();          if (not buff.get()) continue; //ignore timeout/error buffers +        if (recv_debug){ +            std::cout << "len " << buff->size() << std::endl; +            for (size_t i = 0; i < 9; i++){ +                std::cout << boost::format("    0x%08x") % buff->cast<const boost::uint32_t *>()[i] << std::endl; +            } +            std::cout << std::endl << std::endl; +        } +          try{              //extract the vrt header packet info              vrt::if_packet_info_t if_packet_info; @@ -188,7 +118,7 @@ void usrp_e_impl::io_impl::recv_pirate_loop(                  metadata.channel = 0;                  metadata.has_time_spec = if_packet_info.has_tsi and if_packet_info.has_tsf;                  metadata.time_spec = time_spec_t( -                    time_t(if_packet_info.tsi), size_t(if_packet_info.tsf), MASTER_CLOCK_RATE +                    time_t(if_packet_info.tsi), size_t(if_packet_info.tsf), clock_ctrl->get_fpga_clock_rate()                  );                  metadata.event_code = vrt_packet_handler::get_context_code<async_metadata_t::event_code_t>(vrt_hdr, if_packet_info); @@ -198,12 +128,12 @@ void usrp_e_impl::io_impl::recv_pirate_loop(                  continue;              } +            //same number of frames as the data transport -> always immediate +            recv_pirate_booty->push_with_wait(buff); +          }catch(const std::exception &e){              std::cerr << "Error (usrp-e recv pirate loop): " << e.what() << std::endl;          } - -        //usrp-e back-pressures on receive: push with wait -        recv_pirate_booty->push_with_wait(buff);      }  } @@ -211,6 +141,15 @@ void usrp_e_impl::io_impl::recv_pirate_loop(   * Helper Functions   **********************************************************************/  void usrp_e_impl::io_init(void){ +    //setup otw types +    _send_otw_type.width = 16; +    _send_otw_type.shift = 0; +    _send_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; + +    _recv_otw_type.width = 16; +    _recv_otw_type.shift = 0; +    _recv_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; +      //setup rx data path      _iface->poke32(UE_REG_CTRL_RX_NSAMPS_PER_PKT, get_max_recv_samps_per_packet());      _iface->poke32(UE_REG_CTRL_RX_NCHANNELS, 1); @@ -228,11 +167,11 @@ void usrp_e_impl::io_init(void){      _iface->poke32(UE_REG_CTRL_TX_REPORT_SID, tx_async_report_sid);      _iface->poke32(UE_REG_CTRL_TX_POLICY, UE_FLAG_CTRL_TX_POLICY_NEXT_PACKET); -    _io_impl = UHD_PIMPL_MAKE(io_impl, (_iface->get_file_descriptor())); +    _io_impl = UHD_PIMPL_MAKE(io_impl, (_iface));      //spawn a pirate, yarrr!      _io_impl->recv_pirate_crew.create_thread(boost::bind( -        &usrp_e_impl::io_impl::recv_pirate_loop, _io_impl.get() +        &usrp_e_impl::io_impl::recv_pirate_loop, _io_impl.get(), _clock_ctrl      ));  } @@ -242,7 +181,7 @@ void usrp_e_impl::issue_stream_cmd(const stream_cmd_t &stream_cmd){          stream_cmd, get_max_recv_samps_per_packet()      ));      _iface->poke32(UE_REG_CTRL_RX_TIME_SECS,  boost::uint32_t(stream_cmd.time_spec.get_full_secs())); -    _iface->poke32(UE_REG_CTRL_RX_TIME_TICKS, stream_cmd.time_spec.get_tick_count(MASTER_CLOCK_RATE)); +    _iface->poke32(UE_REG_CTRL_RX_TIME_TICKS, stream_cmd.time_spec.get_tick_count(_clock_ctrl->get_fpga_clock_rate()));  }  void usrp_e_impl::handle_overrun(size_t){ @@ -257,34 +196,38 @@ void usrp_e_impl::handle_overrun(size_t){   * Data Send   **********************************************************************/  bool get_send_buffs( -    data_transport *trans, +    zero_copy_if::sptr trans, double timeout,      vrt_packet_handler::managed_send_buffs_t &buffs  ){      UHD_ASSERT_THROW(buffs.size() == 1); -    buffs[0] = trans->get_send_buff(); -    return buffs[0].get(); +    buffs[0] = trans->get_send_buff(timeout); +    return buffs[0].get() != NULL;  } +#if 0 +size_t usrp_e_impl::get_max_send_samps_per_packet(void) const{ +    static const size_t hdr_size = 0 +        + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) +        - sizeof(vrt::if_packet_info_t().cid) //no class id ever used +    ; +    size_t bpp = _io_impl->data_xport->get_send_frame_size() - hdr_size; +    return bpp/_send_otw_type.get_sample_size(); +} +#endif +  size_t usrp_e_impl::send( -    const std::vector<const void *> &buffs, -    size_t num_samps, -    const tx_metadata_t &metadata, -    const io_type_t &io_type, -    send_mode_t send_mode +    const std::vector<const void *> &buffs, size_t num_samps, +    const tx_metadata_t &metadata, const io_type_t &io_type, +    send_mode_t send_mode, double timeout  ){ -    otw_type_t send_otw_type; -    send_otw_type.width = 16; -    send_otw_type.shift = 0; -    send_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; -      return vrt_packet_handler::send(          _io_impl->packet_handler_send_state,       //last state of the send handler          buffs, num_samps,                          //buffer to fill          metadata, send_mode,                       //samples metadata -        io_type, send_otw_type,                    //input and output types to convert -        MASTER_CLOCK_RATE,                         //master clock tick rate +        io_type, _send_otw_type,                   //input and output types to convert +        _clock_ctrl->get_fpga_clock_rate(),        //master clock tick rate          uhd::transport::vrt::if_hdr_pack_le, -        boost::bind(&get_send_buffs, &_io_impl->transport, _1), +        boost::bind(&get_send_buffs, _io_impl->data_xport, timeout, _1),          get_max_send_samps_per_packet()      );  } @@ -292,36 +235,31 @@ size_t usrp_e_impl::send(  /***********************************************************************   * Data Recv   **********************************************************************/ -bool get_recv_buffs( -    data_transport *trans, -    vrt_packet_handler::managed_recv_buffs_t &buffs -){ -    UHD_ASSERT_THROW(buffs.size() == 1); -    buffs[0] = trans->get_recv_buff(); -    return buffs[0].get(); +#if 0 +size_t usrp_e_impl::get_max_recv_samps_per_packet(void) const{ +    static const size_t hdr_size = 0 +        + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) +        + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer +        - sizeof(vrt::if_packet_info_t().cid) //no class id ever used +    ; +    size_t bpp = _io_impl->data_xport->get_recv_frame_size() - hdr_size; +    return bpp/_recv_otw_type.get_sample_size();  } +#endif  size_t usrp_e_impl::recv( -    const std::vector<void *> &buffs, -    size_t num_samps, -    rx_metadata_t &metadata, -    const io_type_t &io_type, -    recv_mode_t recv_mode, -    size_t timeout_ms +    const std::vector<void *> &buffs, size_t num_samps, +    rx_metadata_t &metadata, const io_type_t &io_type, +    recv_mode_t recv_mode, double timeout  ){ -    otw_type_t recv_otw_type; -    recv_otw_type.width = 16; -    recv_otw_type.shift = 0; -    recv_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; -      return vrt_packet_handler::recv(          _io_impl->packet_handler_recv_state,       //last state of the recv handler          buffs, num_samps,                          //buffer to fill          metadata, recv_mode,                       //samples metadata -        io_type, recv_otw_type,                    //input and output types to convert -        MASTER_CLOCK_RATE,                         //master clock tick rate +        io_type, _recv_otw_type,                   //input and output types to convert +        _clock_ctrl->get_fpga_clock_rate(),        //master clock tick rate          uhd::transport::vrt::if_hdr_unpack_le, -        boost::bind(&usrp_e_impl::io_impl::get_recv_buffs, _io_impl.get(), _1, timeout_ms), +        boost::bind(&usrp_e_impl::io_impl::get_recv_buffs, _io_impl.get(), _1, timeout),          boost::bind(&usrp_e_impl::handle_overrun, this, _1)      );  } @@ -330,11 +268,8 @@ size_t usrp_e_impl::recv(   * Async Recv   **********************************************************************/  bool usrp_e_impl::recv_async_msg( -    async_metadata_t &async_metadata, -    size_t timeout_ms +    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, boost::posix_time::milliseconds(timeout_ms) -    ); +    return _io_impl->async_msg_fifo->pop_with_timed_wait(async_metadata, timeout);  } diff --git a/host/lib/usrp/usrp_e/mboard_impl.cpp b/host/lib/usrp/usrp_e/mboard_impl.cpp index 3d4cef069..f0118aa4b 100644 --- a/host/lib/usrp/usrp_e/mboard_impl.cpp +++ b/host/lib/usrp/usrp_e/mboard_impl.cpp @@ -125,7 +125,7 @@ void usrp_e_impl::mboard_set(const wax::obj &key, const wax::obj &val){      case MBOARD_PROP_TIME_NOW:      case MBOARD_PROP_TIME_NEXT_PPS:{              time_spec_t time_spec = val.as<time_spec_t>(); -            _iface->poke32(UE_REG_TIME64_TICKS, time_spec.get_tick_count(MASTER_CLOCK_RATE)); +            _iface->poke32(UE_REG_TIME64_TICKS, time_spec.get_tick_count(_clock_ctrl->get_fpga_clock_rate()));              boost::uint32_t imm_flags = (key.as<mboard_prop_t>() == MBOARD_PROP_TIME_NOW)? 1 : 0;              _iface->poke32(UE_REG_TIME64_IMM, imm_flags);              _iface->poke32(UE_REG_TIME64_SECS, time_spec.get_full_secs()); diff --git a/host/lib/usrp/usrp_e/usrp_e_impl.cpp b/host/lib/usrp/usrp_e/usrp_e_impl.cpp index 5c0e1dbb0..1b71c0a52 100644 --- a/host/lib/usrp/usrp_e/usrp_e_impl.cpp +++ b/host/lib/usrp/usrp_e/usrp_e_impl.cpp @@ -16,13 +16,17 @@  //  #include "usrp_e_impl.hpp" +#include "usrp_e_regs.hpp"  #include <uhd/usrp/device_props.hpp>  #include <uhd/usrp/mboard_props.hpp>  #include <uhd/utils/assert.hpp>  #include <uhd/utils/static.hpp> +#include <uhd/utils/images.hpp>  #include <boost/format.hpp>  #include <boost/filesystem.hpp> +#include <boost/functional/hash.hpp>  #include <iostream> +#include <fstream>  using namespace uhd;  using namespace uhd::usrp; @@ -66,7 +70,61 @@ static device_addrs_t usrp_e_find(const device_addr_t &hint){   * Make   **********************************************************************/  static device::sptr usrp_e_make(const device_addr_t &device_addr){ -    return device::sptr(new usrp_e_impl(device_addr["node"])); + +    //setup the main interface into fpga +    std::string node = device_addr["node"]; +    std::cout << boost::format("Opening USRP-E on %s") % node << std::endl; +    usrp_e_iface::sptr iface = usrp_e_iface::make(node); + +    //------------------------------------------------------------------ +    //-- Handle the FPGA loading... +    //-- The image can be confimed as already loaded when: +    //--   1) The compatibility number matches. +    //--   2) The hash in the hash-file matches. +    //------------------------------------------------------------------ +    static const char *hash_file_path = "/tmp/usrp_e100_hash"; + +    //extract the fpga path for usrp-e +    std::string usrp_e_fpga_image = find_image_path( +        device_addr.has_key("fpga")? device_addr["fpga"] : "usrp_e100_fpga.bin" +    ); + +    //calculate a hash of the fpga file +    size_t fpga_hash = 0; +    { +        std::ifstream file(usrp_e_fpga_image.c_str()); +        if (not file.good()) throw std::runtime_error( +            "cannot open fpga file for read: " + usrp_e_fpga_image +        ); +        do{ +            boost::hash_combine(fpga_hash, file.get()); +        } while (file.good()); +        file.close(); +    } + +    //read the compatibility number +    boost::uint16_t fpga_compat_num = iface->peek16(UE_REG_MISC_COMPAT); + +    //read the hash in the hash-file +    size_t loaded_hash = 0; +    try{std::ifstream(hash_file_path) >> loaded_hash;}catch(...){} + +    //if not loaded: load the fpga image and write the hash-file +    if (fpga_compat_num != USRP_E_COMPAT_NUM or loaded_hash != fpga_hash){ +        usrp_e_load_fpga(usrp_e_fpga_image); +        try{std::ofstream(hash_file_path) << fpga_hash;}catch(...){} +    } + +    //check that the compatibility is correct +    fpga_compat_num = iface->peek16(UE_REG_MISC_COMPAT); +    if (fpga_compat_num != USRP_E_COMPAT_NUM){ +        throw std::runtime_error(str(boost::format( +            "Expected fpga compatibility number 0x%x, but got 0x%x:\n" +            "The fpga build is not compatible with the host code build." +        ) % USRP_E_COMPAT_NUM % fpga_compat_num)); +    } + +    return device::sptr(new usrp_e_impl(iface));  }  UHD_STATIC_BLOCK(register_usrp_e_device){ @@ -76,11 +134,9 @@ UHD_STATIC_BLOCK(register_usrp_e_device){  /***********************************************************************   * Structors   **********************************************************************/ -usrp_e_impl::usrp_e_impl(const std::string &node){ -    std::cout << boost::format("Opening USRP-E on %s") % node << std::endl; +usrp_e_impl::usrp_e_impl(usrp_e_iface::sptr iface): _iface(iface){ -    //setup various interfaces into hardware -    _iface = usrp_e_iface::make(node); +    //setup interfaces into hardware      _clock_ctrl = usrp_e_clock_ctrl::make(_iface);      _codec_ctrl = usrp_e_codec_ctrl::make(_iface); diff --git a/host/lib/usrp/usrp_e/usrp_e_impl.hpp b/host/lib/usrp/usrp_e/usrp_e_impl.hpp index 2457e27cc..e55b46b80 100644 --- a/host/lib/usrp/usrp_e/usrp_e_impl.hpp +++ b/host/lib/usrp/usrp_e/usrp_e_impl.hpp @@ -22,6 +22,7 @@  #include <uhd/utils/pimpl.hpp>  #include <uhd/usrp/subdev_spec.hpp>  #include <uhd/usrp/dboard_eeprom.hpp> +#include <uhd/types/otw_type.hpp>  #include <uhd/types/clock_config.hpp>  #include <uhd/types/stream_cmd.hpp>  #include <uhd/usrp/dboard_manager.hpp> @@ -29,7 +30,7 @@  #ifndef INCLUDED_USRP_E_IMPL_HPP  #define INCLUDED_USRP_E_IMPL_HPP -static const double MASTER_CLOCK_RATE = 64e6; //TODO get from clock control +static const boost::uint16_t USRP_E_COMPAT_NUM = 0x02;  //! load an fpga image from a bin file into the usrp-e fpga  extern void usrp_e_load_fpga(const std::string &bin_file); @@ -78,25 +79,28 @@ private:  class usrp_e_impl : public uhd::device{  public:      //structors -    usrp_e_impl(const std::string &node); +    usrp_e_impl(usrp_e_iface::sptr);      ~usrp_e_impl(void);      //the io interface -    size_t send(const std::vector<const void *> &, size_t, const uhd::tx_metadata_t &, const uhd::io_type_t &, send_mode_t); -    size_t recv(const std::vector<void *> &, size_t, uhd::rx_metadata_t &, const uhd::io_type_t &, recv_mode_t, size_t); -    bool recv_async_msg(uhd::async_metadata_t &, size_t); +    size_t send(const std::vector<const void *> &, size_t, const uhd::tx_metadata_t &, const uhd::io_type_t &, send_mode_t, double); +    size_t recv(const std::vector<void *> &, size_t, uhd::rx_metadata_t &, const uhd::io_type_t &, recv_mode_t, double); +    bool recv_async_msg(uhd::async_metadata_t &, double); +#if 0 +    size_t get_max_send_samps_per_packet(void) const; +    size_t get_max_recv_samps_per_packet(void) const; +#else      size_t get_max_send_samps_per_packet(void) const{return 503;}      size_t get_max_recv_samps_per_packet(void) const{return 503;} +#endif  private:      //interface to ioctls and file descriptor      usrp_e_iface::sptr _iface; -    //FIXME fetch from ioctl? -    static const size_t _max_num_samples = 2048/sizeof(boost::uint32_t); -      //handle io stuff      UHD_PIMPL_DECL(io_impl) _io_impl; +    uhd::otw_type_t _send_otw_type, _recv_otw_type;      void io_init(void);      void issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd);      void handle_overrun(size_t); diff --git a/host/lib/usrp/usrp_e/usrp_e_mmap_zero_copy.cpp b/host/lib/usrp/usrp_e/usrp_e_mmap_zero_copy.cpp new file mode 100644 index 000000000..274bb043e --- /dev/null +++ b/host/lib/usrp/usrp_e/usrp_e_mmap_zero_copy.cpp @@ -0,0 +1,215 @@ +// +// Copyright 2010 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 "usrp_e_iface.hpp" +#include <uhd/transport/zero_copy.hpp> +#include <uhd/utils/assert.hpp> +#include <linux/usrp_e.h> +#include <sys/mman.h> //mmap +#include <unistd.h> //getpagesize +#include <poll.h> //poll +#include <boost/bind.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <iostream> + +using namespace uhd; +using namespace uhd::transport; + +static const bool fp_verbose = false; //fast-path verbose +static const bool sp_verbose = false; //slow-path verbose +static const size_t poll_breakout = 10; //how many poll timeouts constitute a full timeout + +/*********************************************************************** + * The zero copy interface implementation + **********************************************************************/ +class usrp_e_mmap_zero_copy_impl : public zero_copy_if, public boost::enable_shared_from_this<usrp_e_mmap_zero_copy_impl> { +public: +    usrp_e_mmap_zero_copy_impl(usrp_e_iface::sptr iface): +        _fd(iface->get_file_descriptor()), _recv_index(0), _send_index(0) +    { +        //get system sizes +        iface->ioctl(USRP_E_GET_RB_INFO, &_rb_size); +        size_t page_size = getpagesize(); +        _frame_size = page_size/2; + +        //calculate the memory size +        _map_size = +            (_rb_size.num_pages_rx_flags + _rb_size.num_pages_tx_flags) * page_size + +            (_rb_size.num_rx_frames + _rb_size.num_tx_frames) * _frame_size; + +        //print sizes summary +        if (sp_verbose){ +            std::cout << "page_size:          " << page_size                   << std::endl; +            std::cout << "frame_size:         " << _frame_size                 << std::endl; +            std::cout << "num_pages_rx_flags: " << _rb_size.num_pages_rx_flags << std::endl; +            std::cout << "num_rx_frames:      " << _rb_size.num_rx_frames      << std::endl; +            std::cout << "num_pages_tx_flags: " << _rb_size.num_pages_tx_flags << std::endl; +            std::cout << "num_tx_frames:      " << _rb_size.num_tx_frames      << std::endl; +            std::cout << "map_size:           " << _map_size                   << std::endl; +        } + +        //call mmap to get the memory +        _mapped_mem = ::mmap( +            NULL, _map_size, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0 +        ); +        UHD_ASSERT_THROW(_mapped_mem != MAP_FAILED); + +        //calculate the memory offsets for info and buffers +        size_t recv_info_off = 0; +        size_t recv_buff_off = recv_info_off + (_rb_size.num_pages_rx_flags * page_size); +        size_t send_info_off = recv_buff_off + (_rb_size.num_rx_frames * _frame_size); +        size_t send_buff_off = send_info_off + (_rb_size.num_pages_tx_flags * page_size); + +        //print offset summary +        if (sp_verbose){ +            std::cout << "recv_info_off: " << recv_info_off << std::endl; +            std::cout << "recv_buff_off: " << recv_buff_off << std::endl; +            std::cout << "send_info_off: " << send_info_off << std::endl; +            std::cout << "send_buff_off: " << send_buff_off << std::endl; +        } + +        //set the internal pointers for info and buffers +        typedef ring_buffer_info (*rbi_pta)[]; +        char *rb_ptr = reinterpret_cast<char *>(_mapped_mem); +        _recv_info = reinterpret_cast<rbi_pta>(rb_ptr + recv_info_off); +        _recv_buff = rb_ptr + recv_buff_off; +        _send_info = reinterpret_cast<rbi_pta>(rb_ptr + send_info_off); +        _send_buff = rb_ptr + send_buff_off; +    } + +    ~usrp_e_mmap_zero_copy_impl(void){ +        if (sp_verbose) std::cout << "cleanup: munmap" << std::endl; +        ::munmap(_mapped_mem, _map_size); +    } + +    managed_recv_buffer::sptr get_recv_buff(double timeout){ +        if (fp_verbose) std::cout << "get_recv_buff: " << _recv_index << std::endl; + +        //grab pointers to the info and buffer +        ring_buffer_info *info = (*_recv_info) + _recv_index; +        void *mem = _recv_buff + _frame_size*_recv_index; + +        //poll/wait for a ready frame +        if (not (info->flags & RB_USER)){ +            for (size_t i = 0; i < poll_breakout; i++){ +                pollfd pfd; +                pfd.fd = _fd; +                pfd.events = POLLIN; +                ssize_t poll_ret = ::poll(&pfd, 1, size_t(timeout*1e3/poll_breakout)); +                if (fp_verbose) std::cout << "  POLLIN: " << poll_ret << std::endl; +                if (poll_ret > 0) goto found_user_frame; //good poll, continue on +            } +            return managed_recv_buffer::sptr(); //timed-out for real +        } found_user_frame: + +        //the process has claimed the frame +        info->flags = RB_USER_PROCESS; + +        //increment the index for the next call +        if (++_recv_index == size_t(_rb_size.num_rx_frames)) _recv_index = 0; + +        //return the managed buffer for this frame +        if (fp_verbose) std::cout << "  make_recv_buff: " << info->len << std::endl; +        return managed_recv_buffer::make_safe( +            boost::asio::const_buffer(mem, info->len), +            boost::bind(&usrp_e_mmap_zero_copy_impl::release, shared_from_this(), info) +        ); +    } + +    size_t get_num_recv_frames(void) const{ +        return _rb_size.num_rx_frames; +    } + +    size_t get_recv_frame_size(void) const{ +        return _frame_size; +    } + +    managed_send_buffer::sptr get_send_buff(double timeout){ +        if (fp_verbose) std::cout << "get_send_buff: " << _send_index << std::endl; + +        //grab pointers to the info and buffer +        ring_buffer_info *info = (*_send_info) + _send_index; +        void *mem = _send_buff + _frame_size*_send_index; + +        //poll/wait for a ready frame +        if (not (info->flags & RB_KERNEL)){ +            pollfd pfd; +            pfd.fd = _fd; +            pfd.events = POLLOUT; +            ssize_t poll_ret = ::poll(&pfd, 1, size_t(timeout*1e3)); +            if (fp_verbose) std::cout << "  POLLOUT: " << poll_ret << std::endl; +            if (poll_ret <= 0) return managed_send_buffer::sptr(); +        } + +        //increment the index for the next call +        if (++_send_index == size_t(_rb_size.num_tx_frames)) _send_index = 0; + +        //return the managed buffer for this frame +        if (fp_verbose) std::cout << "  make_send_buff: " << _frame_size << std::endl; +        return managed_send_buffer::make_safe( +            boost::asio::mutable_buffer(mem, _frame_size), +            boost::bind(&usrp_e_mmap_zero_copy_impl::commit, shared_from_this(), info, _1) +        ); +    } + +    size_t get_num_send_frames(void) const{ +        return _rb_size.num_tx_frames; +    } + +    size_t get_send_frame_size(void) const{ +        return _frame_size; +    } + +private: + +    void release(ring_buffer_info *info){ +        if (fp_verbose) std::cout << "recv buff: release" << std::endl; +        info->flags = RB_KERNEL; +    } + +    void commit(ring_buffer_info *info, size_t len){ +        if (fp_verbose) std::cout << "send buff: commit " << len << std::endl; +        info->len = len; +        info->flags = RB_USER; +        if (::write(_fd, NULL, 0) < 0){ +            std::cerr << UHD_THROW_SITE_INFO("write error") << std::endl; +        } +    } + +    int _fd; + +    //the mapped memory itself +    void *_mapped_mem; + +    //mapped memory sizes +    usrp_e_ring_buffer_size_t _rb_size; +    size_t _frame_size, _map_size; + +    //pointers to sections in the mapped memory +    ring_buffer_info (*_recv_info)[], (*_send_info)[]; +    char *_recv_buff, *_send_buff; + +    //indexes into sub-sections of mapped memory +    size_t _recv_index, _send_index; +}; + +/*********************************************************************** + * The zero copy interface make function + **********************************************************************/ +zero_copy_if::sptr usrp_e_make_mmap_zero_copy(usrp_e_iface::sptr iface){ +    return zero_copy_if::sptr(new usrp_e_mmap_zero_copy_impl(iface)); +} diff --git a/host/lib/usrp/usrp_e/usrp_e_regs.hpp b/host/lib/usrp/usrp_e/usrp_e_regs.hpp index a4f42093e..f74358f00 100644 --- a/host/lib/usrp/usrp_e/usrp_e_regs.hpp +++ b/host/lib/usrp/usrp_e/usrp_e_regs.hpp @@ -30,7 +30,7 @@  #define UE_REG_MISC_CGEN_ST    UE_REG_MISC_BASE + 6  #define UE_REG_MISC_TEST       UE_REG_MISC_BASE + 8  #define UE_REG_MISC_RX_LEN     UE_REG_MISC_BASE + 10 -#define UE_REG_MISC_TX_LEN     UE_REG_MISC_BASE + 12 +#define UE_REG_MISC_COMPAT     UE_REG_MISC_BASE + 12  /////////////////////////////////////////////////////  // Slave 1 -- UART diff --git a/host/lib/usrp/wrapper_utils.hpp b/host/lib/usrp/wrapper_utils.hpp new file mode 100644 index 000000000..6f9fdbfca --- /dev/null +++ b/host/lib/usrp/wrapper_utils.hpp @@ -0,0 +1,66 @@ +// +// Copyright 2010 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_USRP_WRAPPER_UTILS_HPP +#define INCLUDED_LIBUHD_USRP_WRAPPER_UTILS_HPP + +#include <uhd/wax.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/usrp/dsp_props.hpp> +#include <uhd/utils/warning.hpp> +#include <boost/format.hpp> +#include <cmath> + +static inline uhd::freq_range_t add_dsp_shift( +    const uhd::freq_range_t &range, +    wax::obj dsp +){ +    double codec_rate = dsp[uhd::usrp::DSP_PROP_CODEC_RATE].as<double>(); +    return uhd::freq_range_t(range.min - codec_rate/2.0, range.max + codec_rate/2.0); +} + +static inline void do_samp_rate_warning_message( +    double target_rate, +    double actual_rate, +    const std::string &xx +){ +    static const double max_allowed_error = 1.0; //Sps +    if (std::abs(target_rate - actual_rate) > max_allowed_error){ +        uhd::warning::post(str(boost::format( +            "The hardware does not support the requested %s sample rate:\n" +            "Target sample rate: %f MSps\n" +            "Actual sample rate: %f MSps\n" +        ) % xx % (target_rate/1e6) % (actual_rate/1e6))); +    } +} + +static inline void do_tune_freq_warning_message( +    double target_freq, +    double actual_freq, +    const std::string &xx +){ +    static const double max_allowed_error = 1.0; //Hz +    if (std::abs(target_freq - actual_freq) > max_allowed_error){ +        uhd::warning::post(str(boost::format( +            "The hardware does not support the requested %s frequency:\n" +            "Target frequency: %f MHz\n" +            "Actual frequency: %f MHz\n" +        ) % xx % (target_freq/1e6) % (actual_freq/1e6))); +    } +} + +#endif /* INCLUDED_LIBUHD_USRP_WRAPPER_UTILS_HPP */ diff --git a/host/lib/utils/gain_group.cpp b/host/lib/utils/gain_group.cpp index 078fe56b2..54146726a 100644 --- a/host/lib/utils/gain_group.cpp +++ b/host/lib/utils/gain_group.cpp @@ -63,7 +63,9 @@ public:          /*NOP*/      } -    gain_range_t get_range(void){ +    gain_range_t get_range(const std::string &name){ +        if (not name.empty()) return _name_to_fcns[name].get_range(); +          float overall_min = 0, overall_max = 0, overall_step = 0;          BOOST_FOREACH(const gain_fcns_t &fcns, get_all_fcns()){              const gain_range_t range = fcns.get_range(); @@ -76,7 +78,9 @@ public:          return gain_range_t(overall_min, overall_max, overall_step);      } -    float get_value(void){ +    float get_value(const std::string &name){ +        if (not name.empty()) return _name_to_fcns[name].get_value(); +          float overall_gain = 0;          BOOST_FOREACH(const gain_fcns_t &fcns, get_all_fcns()){              overall_gain += fcns.get_value(); @@ -84,7 +88,9 @@ public:          return overall_gain;      } -    void set_value(float gain){ +    void set_value(float gain, const std::string &name){ +        if (not name.empty()) return _name_to_fcns[name].set_value(gain); +          std::vector<gain_fcns_t> all_fcns = get_all_fcns();          if (all_fcns.size() == 0) return; //nothing to set! @@ -140,10 +146,21 @@ public:          }      } +    const std::vector<std::string> get_names(void){ +        return _name_to_fcns.keys(); +    } +      void register_fcns( -        const gain_fcns_t &gain_fcns, size_t priority +        const std::string &name, +        const gain_fcns_t &gain_fcns, +        size_t priority      ){ +        if (name.empty() or _name_to_fcns.has_key(name)){ +            //ensure the name name is unique and non-empty +            return register_fcns(name + "_", gain_fcns, priority); +        }          _registry[priority].push_back(gain_fcns); +        _name_to_fcns[name] = gain_fcns;      }  private: @@ -158,6 +175,7 @@ private:      }      uhd::dict<size_t, std::vector<gain_fcns_t> > _registry; +    uhd::dict<std::string, gain_fcns_t> _name_to_fcns;  };  /*********************************************************************** diff --git a/host/lib/utils/thread_priority.cpp b/host/lib/utils/thread_priority.cpp index c35e5fcb1..40b74f655 100644 --- a/host/lib/utils/thread_priority.cpp +++ b/host/lib/utils/thread_priority.cpp @@ -16,6 +16,8 @@  //  #include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/warning.hpp> +#include <boost/format.hpp>  #include <stdexcept>  #include <iostream> @@ -24,7 +26,12 @@ bool uhd::set_thread_priority_safe(float priority, bool realtime){          set_thread_priority(priority, realtime);          return true;      }catch(const std::exception &e){ -        std::cerr << "set_thread_priority: " << e.what() << std::endl; +        uhd::warning::post(str(boost::format( +            "%s\n" +            "Failed to set thread priority %d (%s):\n" +            "Performance may be negatively affected.\n" +            "See the general application notes.\n" +        ) % e.what() % priority % (realtime?"realtime":"")));          return false;      }  } diff --git a/host/lib/utils/warning.cpp b/host/lib/utils/warning.cpp index 8a7d35a23..05be7ae4d 100644 --- a/host/lib/utils/warning.cpp +++ b/host/lib/utils/warning.cpp @@ -17,16 +17,67 @@  #include <uhd/utils/warning.hpp>  #include <uhd/utils/algorithm.hpp> +#include <uhd/utils/static.hpp> +#include <uhd/types/dict.hpp>  #include <boost/foreach.hpp> +#include <sstream> +#include <stdexcept>  #include <iostream>  #include <vector>  using namespace uhd; -void uhd::print_warning(const std::string &msg){ -    //print the warning message -    std::cerr << std::endl << "Warning:" << std::endl; +/*********************************************************************** + * Registry implementation + **********************************************************************/ +//create the registry for the handlers +typedef uhd::dict<std::string, warning::handler_t> registry_t; +UHD_SINGLETON_FCN(registry_t, get_registry) + +//the default warning handler +static void stderr_warning(const std::string &msg){ +    std::cerr << msg; +} + +//register a default handler +UHD_STATIC_BLOCK(warning_register_default){ +    warning::register_handler("default", &stderr_warning); +} + +/*********************************************************************** + * Post + format + **********************************************************************/ +void warning::post(const std::string &msg){ +    std::stringstream ss; + +    //format the warning message +    ss << std::endl << "Warning:" << std::endl;      BOOST_FOREACH(const std::string &line, std::split_string(msg, "\n")){ -        std::cerr << "    " << line << std::endl; +        ss << "    " << line << std::endl; +    } + +    //post the formatted message +    BOOST_FOREACH(const std::string &name, get_registry().keys()){ +        get_registry()[name](ss.str());      }  } + +/*********************************************************************** + * Registry accessor functions + **********************************************************************/ +void warning::register_handler( +    const std::string &name, const handler_t &handler +){ +    get_registry()[name] = handler; +} + +warning::handler_t warning::unregister_handler(const std::string &name){ +    if (not get_registry().has_key(name)) throw std::runtime_error( +        "The warning registry does not have a handler registered to " + name +    ); +    return get_registry().pop(name); +} + +const std::vector<std::string> warning::registry_names(void){ +    return get_registry().keys(); +} diff --git a/host/lib/version.cpp b/host/lib/version.cpp index 5edbca09b..93fdecb1a 100644 --- a/host/lib/version.cpp +++ b/host/lib/version.cpp @@ -21,3 +21,17 @@  std::string uhd::get_version_string(void){      return UHD_VERSION_STRING;  } + +#include <uhd/utils/static.hpp> +#include <boost/version.hpp> +#include <iostream> + +UHD_STATIC_BLOCK(print_system_info){ +    std::cout +        << BOOST_PLATFORM << "; " +        << BOOST_COMPILER << "; " +        << "Boost_" << BOOST_VERSION << "; " +        << "UHD_" << uhd::get_version_string() +        << std::endl << std::endl +    ; +} diff --git a/host/test/CMakeLists.txt b/host/test/CMakeLists.txt index 0d4607f68..2cc987f0c 100644 --- a/host/test/CMakeLists.txt +++ b/host/test/CMakeLists.txt @@ -18,8 +18,7 @@  ########################################################################  # unit test suite  ######################################################################## -ADD_EXECUTABLE(main_test -    main_test.cpp +SET(test_sources      addr_test.cpp      buffer_test.cpp      byteswap_test.cpp @@ -28,13 +27,24 @@ ADD_EXECUTABLE(main_test      error_test.cpp      gain_group_test.cpp      subdev_spec_test.cpp +    time_spec_test.cpp      tune_helper_test.cpp      vrt_test.cpp      warning_test.cpp      wax_test.cpp  ) -TARGET_LINK_LIBRARIES(main_test uhd) -ADD_TEST(test main_test) + +#turn each test cpp file into an executable with an int main() function +ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN) + +#for each source: build an executable, register it as a test, and install +FOREACH(test_source ${test_sources}) +    GET_FILENAME_COMPONENT(test_name ${test_source} NAME_WE) +    ADD_EXECUTABLE(${test_name} ${test_source}) +    TARGET_LINK_LIBRARIES(${test_name} uhd) +    ADD_TEST(${test_name} ${test_name}) +    INSTALL(TARGETS ${test_name} RUNTIME DESTINATION ${PKG_DATA_DIR}/tests) +ENDFOREACH(test_source)  ########################################################################  # demo of a loadable module @@ -42,6 +52,5 @@ ADD_TEST(test main_test)  ADD_LIBRARY(module_test MODULE module_test.cpp)  INSTALL(TARGETS -   main_test     RUNTIME DESTINATION ${PKG_DATA_DIR}/tests  ) diff --git a/host/test/buffer_test.cpp b/host/test/buffer_test.cpp index aadb3f951..8445412e7 100644 --- a/host/test/buffer_test.cpp +++ b/host/test/buffer_test.cpp @@ -23,7 +23,7 @@  using namespace boost::assign;  using namespace uhd::transport; -static const boost::posix_time::milliseconds timeout(10); +static const double timeout = 0.01/*secs*/;  BOOST_AUTO_TEST_CASE(test_bounded_buffer_with_timed_wait){      bounded_buffer<int>::sptr bb(bounded_buffer<int>::make(3)); diff --git a/host/test/gain_group_test.cpp b/host/test/gain_group_test.cpp index 761372e5a..555ccaed3 100644 --- a/host/test/gain_group_test.cpp +++ b/host/test/gain_group_test.cpp @@ -81,12 +81,12 @@ static gain_group::sptr get_gain_group(size_t pri1 = 0, size_t pri2 = 0){      gain_fcns.get_range = boost::bind(&gain_element1::get_range, &g1);      gain_fcns.get_value = boost::bind(&gain_element1::get_value, &g1);      gain_fcns.set_value = boost::bind(&gain_element1::set_value, &g1, _1); -    gg->register_fcns(gain_fcns, pri1); +    gg->register_fcns("g1", gain_fcns, pri1);      gain_fcns.get_range = boost::bind(&gain_element2::get_range, &g2);      gain_fcns.get_value = boost::bind(&gain_element2::get_value, &g2);      gain_fcns.set_value = boost::bind(&gain_element2::set_value, &g2, _1); -    gg->register_fcns(gain_fcns, pri2); +    gg->register_fcns("g2", gain_fcns, pri2);      return gg;  } diff --git a/host/test/main_test.cpp b/host/test/main_test.cpp deleted file mode 100644 index 0b47303b7..000000000 --- a/host/test/main_test.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MAIN -#include <boost/test/unit_test.hpp> diff --git a/host/test/time_spec_test.cpp b/host/test/time_spec_test.cpp new file mode 100644 index 000000000..5ad782160 --- /dev/null +++ b/host/test/time_spec_test.cpp @@ -0,0 +1,61 @@ +// +// Copyright 2010 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 <boost/test/unit_test.hpp> +#include <uhd/types/time_spec.hpp> +#include <boost/foreach.hpp> +#include <iostream> + +BOOST_AUTO_TEST_CASE(test_time_spec_compare){ +    std::cout << "Testing time specification compare..." << std::endl; + +    BOOST_CHECK(uhd::time_spec_t(2.0) == uhd::time_spec_t(2.0)); +    BOOST_CHECK(uhd::time_spec_t(2.0) > uhd::time_spec_t(1.0)); +    BOOST_CHECK(uhd::time_spec_t(1.0) < uhd::time_spec_t(2.0)); + +    BOOST_CHECK(uhd::time_spec_t(1.1) == uhd::time_spec_t(1.1)); +    BOOST_CHECK(uhd::time_spec_t(1.1) > uhd::time_spec_t(1.0)); +    BOOST_CHECK(uhd::time_spec_t(1.0) < uhd::time_spec_t(1.1)); + +    BOOST_CHECK(uhd::time_spec_t(0.1) == uhd::time_spec_t(0.1)); +    BOOST_CHECK(uhd::time_spec_t(0.2) > uhd::time_spec_t(0.1)); +    BOOST_CHECK(uhd::time_spec_t(0.1) < uhd::time_spec_t(0.2)); +} + +#define CHECK_TS_EQUAL(lhs, rhs) \ +    BOOST_CHECK_CLOSE((lhs).get_real_secs(), (rhs).get_real_secs(), 0.001) + +BOOST_AUTO_TEST_CASE(test_time_spec_arithmetic){ +    std::cout << "Testing time specification arithmetic..." << std::endl; + +    CHECK_TS_EQUAL(uhd::time_spec_t(2.3) + uhd::time_spec_t(1.0), uhd::time_spec_t(3.3)); +    CHECK_TS_EQUAL(uhd::time_spec_t(2.3) - uhd::time_spec_t(1.0), uhd::time_spec_t(1.3)); +    CHECK_TS_EQUAL(uhd::time_spec_t(1.0) + uhd::time_spec_t(2.3), uhd::time_spec_t(3.3)); +    CHECK_TS_EQUAL(uhd::time_spec_t(1.0) - uhd::time_spec_t(2.3), uhd::time_spec_t(-1.3)); +} + +BOOST_AUTO_TEST_CASE(test_time_spec_parts){ +    std::cout << "Testing time specification parts..." << std::endl; + +    BOOST_CHECK_EQUAL(uhd::time_spec_t(1.1).get_full_secs(), 1); +    BOOST_CHECK_CLOSE(uhd::time_spec_t(1.1).get_frac_secs(), 0.1, 0.001); +    BOOST_CHECK_EQUAL(uhd::time_spec_t(1.1).get_tick_count(100), 10); + +    BOOST_CHECK_EQUAL(uhd::time_spec_t(-1.1).get_full_secs(), -1); +    BOOST_CHECK_CLOSE(uhd::time_spec_t(-1.1).get_frac_secs(), -0.1, 0.001); +    BOOST_CHECK_EQUAL(uhd::time_spec_t(-1.1).get_tick_count(100), -10); +} diff --git a/host/test/tune_helper_test.cpp b/host/test/tune_helper_test.cpp index 1ef4af330..e0500ae3f 100644 --- a/host/test/tune_helper_test.cpp +++ b/host/test/tune_helper_test.cpp @@ -91,6 +91,44 @@ private:      }  }; +class dummy_subdev_bw : public wax::obj{ +private: +    void get(const wax::obj &key, wax::obj &val){ +        switch(key.as<subdev_prop_t>()){ + +        case SUBDEV_PROP_FREQ: +            val = _freq; +            return; + +        case SUBDEV_PROP_USE_LO_OFFSET: +            val = true; +            return; + +        case SUBDEV_PROP_BANDWIDTH: +            val = _bandwidth; +            return; + +        default: UHD_THROW_PROP_GET_ERROR(); +        } +    } + +    void set(const wax::obj &key, const wax::obj &val){ +        switch(key.as<subdev_prop_t>()){ +        case SUBDEV_PROP_FREQ: +            _freq = val.as<double>(); +            return; + +        case SUBDEV_PROP_BANDWIDTH: +            _bandwidth = val.as<double>(); +            return; + +        default: UHD_THROW_PROP_SET_ERROR(); +        } +    } + +    double _freq, _bandwidth; +}; +  class dummy_dsp : public wax::obj{  public:      dummy_dsp(double codec_rate): @@ -106,6 +144,10 @@ private:              val = _codec_rate;              return; +        case DSP_PROP_HOST_RATE: +            val = _host_rate; +            return; +          case DSP_PROP_FREQ_SHIFT:              val = _freq_shift;              return; @@ -125,11 +167,15 @@ private:              _freq_shift = val.as<double>();              return; +        case DSP_PROP_HOST_RATE: +            _host_rate = val.as<double>(); +            return; +          default: UHD_THROW_PROP_SET_ERROR();          }      } -    double _codec_rate, _freq_shift; +    double _codec_rate, _freq_shift, _host_rate;  };  /*********************************************************************** @@ -141,7 +187,7 @@ BOOST_AUTO_TEST_CASE(test_tune_helper_rx){      dummy_subdev subdev(1e6);      dummy_dsp dsp(100e6); -    std::cout << "Testing tune helper RX automatic LO offset" << std::endl; +    std::cout << "Testing tune helper RX automatic IF offset" << std::endl;      tune_result_t tr = tune_rx_subdev_and_dsp(subdev.get_link(), dsp.get_link(), 0, 2.3451e9);      std::cout << tr.to_pp_string() << std::endl;      BOOST_CHECK_CLOSE(tr.actual_inter_freq, 2.345e9, tolerance); @@ -155,7 +201,7 @@ BOOST_AUTO_TEST_CASE(test_tune_helper_tx){      dummy_subdev subdev(1e6);      dummy_dsp dsp(100e6); -    std::cout << "Testing tune helper TX automatic LO offset" << std::endl; +    std::cout << "Testing tune helper TX automatic IF offset" << std::endl;      tune_result_t tr = tune_tx_subdev_and_dsp(subdev.get_link(), dsp.get_link(), 0, 2.3451e9);      std::cout << tr.to_pp_string() << std::endl;      BOOST_CHECK_CLOSE(tr.actual_inter_freq, 2.345e9, tolerance); @@ -178,3 +224,30 @@ BOOST_AUTO_TEST_CASE(test_tune_helper_rx_nyquist){      double freq_derived = derive_freq_from_rx_subdev_and_dsp(subdev.get_link(), dsp.get_link(), 0);      BOOST_CHECK_CLOSE(freq_derived, -45e6, tolerance);  } + +BOOST_AUTO_TEST_CASE(test_tune_helper_rx_lo_off){ +    dummy_subdev_bw subdev; +    dummy_dsp dsp(100e6); +    tune_result_t tr; + +    std::cout << "Testing tune helper RX automatic LO offset B >> fs" << std::endl; +    subdev[SUBDEV_PROP_BANDWIDTH] = double(40e6); +    dsp[DSP_PROP_HOST_RATE] = double(4e6); +    tr = tune_rx_subdev_and_dsp(subdev.get_link(), dsp.get_link(), 0, 2.45e9); +    std::cout << tr.to_pp_string() << std::endl; +    BOOST_CHECK_CLOSE(tr.actual_inter_freq, 2.45e9+4e6/2, tolerance); + +    std::cout << "Testing tune helper RX automatic LO offset B > fs" << std::endl; +    subdev[SUBDEV_PROP_BANDWIDTH] = double(40e6); +    dsp[DSP_PROP_HOST_RATE] = double(25e6); +    tr = tune_rx_subdev_and_dsp(subdev.get_link(), dsp.get_link(), 0, 2.45e9); +    std::cout << tr.to_pp_string() << std::endl; +    BOOST_CHECK_CLOSE(tr.actual_inter_freq, 2.45e9+(40e6-25e6)/2, tolerance); + +    std::cout << "Testing tune helper RX automatic LO offset B < fs" << std::endl; +    subdev[SUBDEV_PROP_BANDWIDTH] = double(20e6); +    dsp[DSP_PROP_HOST_RATE] = double(25e6); +    tr = tune_rx_subdev_and_dsp(subdev.get_link(), dsp.get_link(), 0, 2.45e9); +    std::cout << tr.to_pp_string() << std::endl; +    BOOST_CHECK_CLOSE(tr.actual_inter_freq, 2.45e9, tolerance); +} diff --git a/host/test/warning_test.cpp b/host/test/warning_test.cpp index 6202c4270..db19955de 100644 --- a/host/test/warning_test.cpp +++ b/host/test/warning_test.cpp @@ -19,9 +19,9 @@  #include <uhd/utils/warning.hpp>  #include <iostream> -BOOST_AUTO_TEST_CASE(test_print_warning){ +BOOST_AUTO_TEST_CASE(test_warning_post){      std::cerr << "---begin print test ---" << std::endl; -    uhd::print_warning( +    uhd::warning::post(          "This is a test print for a warning message.\n"          "And this is the second line of the test print.\n"      ); diff --git a/host/utils/usrp-e-loopback.c b/host/utils/usrp-e-loopback.c index f400fe0be..454d81ba7 100644 --- a/host/utils/usrp-e-loopback.c +++ b/host/utils/usrp-e-loopback.c @@ -8,7 +8,7 @@  #include <sys/mman.h>  #include <linux/usrp_e.h> -// max length #define PKT_DATA_LENGTH 1016 +#define MAX_PACKET_SIZE 1016  static int packet_data_length;  static int error; @@ -19,6 +19,30 @@ struct pkt {  	short data[];  }; +static int length_array[2048]; +static int length_array_tail = 0; +static int length_array_head = 0; + +pthread_mutex_t length_array_mutex; //gotta lock the index to keep it from getting hosed + +//yes this is a circular buffer that does not check empty +//no i don't want to hear about it +void push_length_array(int length) { +	pthread_mutex_lock(&length_array_mutex); +	if(length_array_tail > 2047) length_array_tail = 0; +	length_array[length_array_tail++] = length; +	pthread_mutex_unlock(&length_array_mutex); +} + +int pop_length_array(void) { +	int retval; +	pthread_mutex_lock(&length_array_mutex); +	if(length_array_head > 2047) length_array_head = 0; +	retval = length_array[length_array_head++]; +	pthread_mutex_unlock(&length_array_mutex); +	return retval; +} +  static int fp;  static int calc_checksum(struct pkt *p) @@ -29,10 +53,10 @@ static int calc_checksum(struct pkt *p)  	sum = 0;  	for (i=0; i < p->len; i++) -		sum += p->data[i]; +		sum ^= p->data[i]; -	sum += p->seq_num; -	sum += p->len; +	sum ^= p->seq_num; +	sum ^= p->len;  	return sum;  } @@ -44,6 +68,7 @@ static void *read_thread(void *threadid)  	struct pkt *p;  	unsigned long bytes_transfered, elapsed_seconds;  	struct timeval start_time, finish_time; +	int expected_count;  	printf("Greetings from the reading thread!\n"); @@ -78,6 +103,11 @@ static void *read_thread(void *threadid)  				error = 1;  		} +		expected_count = pop_length_array()*2+12; +		if(cnt != expected_count) { +			printf("Received %d bytes, expected %d\n", cnt, expected_count); +		} +  		prev_seq_num = p->seq_num;  		if (calc_checksum(p) != p->checksum) { @@ -131,7 +161,9 @@ static void *write_thread(void *threadid)  		if (packet_data_length > 0)  			p->len = packet_data_length;  		else -			p->len = (random() & 0x1ff) + (1004 - 512); +			p->len = (random()<<1 & 0x1ff) + (1004 - 512); + +		push_length_array(p->len);  		p->checksum = calc_checksum(p); @@ -146,6 +178,7 @@ static void *write_thread(void *threadid)  int main(int argc, char *argv[])  {  	pthread_t tx, rx; +	pthread_mutex_init(&length_array_mutex, 0);  	long int t;  	struct sched_param s = {  		.sched_priority = 1 @@ -159,6 +192,10 @@ int main(int argc, char *argv[])  	}  	packet_data_length = atoi(argv[1]); +	if(packet_data_length > MAX_PACKET_SIZE) { +		printf("Packet size must be smaller than %i\n", MAX_PACKET_SIZE); +		exit(-1); +	}  	fp = open("/dev/usrp_e0", O_RDWR);  	printf("fp = %d\n", fp); diff --git a/images/Makefile b/images/Makefile index 57277e787..92379271d 100644 --- a/images/Makefile +++ b/images/Makefile @@ -112,6 +112,22 @@ $(_usrp2_fpga_bin):  endif  ######################################################################## +# USRP-E100 fpga +######################################################################## +ifdef HAS_XTCLSH + +_usrp_e100_fpga_dir = $(TOP_FPGA_DIR)/usrp2/top/u1e +_usrp_e100_fpga_bin = $(BUILT_IMAGES_DIR)/usrp_e100_fpga.bin +IMAGES_LIST += $(_usrp_e100_fpga_bin) + +$(_usrp_e100_fpga_bin): +	cd $(_usrp_e100_fpga_dir) && make clean +	cd $(_usrp_e100_fpga_dir) && make bin +	cp $(_usrp_e100_fpga_dir)/build/u1e.bin $@ + +endif + +########################################################################  # Build rules  ########################################################################  ##little rule to make the images directory  | 
