diff options
152 files changed, 7312 insertions, 6665 deletions
diff --git a/fpga/usrp2/control_lib/Makefile.srcs b/fpga/usrp2/control_lib/Makefile.srcs index a1c11c026..567feacde 100644 --- a/fpga/usrp2/control_lib/Makefile.srcs +++ b/fpga/usrp2/control_lib/Makefile.srcs @@ -11,7 +11,9 @@ atr_controller.v \ bin2gray.v \ dcache.v \ decoder_3_8.v \ +dbsm.v \ dpram32.v \ +double_buffer.v \ gray2bin.v \ gray_send.v \ icache.v \ @@ -51,4 +53,5 @@ nsgpio16LE.v \ settings_bus_16LE.v \ atr_controller16.v \ fifo_to_wb.v \ +gpio_atr.v \ )) diff --git a/fpga/usrp2/control_lib/dbsm.v b/fpga/usrp2/control_lib/dbsm.v new file mode 100644 index 000000000..fea51096f --- /dev/null +++ b/fpga/usrp2/control_lib/dbsm.v @@ -0,0 +1,164 @@ +// +// Copyright 2011 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +module dbsm + (input clk, + input reset, + input clear, + + output write_ok, + output write_ptr, + input write_done, + + output read_ok, + output read_ptr, + input read_done, + + output access_ok, + output access_ptr, + input access_done, + input access_skip_read + ); + + localparam PORT_WAIT_0 = 0; + localparam PORT_USE_0 = 1; + localparam PORT_WAIT_1 = 2; + localparam PORT_USE_1 = 3; + + reg [1:0] write_port_state, access_port_state, read_port_state; + + localparam BUFF_WRITABLE = 0; + localparam BUFF_ACCESSIBLE = 1; + localparam BUFF_READABLE = 2; + localparam BUFF_ERROR = 3; + + wire [1:0] buff_state[0:1]; + + always @(posedge clk) + if(reset | clear) + write_port_state <= PORT_WAIT_0; + else + case(write_port_state) + PORT_WAIT_0 : + if(buff_state[0]==BUFF_WRITABLE) + write_port_state <= PORT_USE_0; + PORT_USE_0 : + if(write_done) + write_port_state <= PORT_WAIT_1; + PORT_WAIT_1 : + if(buff_state[1]==BUFF_WRITABLE) + write_port_state <= PORT_USE_1; + PORT_USE_1 : + if(write_done) + write_port_state <= PORT_WAIT_0; + endcase // case (write_port_state) + + assign write_ok = (write_port_state == PORT_USE_0) | (write_port_state == PORT_USE_1); + assign write_ptr = (write_port_state == PORT_USE_1); + + always @(posedge clk) + if(reset | clear) + access_port_state <= PORT_WAIT_0; + else + case(access_port_state) + PORT_WAIT_0 : + if(buff_state[0]==BUFF_ACCESSIBLE) + access_port_state <= PORT_USE_0; + PORT_USE_0 : + if(access_done) + access_port_state <= PORT_WAIT_1; + PORT_WAIT_1 : + if(buff_state[1]==BUFF_ACCESSIBLE) + access_port_state <= PORT_USE_1; + PORT_USE_1 : + if(access_done) + access_port_state <= PORT_WAIT_0; + endcase // case (access_port_state) + + assign access_ok = (access_port_state == PORT_USE_0) | (access_port_state == PORT_USE_1); + assign access_ptr = (access_port_state == PORT_USE_1); + + always @(posedge clk) + if(reset | clear) + read_port_state <= PORT_WAIT_0; + else + case(read_port_state) + PORT_WAIT_0 : + if(buff_state[0]==BUFF_READABLE) + read_port_state <= PORT_USE_0; + PORT_USE_0 : + if(read_done) + read_port_state <= PORT_WAIT_1; + PORT_WAIT_1 : + if(buff_state[1]==BUFF_READABLE) + read_port_state <= PORT_USE_1; + PORT_USE_1 : + if(read_done) + read_port_state <= PORT_WAIT_0; + endcase // case (read_port_state) + + assign read_ok = (read_port_state == PORT_USE_0) | (read_port_state == PORT_USE_1); + assign read_ptr = (read_port_state == PORT_USE_1); + + buff_sm #(.PORT_USE_FLAG(PORT_USE_0)) buff0_sm + (.clk(clk), .reset(reset), .clear(clear), + .write_done(write_done), .access_done(access_done), .access_skip_read(access_skip_read), .read_done(read_done), + .write_port_state(write_port_state), .access_port_state(access_port_state), .read_port_state(read_port_state), + .buff_state(buff_state[0])); + + buff_sm #(.PORT_USE_FLAG(PORT_USE_1)) buff1_sm + (.clk(clk), .reset(reset), .clear(clear), + .write_done(write_done), .access_done(access_done), .access_skip_read(access_skip_read), .read_done(read_done), + .write_port_state(write_port_state), .access_port_state(access_port_state), .read_port_state(read_port_state), + .buff_state(buff_state[1])); + +endmodule // dbsm + +module buff_sm + #(parameter PORT_USE_FLAG=0) + (input clk, input reset, input clear, + input write_done, input access_done, input access_skip_read, input read_done, + input [1:0] write_port_state, input [1:0] access_port_state, input [1:0] read_port_state, + output reg [1:0] buff_state); + + localparam BUFF_WRITABLE = 0; + localparam BUFF_ACCESSIBLE = 1; + localparam BUFF_READABLE = 2; + localparam BUFF_ERROR = 3; + + always @(posedge clk) + if(reset | clear) + buff_state <= BUFF_WRITABLE; + else + case(buff_state) + BUFF_WRITABLE : + if(write_done & (write_port_state == PORT_USE_FLAG)) + buff_state <= BUFF_ACCESSIBLE; + BUFF_ACCESSIBLE : + if(access_done & (access_port_state == PORT_USE_FLAG)) + if(access_skip_read) + buff_state <= BUFF_WRITABLE; + else + buff_state <= BUFF_READABLE; + BUFF_READABLE : + if(read_done & (read_port_state == PORT_USE_FLAG)) + buff_state <= BUFF_WRITABLE; + BUFF_ERROR : + ; + endcase + +endmodule // buff_sm diff --git a/fpga/usrp2/control_lib/double_buffer.v b/fpga/usrp2/control_lib/double_buffer.v new file mode 100644 index 000000000..8865bddee --- /dev/null +++ b/fpga/usrp2/control_lib/double_buffer.v @@ -0,0 +1,139 @@ +// +// Copyright 2011 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +module double_buffer + #(parameter BUF_SIZE = 9) + (input clk, input reset, input clear, + + // Random access interface to RAM + input access_we, + input access_stb, + output access_ok, + input access_done, + input access_skip_read, + input [BUF_SIZE-1:0] access_adr, + output [BUF_SIZE-1:0] access_len, + input [35:0] access_dat_i, + output [35:0] access_dat_o, + + // Write FIFO Interface + input [35:0] data_i, + input src_rdy_i, + output dst_rdy_o, + + // Read FIFO Interface + output [35:0] data_o, + output src_rdy_o, + input dst_rdy_i + ); + + wire [35:0] data_o_0, data_o_1; + + wire read, read_ok, read_ptr, read_done; + wire write, write_ok, write_ptr, write_done; + + wire [BUF_SIZE-1:0] rw0_adr, rw1_adr; + reg [BUF_SIZE-1:0] read_adr, write_adr; + reg [BUF_SIZE-1:0] len0, len1; + + assign data_o = read_ptr ? data_o_1 : data_o_0; + assign rw0_adr = (write_ok & ~write_ptr) ? write_adr : read_adr; + assign rw1_adr = (write_ok & write_ptr) ? write_adr : read_adr; + + wire [35:0] access_dat_o_0, access_dat_o_1; + wire access_ptr; + assign access_dat_o = access_ptr? access_dat_o_1 : access_dat_o_0; + + dbsm dbsm + (.clk(clk), .reset(reset), .clear(clear), + .write_ok(write_ok), .write_ptr(write_ptr), .write_done(write_done), + .access_ok(access_ok), .access_ptr(access_ptr), .access_done(access_done), .access_skip_read(access_skip_read), + .read_ok(read_ok), .read_ptr(read_ptr), .read_done(read_done)); + + // Port A for random access, Port B for FIFO read and write + ram_2port #(.DWIDTH(36),.AWIDTH(BUF_SIZE)) buffer0 + (.clka(clk),.ena(access_stb & access_ok & (access_ptr == 0)),.wea(access_we), + .addra(access_adr),.dia(access_dat_i),.doa(access_dat_o_0), + .clkb(clk),.enb((read & read_ok & ~read_ptr)|(write & write_ok & ~write_ptr) ),.web(write&write_ok&~write_ptr), + .addrb(rw0_adr),.dib(data_i),.dob(data_o_0)); + + ram_2port #(.DWIDTH(36),.AWIDTH(BUF_SIZE)) buffer1 + (.clka(clk),.ena(access_stb & access_ok & (access_ptr == 1)),.wea(access_we), + .addra(access_adr),.dia(access_dat_i),.doa(access_dat_o_1), + .clkb(clk),.enb((read & read_ok & read_ptr)|(write & write_ok & write_ptr) ),.web(write&write_ok&write_ptr), + .addrb(rw1_adr),.dib(data_i),.dob(data_o_1)); + + // Write into buffers + assign dst_rdy_o = write_ok; + assign write = src_rdy_i & write_ok; + assign write_done = write & data_i[33]; // done + always @(posedge clk) + if(reset | clear) + write_adr <= 0; + else + if(write_done) + begin + write_adr <= 0; + if(write_ptr) + len1 <= write_adr + 1; + else + len0 <= write_adr + 1; + end + else if(write) + write_adr <= write_adr + 1; + + assign access_len = access_ptr ? len1 : len0; + + reg [1:0] read_state; + localparam IDLE = 0; + localparam PRE_READ = 1; + localparam READING = 2; + + always @(posedge clk) + if(reset | clear) + begin + read_state <= IDLE; + read_adr <= 0; + end + else + case(read_state) + IDLE : + begin + read_adr <= 0; + if(read_ok) + read_state <= PRE_READ; + end + PRE_READ : + begin + read_state <= READING; + read_adr <= 1; + end + + READING : + if(dst_rdy_i) + begin + read_adr <= read_adr + 1; + if(data_o[33]) + read_state <= IDLE; + end + endcase // case (read_state) + + assign read = ~((read_state==READING)& ~dst_rdy_i); + assign read_done = data_o[33] & dst_rdy_i & src_rdy_o; + assign src_rdy_o = (read_state == READING); + +endmodule // double_buffer diff --git a/fpga/usrp2/control_lib/double_buffer_tb.v b/fpga/usrp2/control_lib/double_buffer_tb.v new file mode 100644 index 000000000..a9aae6956 --- /dev/null +++ b/fpga/usrp2/control_lib/double_buffer_tb.v @@ -0,0 +1,253 @@ +// +// Copyright 2011 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +module double_buffer_tb(); + + reg clk = 0; + reg rst = 1; + reg clear = 0; + initial #1000 rst = 0; + always #50 clk = ~clk; + + wire src_rdy_o; + reg src_rdy_i = 0; + wire dst_rdy_o; + + wire dst_rdy_i = 1; + wire [35:0] data_o; + reg [35:0] data_i; + + wire access_we, access_stb, access_done, access_ok, access_skip_read; + + wire [8:0] access_adr, access_len; + wire [35:0] dsp_to_buf, buf_to_dsp; + reg set_stb = 0; + + double_buffer db + (.clk(clk),.reset(rst),.clear(0), + .access_we(access_we), .access_stb(access_stb), .access_ok(access_ok), .access_done(access_done), + .access_skip_read(access_skip_read), .access_adr(access_adr), .access_len(access_len), + .access_dat_i(dsp_to_buf), .access_dat_o(buf_to_dsp), + + .data_i(data_i), .src_rdy_i(src_rdy_i), .dst_rdy_o(dst_rdy_o), + .data_o(data_o), .src_rdy_o(src_rdy_o), .dst_rdy_i(dst_rdy_i)); + + dspengine_16to8 dspengine_16to8 + (.clk(clk),.reset(rst),.clear(0), + .set_stb(set_stb), .set_addr(0), .set_data({13'h0,1'b1,18'h00400}), + .access_we(access_we), .access_stb(access_stb), .access_ok(access_ok), .access_done(access_done), + .access_skip_read(access_skip_read), .access_adr(access_adr), .access_len(access_len), + .access_dat_i(buf_to_dsp), .access_dat_o(dsp_to_buf)); + + always @(posedge clk) + if(src_rdy_o & dst_rdy_i) + begin + $display("SOF %d, EOF %d, OCC %x, DAT %x",data_o[32],data_o[33],data_o[35:34],data_o[31:0]); + if(data_o[33]) + $display(); + end + initial $dumpfile("double_buffer_tb.vcd"); + initial $dumpvars(0,double_buffer_tb); + + initial + begin + @(negedge rst); + @(posedge clk); + @(posedge clk); + @(posedge clk); + + // Passthrough + $display("Passthrough"); + src_rdy_i <= 1; + data_i <= { 2'b00,1'b0,1'b1,32'hFFFFFFFF}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h04050607}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h08090a0b}; + @(posedge clk); + data_i <= { 2'b00,1'b1,1'b0,32'h0c0d0e0f}; + @(posedge clk); + src_rdy_i <= 0; + @(posedge clk); + + repeat (5) + @(posedge clk); + + $display("Enabled"); + set_stb <= 1; + @(posedge clk); + set_stb <= 0; + + @(posedge clk); + $display("Non-IF Data Passthrough"); + src_rdy_i <= 1; + data_i <= { 2'b00,1'b0,1'b1,32'hC0000000}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h14151617}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h18191a1b}; + @(posedge clk); + data_i <= { 2'b00,1'b1,1'b0,32'h1c1d1e1f}; + @(posedge clk); + src_rdy_i <= 0; + @(posedge clk); + + while(~dst_rdy_o) + @(posedge clk); + + $display("No StreamID, No Trailer, Even"); + src_rdy_i <= 1; + data_i <= { 2'b00,1'b0,1'b1,32'h0000FFFF}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h01000200}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h03000400}; + src_rdy_i <= 0; + @(posedge clk); + @(posedge clk); + @(posedge clk); + @(posedge clk); + src_rdy_i <= 1; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h05000600}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h07000800}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h09000a00}; + @(posedge clk); + data_i <= { 2'b00,1'b1,1'b0,32'h0b000c00}; + @(posedge clk); + src_rdy_i <= 0; + @(posedge clk); + + while(~dst_rdy_o) + @(posedge clk); + + $display("No StreamID, No Trailer, Odd"); + src_rdy_i <= 1; + data_i <= { 2'b00,1'b0,1'b1,32'h0000FFFF}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h11001200}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h13001400}; + src_rdy_i <= 0; + @(posedge clk); + src_rdy_i <= 1; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h15001600}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h17001800}; + @(posedge clk); + data_i <= { 2'b00,1'b1,1'b0,32'h19001a00}; + @(posedge clk); + src_rdy_i <= 0; + @(posedge clk); + + while(~dst_rdy_o) + @(posedge clk); + + $display("No StreamID, Trailer, Even"); + src_rdy_i <= 1; + data_i <= { 2'b00,1'b0,1'b1,32'h0400FFFF}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h21002200}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h23002400}; + src_rdy_i <= 0; + @(posedge clk); + src_rdy_i <= 1; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h25002600}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h27002800}; + @(posedge clk); + data_i <= { 2'b00,1'b1,1'b0,32'h29002a00}; + @(posedge clk); + src_rdy_i <= 0; + @(posedge clk); + + while(~dst_rdy_o) + @(posedge clk); + + $display("No StreamID, Trailer, Odd"); + src_rdy_i <= 1; + data_i <= { 2'b00,1'b0,1'b1,32'h0400FFFF}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h31003200}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h33003400}; + src_rdy_i <= 0; + @(posedge clk); + src_rdy_i <= 1; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h35003600}; + @(posedge clk); + data_i <= { 2'b00,1'b1,1'b0,32'h39003a00}; + @(posedge clk); + src_rdy_i <= 0; + @(posedge clk); + + while(~dst_rdy_o) + @(posedge clk); + + $display("StreamID, No Trailer, Even"); + src_rdy_i <= 1; + data_i <= { 2'b00,1'b0,1'b1,32'h1000FFFF}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h11001200}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h13001400}; + src_rdy_i <= 0; + @(posedge clk); + src_rdy_i <= 1; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h15001600}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'h17001800}; + @(posedge clk); + data_i <= { 2'b00,1'b1,1'b0,32'h19001a00}; + @(posedge clk); + src_rdy_i <= 0; + @(posedge clk); + + while(~dst_rdy_o) + @(posedge clk); + + $display("StreamID, Trailer, Odd"); + src_rdy_i <= 1; + data_i <= { 2'b00,1'b0,1'b1,32'h1400FFFF}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'ha100a200}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'ha300a400}; + src_rdy_i <= 0; + @(posedge clk); + src_rdy_i <= 1; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'ha500a600}; + @(posedge clk); + data_i <= { 2'b00,1'b0,1'b0,32'ha700a800}; + @(posedge clk); + data_i <= { 2'b00,1'b1,1'b0,32'hbbb0bbb0}; + @(posedge clk); + src_rdy_i <= 0; + @(posedge clk); + + end + + initial #28000 $finish; +endmodule // double_buffer_tb diff --git a/fpga/usrp2/control_lib/gpio_atr.v b/fpga/usrp2/control_lib/gpio_atr.v new file mode 100644 index 000000000..82d72b815 --- /dev/null +++ b/fpga/usrp2/control_lib/gpio_atr.v @@ -0,0 +1,71 @@ + +// +// Copyright 2011 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + + +module gpio_atr + #(parameter BASE = 0, + parameter WIDTH = 32) + (input clk, input reset, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + input rx, input tx, + inout [WIDTH-1:0] gpio, + output reg [31:0] gpio_readback + ); + + wire [WIDTH-1:0] ddr, in_idle, in_tx, in_rx, in_fdx; + reg [WIDTH-1:0] rgpio, igpio; + + setting_reg #(.my_addr(BASE+0), .width(WIDTH)) reg_idle + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data), + .out(in_idle),.changed()); + + setting_reg #(.my_addr(BASE+1), .width(WIDTH)) reg_rx + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data), + .out(in_rx),.changed()); + + setting_reg #(.my_addr(BASE+2), .width(WIDTH)) reg_tx + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data), + .out(in_tx),.changed()); + + setting_reg #(.my_addr(BASE+3), .width(WIDTH)) reg_fdx + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data), + .out(in_fdx),.changed()); + + setting_reg #(.my_addr(BASE+4), .width(WIDTH)) reg_ddr + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data), + .out(ddr),.changed()); + + always @(posedge clk) + case({tx,rx}) + 2'b00: rgpio <= in_idle; + 2'b01: rgpio <= in_rx; + 2'b10: rgpio <= in_tx; + 2'b11: rgpio <= in_fdx; + endcase // case ({tx,rx}) + + integer n; + always @* + for(n=0;n<WIDTH;n=n+1) + igpio[n] <= ddr[n] ? rgpio[n] : 1'bz; + + assign gpio = igpio; + + always @(posedge clk) + gpio_readback <= gpio; + +endmodule // gpio_atr diff --git a/fpga/usrp2/fifo/Makefile.srcs b/fpga/usrp2/fifo/Makefile.srcs index 02c567049..28d506571 100644 --- a/fpga/usrp2/fifo/Makefile.srcs +++ b/fpga/usrp2/fifo/Makefile.srcs @@ -6,6 +6,7 @@ # FIFO Sources ################################################## FIFO_SRCS = $(abspath $(addprefix $(BASE_DIR)/../fifo/, \ +add_routing_header.v \ buffer_int.v \ buffer_int2.v \ buffer_pool.v \ diff --git a/fpga/usrp2/fifo/add_routing_header.v b/fpga/usrp2/fifo/add_routing_header.v new file mode 100644 index 000000000..ee6a635f5 --- /dev/null +++ b/fpga/usrp2/fifo/add_routing_header.v @@ -0,0 +1,47 @@ +// +// Copyright 2011 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +module add_routing_header + #(parameter PORT_SEL = 0, + parameter PROT_ENG_FLAGS = 1) + (input clk, input reset, input clear, + input [35:0] data_i, input src_rdy_i, output dst_rdy_o, + output [35:0] data_o, output src_rdy_o, input dst_rdy_i); + + reg [1:0] line; + wire [1:0] port_sel_bits = PORT_SEL; + wire [15:0] len = data_i[15:0]; + + always @(posedge clk) + if(reset) + line <= PROT_ENG_FLAGS ? 0 : 1; + else + if(src_rdy_o & dst_rdy_i) + if(data_o[33]) + line <= PROT_ENG_FLAGS ? 0 : 1; + else + if(line != 3) + line <= line + 1; + + assign data_o = (line == 0) ? {4'b0001, 13'b0, port_sel_bits, 1'b1, len[13:0],2'b00} : + (line == 1) ? {3'b000, (PROT_ENG_FLAGS ? 1'b0: 1'b1), data_i[31:0]} : + data_i[35:0]; + + assign dst_rdy_o = dst_rdy_i & (line != 0); + assign src_rdy_o = src_rdy_i; + +endmodule // add_routing_header diff --git a/fpga/usrp2/gpif/gpif.v b/fpga/usrp2/gpif/gpif.v index 51d6e8ba9..e5b63d5a3 100644 --- a/fpga/usrp2/gpif/gpif.v +++ b/fpga/usrp2/gpif/gpif.v @@ -37,10 +37,13 @@ module gpif input [35:0] tx_err_data_i, input tx_err_src_rdy_i, output tx_err_dst_rdy_o, output tx_underrun, output rx_overrun, - input [7:0] frames_per_packet, input [15:0] test_len, input [7:0] test_rate, input [3:0] test_ctrl, + input [7:0] frames_per_packet, output [31:0] debug0, output [31:0] debug1 ); + assign tx_underrun = 0; + assign rx_overrun = 0; + wire WR = gpif_ctl[0]; wire RD = gpif_ctl[1]; wire OE = gpif_ctl[2]; @@ -62,8 +65,8 @@ module gpif wire [18:0] tx19_data; wire tx19_src_rdy, tx19_dst_rdy; - wire [35:0] tx36_data, tx_data; - wire tx36_src_rdy, tx36_dst_rdy, tx_src_rdy, tx_dst_rdy; + wire [35:0] tx36_data; + wire tx36_src_rdy, tx36_dst_rdy; wire [18:0] ctrl_data; wire ctrl_src_rdy, ctrl_dst_rdy; @@ -95,13 +98,13 @@ module gpif fifo_cascade #(.WIDTH(36), .SIZE(TXFIFOSIZE)) tx_fifo36 (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), .datain(tx36_data), .src_rdy_i(tx36_src_rdy), .dst_rdy_o(tx36_dst_rdy), - .dataout(tx_data), .src_rdy_o(tx_src_rdy), .dst_rdy_i(tx_dst_rdy)); + .dataout(tx_data_o), .src_rdy_o(tx_src_rdy_o), .dst_rdy_i(tx_dst_rdy_i)); // //////////////////////////////////////////// // RX Data Path - wire [35:0] rx36_data, rx_data; - wire rx36_src_rdy, rx36_dst_rdy, rx_src_rdy, rx_dst_rdy; + wire [35:0] rx36_data; + wire rx36_src_rdy, rx36_dst_rdy; wire [18:0] rx19_data, splt_data; wire rx19_src_rdy, rx19_dst_rdy, splt_src_rdy, splt_dst_rdy; wire [18:0] resp_data, resp_int1, resp_int2; @@ -110,7 +113,7 @@ module gpif fifo_cascade #(.WIDTH(36), .SIZE(RXFIFOSIZE)) rx_fifo36 (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), - .datain(rx_data), .src_rdy_i(rx_src_rdy), .dst_rdy_o(rx_dst_rdy), + .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 @@ -166,81 +169,6 @@ module gpif .data_i(resp_int2), .src_rdy_i(resp_src_rdy_int2), .dst_rdy_o(resp_dst_rdy_int2), .data_o(resp_data), .src_rdy_o(resp_src_rdy), .dst_rdy_i(resp_dst_rdy)); - // //////////////////////////////////////////////////////////////////// - // Debug support, timed and loopback - // RX side muxes test data into the same stream - wire [35:0] timedrx_data, loopbackrx_data, testrx_data; - wire [35:0] timedtx_data, loopbacktx_data, testtx_data; - wire timedrx_src_rdy, timedrx_dst_rdy, loopbackrx_src_rdy, loopbackrx_dst_rdy, - testrx_src_rdy, testrx_dst_rdy; - wire timedtx_src_rdy, timedtx_dst_rdy, loopbacktx_src_rdy, loopbacktx_dst_rdy, - testtx_src_rdy, testtx_dst_rdy; - wire timedrx_src_rdy_int, timedrx_dst_rdy_int, timedtx_src_rdy_int, timedtx_dst_rdy_int; - - wire [31:0] total, crc_err, seq_err, len_err; - wire sel_testtx = test_ctrl[0]; - wire sel_loopbacktx = test_ctrl[1]; - wire pkt_src_enable = test_ctrl[2]; - wire pkt_sink_enable = test_ctrl[3]; - - fifo36_mux rx_test_mux_lvl_1 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), - .data0_i(timedrx_data), .src0_rdy_i(timedrx_src_rdy), .dst0_rdy_o(timedrx_dst_rdy), - .data1_i(loopbackrx_data), .src1_rdy_i(loopbackrx_src_rdy), .dst1_rdy_o(loopbackrx_dst_rdy), - .data_o(testrx_data), .src_rdy_o(testrx_src_rdy), .dst_rdy_i(testrx_dst_rdy)); - - fifo36_mux rx_test_mux_lvl_2 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), - .data0_i(testrx_data), .src0_rdy_i(testrx_src_rdy), .dst0_rdy_o(testrx_dst_rdy), - .data1_i(rx_data_i), .src1_rdy_i(rx_src_rdy_i), .dst1_rdy_o(rx_dst_rdy_o), - .data_o(rx_data), .src_rdy_o(rx_src_rdy), .dst_rdy_i(rx_dst_rdy)); - - fifo_short #(.WIDTH(36)) loopback_fifo - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx | clear_rx), - .datain(loopbacktx_data), .src_rdy_i(loopbacktx_src_rdy), .dst_rdy_o(loopbacktx_dst_rdy), - .dataout(loopbackrx_data), .src_rdy_o(loopbackrx_src_rdy), .dst_rdy_i(loopbackrx_dst_rdy)); - - // Crossbar used as a demux for switching TX stream to main DSP or to test logic - crossbar36 tx_crossbar_lvl_1 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), - .cross(sel_testtx), - .data0_i(tx_data), .src0_rdy_i(tx_src_rdy), .dst0_rdy_o(tx_dst_rdy), - .data1_i(tx_data), .src1_rdy_i(1'b0), .dst1_rdy_o(), // No 2nd input - .data0_o(tx_data_o), .src0_rdy_o(tx_src_rdy_o), .dst0_rdy_i(tx_dst_rdy_i), - .data1_o(testtx_data), .src1_rdy_o(testtx_src_rdy), .dst1_rdy_i(testtx_dst_rdy) ); - - crossbar36 tx_crossbar_lvl_2 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), - .cross(sel_loopbacktx), - .data0_i(testtx_data), .src0_rdy_i(testtx_src_rdy), .dst0_rdy_o(testtx_dst_rdy), - .data1_i(testtx_data), .src1_rdy_i(1'b0), .dst1_rdy_o(), // No 2nd input - .data0_o(timedtx_data), .src0_rdy_o(timedtx_src_rdy), .dst0_rdy_i(timedtx_dst_rdy), - .data1_o(loopbacktx_data), .src1_rdy_o(loopbacktx_src_rdy), .dst1_rdy_i(loopbacktx_dst_rdy) ); - - // Fixed rate TX traffic consumer - fifo_pacer tx_pacer - (.clk(fifo_clk), .reset(fifo_rst), .rate(test_rate), .enable(pkt_sink_enable), - .src1_rdy_i(timedtx_src_rdy), .dst1_rdy_o(timedtx_dst_rdy), - .src2_rdy_o(timedtx_src_rdy_int), .dst2_rdy_i(timedtx_dst_rdy_int), - .underrun(tx_underrun), .overrun()); - - packet_verifier32 pktver32 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), - .data_i(timedtx_data), .src_rdy_i(timedtx_src_rdy_int), .dst_rdy_o(timedtx_dst_rdy_int), - .total(total), .crc_err(crc_err), .seq_err(seq_err), .len_err(len_err)); - - // Fixed rate RX traffic generator - vita_pkt_gen pktgen - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), - .len(test_len), - .data_o(timedrx_data), .src_rdy_o(timedrx_src_rdy_int), .dst_rdy_i(timedrx_dst_rdy_int)); - - fifo_pacer rx_pacer - (.clk(fifo_clk), .reset(fifo_rst), .rate(test_rate), .enable(pkt_src_enable), - .src1_rdy_i(timedrx_src_rdy_int), .dst1_rdy_o(timedrx_dst_rdy_int), - .src2_rdy_o(timedrx_src_rdy), .dst2_rdy_i(timedrx_dst_rdy), - .underrun(), .overrun(rx_overrun)); - // //////////////////////////////////////////// // DEBUG diff --git a/fpga/usrp2/gpif/lint b/fpga/usrp2/gpif/lint new file mode 100755 index 000000000..4316c89a9 --- /dev/null +++ b/fpga/usrp2/gpif/lint @@ -0,0 +1,2 @@ +iverilog -Wall -y . -y ../fifo/ -y ../control_lib/ -y ../models/ -y ../coregen/ -y ../simple_gemac/ -y ../sdr_lib/ -y ../vrt/ gpif.v 2>&1 | grep -v coregen | grep -v models + diff --git a/fpga/usrp2/sdr_lib/Makefile.srcs b/fpga/usrp2/sdr_lib/Makefile.srcs index defbced17..629b92cc8 100644 --- a/fpga/usrp2/sdr_lib/Makefile.srcs +++ b/fpga/usrp2/sdr_lib/Makefile.srcs @@ -25,8 +25,11 @@ cordic_z24.v \ cordic_stage.v \ dsp_core_rx.v \ dsp_core_tx.v \ +dspengine_16to8.v \ hb_dec.v \ hb_interp.v \ +pipectrl.v \ +pipestage.v \ round.v \ round_reg.v \ round_sd.v \ diff --git a/fpga/usrp2/sdr_lib/clip_reg.v b/fpga/usrp2/sdr_lib/clip_reg.v index d5e98d982..9098fd5b8 100644 --- a/fpga/usrp2/sdr_lib/clip_reg.v +++ b/fpga/usrp2/sdr_lib/clip_reg.v @@ -23,16 +23,24 @@ module clip_reg #(parameter bits_in=0, - parameter bits_out=0) + parameter bits_out=0, + parameter STROBED=1'b0) (input clk, input [bits_in-1:0] in, - output reg [bits_out-1:0] out); + output reg [bits_out-1:0] out, + input strobe_in, + output reg strobe_out); wire [bits_out-1:0] temp; clip #(.bits_in(bits_in),.bits_out(bits_out)) clip (.in(in),.out(temp)); + + always @(posedge clk) + strobe_out <= strobe_in; + always @(posedge clk) - out <= temp; + if(strobe_in | ~STROBED) + out <= temp; endmodule // clip_reg diff --git a/fpga/usrp2/sdr_lib/dsp_core_rx.v b/fpga/usrp2/sdr_lib/dsp_core_rx.v index 639744de7..d1c7e238a 100644 --- a/fpga/usrp2/sdr_lib/dsp_core_rx.v +++ b/fpga/usrp2/sdr_lib/dsp_core_rx.v @@ -94,8 +94,10 @@ module dsp_core_rx .xi({adc_i_mux[23],adc_i_mux}),. yi({adc_q_mux[23],adc_q_mux}), .zi(phase[31:8]), .xo(i_cordic),.yo(q_cordic),.zo() ); - clip_reg #(.bits_in(25), .bits_out(24)) clip_i (.clk(clk), .in(i_cordic), .out(i_cordic_clip)); - clip_reg #(.bits_in(25), .bits_out(24)) clip_q (.clk(clk), .in(q_cordic), .out(q_cordic_clip)); + clip_reg #(.bits_in(25), .bits_out(24)) clip_i + (.clk(clk), .in(i_cordic), .strobe_in(1'b1), .out(i_cordic_clip)); + clip_reg #(.bits_in(25), .bits_out(24)) clip_q + (.clk(clk), .in(q_cordic), .strobe_in(1'b1), .out(q_cordic_clip)); // CIC decimator 24 bit I/O cic_strober cic_strober(.clock(clk),.reset(rst),.enable(run),.rate(cic_decim_rate), diff --git a/fpga/usrp2/sdr_lib/dspengine_16to8.v b/fpga/usrp2/sdr_lib/dspengine_16to8.v new file mode 100644 index 000000000..53c5d29da --- /dev/null +++ b/fpga/usrp2/sdr_lib/dspengine_16to8.v @@ -0,0 +1,221 @@ + +// Copyright 2011 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +module dspengine_16to8 + #(parameter BASE = 0, + parameter BUF_SIZE = 9) + (input clk, input reset, input clear, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + + output access_we, + output access_stb, + input access_ok, + output access_done, + output access_skip_read, + output [BUF_SIZE-1:0] access_adr, + input [BUF_SIZE-1:0] access_len, + output [35:0] access_dat_o, + input [35:0] access_dat_i + ); + + wire convert; + wire [17:0] scale_factor; + + setting_reg #(.my_addr(BASE),.width(19)) sr_16to8 + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out({convert,scale_factor}),.changed()); + + reg [2:0] dsp_state; + localparam DSP_IDLE = 0; + localparam DSP_PARSE_HEADER = 1; + localparam DSP_CONVERT = 2; + localparam DSP_CONVERT_DRAIN_PIPE = 3; + localparam DSP_READ_TRAILER = 4; + localparam DSP_WRITE_TRAILER = 5; + localparam DSP_WRITE_HEADER = 6; + localparam DSP_DONE = 7; + + // Parse VITA header + wire is_if_data = (access_dat_i[31:29] == 3'b000); + wire has_streamid = access_dat_i[28]; + wire has_classid = access_dat_i[27]; + wire has_trailer = access_dat_i[26]; + // 25:24 reserved, aka SOB/EOB + wire has_secs = |access_dat_i[23:22]; + wire has_tics = |access_dat_i[21:20]; + wire [3:0] hdr_length = 1 + has_streamid + has_classid + has_classid + has_secs + has_tics + has_tics; + + wire [35:0] prod_i, prod_q; + wire [15:0] scaled_i, scaled_q; + wire [7:0] i8, q8; + reg [7:0] i8_reg, q8_reg; + wire stb_read, stb_mult, stb_clip, stb_round, val_read, val_mult, val_clip, val_round; + wire stb_out, stb_reg; + reg even; + + reg [BUF_SIZE-1:0] read_adr, write_adr; + reg has_trailer_reg; + + wire last = (read_adr + 1) == (access_len - has_trailer_reg); + wire last_o, even_o; + + wire stb_write = stb_out & (even_o | last_o); + wire send_to_pipe = ~stb_write & (dsp_state == DSP_CONVERT); + reg [31:0] new_header, new_trailer, trailer_mask; + reg [15:0] length; + reg wait_for_trailer; + + always @(posedge clk) + if(reset | clear) + dsp_state <= DSP_IDLE; + else + case(dsp_state) + DSP_IDLE : + begin + read_adr <= 0; + write_adr <= 0; + even <= 0; + if(access_ok) + dsp_state <= DSP_PARSE_HEADER; + end + + DSP_PARSE_HEADER : + begin + has_trailer_reg <= has_trailer; + new_header[31:16] <= access_dat_i[31:16]; + new_header[15:0] <= access_len; + length <= access_len; + if(is_if_data & convert) + begin + read_adr <= hdr_length; + write_adr <= hdr_length; + dsp_state <= DSP_CONVERT; + end + else + dsp_state <= DSP_WRITE_HEADER; + end + + DSP_CONVERT: + begin + new_header[26] <= 1'b1; // all converted packets have a trailer + if(stb_write) + write_adr <= write_adr + 1; + else if(stb_read) // should always be 1 if we are here + begin + read_adr <= read_adr + 1; + even <= ~even; + if(last) + begin + dsp_state <= DSP_CONVERT_DRAIN_PIPE; + if(~even) + trailer_mask <= 32'h00400400; + else + trailer_mask <= 32'h00400000; + end + end + end + + DSP_CONVERT_DRAIN_PIPE : + if(stb_write) + begin + write_adr <= write_adr + 1; + if(last_o) + if(has_trailer_reg) + begin + dsp_state <= DSP_READ_TRAILER; + wait_for_trailer <= 0; + end + else + begin + dsp_state <= DSP_WRITE_TRAILER; + new_trailer <= trailer_mask; + end + end + + DSP_READ_TRAILER : + begin + wait_for_trailer <= 1; + if(wait_for_trailer) + dsp_state <= DSP_WRITE_TRAILER; + new_trailer <= access_dat_i[31:0] | trailer_mask; + end + + DSP_WRITE_TRAILER : + begin + dsp_state <= DSP_WRITE_HEADER; + write_adr <= 0; + new_header[15:0] <= write_adr + 1; + end + + DSP_WRITE_HEADER : + dsp_state <= DSP_DONE; + + DSP_DONE : + begin + read_adr <= 0; + write_adr <= 0; + dsp_state <= DSP_IDLE; + end + endcase // case (dsp_state) + + assign access_skip_read = 0; + assign access_done = (dsp_state == DSP_DONE); + + assign access_stb = 1; + + assign access_we = (dsp_state == DSP_WRITE_HEADER) | + (dsp_state == DSP_WRITE_TRAILER) | + stb_write; + + assign access_dat_o = (dsp_state == DSP_WRITE_HEADER) ? { 4'h1, new_header } : + (dsp_state == DSP_WRITE_TRAILER) ? { 4'h2, new_trailer } : + (last_o&~even_o) ? {4'h0, 16'd0, i8, q8 } : + {4'h0, i8, q8, i8_reg, q8_reg }; + + assign access_adr = (stb_write|(dsp_state == DSP_WRITE_HEADER)|(dsp_state == DSP_WRITE_TRAILER)) ? write_adr : read_adr; + + // DSP Pipeline + + wire [15:0] i16 = access_dat_i[31:16]; + wire [15:0] q16 = access_dat_i[15:0]; + + pipectrl #(.STAGES(4), .TAGWIDTH(2)) pipectrl + (.clk(clk), .reset(reset), + .src_rdy_i(send_to_pipe), .dst_rdy_o(), // dst_rdy_o will always be 1 since dst_rdy_i is 1, below + .src_rdy_o(stb_out), .dst_rdy_i(1), // always accept output of chain + .strobes({stb_round,stb_clip,stb_mult,stb_read}), .valids({val_round,val_clip,val_mult,val_read}), + .tag_i({last,even}), .tag_o({last_o,even_o})); + + always @(posedge clk) + if(stb_out & ~even_o) + {i8_reg,q8_reg} <= {i8,q8}; + + MULT18X18S mult_i + (.P(prod_i), .A(scale_factor), .B({i16,2'b00}), .C(clk), .CE(stb_mult), .R(reset) ); + clip_reg #(.bits_in(24),.bits_out(16),.STROBED(1)) clip_i + (.clk(clk), .in(prod_i[35:12]), .out(scaled_i), .strobe_in(stb_clip), .strobe_out()); + round_sd #(.WIDTH_IN(16),.WIDTH_OUT(8),.DISABLE_SD(1)) round_i + (.clk(clk), .reset(reset), .in(scaled_i), .strobe_in(stb_round), .out(i8), .strobe_out()); + + MULT18X18S mult_q + (.P(prod_q), .A(scale_factor), .B({q16,2'b00}), .C(clk), .CE(stb_mult), .R(reset) ); + clip_reg #(.bits_in(24),.bits_out(16),.STROBED(1)) clip_q + (.clk(clk), .in(prod_q[35:12]), .out(scaled_q), .strobe_in(stb_clip), .strobe_out()); + round_sd #(.WIDTH_IN(16),.WIDTH_OUT(8),.DISABLE_SD(1)) round_q + (.clk(clk), .reset(reset), .in(scaled_q), .strobe_in(stb_round), .out(q8), .strobe_out()); + +endmodule // dspengine_16to8 diff --git a/fpga/usrp2/sdr_lib/pipectrl.v b/fpga/usrp2/sdr_lib/pipectrl.v new file mode 100644 index 000000000..85d0ce04f --- /dev/null +++ b/fpga/usrp2/sdr_lib/pipectrl.v @@ -0,0 +1,66 @@ +// +// Copyright 2011 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/>. +// + +// Control DSP pipeline with 1 cycle per stage. Minimum 2 stages or this won't work +module pipectrl + #(parameter STAGES = 2, + parameter TAGWIDTH = 1) + (input clk, + input reset, + input src_rdy_i, + output dst_rdy_o, + output src_rdy_o, + input dst_rdy_i, + output [STAGES-1:0] strobes, + output [STAGES-1:0] valids, + input [TAGWIDTH-1:0] tag_i, + output [TAGWIDTH-1:0] tag_o); + + wire new_input = src_rdy_i & dst_rdy_o; + wire new_output = src_rdy_o & dst_rdy_i; + wire [TAGWIDTH-1:0] tags [STAGES-1:0]; + + assign dst_rdy_o = ~valids[0] | strobes[1]; + + pipestage #(.TAGWIDTH(TAGWIDTH)) head + (.clk(clk),.reset(reset), .stb_in(strobes[0]), .stb_out(strobes[1]),.valid(valids[0]), + .tag_in(tag_i), .tag_out(tags[0])); + assign strobes[0] = src_rdy_i & (~valids[0] | strobes[1]); + + genvar i; + generate + for(i = 1; i < STAGES - 1; i = i + 1) + begin : gen_stages + pipestage #(.TAGWIDTH(TAGWIDTH)) pipestage + (.clk(clk),.reset(reset), .stb_in(strobes[i]),.stb_out(strobes[i+1]),.valid(valids[i]), + .tag_in(tags[i-1]),.tag_out(tags[i])); + assign strobes[i] = valids[i-1] & (~valids[i] | strobes[i+1]); + end + endgenerate + + pipestage #(.TAGWIDTH(TAGWIDTH)) tail + (.clk(clk),.reset(reset), .stb_in(strobes[STAGES-1]), .stb_out(dst_rdy_i),.valid(valids[STAGES-1]), + .tag_in(tags[STAGES-2]), .tag_out(tags[STAGES-1])); + assign strobes[STAGES-1] = valids[STAGES-2] & (~valids[STAGES-1] | new_output); + + assign src_rdy_o = valids[STAGES-1]; + + assign tag_o = tags[STAGES-1]; + +endmodule // pipectrl + + diff --git a/host/include/uhd/usrp/single_usrp.hpp b/fpga/usrp2/sdr_lib/pipestage.v index 0520db162..011afb1ba 100644 --- a/host/include/uhd/usrp/single_usrp.hpp +++ b/fpga/usrp2/sdr_lib/pipestage.v @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2011 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 @@ -15,16 +15,31 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#ifndef INCLUDED_UHD_USRP_SINGLE_USRP_HPP -#define INCLUDED_UHD_USRP_SINGLE_USRP_HPP +module pipestage + #(parameter TAGWIDTH = 1) + (input clk, + input reset, + input stb_in, + input stb_out, + output reg valid, + input [TAGWIDTH-1:0] tag_in, + output reg [TAGWIDTH-1:0] tag_out); -#include <uhd/usrp/multi_usrp.hpp> - -namespace uhd{ namespace usrp{ - - //! Multi-USRP is a superset of Single-USRP - typedef multi_usrp single_usrp; - -}} - -#endif /* INCLUDED_UHD_USRP_SINGLE_USRP_HPP */ + always @(posedge clk) + if(reset) + begin + valid <= 0; + tag_out <= 0; + end + else if(stb_in) + begin + valid <= 1; + tag_out <= tag_in; + end + else if(stb_out) + begin + valid <= 0; + tag_out <= 0; + end + +endmodule // pipestage diff --git a/fpga/usrp2/sdr_lib/round.v b/fpga/usrp2/sdr_lib/round.v index 7a137d702..26d5a4cf4 100644 --- a/fpga/usrp2/sdr_lib/round.v +++ b/fpga/usrp2/sdr_lib/round.v @@ -2,7 +2,7 @@ // // USRP - Universal Software Radio Peripheral // -// Copyright (C) 2007 Matt Ettus +// Copyright (C) 2011 Matt Ettus // // 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 @@ -24,12 +24,36 @@ module round #(parameter bits_in=0, - parameter bits_out=0) + parameter bits_out=0, + parameter round_to_zero=0, // original behavior + parameter round_to_nearest=1, // lowest noise + parameter trunc=0) // round to negative infinity (input [bits_in-1:0] in, output [bits_out-1:0] out, output [bits_in-bits_out:0] err); - assign out = in[bits_in-1:bits_in-bits_out] + (in[bits_in-1] & |in[bits_in-bits_out-1:0]); + wire round_corr,round_corr_trunc,round_corr_rtz,round_corr_nearest,round_corr_nearest_safe; + + assign round_corr_trunc = 0; + assign round_corr_rtz = (in[bits_in-1] & |in[bits_in-bits_out-1:0]); + assign round_corr_nearest = in[bits_in-bits_out-1]; + + generate + if(bits_in-bits_out > 1) + assign round_corr_nearest_safe = (~in[bits_in-1] & (&in[bits_in-2:bits_out])) ? 0 : + round_corr_nearest; + else + assign round_corr_nearest_safe = round_corr_nearest; + endgenerate + + + assign round_corr = round_to_nearest ? round_corr_nearest_safe : + trunc ? round_corr_trunc : + round_to_zero ? round_corr_rtz : + 0; // default to trunc + + assign out = in[bits_in-1:bits_in-bits_out] + round_corr; + assign err = in - {out,{(bits_in-bits_out){1'b0}}}; endmodule // round diff --git a/fpga/usrp2/sdr_lib/round_sd.v b/fpga/usrp2/sdr_lib/round_sd.v index aeeb3502f..94584f6ef 100644 --- a/fpga/usrp2/sdr_lib/round_sd.v +++ b/fpga/usrp2/sdr_lib/round_sd.v @@ -2,7 +2,8 @@ module round_sd #(parameter WIDTH_IN=18, - parameter WIDTH_OUT=16) + parameter WIDTH_OUT=16, + parameter DISABLE_SD=0) (input clk, input reset, input [WIDTH_IN-1:0] in, input strobe_in, output [WIDTH_OUT-1:0] out, output strobe_out); @@ -15,7 +16,7 @@ module round_sd sign_extend #(.bits_in(ERR_WIDTH),.bits_out(WIDTH_IN)) ext_err (.in(err), .out(err_ext)); add2_and_clip_reg #(.WIDTH(WIDTH_IN)) add2_and_clip_reg - (.clk(clk), .rst(reset), .in1(in), .in2(err_ext), .strobe_in(strobe_in), .sum(sum), .strobe_out(strobe_out)); + (.clk(clk), .rst(reset), .in1(in), .in2((DISABLE_SD == 0) ? err_ext : 0), .strobe_in(strobe_in), .sum(sum), .strobe_out(strobe_out)); round #(.bits_in(WIDTH_IN),.bits_out(WIDTH_OUT)) round_sum (.in(sum), .out(out), .err(err)); diff --git a/fpga/usrp2/sdr_lib/round_tb.v b/fpga/usrp2/sdr_lib/round_tb.v new file mode 100644 index 000000000..ddc464f4a --- /dev/null +++ b/fpga/usrp2/sdr_lib/round_tb.v @@ -0,0 +1,61 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2011 Matt Ettus +// +// 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 2 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Boston, MA 02110-1301 USA +// + +// Rounding "macro" +// Keeps the topmost bits, does proper 2s comp round to zero (unbiased truncation) + +module round_tb(); + + localparam IW=8; + localparam OW=4; + localparam EW=IW-OW+1; + + reg signed [IW-1:0] in; + wire signed [OW-1:0] out; + wire signed [EW-1:0] err; + + round #(.bits_in(IW), + .bits_out(OW), + .round_to_zero(0), // original behavior + .round_to_nearest(1), // lowest noise + .trunc(0)) // round to negative infinity + round (.in(in),.out(out),.err(err)); + + initial $dumpfile("round_tb.vcd"); + initial $dumpvars(0,round_tb); + + wire signed [IW-1:0] out_round = {out,{IW-OW{1'b0}}}; + + initial + begin + in <= -129; + #1; + repeat (260) + begin + in <= in + 1; + #1; + $display("In %d, out %d, out_rnd %d, err %d, real err %d",in,out,out_round,-err,out_round-in); + #1; + end + $finish; + end + +endmodule // round diff --git a/fpga/usrp2/sdr_lib/rx_dcoffset.v b/fpga/usrp2/sdr_lib/rx_dcoffset.v index 9840e9e1f..04d7795c0 100644 --- a/fpga/usrp2/sdr_lib/rx_dcoffset.v +++ b/fpga/usrp2/sdr_lib/rx_dcoffset.v @@ -42,8 +42,9 @@ module rx_dcoffset end else if(set_now) begin - //integrator <= {set_data[30:0],{(31-int_width){1'b0}}}; fixed <= set_data[31]; + if(set_data[30]) + integrator <= {set_data[29:0],{(int_width-30){1'b0}}}; end else if(~fixed) integrator <= integrator + {{(alpha_shift){out[WIDTH-1]}},out}; diff --git a/fpga/usrp2/sdr_lib/small_hb_int.v b/fpga/usrp2/sdr_lib/small_hb_int.v index 387f9e1cb..b69c45413 100644 --- a/fpga/usrp2/sdr_lib/small_hb_int.v +++ b/fpga/usrp2/sdr_lib/small_hb_int.v @@ -73,8 +73,8 @@ module small_hb_int final_round (.clk(clk),.in(accum),.out(accum_rnd)); wire [WIDTH-1:0] clipped; - clip_reg #(.bits_in(WIDTH+3),.bits_out(WIDTH)) - final_clip (.clk(clk),.in(accum_rnd),.out(clipped)); + clip_reg #(.bits_in(WIDTH+3),.bits_out(WIDTH)) final_clip + (.clk(clk),.in(accum_rnd),.strobe_in(1'b1), .out(clipped)); reg [WIDTH-1:0] saved, saved_d3; always @(posedge clk) diff --git a/fpga/usrp2/top/B100/Makefile b/fpga/usrp2/top/B100/Makefile new file mode 100644 index 000000000..3ddef1024 --- /dev/null +++ b/fpga/usrp2/top/B100/Makefile @@ -0,0 +1,14 @@ +# +# Copyright 2011 Ettus Research LLC +# + +all: B100 + find -name "*.twr" | xargs grep constraint | grep met + +clean: + rm -rf build* + +B100: + make -f Makefile.$@ bin + +.PHONY: all clean diff --git a/fpga/usrp2/top/B100/u1plus_core.v b/fpga/usrp2/top/B100/u1plus_core.v index e11a4c37e..86bf747a0 100644 --- a/fpga/usrp2/top/B100/u1plus_core.v +++ b/fpga/usrp2/top/B100/u1plus_core.v @@ -56,17 +56,14 @@ module u1plus_core localparam SR_CLEAR_TX_FIFO = 62; // 1 reg localparam SR_GLOBAL_RESET = 63; // 1 reg - wire [7:0] COMPAT_NUM = 8'd5; + localparam SR_GPIO = 128; // 5 regs wire wb_clk = clk_fpga; wire wb_rst, global_reset; wire pps_int; wire [63:0] vita_time, vita_time_pps; - reg [15:0] reg_leds, reg_cgen_ctrl, reg_test; - wire [15:0] xfer_rate = 0; - wire [7:0] test_rate; - wire [3:0] test_ctrl; + reg [15:0] reg_cgen_ctrl, reg_test; wire [7:0] set_addr; wire [31:0] set_data; @@ -90,7 +87,6 @@ module u1plus_core reset_sync reset_sync_wb(.clk(wb_clk), .reset_in(rst_fpga | global_reset), .reset_out(wb_rst)); reset_sync reset_sync_gp(.clk(gpif_clk), .reset_in(rst_fpga | global_reset), .reset_out(gpif_rst)); - wire [15:0] test_len; // ///////////////////////////////////////////////////////////////////////////////////// // GPIF Slave to Wishbone Master @@ -135,7 +131,7 @@ module u1plus_core .tx_underrun(tx_underrun_gpif), .rx_overrun(rx_overrun_gpif), - .frames_per_packet(frames_per_packet), .test_len(test_len), .test_rate(test_rate), .test_ctrl(test_ctrl), + .frames_per_packet(frames_per_packet), .debug0(debug0), .debug1(debug1)); // ///////////////////////////////////////////////////////////////////////// @@ -166,7 +162,7 @@ module u1plus_core .sample(sample_rx0), .run(run_rx0), .strobe(strobe_rx0), .debug() ); - vita_rx_chain #(.BASE(SR_RX_CTRL0), .UNIT(0), .FIFOSIZE(9), .PROT_ENG_FLAGS(0)) vita_rx_chain0 + vita_rx_chain #(.BASE(SR_RX_CTRL0), .UNIT(0), .FIFOSIZE(10), .PROT_ENG_FLAGS(0)) vita_rx_chain0 (.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_dsp0), @@ -189,7 +185,7 @@ module u1plus_core .sample(sample_rx1), .run(run_rx1), .strobe(strobe_rx1), .debug() ); - vita_rx_chain #(.BASE(SR_RX_CTRL1), .UNIT(1), .FIFOSIZE(9), .PROT_ENG_FLAGS(0)) vita_rx_chain1 + vita_rx_chain #(.BASE(SR_RX_CTRL1), .UNIT(1), .FIFOSIZE(10), .PROT_ENG_FLAGS(0)) vita_rx_chain1 (.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_dsp1), @@ -252,14 +248,20 @@ module u1plus_core 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'hE), .s9_addr(4'hf), .s9_mask(4'hF), // slave 8 is double wide - .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)) + .s0_addr(4'h0), .s0_mask(4'hF), // Misc Regs + .s1_addr(4'h1), .s1_mask(4'hF), // Unused + .s2_addr(4'h2), .s2_mask(4'hF), // SPI + .s3_addr(4'h3), .s3_mask(4'hF), // I2C + .s4_addr(4'h4), .s4_mask(4'hF), // Unused + .s5_addr(4'h5), .s5_mask(4'hF), // Unused on B1x0, Async Msg on E1x0 + .s6_addr(4'h6), .s6_mask(4'hF), // Unused + .s7_addr(4'h7), .s7_mask(4'hF), // Readback MUX + .s8_addr(4'h8), .s8_mask(4'h8), // Setting Regs -- slave 8 is 8 slaves wide + // slaves 9-f alias to slave 1, all are unused + .s9_addr(4'h1), .s9_mask(4'hF), + .sa_addr(4'h1), .sa_mask(4'hF), .sb_addr(4'h1), .sb_mask(4'hF), + .sc_addr(4'h1), .sc_mask(4'hF), .sd_addr(4'h1), .sd_mask(4'hF), + .se_addr(4'h1), .se_mask(4'hF), .sf_addr(4'h1), .sf_mask(4'hF)) wb_1master (.clk_i(wb_clk),.rst_i(wb_rst), .m0_dat_o(m0_dat_miso),.m0_ack_o(m0_ack),.m0_err_o(m0_err),.m0_rty_o(m0_rty),.m0_dat_i(m0_dat_mosi), @@ -297,76 +299,48 @@ module u1plus_core .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 s5_ack = 0; + assign s1_ack = 0; assign s4_ack = 0; assign s5_ack = 0; assign s6_ack = 0; assign s9_ack = 0; assign sa_ack = 0; assign sb_ack = 0; assign sc_ack = 0; assign sd_ack = 0; assign se_ack = 0; assign sf_ack = 0; // ///////////////////////////////////////////////////////////////////////////////////// // Slave 0, Misc LEDs, Switches, controls - localparam REG_LEDS = 7'd0; // out 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; - //xfer_rate <= 0; frames_per_packet <= 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_RX_FRAMELEN : frames_per_packet <= s0_dat_mosi[7:0]; - //REG_XFER_RATE : - //xfer_rate <= s0_dat_mosi; endcase // case (s0_adr[6:0]) - assign test_ctrl = xfer_rate[11:8]; - assign test_rate = xfer_rate[7:0]; - assign test_len = reg_test[15:0]; - assign debug_led = {run_tx, (run_rx0 | run_rx1), cgen_st_ld}; assign { cgen_sync_b, cgen_ref_sel } = reg_cgen_ctrl; - assign s0_dat_miso = (s0_adr[6:0] == REG_LEDS) ? reg_leds : - (s0_adr[6:0] == REG_CGEN_CTRL) ? reg_cgen_ctrl : + assign s0_dat_miso = (s0_adr[6:0] == REG_CGEN_CTRL) ? reg_cgen_ctrl : (s0_adr[6:0] == REG_CGEN_ST) ? {13'b0,cgen_st_status,cgen_st_ld,cgen_st_refmon} : (s0_adr[6:0] == REG_TEST) ? reg_test : - (s0_adr[6:0] == REG_COMPAT) ? { 8'd0, 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 @@ -394,39 +368,31 @@ module u1plus_core IOBUF sda_pin(.O(sda_pad_i), .IO(db_sda), .I(sda_pad_o), .T(sda_pad_oen_o)); // ///////////////////////////////////////////////////////////////////////// - // GPIOs -- Slave #4 + // GPIOs - wire [31:0] atr_lines; - wire [31:0] debug_gpio_0, debug_gpio_1; + wire [31:0] gpio_readback; - 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} ) ); + gpio_atr #(.BASE(SR_GPIO), .WIDTH(32)) + gpio_atr(.clk(wb_clk),.reset(wb_rst), + .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), + .rx(run_rx0 | run_rx1), .tx(run_tx), + .gpio({io_tx,io_rx}), .gpio_readback(gpio_readback) ); // ///////////////////////////////////////////////////////////////////////// // Settings Bus -- Slave #8 + 9 // only have 64 regs, 32 bits each with current setup... - settings_bus_16LE #(.AWIDTH(11),.RWIDTH(6)) settings_bus_16LE + settings_bus_16LE #(.AWIDTH(11),.RWIDTH(8)) settings_bus_16LE (.wb_clk(wb_clk),.wb_rst(wb_rst),.wb_adr_i(s8_adr),.wb_dat_i(s8_dat_mosi), .wb_stb_i(s8_stb),.wb_we_i(s8_we),.wb_ack_o(s8_ack), .strobe(set_stb),.addr(set_addr),.data(set_data) ); // ///////////////////////////////////////////////////////////////////////// - // ATR Controller -- Slave #6 - - atr_controller16 atr_controller16 - (.clk_i(wb_clk), .rst_i(wb_rst), - .adr_i(s6_adr[5:0]), .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_rx0 | run_rx1), .run_tx(run_tx), .ctrl_lines(atr_lines)); - - // ///////////////////////////////////////////////////////////////////////// // Readback mux 32 -- Slave #7 + //compatibility number -> increment when the fpga has been sufficiently altered + localparam compat_num = {16'd8, 16'd0}; //major, minor + wire [31:0] reg_test32; setting_reg #(.my_addr(SR_REG_TEST32)) sr_reg_test32 @@ -440,7 +406,7 @@ module u1plus_core .word00(vita_time[63:32]), .word01(vita_time[31:0]), .word02(vita_time_pps[63:32]), .word03(vita_time_pps[31:0]), .word04(reg_test32), .word05(32'b0), - .word06(32'b0), .word07(32'b0), + .word06(compat_num), .word07(gpio_readback), .word08(32'b0), .word09(32'b0), .word10(32'b0), .word11(32'b0), .word12(32'b0), .word13(32'b0), @@ -460,7 +426,5 @@ module u1plus_core assign debug_clk = 2'b00; // { gpif_clk, clk_fpga }; assign debug = 0; - assign debug_gpio_0 = 0; - assign debug_gpio_1 = 0; endmodule // u1plus_core diff --git a/fpga/usrp2/top/E1x0/Makefile b/fpga/usrp2/top/E1x0/Makefile new file mode 100644 index 000000000..0ca8ed2dd --- /dev/null +++ b/fpga/usrp2/top/E1x0/Makefile @@ -0,0 +1,17 @@ +# +# Copyright 2011 Ettus Research LLC +# + +all: E100 E110 + find -name "*.twr" | xargs grep constraint | grep met + +clean: + rm -rf build* + +E100: + make -f Makefile.$@ bin + +E110: + make -f Makefile.$@ bin + +.PHONY: all clean diff --git a/fpga/usrp2/top/E1x0/u1e_core.v b/fpga/usrp2/top/E1x0/u1e_core.v index adc3c5aab..496a7ef4c 100644 --- a/fpga/usrp2/top/E1x0/u1e_core.v +++ b/fpga/usrp2/top/E1x0/u1e_core.v @@ -60,14 +60,14 @@ module u1e_core localparam SR_CLEAR_TX_FIFO = 62; // 1 reg localparam SR_GLOBAL_RESET = 63; // 1 reg - wire [7:0] COMPAT_NUM = 8'd6; + localparam SR_GPIO = 128; // 5 regs wire wb_clk = clk_fpga; wire wb_rst, global_reset; wire pps_int; wire [63:0] vita_time, vita_time_pps; - reg [15:0] reg_leds, reg_cgen_ctrl, reg_test, xfer_rate; + reg [15:0] reg_cgen_ctrl, reg_test, xfer_rate; wire [7:0] test_rate; wire [3:0] test_ctrl; @@ -167,7 +167,7 @@ module u1e_core .sample(sample_rx0), .run(run_rx0), .strobe(strobe_rx0), .debug() ); - vita_rx_chain #(.BASE(SR_RX_CTRL0), .UNIT(0), .FIFOSIZE(9), .PROT_ENG_FLAGS(0)) vita_rx_chain0 + vita_rx_chain #(.BASE(SR_RX_CTRL0), .UNIT(0), .FIFOSIZE(10), .PROT_ENG_FLAGS(0)) vita_rx_chain0 (.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_dsp0), @@ -190,7 +190,7 @@ module u1e_core .sample(sample_rx1), .run(run_rx1), .strobe(strobe_rx1), .debug() ); - vita_rx_chain #(.BASE(SR_RX_CTRL1), .UNIT(1), .FIFOSIZE(9), .PROT_ENG_FLAGS(0)) vita_rx_chain1 + vita_rx_chain #(.BASE(SR_RX_CTRL1), .UNIT(1), .FIFOSIZE(10), .PROT_ENG_FLAGS(0)) vita_rx_chain1 (.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_dsp1), @@ -253,14 +253,20 @@ module u1e_core 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'hE), .s9_addr(4'hf), .s9_mask(4'hF), // slave 8 is double wide - .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)) + .s0_addr(4'h0), .s0_mask(4'hF), // Misc Regs + .s1_addr(4'h1), .s1_mask(4'hF), // Unused + .s2_addr(4'h2), .s2_mask(4'hF), // SPI + .s3_addr(4'h3), .s3_mask(4'hF), // I2C + .s4_addr(4'h4), .s4_mask(4'hF), // Unused + .s5_addr(4'h5), .s5_mask(4'hF), // Unused on B1x0, Async Msg on E1x0 + .s6_addr(4'h6), .s6_mask(4'hF), // Unused + .s7_addr(4'h7), .s7_mask(4'hF), // Readback MUX + .s8_addr(4'h8), .s8_mask(4'h8), // Setting Regs -- slave 8 is 8 slaves wide + // slaves 9-f alias to slave 1, all are unused + .s9_addr(4'h1), .s9_mask(4'hF), + .sa_addr(4'h1), .sa_mask(4'hF), .sb_addr(4'h1), .sb_mask(4'hF), + .sc_addr(4'h1), .sc_mask(4'hF), .sd_addr(4'h1), .sd_mask(4'hF), + .se_addr(4'h1), .se_mask(4'hF), .sf_addr(4'h1), .sf_mask(4'hF)) wb_1master (.clk_i(wb_clk),.rst_i(wb_rst), .m0_dat_o(m0_dat_miso),.m0_ack_o(m0_ack),.m0_err_o(m0_err),.m0_rty_o(m0_rty),.m0_dat_i(m0_dat_mosi), @@ -298,23 +304,21 @@ module u1e_core .sf_dat_o(sf_dat_mosi),.sf_adr_o(sf_adr),.sf_sel_o(sf_sel),.sf_we_o(sf_we),.sf_cyc_o(sf_cyc),.sf_stb_o(sf_stb), .sf_dat_i(sf_dat_miso),.sf_ack_i(sf_ack),.sf_err_i(0),.sf_rty_i(0) ); + assign s1_ack = 0; assign s4_ack = 0; assign s6_ack = 0; assign s9_ack = 0; assign sa_ack = 0; assign sb_ack = 0; assign sc_ack = 0; assign sd_ack = 0; assign se_ack = 0; assign sf_ack = 0; // ///////////////////////////////////////////////////////////////////////////////////// // Slave 0, Misc LEDs, Switches, controls - localparam REG_LEDS = 7'd0; // out localparam REG_CGEN_CTRL = 7'd4; // out localparam REG_CGEN_ST = 7'd6; // in localparam REG_TEST = 7'd8; // 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; xfer_rate <= 0; @@ -322,8 +326,6 @@ module u1e_core 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 : @@ -338,27 +340,14 @@ module u1e_core assign { debug_led[3:0] } = ~{1'b1, run_tx, run_rx0 | run_rx1, cgen_st_ld}; assign { cgen_sync_b, cgen_ref_sel } = reg_cgen_ctrl; - assign s0_dat_miso = (s0_adr[6:0] == REG_LEDS) ? reg_leds : - (s0_adr[6:0] == REG_CGEN_CTRL) ? reg_cgen_ctrl : + assign s0_dat_miso = (s0_adr[6:0] == REG_CGEN_CTRL) ? reg_cgen_ctrl : (s0_adr[6:0] == REG_CGEN_ST) ? {13'b0,cgen_st_status,cgen_st_ld,cgen_st_refmon} : (s0_adr[6:0] == REG_TEST) ? reg_test : - (s0_adr[6:0] == REG_COMPAT) ? { 8'd0, 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 @@ -386,17 +375,15 @@ module u1e_core IOBUF sda_pin(.O(sda_pad_i), .IO(db_sda), .I(sda_pad_o), .T(sda_pad_oen_o)); // ///////////////////////////////////////////////////////////////////////// - // GPIOs -- Slave #4 + // GPIOs - wire [31:0] atr_lines; - wire [31:0] debug_gpio_0, debug_gpio_1; + wire [31:0] gpio_readback; - 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} ) ); + gpio_atr #(.BASE(SR_GPIO), .WIDTH(32)) + gpio_atr(.clk(wb_clk),.reset(wb_rst), + .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), + .rx(run_rx0 | run_rx1), .tx(run_tx), + .gpio({io_tx,io_rx}), .gpio_readback(gpio_readback) ); //////////////////////////////////////////////////////////////////////////// // FIFO to WB slave for async messages - Slave #5 @@ -440,23 +427,17 @@ module u1e_core // Settings Bus -- Slave #8 + 9 // only have 64 regs, 32 bits each with current setup... - settings_bus_16LE #(.AWIDTH(11),.RWIDTH(6)) settings_bus_16LE + settings_bus_16LE #(.AWIDTH(11),.RWIDTH(8)) settings_bus_16LE (.wb_clk(wb_clk),.wb_rst(wb_rst),.wb_adr_i(s8_adr),.wb_dat_i(s8_dat_mosi), .wb_stb_i(s8_stb),.wb_we_i(s8_we),.wb_ack_o(s8_ack), .strobe(set_stb),.addr(set_addr),.data(set_data) ); // ///////////////////////////////////////////////////////////////////////// - // ATR Controller -- Slave #6 - - atr_controller16 atr_controller16 - (.clk_i(wb_clk), .rst_i(wb_rst), - .adr_i(s6_adr[5:0]), .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_rx0 | run_rx1), .run_tx(run_tx), .ctrl_lines(atr_lines)); - - // ///////////////////////////////////////////////////////////////////////// // Readback mux 32 -- Slave #7 + //compatibility number -> increment when the fpga has been sufficiently altered + localparam compat_num = {16'd8, 16'd0}; //major, minor + wire [31:0] reg_test32; //this setting reg is persistent across resets, to check for fpga loaded @@ -471,7 +452,7 @@ module u1e_core .word00(vita_time[63:32]), .word01(vita_time[31:0]), .word02(vita_time_pps[63:32]), .word03(vita_time_pps[31:0]), .word04(reg_test32), .word05(err_status), - .word06(32'b0), .word07(32'b0), + .word06(compat_num), .word07(gpio_readback), .word08(32'b0), .word09(32'b0), .word10(32'b0), .word11(32'b0), .word12(32'b0), .word13(32'b0), @@ -491,7 +472,5 @@ module u1e_core assign debug_clk = 2'b00; //{ EM_CLK, clk_fpga }; assign debug = 0; - assign debug_gpio_0 = 0; - assign debug_gpio_1 = 0; endmodule // u1e_core diff --git a/fpga/usrp2/top/N2x0/u2plus_core.v b/fpga/usrp2/top/N2x0/u2plus_core.v index 4d612bfab..dd3d33b37 100644 --- a/fpga/usrp2/top/N2x0/u2plus_core.v +++ b/fpga/usrp2/top/N2x0/u2plus_core.v @@ -129,7 +129,7 @@ module u2plus_core // External RAM input [35:0] RAM_D_pi, output [35:0] RAM_D_po, - output RAM_D_poe, + output RAM_D_poe, output [20:0] RAM_A, output RAM_CE1n, output RAM_CENn, @@ -163,6 +163,7 @@ module u2plus_core localparam SR_TX_CTRL = 144; // 6 localparam SR_TX_DSP = 160; // 5 + localparam SR_GPIO = 184; // 5 localparam SR_UDP_SM = 192; // 64 // FIFO Sizes, 9 = 512 lines, 10 = 1024, 11 = 2048 @@ -227,14 +228,14 @@ module u2plus_core .s1_addr(8'b0100_0000),.s1_mask(8'b1111_0000), // Packet Router (16-20K) .s2_addr(8'b0101_0000),.s2_mask(8'b1111_1100), // SPI .s3_addr(8'b0101_0100),.s3_mask(8'b1111_1100), // I2C - .s4_addr(8'b0101_1000),.s4_mask(8'b1111_1100), // GPIO + .s4_addr(8'b0101_1000),.s4_mask(8'b1111_1100), // Unused .s5_addr(8'b0101_1100),.s5_mask(8'b1111_1100), // Readback .s6_addr(8'b0110_0000),.s6_mask(8'b1111_0000), // Ethernet MAC - .s7_addr(8'b0111_0000),.s7_mask(8'b1111_0000), // 20K-24K, Settings Bus (only uses 1K) + .s7_addr(8'b0111_0000),.s7_mask(8'b1111_0000), // Settings Bus (only uses 1K) .s8_addr(8'b1000_0000),.s8_mask(8'b1111_1100), // PIC .s9_addr(8'b1000_0100),.s9_mask(8'b1111_1100), // Unused .sa_addr(8'b1000_1000),.sa_mask(8'b1111_1100), // UART - .sb_addr(8'b1000_1100),.sb_mask(8'b1111_1100), // ATR + .sb_addr(8'b1000_1100),.sb_mask(8'b1111_1100), // Unused .sc_addr(8'b1001_0000),.sc_mask(8'b1111_0000), // Unused .sd_addr(8'b1010_0000),.sd_mask(8'b1111_0000), // ICAP .se_addr(8'b1011_0000),.se_mask(8'b1111_0000), // SPI Flash @@ -275,7 +276,11 @@ module u2plus_core .se_dat_i(se_dat_i),.se_ack_i(se_ack),.se_err_i(0),.se_rty_i(0), .sf_dat_o(sf_dat_o),.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_i),.sf_ack_i(sf_ack),.sf_err_i(0),.sf_rty_i(0)); - + + // Unused Slaves 9, b, c + assign s4_ack = 0; + assign s9_ack = 0; assign sb_ack = 0; assign sc_ack = 0; + // //////////////////////////////////////////////////////////////////////////////////////// // Reset Controller @@ -416,18 +421,21 @@ module u2plus_core assign s3_dat_i[31:8] = 24'd0; // ///////////////////////////////////////////////////////////////////////// - // GPIOs -- Slave #4 + // GPIOs - nsgpio nsgpio(.clk_i(wb_clk),.rst_i(wb_rst), - .cyc_i(s4_cyc),.stb_i(s4_stb),.adr_i(s4_adr[4:0]),.we_i(s4_we), - .dat_i(s4_dat_o),.dat_o(s4_dat_i),.ack_o(s4_ack), - .rx(run_rx0_d1 | rx_rx1_d1), .tx(run_tx), .gpio({io_tx,io_rx}) ); + wire [31:0] gpio_readback; + + gpio_atr #(.BASE(SR_GPIO), .WIDTH(32)) + gpio_atr(.clk(dsp_clk),.reset(dsp_rst), + .set_stb(set_stb_dsp),.set_addr(set_addr_dsp),.set_data(set_data_dsp), + .rx(run_rx0_d1 | run_rx1_d1), .tx(run_tx), + .gpio({io_tx,io_rx}), .gpio_readback(gpio_readback) ); // ///////////////////////////////////////////////////////////////////////// // Buffer Pool Status -- Slave #5 //compatibility number -> increment when the fpga has been sufficiently altered - localparam compat_num = {16'd7, 16'd3}; //major, minor + localparam compat_num = {16'd8, 16'd0}; //major, minor wb_readback_mux buff_pool_status (.wb_clk_i(wb_clk), .wb_rst_i(wb_rst), .wb_stb_i(s5_stb), @@ -435,8 +443,8 @@ module u2plus_core .word00(32'b0),.word01(32'b0),.word02(32'b0),.word03(32'b0), .word04(32'b0),.word05(32'b0),.word06(32'b0),.word07(32'b0), - .word08(status),.word09(32'b0),.word10(vita_time[63:32]), - .word11(vita_time[31:0]),.word12(compat_num),.word13(irq), + .word08(status),.word09(gpio_readback),.word10(vita_time[63:32]), + .word11(vita_time[31:0]),.word12(compat_num),.word13({18'b0, button, 13'b0}), .word14(vita_time_pps[63:32]),.word15(vita_time_pps[31:0]) ); @@ -480,16 +488,20 @@ module u2plus_core wire phy_reset; assign PHY_RESETn = ~phy_reset; - setting_reg #(.my_addr(SR_MISC+0),.width(8)) sr_clk (.clk(wb_clk),.rst(wb_rst),.strobe(s7_ack),.addr(set_addr), - .in(set_data),.out(clock_outs),.changed()); - setting_reg #(.my_addr(SR_MISC+1),.width(8)) sr_ser (.clk(wb_clk),.rst(wb_rst),.strobe(set_stb),.addr(set_addr), - .in(set_data),.out(serdes_outs),.changed()); - setting_reg #(.my_addr(SR_MISC+2),.width(8)) sr_adc (.clk(wb_clk),.rst(wb_rst),.strobe(set_stb),.addr(set_addr), - .in(set_data),.out(adc_outs),.changed()); - setting_reg #(.my_addr(SR_MISC+4),.width(1)) sr_phy (.clk(wb_clk),.rst(wb_rst),.strobe(set_stb),.addr(set_addr), - .in(set_data),.out(phy_reset),.changed()); - setting_reg #(.my_addr(SR_MISC+5),.width(1)) sr_bld (.clk(wb_clk),.rst(wb_rst),.strobe(set_stb),.addr(set_addr), - .in(set_data),.out(bldr_done),.changed()); + setting_reg #(.my_addr(SR_MISC+0),.width(8)) sr_clk + (.clk(wb_clk),.rst(wb_rst),.strobe(s7_ack),.addr(set_addr),.in(set_data),.out(clock_outs),.changed()); + + setting_reg #(.my_addr(SR_MISC+1),.width(8)) sr_ser + (.clk(dsp_clk),.rst(dsp_rst),.strobe(set_stb_dsp),.addr(set_addr_dsp),.in(set_data_dsp),.out(serdes_outs),.changed()); + + setting_reg #(.my_addr(SR_MISC+2),.width(8)) sr_adc + (.clk(dsp_clk),.rst(dsp_rst),.strobe(set_stb_dsp),.addr(set_addr_dsp),.in(set_data_dsp),.out(adc_outs),.changed()); + + setting_reg #(.my_addr(SR_MISC+4),.width(1)) sr_phy + (.clk(dsp_clk),.rst(dsp_rst),.strobe(set_stb_dsp),.addr(set_addr_dsp),.in(set_data_dsp),.out(phy_reset),.changed()); + + setting_reg #(.my_addr(SR_MISC+5),.width(1)) sr_bld + (.clk(wb_clk),.rst(wb_rst),.strobe(set_stb),.addr(set_addr),.in(set_data),.out(bldr_done),.changed()); // ///////////////////////////////////////////////////////////////////////// // LEDS @@ -500,48 +512,27 @@ module u2plus_core wire [7:0] led_src, led_sw; wire [7:0] led_hw = {run_tx, (run_rx0_d1 | run_rx1_d1), clk_status, serdes_link_up & good_sync, 1'b0}; - setting_reg #(.my_addr(SR_MISC+3),.width(8)) sr_led (.clk(wb_clk),.rst(wb_rst),.strobe(set_stb),.addr(set_addr), - .in(set_data),.out(led_sw),.changed()); + setting_reg #(.my_addr(SR_MISC+3),.width(8)) sr_led + (.clk(dsp_clk),.rst(dsp_rst),.strobe(set_stb_dsp),.addr(set_addr_dsp),.in(set_data_dsp),.out(led_sw),.changed()); - setting_reg #(.my_addr(SR_MISC+6),.width(8), .at_reset(8'b0001_1110)) - sr_led_src (.clk(wb_clk),.rst(wb_rst), .strobe(set_stb),.addr(set_addr), .in(set_data),.out(led_src),.changed()); + setting_reg #(.my_addr(SR_MISC+6),.width(8), .at_reset(8'b0001_1110)) sr_led_src + (.clk(dsp_clk),.rst(dsp_rst),.strobe(set_stb_dsp),.addr(set_addr_dsp), .in(set_data_dsp),.out(led_src),.changed()); assign leds = (led_src & led_hw) | (~led_src & led_sw); // ///////////////////////////////////////////////////////////////////////// // Interrupt Controller, Slave #8 - // Pass interrupts on dsp_clk to wb_clk. These need edge triggering in the pic - wire underrun_wb, overrun_wb, pps_wb; - - oneshot_2clk underrun_1s (.clk_in(dsp_clk), .in(underrun), .clk_out(wb_clk), .out(underrun_wb)); - oneshot_2clk overrun_1s (.clk_in(dsp_clk), .in(overrun0 | overrun1), .clk_out(wb_clk), .out(overrun_wb)); - oneshot_2clk pps_1s (.clk_in(dsp_clk), .in(pps_int), .clk_out(wb_clk), .out(pps_wb)); - assign irq= {{8'b0}, {uart_tx_int[3:0], uart_rx_int[3:0]}, - {2'b0, button, periodic_int, clk_status, serdes_link_up, 2'b00}, - {pps_wb,overrun_wb,underrun_wb,PHY_INTn,i2c_int,spi_int,onetime_int,buffer_int}}; + {4'b0, clk_status, 3'b0}, + {3'b0, PHY_INTn,i2c_int,spi_int,2'b00}}; pic pic(.clk_i(wb_clk),.rst_i(wb_rst),.cyc_i(s8_cyc),.stb_i(s8_stb),.adr_i(s8_adr[4:2]), .we_i(s8_we),.dat_i(s8_dat_o),.dat_o(s8_dat_i),.ack_o(s8_ack),.int_o(proc_int), .irq(irq) ); // ///////////////////////////////////////////////////////////////////////// - // Master Timer, Slave #9 - - // No longer used, replaced with simple_timer below - assign s9_ack = 0; - - // ///////////////////////////////////////////////////////////////////////// - // Simple Timer interrupts - /* - simple_timer #(.BASE(SR_SIMTIMER)) simple_timer - (.clk(wb_clk), .reset(wb_rst), - .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), - .onetime_int(onetime_int), .periodic_int(periodic_int)); - */ - // ///////////////////////////////////////////////////////////////////////// // UART, Slave #10 quad_uart #(.TXDEPTH(3),.RXDEPTH(3)) uart // depth of 3 is 128 entries @@ -550,24 +541,6 @@ module u2plus_core .adr_i(sa_adr[6:2]),.dat_i(sa_dat_o),.dat_o(sa_dat_i), .rx_int_o(uart_rx_int),.tx_int_o(uart_tx_int), .tx_o(uart_tx_o),.rx_i(uart_rx_i),.baud_o(uart_baud_o)); - - // ///////////////////////////////////////////////////////////////////////// - // ATR Controller, Slave #11 - - /* - atr_controller atr_controller - (.clk_i(wb_clk),.rst_i(wb_rst), - .adr_i(sb_adr[5:0]),.sel_i(sb_sel),.dat_i(sb_dat_o),.dat_o(sb_dat_i), - .we_i(sb_we),.stb_i(sb_stb),.cyc_i(sb_cyc),.ack_o(sb_ack), - .run_rx(run_rx0_d1 | run_rx1_d1),.run_tx(run_tx),.ctrl_lines(atr_lines) ); - */ - - // ////////////////////////////////////////////////////////////////////////// - // Time Sync, Slave #12 - - // No longer used, see time_64bit. Still need to handle mimo time, though - assign sc_ack = 0; - // ///////////////////////////////////////////////////////////////////////// // ICAP for reprogramming the FPGA, Slave #13 (D) @@ -660,8 +633,8 @@ module u2plus_core wire clear_tx; setting_reg #(.my_addr(SR_TX_CTRL+1)) sr_clear_tx - (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), - .in(set_data),.out(),.changed(clear_tx)); + (.clk(dsp_clk),.rst(dsp_rst),.strobe(set_stb_dsp),.addr(set_addr_dsp), + .in(set_data_dsp),.out(),.changed(clear_tx)); assign RAM_A[20:18] = 3'b0; diff --git a/fpga/usrp2/top/USRP2/u2_core.v b/fpga/usrp2/top/USRP2/u2_core.v index 7415f68e5..d3524c304 100644 --- a/fpga/usrp2/top/USRP2/u2_core.v +++ b/fpga/usrp2/top/USRP2/u2_core.v @@ -136,7 +136,7 @@ module u2_core // External RAM input [17:0] RAM_D_pi, - output [17:0] RAM_D_po, + output [17:0] RAM_D_po, output RAM_D_poe, output [18:0] RAM_A, output RAM_CE1n, @@ -168,6 +168,7 @@ module u2_core localparam SR_TX_CTRL = 144; // 6 localparam SR_TX_DSP = 160; // 5 + localparam SR_GPIO = 184; // 5 localparam SR_UDP_SM = 192; // 64 // FIFO Sizes, 9 = 512 lines, 10 = 1024, 11 = 2048 @@ -233,16 +234,16 @@ module u2_core .s1_addr(8'b0100_0000),.s1_mask(8'b1111_0000), // Packet Router (16-20K) .s2_addr(8'b0101_0000),.s2_mask(8'b1111_1100), // SPI .s3_addr(8'b0101_0100),.s3_mask(8'b1111_1100), // I2C - .s4_addr(8'b0101_1000),.s4_mask(8'b1111_1100), // GPIO + .s4_addr(8'b0101_1000),.s4_mask(8'b1111_1100), // Unused .s5_addr(8'b0101_1100),.s5_mask(8'b1111_1100), // Readback .s6_addr(8'b0110_0000),.s6_mask(8'b1111_0000), // Ethernet MAC - .s7_addr(8'b0111_0000),.s7_mask(8'b1111_0000), // 20K-24K, Settings Bus (only uses 1K) + .s7_addr(8'b0111_0000),.s7_mask(8'b1111_0000), // Settings Bus (only uses 1K) .s8_addr(8'b1000_0000),.s8_mask(8'b1111_1100), // PIC .s9_addr(8'b1000_0100),.s9_mask(8'b1111_1100), // Unused .sa_addr(8'b1000_1000),.sa_mask(8'b1111_1100), // UART - .sb_addr(8'b1000_1100),.sb_mask(8'b1111_1100), // ATR + .sb_addr(8'b1000_1100),.sb_mask(8'b1111_1100), // Unused .sc_addr(8'b1001_0000),.sc_mask(8'b1111_0000), // Unused - .sd_addr(8'b1010_0000),.sd_mask(8'b1111_0000), // SD Card access + .sd_addr(8'b1010_0000),.sd_mask(8'b1111_0000), // Unused .se_addr(8'b1011_0000),.se_mask(8'b1111_0000), // Unused .sf_addr(8'b1100_0000),.sf_mask(8'b1100_0000), // Unused .dw(dw),.aw(aw),.sw(sw)) wb_1master @@ -281,7 +282,12 @@ module u2_core .se_dat_i(se_dat_i),.se_ack_i(se_ack),.se_err_i(0),.se_rty_i(0), .sf_dat_o(sf_dat_o),.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_i),.sf_ack_i(sf_ack),.sf_err_i(0),.sf_rty_i(0)); - + + // Unused Slaves 4, 9 and b-f + assign s4_ack = 0; + assign s9_ack = 0; assign sb_ack = 0; assign sc_ack = 0; + assign sd_ack = 0; assign se_ack = 0; assign fc_ack = 0; + // //////////////////////////////////////////////////////////////////////////////////////// // Reset Controller system_control sysctrl (.wb_clk_i(wb_clk), // .por_i(por), @@ -421,18 +427,21 @@ module u2_core assign s3_dat_i[31:8] = 24'd0; // ///////////////////////////////////////////////////////////////////////// - // GPIOs -- Slave #4 + // GPIOs - nsgpio nsgpio(.clk_i(wb_clk),.rst_i(wb_rst), - .cyc_i(s4_cyc),.stb_i(s4_stb),.adr_i(s4_adr[4:0]),.we_i(s4_we), - .dat_i(s4_dat_o),.dat_o(s4_dat_i),.ack_o(s4_ack), - .rx(run_rx0_d1 | rx_rx1_d1), .tx(run_tx), .gpio({io_tx,io_rx}) ); + wire [31:0] gpio_readback; + + gpio_atr #(.BASE(SR_GPIO), .WIDTH(32)) + gpio_atr(.clk(dsp_clk),.reset(dsp_rst), + .set_stb(set_stb_dsp),.set_addr(set_addr_dsp),.set_data(set_data_dsp), + .rx(run_rx0_d1 | run_rx1_d1), .tx(run_tx), + .gpio({io_tx,io_rx}), .gpio_readback(gpio_readback) ); // ///////////////////////////////////////////////////////////////////////// // Buffer Pool Status -- Slave #5 //compatibility number -> increment when the fpga has been sufficiently altered - localparam compat_num = {16'd7, 16'd3}; //major, minor + localparam compat_num = {16'd8, 16'd0}; //major, minor wb_readback_mux buff_pool_status (.wb_clk_i(wb_clk), .wb_rst_i(wb_rst), .wb_stb_i(s5_stb), @@ -440,8 +449,8 @@ module u2_core .word00(32'b0),.word01(32'b0),.word02(32'b0),.word03(32'b0), .word04(32'b0),.word05(32'b0),.word06(32'b0),.word07(32'b0), - .word08(status),.word09(32'b0),.word10(vita_time[63:32]), - .word11(vita_time[31:0]),.word12(compat_num),.word13(irq), + .word08(status),.word09(gpio_readback),.word10(vita_time[63:32]), + .word11(vita_time[31:0]),.word12(compat_num),.word13(32'b0), .word14(vita_time_pps[63:32]),.word15(vita_time_pps[31:0]) ); @@ -485,14 +494,17 @@ module u2_core wire phy_reset; assign PHY_RESETn = ~phy_reset; - setting_reg #(.my_addr(SR_MISC+0),.width(8)) sr_clk (.clk(wb_clk),.rst(wb_rst),.strobe(s7_ack),.addr(set_addr), - .in(set_data),.out(clock_outs),.changed()); - setting_reg #(.my_addr(SR_MISC+1),.width(8)) sr_ser (.clk(wb_clk),.rst(wb_rst),.strobe(set_stb),.addr(set_addr), - .in(set_data),.out(serdes_outs),.changed()); - setting_reg #(.my_addr(SR_MISC+2),.width(8)) sr_adc (.clk(wb_clk),.rst(wb_rst),.strobe(set_stb),.addr(set_addr), - .in(set_data),.out(adc_outs),.changed()); - setting_reg #(.my_addr(SR_MISC+4),.width(1)) sr_phy (.clk(wb_clk),.rst(wb_rst),.strobe(set_stb),.addr(set_addr), - .in(set_data),.out(phy_reset),.changed()); + setting_reg #(.my_addr(SR_MISC+0),.width(8)) sr_clk + (.clk(wb_clk),.rst(wb_rst),.strobe(s7_ack),.addr(set_addr),.in(set_data),.out(clock_outs),.changed()); + + setting_reg #(.my_addr(SR_MISC+1),.width(8)) sr_ser + (.clk(dsp_clk),.rst(dsp_rst),.strobe(set_stb_dsp),.addr(set_addr_dsp),.in(set_data_dsp),.out(serdes_outs),.changed()); + + setting_reg #(.my_addr(SR_MISC+2),.width(8)) sr_adc + (.clk(dsp_clk),.rst(dsp_rst),.strobe(set_stb_dsp),.addr(set_addr_dsp),.in(set_data_dsp),.out(adc_outs),.changed()); + + setting_reg #(.my_addr(SR_MISC+4),.width(1)) sr_phy + (.clk(dsp_clk),.rst(dsp_rst),.strobe(set_stb_dsp),.addr(set_addr_dsp),.in(set_data_dsp),.out(phy_reset),.changed()); // ///////////////////////////////////////////////////////////////////////// // LEDS @@ -503,48 +515,27 @@ module u2_core wire [7:0] led_src, led_sw; wire [7:0] led_hw = {run_tx, (run_rx0_d1 | run_rx1_d1), clk_status, serdes_link_up & good_sync, 1'b0}; - setting_reg #(.my_addr(SR_MISC+3),.width(8)) sr_led (.clk(wb_clk),.rst(wb_rst),.strobe(set_stb),.addr(set_addr), - .in(set_data),.out(led_sw),.changed()); + setting_reg #(.my_addr(SR_MISC+3),.width(8)) sr_led + (.clk(dsp_clk),.rst(dsp_rst),.strobe(set_stb_dsp),.addr(set_addr_dsp),.in(set_data_dsp),.out(led_sw),.changed()); - setting_reg #(.my_addr(SR_MISC+6),.width(8), .at_reset(8'b0001_1110)) - sr_led_src (.clk(wb_clk),.rst(wb_rst), .strobe(set_stb),.addr(set_addr), .in(set_data),.out(led_src),.changed()); + setting_reg #(.my_addr(SR_MISC+6),.width(8), .at_reset(8'b0001_1110)) sr_led_src + (.clk(dsp_clk),.rst(dsp_rst),.strobe(set_stb_dsp),.addr(set_addr_dsp), .in(set_data_dsp),.out(led_src),.changed()); assign leds = (led_src & led_hw) | (~led_src & led_sw); // ///////////////////////////////////////////////////////////////////////// // Interrupt Controller, Slave #8 - // Pass interrupts on dsp_clk to wb_clk. These need edge triggering in the pic - wire underrun_wb, overrun_wb, pps_wb; - - oneshot_2clk underrun_1s (.clk_in(dsp_clk), .in(underrun), .clk_out(wb_clk), .out(underrun_wb)); - oneshot_2clk overrun_1s (.clk_in(dsp_clk), .in(overrun0 | overrun1), .clk_out(wb_clk), .out(overrun_wb)); - oneshot_2clk pps_1s (.clk_in(dsp_clk), .in(pps_int), .clk_out(wb_clk), .out(pps_wb)); - assign irq= {{8'b0}, - {8'b0}, - {2'b0, good_sync, periodic_int, clk_status, serdes_link_up, uart_tx_int, uart_rx_int}, - {pps_wb,overrun_wb,underrun_wb,PHY_INTn,i2c_int,spi_int,onetime_int,buffer_int}}; + {3'b0, uart_tx_int, 2'b0, uart_rx_int}, + {4'b0, clk_status, 3'b0}, + {3'b0, PHY_INTn,i2c_int,spi_int,2'b00}}; pic pic(.clk_i(wb_clk),.rst_i(wb_rst),.cyc_i(s8_cyc),.stb_i(s8_stb),.adr_i(s8_adr[4:2]), .we_i(s8_we),.dat_i(s8_dat_o),.dat_o(s8_dat_i),.ack_o(s8_ack),.int_o(proc_int), .irq(irq) ); // ///////////////////////////////////////////////////////////////////////// - // Master Timer, Slave #9 - - // No longer used, replaced with simple_timer below - assign s9_ack = 0; - - // ///////////////////////////////////////////////////////////////////////// - // Simple Timer interrupts - /* - simple_timer #(.BASE(SR_SIMTIMER)) simple_timer - (.clk(wb_clk), .reset(wb_rst), - .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), - .onetime_int(onetime_int), .periodic_int(periodic_int)); - */ - // ///////////////////////////////////////////////////////////////////////// // UART, Slave #10 simple_uart #(.TXDEPTH(3),.RXDEPTH(3)) uart // depth of 3 is 128 entries @@ -553,36 +544,7 @@ module u2_core .adr_i(sa_adr[4:2]),.dat_i(sa_dat_o),.dat_o(sa_dat_i), .rx_int_o(uart_rx_int),.tx_int_o(uart_tx_int), .tx_o(uart_tx_o),.rx_i(uart_rx_i),.baud_o(uart_baud_o)); - - // ///////////////////////////////////////////////////////////////////////// - // ATR Controller, Slave #11 - /* - atr_controller atr_controller - (.clk_i(wb_clk),.rst_i(wb_rst), - .adr_i(sb_adr[5:0]),.sel_i(sb_sel),.dat_i(sb_dat_o),.dat_o(sb_dat_i), - .we_i(sb_we),.stb_i(sb_stb),.cyc_i(sb_cyc),.ack_o(sb_ack), - .run_rx(run_rx0_d1 | run_rx1_d1),.run_tx(run_tx),.ctrl_lines(atr_lines) ); - */ - - // ////////////////////////////////////////////////////////////////////////// - // Time Sync, Slave #12 - - // No longer used, see time_64bit. Still need to handle mimo time, though - assign sc_ack = 0; - - // ///////////////////////////////////////////////////////////////////////// - // SD Card Reader / Writer, Slave #13 - /* - sd_spi_wb sd_spi_wb - (.clk(wb_clk),.rst(wb_rst), - .sd_clk(sd_clk),.sd_csn(sd_csn),.sd_mosi(sd_mosi),.sd_miso(sd_miso), - .wb_cyc_i(sd_cyc),.wb_stb_i(sd_stb),.wb_we_i(sd_we), - .wb_adr_i(sd_adr[3:2]),.wb_dat_i(sd_dat_o[7:0]),.wb_dat_o(sd_dat_i[7:0]), - .wb_ack_o(sd_ack) ); - - assign sd_dat_i[31:8] = 0; - */ // ///////////////////////////////////////////////////////////////////////// // ADC Frontend wire [23:0] adc_i, adc_q; @@ -659,8 +621,8 @@ module u2_core wire clear_tx; setting_reg #(.my_addr(SR_TX_CTRL+1)) sr_clear_tx - (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), - .in(set_data),.out(),.changed(clear_tx)); + (.clk(dsp_clk),.rst(dsp_rst),.strobe(set_stb_dsp),.addr(set_addr_dsp), + .in(set_data_dsp),.out(),.changed(clear_tx)); ext_fifo #(.EXT_WIDTH(18),.INT_WIDTH(36),.RAM_DEPTH(19),.FIFO_DEPTH(19)) ext_fifo_i1 diff --git a/fpga/usrp2/vrt/vita_rx_chain.v b/fpga/usrp2/vrt/vita_rx_chain.v index 1986743b3..8b41e5fa8 100644 --- a/fpga/usrp2/vrt/vita_rx_chain.v +++ b/fpga/usrp2/vrt/vita_rx_chain.v @@ -50,11 +50,32 @@ module vita_rx_chain .data_o(rx_data_int), .src_rdy_o(rx_src_rdy_int), .dst_rdy_i(rx_dst_rdy_int), .debug_rx(vrf_debug) ); - dsp_framer36 #(.BUF_SIZE(FIFOSIZE), - .PORT_SEL(UNIT), - .PROT_ENG_FLAGS(PROT_ENG_FLAGS)) dsp0_framer36 - (.clk(clk), .reset(reset), .clear(clear), + wire [FIFOSIZE-1:0] access_adr, access_len; + wire access_we, access_stb, access_ok, access_done, access_skip_read; + wire [35:0] dsp_to_buf, buf_to_dsp; + wire [35:0] rx_data_int2; + wire rx_src_rdy_int2, rx_dst_rdy_int2; + + double_buffer #(.BUF_SIZE(FIFOSIZE)) db + (.clk(clk),.reset(reset),.clear(clear), + .access_we(access_we), .access_stb(access_stb), .access_ok(access_ok), .access_done(access_done), + .access_skip_read(access_skip_read), .access_adr(access_adr), .access_len(access_len), + .access_dat_i(dsp_to_buf), .access_dat_o(buf_to_dsp), + .data_i(rx_data_int), .src_rdy_i(rx_src_rdy_int), .dst_rdy_o(rx_dst_rdy_int), + .data_o(rx_data_int2), .src_rdy_o(rx_src_rdy_int2), .dst_rdy_i(rx_dst_rdy_int2)); + + dspengine_16to8 #(.BASE(BASE+9), .BUF_SIZE(FIFOSIZE)) dspengine_16to8 + (.clk(clk),.reset(reset),.clear(clear), + .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .access_we(access_we), .access_stb(access_stb), .access_ok(access_ok), .access_done(access_done), + .access_skip_read(access_skip_read), .access_adr(access_adr), .access_len(access_len), + .access_dat_i(buf_to_dsp), .access_dat_o(dsp_to_buf)); + + add_routing_header #(.PORT_SEL(UNIT), + .PROT_ENG_FLAGS(PROT_ENG_FLAGS)) dsp_routing_header + (.clk(clk), .reset(reset), .clear(clear), + .data_i(rx_data_int2), .src_rdy_i(rx_src_rdy_int2), .dst_rdy_o(rx_dst_rdy_int2), .data_o(rx_data_o), .src_rdy_o(rx_src_rdy_o), .dst_rdy_i(rx_dst_rdy_i) ); assign debug = vrc_debug; // | vrf_debug; diff --git a/host/cmake/Modules/UHDVersion.cmake b/host/cmake/Modules/UHDVersion.cmake index 2aca64891..adc653d87 100644 --- a/host/cmake/Modules/UHDVersion.cmake +++ b/host/cmake/Modules/UHDVersion.cmake @@ -26,8 +26,8 @@ FIND_PACKAGE(Git QUIET) # - increment patch on for bug fixes and docs ######################################################################## SET(UHD_VERSION_MAJOR 003) -SET(UHD_VERSION_MINOR 003) -SET(UHD_VERSION_PATCH 001) +SET(UHD_VERSION_MINOR 004) +SET(UHD_VERSION_PATCH 000) ######################################################################## # Version information discovery through git log diff --git a/host/docs/CMakeLists.txt b/host/docs/CMakeLists.txt index be00a6655..fe3799a2f 100644 --- a/host/docs/CMakeLists.txt +++ b/host/docs/CMakeLists.txt @@ -27,6 +27,7 @@ SET(manual_sources gpsdo.rst general.rst images.rst + stream.rst sync.rst transport.rst usrp1.rst diff --git a/host/docs/index.rst b/host/docs/index.rst index 7b6d026b5..0b24927e9 100644 --- a/host/docs/index.rst +++ b/host/docs/index.rst @@ -36,4 +36,5 @@ API Documentation ^^^^^^^^^^^^^^^^^^^^^ * `Doxygen <./../../doxygen/html/index.html>`_ * `Using the API <./coding.html>`_ +* `Device streaming <./stream.html>`_ diff --git a/host/docs/stream.rst b/host/docs/stream.rst new file mode 100644 index 000000000..9ffec22e5 --- /dev/null +++ b/host/docs/stream.rst @@ -0,0 +1,59 @@ +======================================================================== +UHD - Device streaming +======================================================================== + +.. contents:: Table of Contents + +------------------------------------------------------------------------ +Introduction to streaming +------------------------------------------------------------------------ +The concept of streaming refers to the transportation of samples between host and device. +A stream is an object that facilitates streaming between host application and device. +A RX stream allows the user to receive samples from the device. +A TX stream allows the user to transmit samples to the device. + +------------------------------------------------------------------------ +Link layer encapsulation +------------------------------------------------------------------------ +The VITA49 standard provides encapsulation for sample data across a link layer. +On all generation2 hardware, samples are encapsulated into VRT IF data packets. +These packets also provide sample decoration such as stream time and burst flags. +Sample decoration is exposed to the user in the form of RX and TX metadata structs. + +The length of an IF data packet can be limited by several factors: + +* MTU of the link layer - network card, network switch +* Buffering on the host - frame size in a ring buffer +* Buffering on the device - size of BRAM FIFOs + +------------------------------------------------------------------------ +Data types +------------------------------------------------------------------------ +There are two important data types to consider when streaming: + +* The data type of the samples used on the host for processing +* The data type of the samples sent through the link-layer + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The host/CPU data type +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The host data type refers to the format of samples used in the host for baseband processing. +Typically, the data type is complex baseband such as normalized complex-float32 or complex-int16. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The link-layer data type +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The link-layer or "over-the-wire" data type refers to the format of the samples sent through the link. +Typically, this data type is complex-int16. +However, To increase throughput over the link-layer, +at the expense of precision, complex-int8 may be used. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Conversion +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The user may request arbitrary combinations of host and link data types; +however, not all combinations are supported. +The user may register custom data type formats and conversion routines. +See uhd/convert.hpp for futher documentation. + +TODO provide example of convert API diff --git a/host/docs/usrp1.rst b/host/docs/usrp1.rst index 8b274a2b3..597b5b17f 100644 --- a/host/docs/usrp1.rst +++ b/host/docs/usrp1.rst @@ -84,3 +84,9 @@ Run the following commands to record the setting into the EEPROM: cd <install-path>/share/uhd/utils ./usrp_burn_mb_eeprom --args=<optional device args> --key=mcr --val=<rate> + +The user may override the clock rate specified in the EEPROM by using a device address: +Example: +:: + + uhd_usrp_probe --args="mcr=52e6" diff --git a/host/examples/benchmark_rate.cpp b/host/examples/benchmark_rate.cpp index 774b240d4..fce184514 100644 --- a/host/examples/benchmark_rate.cpp +++ b/host/examples/benchmark_rate.cpp @@ -43,6 +43,10 @@ unsigned long long num_seq_errors = 0; void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp){ uhd::set_thread_priority_safe(); + //create a receive streamer + uhd::stream_args_t stream_args("fc32"); //complex floats + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + //print pre-test summary std::cout << boost::format( "Testing receive rate %f Msps" @@ -50,7 +54,7 @@ void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp){ //setup variables and allocate buffer uhd::rx_metadata_t md; - const size_t max_samps_per_packet = usrp->get_device()->get_max_recv_samps_per_packet(); + const size_t max_samps_per_packet = rx_stream->get_max_num_samps(); std::vector<std::complex<float> > buff(max_samps_per_packet); bool had_an_overflow = false; uhd::time_spec_t last_time; @@ -58,10 +62,8 @@ void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp){ usrp->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); while (not boost::this_thread::interruption_requested()){ - num_rx_samps += usrp->get_device()->recv( - &buff.front(), buff.size(), md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_ONE_PACKET + num_rx_samps += rx_stream->recv( + &buff.front(), buff.size(), md ); //handle the error codes @@ -95,6 +97,10 @@ void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp){ void benchmark_tx_rate(uhd::usrp::multi_usrp::sptr usrp){ uhd::set_thread_priority_safe(); + //create a transmit streamer + uhd::stream_args_t stream_args("fc32"); //complex floats + uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); + //print pre-test summary std::cout << boost::format( "Testing transmit rate %f Msps" @@ -103,23 +109,16 @@ void benchmark_tx_rate(uhd::usrp::multi_usrp::sptr usrp){ //setup variables and allocate buffer uhd::tx_metadata_t md; md.has_time_spec = false; - const size_t max_samps_per_packet = usrp->get_device()->get_max_send_samps_per_packet(); + const size_t max_samps_per_packet = tx_stream->get_max_num_samps(); std::vector<std::complex<float> > buff(max_samps_per_packet); while (not boost::this_thread::interruption_requested()){ - num_tx_samps += usrp->get_device()->send( - &buff.front(), buff.size(), md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::SEND_MODE_ONE_PACKET - ); + num_tx_samps += tx_stream->send(&buff.front(), buff.size(), md); } //send a mini EOB packet - md.end_of_burst = true; - usrp->get_device()->send("", 0, md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::SEND_MODE_FULL_BUFF - ); + md.end_of_burst = true; + tx_stream->send("", 0, md); } void benchmark_tx_rate_async_helper(uhd::usrp::multi_usrp::sptr usrp){ diff --git a/host/examples/latency_test.cpp b/host/examples/latency_test.cpp index 85ac73567..518d2383a 100644 --- a/host/examples/latency_test.cpp +++ b/host/examples/latency_test.cpp @@ -83,6 +83,11 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //allocate a buffer to use std::vector<std::complex<float> > buffer(nsamps); + //create RX and TX streamers + uhd::stream_args_t stream_args("fc32"); //complex floats + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); + //initialize result counts int time_error = 0; int ack = 0; @@ -104,10 +109,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ * Receive the requested packet **************************************************************/ uhd::rx_metadata_t rx_md; - size_t num_rx_samps = usrp->get_device()->recv( - &buffer.front(), buffer.size(), rx_md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_FULL_BUFF + size_t num_rx_samps = rx_stream->recv( + &buffer.front(), buffer.size(), rx_md ); if(verbose) std::cout << boost::format("Got packet: %u samples, %u full secs, %f frac secs") @@ -121,10 +124,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ tx_md.end_of_burst = true; tx_md.has_time_spec = true; tx_md.time_spec = rx_md.time_spec + uhd::time_spec_t(rtt); - size_t num_tx_samps = usrp->get_device()->send( - &buffer.front(), buffer.size(), tx_md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::SEND_MODE_FULL_BUFF + size_t num_tx_samps = tx_stream->send( + &buffer.front(), buffer.size(), tx_md ); if(verbose) std::cout << boost::format("Sent %d samples") % num_tx_samps << std::endl; diff --git a/host/examples/rx_ascii_art_dft.cpp b/host/examples/rx_ascii_art_dft.cpp index 11a0ccc24..cba72472a 100644 --- a/host/examples/rx_ascii_art_dft.cpp +++ b/host/examples/rx_ascii_art_dft.cpp @@ -54,7 +54,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("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)") - ("ref", po::value<std::string>(&ref)->default_value("INTERNAL"), "waveform type (INTERNAL, EXTERNAL, MIMO)") + ("ref", po::value<std::string>(&ref)->default_value("internal"), "waveform type (internal, external, mimo)") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); @@ -72,18 +72,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); //Lock mboard clocks - if (ref == "MIMO") { - uhd::clock_config_t clock_config; - clock_config.ref_source = uhd::clock_config_t::REF_MIMO; - clock_config.pps_source = uhd::clock_config_t::PPS_MIMO; - usrp->set_clock_config(clock_config, 0); - } - else if (ref == "EXTERNAL") { - usrp->set_clock_config(uhd::clock_config_t::external(), 0); - } - else if (ref == "INTERNAL") { - usrp->set_clock_config(uhd::clock_config_t::internal(), 0); - } + usrp->set_clock_source(ref); //always select the subdevice first, the channel mapping affects the other settings if (vm.count("subdev")) usrp->set_rx_subdev_spec(subdev); @@ -147,6 +136,10 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ UHD_ASSERT_THROW(ref_locked.to_bool()); } + //create a receive streamer + uhd::stream_args_t stream_args("fc32"); //complex floats + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + //allocate recv buffer and metatdata uhd::rx_metadata_t md; std::vector<std::complex<float> > buff(num_bins); @@ -162,10 +155,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //------------------------------------------------------------------ while (true){ //read a buffer's worth of samples every iteration - size_t num_rx_samps = usrp->get_device()->recv( - &buff.front(), buff.size(), md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_FULL_BUFF + size_t num_rx_samps = rx_stream->recv( + &buff.front(), buff.size(), md ); if (num_rx_samps != buff.size()) continue; diff --git a/host/examples/rx_multi_samples.cpp b/host/examples/rx_multi_samples.cpp index 8f6d7c939..42ef33d70 100644 --- a/host/examples/rx_multi_samples.cpp +++ b/host/examples/rx_multi_samples.cpp @@ -92,6 +92,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ usrp->set_time_now(uhd::time_spec_t(0.0)); } else if (sync == "pps"){ + usrp->set_time_source("external"); usrp->set_time_unknown_pps(uhd::time_spec_t(0.0)); boost::this_thread::sleep(boost::posix_time::seconds(1)); //wait for pps sync pulse } @@ -99,10 +100,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ UHD_ASSERT_THROW(usrp->get_num_mboards() == 2); //make mboard 1 a slave over the MIMO Cable - uhd::clock_config_t clock_config; - clock_config.ref_source = uhd::clock_config_t::REF_MIMO; - clock_config.pps_source = uhd::clock_config_t::PPS_MIMO; - usrp->set_clock_config(clock_config, 1); + usrp->set_clock_source("mimo", 1); + usrp->set_time_source("mimo", 1); //set time on the master (mboard 0) usrp->set_time_now(uhd::time_spec_t(0.0), 0); @@ -111,6 +110,13 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ boost::this_thread::sleep(boost::posix_time::milliseconds(100)); } + //create a receive streamer + //linearly map channels (index0 = channel0, index1 = channel1, ...) + uhd::stream_args_t stream_args("fc32"); //complex floats + for (size_t chan = 0; chan < usrp->get_rx_num_channels(); chan++) + stream_args.channels.push_back(chan); //linear mapping + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + //setup streaming std::cout << std::endl; std::cout << boost::format( @@ -126,7 +132,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::rx_metadata_t md; //allocate buffers to receive with samples (one buffer per channel) - size_t samps_per_buff = usrp->get_device()->get_max_recv_samps_per_packet(); + const size_t samps_per_buff = rx_stream->get_max_num_samps(); std::vector<std::vector<std::complex<float> > > buffs( usrp->get_rx_num_channels(), std::vector<std::complex<float> >(samps_per_buff) ); @@ -141,10 +147,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ size_t num_acc_samps = 0; //number of accumulated samples while(num_acc_samps < total_num_samps){ //receive a single packet - size_t num_rx_samps = usrp->get_device()->recv( - buff_ptrs, samps_per_buff, md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_ONE_PACKET, timeout + size_t num_rx_samps = rx_stream->recv( + buff_ptrs, samps_per_buff, md, timeout ); //use a small timeout for subsequent packets diff --git a/host/examples/rx_samples_to_file.cpp b/host/examples/rx_samples_to_file.cpp index 1e213c83a..ed242b07a 100644 --- a/host/examples/rx_samples_to_file.cpp +++ b/host/examples/rx_samples_to_file.cpp @@ -34,20 +34,21 @@ void sig_int_handler(int){stop_signal_called = true;} template<typename samp_type> void recv_to_file( uhd::usrp::multi_usrp::sptr usrp, - const uhd::io_type_t &io_type, + const std::string &cpu_format, const std::string &file, size_t samps_per_buff ){ + //create a receive streamer + uhd::stream_args_t stream_args(cpu_format); + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + uhd::rx_metadata_t md; std::vector<samp_type> buff(samps_per_buff); std::ofstream outfile(file.c_str(), std::ofstream::binary); bool overflow_message = true; while(not stop_signal_called){ - size_t num_rx_samps = usrp->get_device()->recv( - &buff.front(), buff.size(), md, io_type, - uhd::device::RECV_MODE_FULL_BUFF - ); + size_t num_rx_samps = rx_stream->recv(&buff.front(), buff.size(), md); if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) break; if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW){ @@ -98,7 +99,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("ant", po::value<std::string>(&ant), "daughterboard antenna selection") ("subdev", po::value<std::string>(&subdev), "daughterboard subdevice specification") ("bw", po::value<double>(&bw), "daughterboard IF filter bandwidth in Hz") - ("ref", po::value<std::string>(&ref)->default_value("INTERNAL"), "waveform type (INTERNAL, EXTERNAL, MIMO)") + ("ref", po::value<std::string>(&ref)->default_value("internal"), "waveform type (internal, external, mimo)") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); @@ -116,18 +117,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); //Lock mboard clocks - if (ref == "MIMO") { - uhd::clock_config_t clock_config; - clock_config.ref_source = uhd::clock_config_t::REF_MIMO; - clock_config.pps_source = uhd::clock_config_t::PPS_MIMO; - usrp->set_clock_config(clock_config, 0); - } - else if (ref == "EXTERNAL") { - usrp->set_clock_config(uhd::clock_config_t::external(), 0); - } - else if (ref == "INTERNAL") { - usrp->set_clock_config(uhd::clock_config_t::internal(), 0); - } + usrp->set_clock_source(ref); //always select the subdevice first, the channel mapping affects the other settings if (vm.count("subdev")) usrp->set_rx_subdev_spec(subdev); @@ -205,9 +195,9 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ } //recv to file - if (type == "double") recv_to_file<std::complex<double> >(usrp, uhd::io_type_t::COMPLEX_FLOAT64, file, spb); - else if (type == "float") recv_to_file<std::complex<float> >(usrp, uhd::io_type_t::COMPLEX_FLOAT32, file, spb); - else if (type == "short") recv_to_file<std::complex<short> >(usrp, uhd::io_type_t::COMPLEX_INT16, file, spb); + if (type == "double") recv_to_file<std::complex<double> >(usrp, "fc64", file, spb); + else if (type == "float") recv_to_file<std::complex<float> >(usrp, "fc32", file, spb); + else if (type == "short") recv_to_file<std::complex<short> >(usrp, "sc16", file, spb); else throw std::runtime_error("Unknown type " + type); //finished diff --git a/host/examples/rx_samples_to_udp.cpp b/host/examples/rx_samples_to_udp.cpp index d06f1bc6e..c456f05c3 100644 --- a/host/examples/rx_samples_to_udp.cpp +++ b/host/examples/rx_samples_to_udp.cpp @@ -51,7 +51,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("bw", po::value<double>(&bw), "daughterboard IF filter bandwidth in Hz") ("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") - ("ref", po::value<std::string>(&ref)->default_value("INTERNAL"), "waveform type (INTERNAL, EXTERNAL, MIMO)") + ("ref", po::value<std::string>(&ref)->default_value("internal"), "waveform type (internal, external, mimo)") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); @@ -70,18 +70,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; //Lock mboard clocks - if (ref == "MIMO") { - uhd::clock_config_t clock_config; - clock_config.ref_source = uhd::clock_config_t::REF_MIMO; - clock_config.pps_source = uhd::clock_config_t::PPS_MIMO; - usrp->set_clock_config(clock_config, 0); - } - else if (ref == "EXTERNAL") { - usrp->set_clock_config(uhd::clock_config_t::external(), 0); - } - else if (ref == "INTERNAL") { - usrp->set_clock_config(uhd::clock_config_t::internal(), 0); - } + usrp->set_clock_source(ref); //set the rx sample rate std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate/1e6) << std::endl; @@ -130,6 +119,10 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ UHD_ASSERT_THROW(ref_locked.to_bool()); } + //create a receive streamer + uhd::stream_args_t stream_args("fc32"); //complex floats + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + //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; @@ -139,14 +132,12 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //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(usrp->get_device()->get_max_recv_samps_per_packet()); + std::vector<std::complex<float> > buff(rx_stream->get_max_num_samps()); 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 = usrp->get_device()->recv( - &buff.front(), buff.size(), md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_ONE_PACKET + size_t num_rx_samps = rx_stream->recv( + &buff.front(), buff.size(), md ); //handle the error codes diff --git a/host/examples/rx_timed_samples.cpp b/host/examples/rx_timed_samples.cpp index 05cc0717b..143bceb03 100644 --- a/host/examples/rx_timed_samples.cpp +++ b/host/examples/rx_timed_samples.cpp @@ -70,6 +70,10 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << boost::format("Setting device timestamp to 0...") << std::endl; usrp->set_time_now(uhd::time_spec_t(0.0)); + //create a receive streamer + uhd::stream_args_t stream_args("fc32"); //complex floats + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + //setup streaming std::cout << std::endl; std::cout << boost::format( @@ -85,7 +89,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::rx_metadata_t md; //allocate buffer to receive with samples - std::vector<std::complex<float> > buff(usrp->get_device()->get_max_recv_samps_per_packet()); + std::vector<std::complex<float> > buff(rx_stream->get_max_num_samps()); //the first call to recv() will block this many seconds before receiving double timeout = seconds_in_future + 0.1; //timeout (delay before receive + padding) @@ -93,10 +97,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ size_t num_acc_samps = 0; //number of accumulated samples while(num_acc_samps < total_num_samps){ //receive a single packet - size_t num_rx_samps = usrp->get_device()->recv( - &buff.front(), buff.size(), md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_ONE_PACKET, timeout + size_t num_rx_samps = rx_stream->recv( + &buff.front(), buff.size(), md, timeout, true ); //use a small timeout for subsequent packets diff --git a/host/examples/test_messages.cpp b/host/examples/test_messages.cpp index 60d108184..f24a172d1 100644 --- a/host/examples/test_messages.cpp +++ b/host/examples/test_messages.cpp @@ -36,24 +36,22 @@ namespace po = boost::program_options; * Issue a stream command with a time that is in the past. * We expect to get an inline late command message. */ -bool test_late_command_message(uhd::usrp::multi_usrp::sptr usrp){ +bool test_late_command_message(uhd::usrp::multi_usrp::sptr usrp, uhd::rx_streamer::sptr rx_stream, uhd::tx_streamer::sptr){ std::cout << "Test late command message... " << std::flush; usrp->set_time_now(uhd::time_spec_t(200.0)); //set time uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); - stream_cmd.num_samps = usrp->get_device()->get_max_recv_samps_per_packet(); + stream_cmd.num_samps = rx_stream->get_max_num_samps(); stream_cmd.stream_now = false; stream_cmd.time_spec = uhd::time_spec_t(100.0); //time in the past usrp->issue_stream_cmd(stream_cmd); - std::vector<std::complex<float> > buff(usrp->get_device()->get_max_recv_samps_per_packet()); + std::vector<std::complex<float> > buff(rx_stream->get_max_num_samps()); uhd::rx_metadata_t md; - const size_t nsamps = usrp->get_device()->recv( - &buff.front(), buff.size(), md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_FULL_BUFF + const size_t nsamps = rx_stream->recv( + &buff.front(), buff.size(), md ); switch(md.error_code){ @@ -85,27 +83,23 @@ bool test_late_command_message(uhd::usrp::multi_usrp::sptr usrp){ * Issue a stream command with num samps and more. * We expect to get an inline broken chain message. */ -bool test_broken_chain_message(uhd::usrp::multi_usrp::sptr usrp){ +bool test_broken_chain_message(uhd::usrp::multi_usrp::sptr usrp, uhd::rx_streamer::sptr rx_stream, uhd::tx_streamer::sptr){ std::cout << "Test broken chain message... " << std::flush; uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE); stream_cmd.stream_now = true; - stream_cmd.num_samps = usrp->get_device()->get_max_recv_samps_per_packet(); + stream_cmd.num_samps = rx_stream->get_max_num_samps(); usrp->issue_stream_cmd(stream_cmd); - std::vector<std::complex<float> > buff(usrp->get_device()->get_max_recv_samps_per_packet()); + std::vector<std::complex<float> > buff(rx_stream->get_max_num_samps()); uhd::rx_metadata_t md; - usrp->get_device()->recv( //once for the requested samples - &buff.front(), buff.size(), md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_FULL_BUFF + rx_stream->recv( //once for the requested samples + &buff.front(), buff.size(), md ); - usrp->get_device()->recv( //again for the inline message - &buff.front(), buff.size(), md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_FULL_BUFF + rx_stream->recv( //again for the inline message + &buff.front(), buff.size(), md ); switch(md.error_code){ @@ -137,7 +131,7 @@ bool test_broken_chain_message(uhd::usrp::multi_usrp::sptr usrp){ * Send a burst of many samples that will fragment internally. * We expect to get an burst ack async message. */ -bool test_burst_ack_message(uhd::usrp::multi_usrp::sptr usrp){ +bool test_burst_ack_message(uhd::usrp::multi_usrp::sptr usrp, uhd::rx_streamer::sptr, uhd::tx_streamer::sptr tx_stream){ std::cout << "Test burst ack message... " << std::flush; uhd::tx_metadata_t md; @@ -146,12 +140,10 @@ bool test_burst_ack_message(uhd::usrp::multi_usrp::sptr usrp){ md.has_time_spec = false; //3 times max-sps guarantees a SOB, no burst, and EOB packet - std::vector<std::complex<float> > buff(usrp->get_device()->get_max_send_samps_per_packet()*3); + std::vector<std::complex<float> > buff(tx_stream->get_max_num_samps()*3); - usrp->get_device()->send( - &buff.front(), buff.size(), md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::SEND_MODE_FULL_BUFF + tx_stream->send( + &buff.front(), buff.size(), md ); uhd::async_metadata_t async_md; @@ -185,7 +177,7 @@ bool test_burst_ack_message(uhd::usrp::multi_usrp::sptr usrp){ * Send a start of burst packet with no following end of burst. * We expect to get an underflow(within a burst) async message. */ -bool test_underflow_message(uhd::usrp::multi_usrp::sptr usrp){ +bool test_underflow_message(uhd::usrp::multi_usrp::sptr usrp, uhd::rx_streamer::sptr, uhd::tx_streamer::sptr tx_stream){ std::cout << "Test underflow message... " << std::flush; uhd::tx_metadata_t md; @@ -193,10 +185,7 @@ bool test_underflow_message(uhd::usrp::multi_usrp::sptr usrp){ md.end_of_burst = false; md.has_time_spec = false; - usrp->get_device()->send("", 0, md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::SEND_MODE_FULL_BUFF - ); + tx_stream->send("", 0, md); uhd::async_metadata_t async_md; if (not usrp->get_device()->recv_async_msg(async_md, 1)){ @@ -229,7 +218,7 @@ bool test_underflow_message(uhd::usrp::multi_usrp::sptr usrp){ * Send a burst packet that occurs at a time in the past. * We expect to get a time error async message. */ -bool test_time_error_message(uhd::usrp::multi_usrp::sptr usrp){ +bool test_time_error_message(uhd::usrp::multi_usrp::sptr usrp, uhd::rx_streamer::sptr, uhd::tx_streamer::sptr tx_stream){ std::cout << "Test time error message... " << std::flush; uhd::tx_metadata_t md; @@ -240,10 +229,7 @@ bool test_time_error_message(uhd::usrp::multi_usrp::sptr usrp){ usrp->set_time_now(uhd::time_spec_t(200.0)); //time at 200s - usrp->get_device()->send("", 0, md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::SEND_MODE_FULL_BUFF - ); + tx_stream->send("", 0, md); uhd::async_metadata_t async_md; if (not usrp->get_device()->recv_async_msg(async_md)){ @@ -276,16 +262,12 @@ void flush_async(uhd::usrp::multi_usrp::sptr usrp){ while (usrp->get_device()->recv_async_msg(async_md)){} } -void flush_recv(uhd::usrp::multi_usrp::sptr usrp){ - std::vector<std::complex<float> > buff(usrp->get_device()->get_max_recv_samps_per_packet()); +void flush_recv(uhd::rx_streamer::sptr rx_stream){ + std::vector<std::complex<float> > buff(rx_stream->get_max_num_samps()); uhd::rx_metadata_t md; do{ - usrp->get_device()->recv( - &buff.front(), buff.size(), md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_FULL_BUFF - ); + rx_stream->recv(&buff.front(), buff.size(), md); } while (md.error_code != uhd::rx_metadata_t::ERROR_CODE_TIMEOUT); } @@ -319,10 +301,15 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + //create RX and TX streamers + uhd::stream_args_t stream_args("fc32"); //complex floats + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); + //------------------------------------------------------------------ // begin messages test //------------------------------------------------------------------ - static const uhd::dict<std::string, boost::function<bool(uhd::usrp::multi_usrp::sptr)> > + static const uhd::dict<std::string, boost::function<bool(uhd::usrp::multi_usrp::sptr, uhd::rx_streamer::sptr, uhd::tx_streamer::sptr)> > tests = boost::assign::map_list_of ("Test Burst ACK ", &test_burst_ack_message) ("Test Underflow ", &test_underflow_message) @@ -342,9 +329,9 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::srand(uhd::time_spec_t::get_system_time().get_full_secs()); for (size_t n = 0; n < ntests; n++){ std::string key = tests.keys()[std::rand() % tests.size()]; - bool pass = tests[key](usrp); + bool pass = tests[key](usrp, rx_stream, tx_stream); flush_async(usrp); - flush_recv(usrp); + flush_recv(rx_stream); //store result if (pass) successes[key]++; diff --git a/host/examples/tx_bursts.cpp b/host/examples/tx_bursts.cpp index a66fb85b6..f5ae18a9f 100644 --- a/host/examples/tx_bursts.cpp +++ b/host/examples/tx_bursts.cpp @@ -93,15 +93,19 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << boost::format("Setting device timestamp to 0...") << std::endl; usrp->set_time_now(uhd::time_spec_t(0.0)); + //create a transmit streamer + uhd::stream_args_t stream_args("fc32"); //complex floats + uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); + //allocate buffer with data to send - size_t spb = usrp->get_device()->get_max_send_samps_per_packet(); + const size_t spb = tx_stream->get_max_num_samps(); std::vector<std::complex<float> *> buffs; for(size_t i=0; i < usrp->get_num_mboards(); i++) { buffs.push_back(new std::complex<float>[spb]); for(size_t n=0; n < spb; n++) buffs.back()[n] = std::complex<float>(ampl, ampl); - } + }; std::signal(SIGINT, &sig_int_handler); if(repeat) std::cout << "Press Ctrl + C to quit..." << std::endl; @@ -127,10 +131,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ md.end_of_burst = samps_to_send < spb; //send a single packet - size_t num_tx_samps = usrp->get_device()->send( - buffs, samps_to_send, md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::SEND_MODE_ONE_PACKET, timeout + size_t num_tx_samps = tx_stream->send( + buffs, samps_to_send, md, timeout ); //do not use time spec for subsequent packets diff --git a/host/examples/tx_samples_from_file.cpp b/host/examples/tx_samples_from_file.cpp index 03fc4c8d4..8e3614f6c 100644 --- a/host/examples/tx_samples_from_file.cpp +++ b/host/examples/tx_samples_from_file.cpp @@ -29,10 +29,14 @@ namespace po = boost::program_options; template<typename samp_type> void send_from_file( uhd::usrp::multi_usrp::sptr usrp, - const uhd::io_type_t &io_type, + const std::string &cpu_format, const std::string &file, size_t samps_per_buff ){ + //create a transmit streamer + uhd::stream_args_t stream_args(cpu_format); + uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); + uhd::tx_metadata_t md; md.start_of_burst = false; md.end_of_burst = false; @@ -47,10 +51,7 @@ template<typename samp_type> void send_from_file( md.end_of_burst = infile.eof(); - usrp->get_device()->send( - &buff.front(), num_tx_samps, md, io_type, - uhd::device::SEND_MODE_FULL_BUFF - ); + tx_stream->send(&buff.front(), num_tx_samps, md); } infile.close(); @@ -78,7 +79,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("ant", po::value<std::string>(&ant), "daughterboard antenna selection") ("subdev", po::value<std::string>(&subdev), "daughterboard subdevice specification") ("bw", po::value<double>(&bw), "daughterboard IF filter bandwidth in Hz") - ("ref", po::value<std::string>(&ref)->default_value("INTERNAL"), "waveform type (INTERNAL, EXTERNAL, MIMO)") + ("ref", po::value<std::string>(&ref)->default_value("internal"), "waveform type (internal, external, mimo)") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); @@ -96,18 +97,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); //Lock mboard clocks - if (ref == "MIMO") { - uhd::clock_config_t clock_config; - clock_config.ref_source = uhd::clock_config_t::REF_MIMO; - clock_config.pps_source = uhd::clock_config_t::PPS_MIMO; - usrp->set_clock_config(clock_config, 0); - } - else if (ref == "EXTERNAL") { - usrp->set_clock_config(uhd::clock_config_t::external(), 0); - } - else if (ref == "INTERNAL") { - usrp->set_clock_config(uhd::clock_config_t::internal(), 0); - } + usrp->set_clock_source(ref); //always select the subdevice first, the channel mapping affects the other settings if (vm.count("subdev")) usrp->set_tx_subdev_spec(subdev); @@ -172,9 +162,9 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ } //send from file - if (type == "double") send_from_file<std::complex<double> >(usrp, uhd::io_type_t::COMPLEX_FLOAT64, file, spb); - else if (type == "float") send_from_file<std::complex<float> >(usrp, uhd::io_type_t::COMPLEX_FLOAT32, file, spb); - else if (type == "short") send_from_file<std::complex<short> >(usrp, uhd::io_type_t::COMPLEX_INT16, file, spb); + if (type == "double") send_from_file<std::complex<double> >(usrp, "fc64", file, spb); + else if (type == "float") send_from_file<std::complex<float> >(usrp, "fc32", file, spb); + else if (type == "short") send_from_file<std::complex<short> >(usrp, "sc16", file, spb); else throw std::runtime_error("Unknown type " + type); //finished diff --git a/host/examples/tx_timed_samples.cpp b/host/examples/tx_timed_samples.cpp index 1c222d414..3b8cc75d4 100644 --- a/host/examples/tx_timed_samples.cpp +++ b/host/examples/tx_timed_samples.cpp @@ -73,8 +73,12 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << boost::format("Setting device timestamp to 0...") << std::endl; usrp->set_time_now(uhd::time_spec_t(0.0)); + //create a transmit streamer + uhd::stream_args_t stream_args("fc32"); //complex floats + uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); + //allocate buffer with data to send - std::vector<std::complex<float> > buff(usrp->get_device()->get_max_send_samps_per_packet(), std::complex<float>(ampl, ampl)); + std::vector<std::complex<float> > buff(tx_stream->get_max_num_samps(), std::complex<float>(ampl, ampl)); //setup metadata for the first packet uhd::tx_metadata_t md; @@ -91,10 +95,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ size_t samps_to_send = std::min(total_num_samps - num_acc_samps, buff.size()); //send a single packet - size_t num_tx_samps = usrp->get_device()->send( - &buff.front(), samps_to_send, md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::SEND_MODE_ONE_PACKET, timeout + size_t num_tx_samps = tx_stream->send( + &buff.front(), samps_to_send, md, timeout ); //do not use time spec for subsequent packets @@ -108,10 +110,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //send a mini EOB packet md.end_of_burst = true; - usrp->get_device()->send("", 0, md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::SEND_MODE_FULL_BUFF - ); + tx_stream->send("", 0, md); std::cout << std::endl << "Waiting for async burst ACK... " << std::flush; uhd::async_metadata_t async_md; diff --git a/host/examples/tx_waveforms.cpp b/host/examples/tx_waveforms.cpp index 469e27621..5b17e0595 100644 --- a/host/examples/tx_waveforms.cpp +++ b/host/examples/tx_waveforms.cpp @@ -100,7 +100,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ desc.add_options() ("help", "help message") ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args") - ("spb", po::value<size_t>(&spb)->default_value(10000), "samples per buffer") + ("spb", po::value<size_t>(&spb)->default_value(0), "samples per buffer, 0 for default") ("rate", po::value<double>(&rate), "rate of outgoing samples") ("freq", po::value<double>(&freq), "RF center frequency in Hz") ("ampl", po::value<float>(&l)->default_value(float(0.3)), "amplitude of the waveform [0 to 0.7]") @@ -110,7 +110,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("bw", po::value<double>(&bw), "daughterboard IF filter bandwidth in Hz") ("wave-type", po::value<std::string>(&wave_type)->default_value("CONST"), "waveform type (CONST, SQUARE, RAMP, SINE)") ("wave-freq", po::value<double>(&wave_freq)->default_value(0), "waveform frequency in Hz") - ("ref", po::value<std::string>(&ref)->default_value("INTERNAL"), "waveform type (INTERNAL, EXTERNAL, MIMO)") + ("ref", po::value<std::string>(&ref)->default_value("internal"), "waveform type (internal, external, mimo)") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); @@ -128,18 +128,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); //Lock mboard clocks - if (ref == "MIMO") { - uhd::clock_config_t clock_config; - clock_config.ref_source = uhd::clock_config_t::REF_MIMO; - clock_config.pps_source = uhd::clock_config_t::PPS_MIMO; - usrp->set_clock_config(clock_config, 0); - } - else if (ref == "EXTERNAL") { - usrp->set_clock_config(uhd::clock_config_t::external(), 0); - } - else if (ref == "INTERNAL") { - usrp->set_clock_config(uhd::clock_config_t::internal(), 0); - } + usrp->set_clock_source(ref); //always select the subdevice first, the channel mapping affects the other settings if (vm.count("subdev")) usrp->set_tx_subdev_spec(subdev); @@ -202,7 +191,15 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ const double cps = wave_freq/usrp->get_tx_rate(); double theta = 0; + //create a transmit streamer + //linearly map channels (index0 = channel0, index1 = channel1, ...) + uhd::stream_args_t stream_args("fc32"); + for (size_t chan = 0; chan < usrp->get_tx_num_channels(); chan++) + stream_args.channels.push_back(chan); //linear mapping + uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); + //allocate a buffer which we re-use for each channel + if (spb == 0) spb = tx_stream->get_max_num_samps()*10; std::vector<std::complex<float> > buff(spb); std::vector<std::complex<float> *> buffs(usrp->get_tx_num_channels(), &buff.front()); @@ -250,11 +247,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ theta = std::fmod(theta, 1); //send the entire contents of the buffer - usrp->get_device()->send( - buffs, buff.size(), md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::SEND_MODE_FULL_BUFF - ); + tx_stream->send(buffs, buff.size(), md); md.start_of_burst = false; md.has_time_spec = false; @@ -262,10 +255,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //send a mini EOB packet md.end_of_burst = true; - usrp->get_device()->send("", 0, md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::SEND_MODE_FULL_BUFF - ); + tx_stream->send("", 0, md); //finished std::cout << std::endl << "Done!" << std::endl << std::endl; diff --git a/host/include/uhd/CMakeLists.txt b/host/include/uhd/CMakeLists.txt index 49562a7a0..1df04d577 100644 --- a/host/include/uhd/CMakeLists.txt +++ b/host/include/uhd/CMakeLists.txt @@ -24,12 +24,14 @@ ADD_SUBDIRECTORY(utils) INSTALL(FILES config.hpp convert.hpp + deprecated.hpp device.hpp + device_deprecated.ipp exception.hpp property_tree.ipp property_tree.hpp + stream.hpp version.hpp - wax.hpp DESTINATION ${INCLUDE_DIR}/uhd COMPONENT headers ) diff --git a/host/include/uhd/convert.hpp b/host/include/uhd/convert.hpp index 99f1860ae..c42edfdec 100644 --- a/host/include/uhd/convert.hpp +++ b/host/include/uhd/convert.hpp @@ -19,17 +19,18 @@ #define INCLUDED_UHD_CONVERT_HPP #include <uhd/config.hpp> -#include <uhd/types/io_type.hpp> -#include <uhd/types/otw_type.hpp> #include <uhd/types/ref_vector.hpp> #include <boost/function.hpp> +#include <boost/operators.hpp> #include <string> namespace uhd{ namespace convert{ typedef uhd::ref_vector<void *> output_type; typedef uhd::ref_vector<const void *> input_type; - typedef boost::function<void(const input_type&, const output_type&, size_t, double)> function_type; + + //! input vectors, output vectors, num samples, scale factor + typedef boost::function<void(const input_type&, const output_type&, const size_t, const double)> function_type; /*! * Describe the priority of a converter function. @@ -41,50 +42,54 @@ namespace uhd{ namespace convert{ enum priority_type{ PRIORITY_GENERAL = 0, PRIORITY_LIBORC = 1, - PRIORITY_CUSTOM = 2, + PRIORITY_SIMD = 2, + PRIORITY_CUSTOM = 3, PRIORITY_EMPTY = -1, }; + //! Identify a conversion routine in the registry + struct id_type : boost::equality_comparable<id_type>{ + std::string input_format; + size_t num_inputs; + std::string output_format; + size_t num_outputs; + std::string to_pp_string(void) const; + }; + + //! Implement equality_comparable interface + UHD_API bool operator==(const id_type &, const id_type &); + /*! - * Register a converter function that converts cpu type to/from otw type. - * \param markup representing the signature + * Register a converter function. + * \param id identify the conversion * \param fcn a pointer to the converter * \param prio the function priority */ UHD_API void register_converter( - const std::string &markup, + const id_type &id, function_type fcn, priority_type prio ); /*! - * Get a converter function that converts cpu to otw. - * \param io_type the type of the input samples - * \param otw_type the type of the output samples - * \param num_input_buffs the number of inputs - * \param num_output_buffs the number of outputs + * Get a converter function. + * \param id identify the conversion + * \return the converter function */ - UHD_API const function_type &get_converter_cpu_to_otw( - const io_type_t &io_type, - const otw_type_t &otw_type, - size_t num_input_buffs, - size_t num_output_buffs - ); + UHD_API function_type get_converter(const id_type &id); /*! - * Get a converter function that converts otw to cpu. - * \param io_type the type of the input samples - * \param otw_type the type of the output samples - * \param num_input_buffs the number of inputs - * \param num_output_buffs the number of outputs + * Register the size of a particular item. + * \param format the item format + * \param size the size in bytes */ - UHD_API const function_type &get_converter_otw_to_cpu( - const io_type_t &io_type, - const otw_type_t &otw_type, - size_t num_input_buffs, - size_t num_output_buffs + UHD_API void register_bytes_per_item( + const std::string &format, const size_t size ); + //! Convert an item format to a size in bytes + UHD_API size_t get_bytes_per_item(const std::string &format); + }} //namespace #endif /* INCLUDED_UHD_CONVERT_HPP */ diff --git a/host/include/uhd/deprecated.hpp b/host/include/uhd/deprecated.hpp new file mode 100644 index 000000000..95cce58e9 --- /dev/null +++ b/host/include/uhd/deprecated.hpp @@ -0,0 +1,76 @@ +//---------------------------------------------------------------------- +//-- deprecated interfaces below, to be removed when the API is changed +//---------------------------------------------------------------------- + +// +// 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_OTW_TYPE_HPP +#define INCLUDED_UHD_TYPES_OTW_TYPE_HPP + +#include <uhd/config.hpp> + +namespace uhd{ + + /*! + * Description for over-the-wire integers: + * The DSP units in the FPGA deal with signed 16-bit integers. + * The width and shift define the translation between OTW and DSP, + * defined by the following relation: otw_int = dsp_int >> shift + * + * Note: possible combinations of width, shift, and byteorder + * depend on the internals of the FPGA. Not all are supported! + */ + struct UHD_API otw_type_t{ + + /*! + * Width of an over-the-wire integer in bits. + */ + size_t width; //in bits + + /*! + * Shift of an over-the-wire integer in bits. + * otw_int = dsp_int >> shift + * dsp_int = otw_int << shift + */ + size_t shift; //in bits + + /*! + * Constants for byte order (borrowed from numpy's dtype) + */ + enum /*bo_t*/ { + BO_NATIVE = int('='), + BO_LITTLE_ENDIAN = int('<'), + BO_BIG_ENDIAN = int('>'), + BO_NOT_APPLICABLE = int('|') + } byteorder; + + /*! + * Get the sample size of this otw type. + * \return the size of a sample in bytes + */ + size_t get_sample_size(void) const; + + otw_type_t(void); + }; + +} //namespace uhd + +#endif /* INCLUDED_UHD_TYPES_OTW_TYPE_HPP */ + +#include <uhd/types/io_type.hpp> //wish it was in here +#include <uhd/types/clock_config.hpp> //wish it was in here diff --git a/host/include/uhd/device.hpp b/host/include/uhd/device.hpp index a6571d027..89c6332da 100644 --- a/host/include/uhd/device.hpp +++ b/host/include/uhd/device.hpp @@ -19,11 +19,9 @@ #define INCLUDED_UHD_DEVICE_HPP #include <uhd/config.hpp> +#include <uhd/stream.hpp> +#include <uhd/deprecated.hpp> #include <uhd/types/device_addr.hpp> -#include <uhd/types/metadata.hpp> -#include <uhd/types/io_type.hpp> -#include <uhd/types/ref_vector.hpp> -#include <uhd/wax.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> #include <boost/function.hpp> @@ -36,7 +34,7 @@ class property_tree; //forward declaration * The usrp device interface represents the usrp hardware. * The api allows for discovery, configuration, and streaming. */ -class UHD_API device : boost::noncopyable, public wax::obj{ +class UHD_API device : boost::noncopyable{ public: typedef boost::shared_ptr<device> sptr; @@ -78,116 +76,11 @@ public: */ static sptr make(const device_addr_t &hint, size_t which = 0); - /*! - * Send modes for the device send routine. - */ - enum send_mode_t{ - //! Tells the send routine to send the entire buffer - SEND_MODE_FULL_BUFF = 0, - //! Tells the send routine to return after one packet - SEND_MODE_ONE_PACKET = 1 - }; - - /*! - * Recv modes for the device recv routine. - */ - enum recv_mode_t{ - //! Tells the recv routine to recv the entire buffer - RECV_MODE_FULL_BUFF = 0, - //! Tells the recv routine to return after one packet - RECV_MODE_ONE_PACKET = 1 - }; - - //! Typedef for a pointer to a single, or a collection of send buffers - typedef ref_vector<const void *> send_buffs_type; - - //! Typedef for a pointer to a single, or a collection of recv buffers - typedef ref_vector<void *> recv_buffs_type; - - /*! - * Send buffers containing IF data described by the metadata. - * - * Send handles fragmentation as follows: - * If the buffer has more samples than the maximum per packet, - * the send method will fragment the samples across several packets. - * Send will respect the burst flags when fragmenting to ensure - * that start of burst can only be set on the first fragment and - * that end of burst can only be set on the final fragment. - * Fragmentation only applies in the full buffer send mode. - * - * 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( - const send_buffs_type &buffs, - size_t nsamps_per_buff, - const tx_metadata_t &metadata, - const io_type_t &io_type, - send_mode_t send_mode, - double timeout = 0.1 - ) = 0; + //! Make a new receive streamer from the streamer arguments + virtual rx_streamer::sptr get_rx_stream(const stream_args_t &args) = 0; - /*! - * Receive buffers containing IF data described by the metadata. - * - * Receive handles fragmentation as follows: - * If the buffer has insufficient space to hold all samples - * that were received in a single packet over-the-wire, - * then the buffer will be completely filled and the implementation - * will hold a pointer into the remaining portion of the packet. - * Subsequent calls will load from the remainder of the packet, - * and will flag the metadata to show that this is a fragment. - * The next call to receive, after the remainder becomes exahausted, - * will perform an over-the-wire receive as usual. - * 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. - * 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. - * Use the one packet recv mode to get per packet metadata. - * - * \param buffs a vector of writable memory to fill with IF data - * \param nsamps_per_buff the size of each buffer in number of samples - * \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 the timeout in seconds to wait for a packet - * \return the number of samples received or 0 on error - */ - virtual size_t recv( - const recv_buffs_type &buffs, - size_t nsamps_per_buff, - rx_metadata_t &metadata, - const io_type_t &io_type, - recv_mode_t recv_mode, - double timeout = 0.1 - ) = 0; - - /*! - * Get the maximum number of samples per packet on send. - * \return the number of samples - */ - virtual size_t get_max_send_samps_per_packet(void) const = 0; - - /*! - * Get the maximum number of samples per packet on recv. - * \return the number of samples - */ - virtual size_t get_max_recv_samps_per_packet(void) const = 0; + //! Make a new transmit streamer from the streamer arguments + virtual tx_streamer::sptr get_tx_stream(const stream_args_t &args) = 0; /*! * Receive and asynchronous message from the device. @@ -202,6 +95,8 @@ public: //! Get access to the underlying property structure virtual boost::shared_ptr<property_tree> get_tree(void) const = 0; + #include <uhd/device_deprecated.ipp> + }; } //namespace uhd diff --git a/host/include/uhd/device_deprecated.ipp b/host/include/uhd/device_deprecated.ipp new file mode 100644 index 000000000..8e61c389f --- /dev/null +++ b/host/include/uhd/device_deprecated.ipp @@ -0,0 +1,185 @@ +// +// Copyright 2010-2011 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/>. +// + +//this file is included inside device class +//it supports the old send/recv functions +//this was replaced by the streamer API + +/*! + * Send modes for the device send routine. + */ +enum send_mode_t{ + //! Tells the send routine to send the entire buffer + SEND_MODE_FULL_BUFF = 0, + //! Tells the send routine to return after one packet + SEND_MODE_ONE_PACKET = 1 +}; + +/*! + * Recv modes for the device recv routine. + */ +enum recv_mode_t{ + //! Tells the recv routine to recv the entire buffer + RECV_MODE_FULL_BUFF = 0, + //! Tells the recv routine to return after one packet + RECV_MODE_ONE_PACKET = 1 +}; + +//! Typedef for a pointer to a single, or a collection of send buffers +typedef ref_vector<const void *> send_buffs_type; + +//! Typedef for a pointer to a single, or a collection of recv buffers +typedef ref_vector<void *> recv_buffs_type; + +/*! + * Send buffers containing IF data described by the metadata. + * + * Send handles fragmentation as follows: + * If the buffer has more samples than the maximum per packet, + * the send method will fragment the samples across several packets. + * Send will respect the burst flags when fragmenting to ensure + * that start of burst can only be set on the first fragment and + * that end of burst can only be set on the final fragment. + * Fragmentation only applies in the full buffer send mode. + * + * 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 + */ +size_t send( + const send_buffs_type &buffs, + size_t nsamps_per_buff, + const tx_metadata_t &metadata, + const io_type_t &io_type, + send_mode_t send_mode, + double timeout = 0.1 +){ + if (_tx_streamer.get() == NULL or _tx_streamer->get_num_channels() != buffs.size() or _send_tid != io_type.tid){ + _send_tid = io_type.tid; + _tx_streamer.reset(); //cleanup possible old one + stream_args_t args; + args.cpu_format = (_send_tid == io_type_t::COMPLEX_FLOAT32)? "fc32" : "sc16"; + args.otw_format = "sc16"; + for (size_t ch = 0; ch < buffs.size(); ch++) + args.channels.push_back(ch); //linear mapping + _tx_streamer = get_tx_stream(args); + } + const size_t nsamps = (send_mode == SEND_MODE_ONE_PACKET)? + std::min(nsamps_per_buff, get_max_send_samps_per_packet()) : + nsamps_per_buff; + return _tx_streamer->send(buffs, nsamps, metadata, timeout); +} + +/*! + * Receive buffers containing IF data described by the metadata. + * + * Receive handles fragmentation as follows: + * If the buffer has insufficient space to hold all samples + * that were received in a single packet over-the-wire, + * then the buffer will be completely filled and the implementation + * will hold a pointer into the remaining portion of the packet. + * Subsequent calls will load from the remainder of the packet, + * and will flag the metadata to show that this is a fragment. + * The next call to receive, after the remainder becomes exahausted, + * will perform an over-the-wire receive as usual. + * 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. + * 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. + * Use the one packet recv mode to get per packet metadata. + * + * \param buffs a vector of writable memory to fill with IF data + * \param nsamps_per_buff the size of each buffer in number of samples + * \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 the timeout in seconds to wait for a packet + * \return the number of samples received or 0 on error + */ +size_t recv( + const recv_buffs_type &buffs, + size_t nsamps_per_buff, + rx_metadata_t &metadata, + const io_type_t &io_type, + recv_mode_t recv_mode, + double timeout = 0.1 +){ + if (_rx_streamer.get() == NULL or _rx_streamer->get_num_channels() != buffs.size() or _recv_tid != io_type.tid){ + _recv_tid = io_type.tid; + _rx_streamer.reset(); //cleanup possible old one + stream_args_t args; + args.cpu_format = (_recv_tid == io_type_t::COMPLEX_FLOAT32)? "fc32" : "sc16"; + args.otw_format = "sc16"; + for (size_t ch = 0; ch < buffs.size(); ch++) + args.channels.push_back(ch); //linear mapping + _rx_streamer = get_rx_stream(args); + } + const size_t nsamps = (recv_mode == RECV_MODE_ONE_PACKET)? + std::min(nsamps_per_buff, get_max_recv_samps_per_packet()) : + nsamps_per_buff; + return _rx_streamer->recv(buffs, nsamps, metadata, timeout); +} + +/*! + * Get the maximum number of samples per packet on send. + * \return the number of samples + */ +size_t get_max_send_samps_per_packet(void){ + if (_tx_streamer.get() == NULL){ + stream_args_t args; + args.cpu_format = "fc32"; + args.otw_format = "sc16"; + _tx_streamer = get_tx_stream(args); + _send_tid = io_type_t::COMPLEX_FLOAT32; + } + return _tx_streamer->get_max_num_samps(); +} + +/*! + * Get the maximum number of samples per packet on recv. + * \return the number of samples + */ +size_t get_max_recv_samps_per_packet(void){ + if (_rx_streamer.get() == NULL){ + stream_args_t args; + args.cpu_format = "fc32"; + args.otw_format = "sc16"; + _rx_streamer = get_rx_stream(args); + _recv_tid = io_type_t::COMPLEX_FLOAT32; + } + return _rx_streamer->get_max_num_samps(); +} + +private: + rx_streamer::sptr _rx_streamer; + io_type_t::tid_t _recv_tid; + tx_streamer::sptr _tx_streamer; + io_type_t::tid_t _send_tid; diff --git a/host/include/uhd/stream.hpp b/host/include/uhd/stream.hpp new file mode 100644 index 000000000..1f0332088 --- /dev/null +++ b/host/include/uhd/stream.hpp @@ -0,0 +1,198 @@ +// +// Copyright 2011 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_STREAM_HPP +#define INCLUDED_UHD_STREAM_HPP + +#include <uhd/config.hpp> +#include <uhd/types/metadata.hpp> +#include <uhd/types/device_addr.hpp> +#include <uhd/types/ref_vector.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <vector> +#include <string> + +namespace uhd{ + +/*! + * A struct of parameters to construct a streamer. + * + * Note: + * Not all combinations of CPU and OTW format have conversion support. + * You may however write and register your own conversion routines. + */ +struct UHD_API stream_args_t{ + + //! Convenience constructor for streamer args + stream_args_t( + const std::string &cpu = "", + const std::string &otw = "" + ){ + cpu_format = cpu; + otw_format = otw; + } + + /*! + * The CPU format is a string that describes the format of host memory. + * Common CPU formats are: + * - fc32 - complex<float> + * - fc64 - complex<double> + * - sc16 - complex<int16_t> + * - sc8 - complex<int8_t> + * - f32 - float + * - f64 - double + * - s16 - int16_t + * - s8 - int8_t + */ + std::string cpu_format; + + /*! + * The OTW format is a string that describes the format over-the-wire. + * Common OTW format are: + * - sc16 - Q16 I16 + * - sc8 - Q8_1 I8_1 Q8_0 I8_0 + * - s16 - R16_1 R16_0 + * - s8 - R8_3 R8_2 R8_1 R8_0 + */ + std::string otw_format; + + /*! + * The args parameter is used to pass arbitrary key/value pairs. + * Possible keys used by args (depends on implementation): + * - scaler: 8sc converter scaling factor + * - function: magnitude or phase/magnitude + * - units: numeric units like counts or dBm + */ + device_addr_t args; + + /*! + * The channels is a list of channel numbers. + * Leave this blank to default to channel 0. + * Set channels for a multi-channel application. + * Channel mapping depends on the front-end selection. + */ + std::vector<size_t> channels; +}; + +/*! + * The RX streamer is the host interface to receiving samples. + * It represents the layer between the samples on the host + * and samples inside the device's receive DSP processing. + */ +class UHD_API rx_streamer : boost::noncopyable{ +public: + typedef boost::shared_ptr<rx_streamer> sptr; + + //! Get the number of channels associated with this streamer + virtual size_t get_num_channels(void) const = 0; + + //! Get the max number of samples per buffer per packet + virtual size_t get_max_num_samps(void) const = 0; + + //! Typedef for a pointer to a single, or a collection of recv buffers + typedef ref_vector<void *> buffs_type; + + /*! + * Receive buffers containing samples described by the metadata. + * + * Receive handles fragmentation as follows: + * If the buffer has insufficient space to hold all samples + * that were received in a single packet over-the-wire, + * then the buffer will be completely filled and the implementation + * will hold a pointer into the remaining portion of the packet. + * Subsequent calls will load from the remainder of the packet, + * and will flag the metadata to show that this is a fragment. + * The next call to receive, after the remainder becomes exahausted, + * will perform an over-the-wire receive as usual. + * 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. + * Under a timeout condition, the number of samples returned + * may be less than the number of samples specified. + * + * The one_packet option allows the user to guarantee that + * the call will return after a single packet has been processed. + * This may be useful to maintain packet boundaries in some cases. + * + * \param buffs a vector of writable memory to fill with samples + * \param nsamps_per_buff the size of each buffer in number of samples + * \param metadata data to fill describing the buffer + * \param timeout the timeout in seconds to wait for a packet + * \param one_packet return after the first packet is received + * \return the number of samples received or 0 on error + */ + virtual size_t recv( + const buffs_type &buffs, + const size_t nsamps_per_buff, + rx_metadata_t &metadata, + const double timeout = 0.1, + const bool one_packet = false + ) = 0; +}; + +/*! + * The TX streamer is the host interface to transmitting samples. + * It represents the layer between the samples on the host + * and samples inside the device's transmit DSP processing. + */ +class UHD_API tx_streamer : boost::noncopyable{ +public: + typedef boost::shared_ptr<tx_streamer> sptr; + + //! Get the number of channels associated with this streamer + virtual size_t get_num_channels(void) const = 0; + + //! Get the max number of samples per buffer per packet + virtual size_t get_max_num_samps(void) const = 0; + + //! Typedef for a pointer to a single, or a collection of send buffers + typedef ref_vector<const void *> buffs_type; + + /*! + * Send buffers containing samples described by the metadata. + * + * Send handles fragmentation as follows: + * If the buffer has more items than the maximum per packet, + * the send method will fragment the samples across several packets. + * Send will respect the burst flags when fragmenting to ensure + * that start of burst can only be set on the first fragment and + * that end of burst can only be set on the final fragment. + * + * 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 samples + * \param nsamps_per_buff the number of samples to send, per buffer + * \param metadata data describing the buffer's contents + * \param timeout the timeout in seconds to wait on a packet + * \return the number of samples sent + */ + virtual size_t send( + const buffs_type &buffs, + const size_t nsamps_per_buff, + const tx_metadata_t &metadata, + const double timeout = 0.1 + ) = 0; +}; + +} //namespace uhd + +#endif /* INCLUDED_UHD_STREAM_HPP */ diff --git a/host/include/uhd/transport/vrt_if_packet.hpp b/host/include/uhd/transport/vrt_if_packet.hpp index 51bd81bb1..1be480874 100644 --- a/host/include/uhd/transport/vrt_if_packet.hpp +++ b/host/include/uhd/transport/vrt_if_packet.hpp @@ -44,6 +44,7 @@ namespace vrt{ //size fields size_t num_payload_words32; //required in pack, derived in unpack + size_t num_payload_bytes; //required in pack, derived in unpack size_t num_header_words32; //derived in pack, derived in unpack size_t num_packet_words32; //derived in pack, required in unpack diff --git a/host/include/uhd/types/clock_config.hpp b/host/include/uhd/types/clock_config.hpp index 24bd96d14..27b312245 100644 --- a/host/include/uhd/types/clock_config.hpp +++ b/host/include/uhd/types/clock_config.hpp @@ -23,10 +23,13 @@ namespace uhd{ /*! - * Clock configuration settings: + * The DEPRECATED Clock configuration settings: * The source for the 10MHz reference clock. * The source and polarity for the PPS clock. * + * Deprecated in favor of set time/clock source calls. + * Its still in this file for the sake of gr-uhd swig. + * * Use the convenience functions external() and internal(), * unless you have a special purpose and cannot use them. */ diff --git a/host/include/uhd/types/io_type.hpp b/host/include/uhd/types/io_type.hpp index ace643abc..967ad7908 100644 --- a/host/include/uhd/types/io_type.hpp +++ b/host/include/uhd/types/io_type.hpp @@ -23,8 +23,11 @@ namespace uhd{ /*! - * The Input/Output configuration struct: + * The DEPRECATED Input/Output configuration struct: * Used to specify the IO type with device send/recv. + * + * Deprecated in favor of streamer interface. + * Its still in this file for the sake of gr-uhd swig. */ class UHD_API io_type_t{ public: diff --git a/host/include/uhd/types/otw_type.hpp b/host/include/uhd/types/otw_type.hpp index 11a6af38e..6dc634fc8 100644 --- a/host/include/uhd/types/otw_type.hpp +++ b/host/include/uhd/types/otw_type.hpp @@ -1,69 +1,2 @@ -// -// 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_OTW_TYPE_HPP -#define INCLUDED_UHD_TYPES_OTW_TYPE_HPP - -#include <uhd/config.hpp> - -namespace uhd{ - - /*! - * Description for over-the-wire integers: - * The DSP units in the FPGA deal with signed 16-bit integers. - * The width and shift define the translation between OTW and DSP, - * defined by the following relation: otw_int = dsp_int >> shift - * - * Note: possible combinations of width, shift, and byteorder - * depend on the internals of the FPGA. Not all are supported! - */ - struct UHD_API otw_type_t{ - - /*! - * Width of an over-the-wire integer in bits. - */ - size_t width; //in bits - - /*! - * Shift of an over-the-wire integer in bits. - * otw_int = dsp_int >> shift - * dsp_int = otw_int << shift - */ - size_t shift; //in bits - - /*! - * Constants for byte order (borrowed from numpy's dtype) - */ - enum /*bo_t*/ { - BO_NATIVE = int('='), - BO_LITTLE_ENDIAN = int('<'), - BO_BIG_ENDIAN = int('>'), - BO_NOT_APPLICABLE = int('|') - } byteorder; - - /*! - * Get the sample size of this otw type. - * \return the size of a sample in bytes - */ - size_t get_sample_size(void) const; - - otw_type_t(void); - }; - -} //namespace uhd - -#endif /* INCLUDED_UHD_TYPES_OTW_TYPE_HPP */ +//The OTW type API has been deprecated in favor of the streamer interface +#include <uhd/deprecated.hpp> diff --git a/host/include/uhd/usrp/CMakeLists.txt b/host/include/uhd/usrp/CMakeLists.txt index ba38a67ea..d7b936fc2 100644 --- a/host/include/uhd/usrp/CMakeLists.txt +++ b/host/include/uhd/usrp/CMakeLists.txt @@ -31,9 +31,7 @@ INSTALL(FILES subdev_spec.hpp ### interfaces ### - single_usrp.hpp multi_usrp.hpp - mboard_iface.hpp DESTINATION ${INCLUDE_DIR}/uhd/usrp COMPONENT headers diff --git a/host/include/uhd/usrp/dboard_base.hpp b/host/include/uhd/usrp/dboard_base.hpp index 7e9557a95..31b3643c7 100644 --- a/host/include/uhd/usrp/dboard_base.hpp +++ b/host/include/uhd/usrp/dboard_base.hpp @@ -19,7 +19,7 @@ #define INCLUDED_UHD_USRP_DBOARD_BASE_HPP #include <uhd/config.hpp> -#include <uhd/wax.hpp> +#include <uhd/property_tree.hpp> #include <uhd/utils/pimpl.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> @@ -28,43 +28,6 @@ namespace uhd{ namespace usrp{ - /*! - * Possible subdev connection types: - * - * A complex subdevice is physically connected to both channels, - * which may be connected in one of two ways: IQ or QI (swapped). - * - * A real subdevice is only physically connected one channel, - * either only the I channel or only the Q channel. - */ - enum subdev_conn_t{ - SUBDEV_CONN_COMPLEX_IQ = 'C', - SUBDEV_CONN_COMPLEX_QI = 'c', - SUBDEV_CONN_REAL_I = 'R', - SUBDEV_CONN_REAL_Q = 'r' - }; - - /*! - * Possible device subdev properties - */ - enum subdev_prop_t{ - SUBDEV_PROP_NAME, //ro, std::string - SUBDEV_PROP_OTHERS, //ro, prop_names_t - SUBDEV_PROP_SENSOR, //ro, sensor_value_t - SUBDEV_PROP_SENSOR_NAMES, //ro, prop_names_t - SUBDEV_PROP_GAIN, //rw, double - SUBDEV_PROP_GAIN_RANGE, //ro, gain_range_t - SUBDEV_PROP_GAIN_NAMES, //ro, prop_names_t - SUBDEV_PROP_FREQ, //rw, double - SUBDEV_PROP_FREQ_RANGE, //ro, freq_range_t - SUBDEV_PROP_ANTENNA, //rw, std::string - SUBDEV_PROP_ANTENNA_NAMES, //ro, prop_names_t - SUBDEV_PROP_CONNECTION, //ro, subdev_conn_t - SUBDEV_PROP_ENABLED, //rw, bool - SUBDEV_PROP_USE_LO_OFFSET, //ro, bool - SUBDEV_PROP_BANDWIDTH //rw, double - }; - /*! * A daughter board dboard_base class for all dboards. * Only other dboard dboard_base classes should inherit this. @@ -81,19 +44,14 @@ public: //structors dboard_base(ctor_args_t); - virtual ~dboard_base(void); - - //interface - virtual void rx_get(const wax::obj &key, wax::obj &val) = 0; - virtual void rx_set(const wax::obj &key, const wax::obj &val) = 0; - virtual void tx_get(const wax::obj &key, wax::obj &val) = 0; - virtual void tx_set(const wax::obj &key, const wax::obj &val) = 0; protected: std::string get_subdev_name(void); dboard_iface::sptr get_iface(void); dboard_id_t get_rx_id(void); dboard_id_t get_tx_id(void); + property_tree::sptr get_rx_subtree(void); + property_tree::sptr get_tx_subtree(void); private: UHD_PIMPL_DECL(impl) _impl; @@ -109,8 +67,6 @@ public: * Create a new xcvr dboard object, override in subclasses. */ xcvr_dboard_base(ctor_args_t); - - virtual ~xcvr_dboard_base(void); }; /*! @@ -123,12 +79,6 @@ public: * Create a new rx dboard object, override in subclasses. */ rx_dboard_base(ctor_args_t); - - virtual ~rx_dboard_base(void); - - //override here so the derived classes cannot - void tx_get(const wax::obj &key, wax::obj &val); - void tx_set(const wax::obj &key, const wax::obj &val); }; /*! @@ -141,12 +91,6 @@ public: * Create a new rx dboard object, override in subclasses. */ tx_dboard_base(ctor_args_t); - - virtual ~tx_dboard_base(void); - - //override here so the derived classes cannot - void rx_get(const wax::obj &key, wax::obj &val); - void rx_set(const wax::obj &key, const wax::obj &val); }; }} //namespace diff --git a/host/include/uhd/usrp/dboard_manager.hpp b/host/include/uhd/usrp/dboard_manager.hpp index 091769a5c..d3a3ffb5c 100644 --- a/host/include/uhd/usrp/dboard_manager.hpp +++ b/host/include/uhd/usrp/dboard_manager.hpp @@ -20,11 +20,12 @@ #include <uhd/config.hpp> #include <uhd/property_tree.hpp> -#include <uhd/utils/props.hpp> #include <uhd/usrp/dboard_base.hpp> #include <uhd/usrp/dboard_id.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> +#include <string> +#include <vector> namespace uhd{ namespace usrp{ @@ -37,11 +38,6 @@ class UHD_API dboard_manager : boost::noncopyable{ public: typedef boost::shared_ptr<dboard_manager> sptr; - //! It does what it says... - static void populate_prop_tree_from_subdev( - property_tree::sptr subtree, wax::obj subdev - ); - //dboard constructor (each dboard should have a ::make with this signature) typedef dboard_base::sptr(*dboard_ctor_t)(dboard_base::ctor_args_t); @@ -57,7 +53,7 @@ public: const dboard_id_t &dboard_id, dboard_ctor_t dboard_ctor, const std::string &name, - const prop_names_t &subdev_names = prop_names_t(1, "0") + const std::vector<std::string> &subdev_names = std::vector<std::string>(1, "0") ); /*! @@ -74,27 +70,25 @@ public: const dboard_id_t &tx_dboard_id, dboard_ctor_t dboard_ctor, const std::string &name, - const prop_names_t &subdev_names = prop_names_t(1, "0") + const std::vector<std::string> &subdev_names = std::vector<std::string>(1, "0") ); /*! * Make a new dboard manager. * \param rx_dboard_id the id of the rx dboard * \param tx_dboard_id the id of the tx dboard + * \param gdboard_id the id of the grand-dboard * \param iface the custom dboard interface + * \param subtree the subtree to load with props * \return an sptr to the new dboard manager */ static sptr make( dboard_id_t rx_dboard_id, dboard_id_t tx_dboard_id, - dboard_iface::sptr iface + dboard_id_t gdboard_id, + dboard_iface::sptr iface, + property_tree::sptr subtree ); - - //dboard manager interface - virtual prop_names_t get_rx_subdev_names(void) = 0; - virtual prop_names_t get_tx_subdev_names(void) = 0; - virtual wax::obj get_rx_subdev(const std::string &subdev_name) = 0; - virtual wax::obj get_tx_subdev(const std::string &subdev_name) = 0; }; }} //namespace diff --git a/host/include/uhd/usrp/mboard_iface.hpp b/host/include/uhd/usrp/mboard_iface.hpp deleted file mode 100644 index bbee8f2de..000000000 --- a/host/include/uhd/usrp/mboard_iface.hpp +++ /dev/null @@ -1,71 +0,0 @@ -// -// Copyright 2010-2011 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_MBOARD_IFACE_HPP -#define INCLUDED_UHD_USRP_MBOARD_IFACE_HPP - -#include <uhd/types/serial.hpp> -#include <uhd/usrp/mboard_eeprom.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/utility.hpp> -#include <boost/cstdint.hpp> -#include <utility> -#include <string> - -namespace uhd{ namespace usrp{ - -/*! - * The mboard interface class: - * Provides a set of functions to implementation layer. - * Including spi, peek, poke, control... - */ -class mboard_iface : public uhd::i2c_iface, public uhd::spi_iface, public uhd::uart_iface { -public: - typedef boost::shared_ptr<mboard_iface> sptr; - /*! - * Write a register (32 bits) - * \param addr the address - * \param data the 32bit data - */ - virtual void poke32(boost::uint32_t addr, boost::uint32_t data) = 0; - - /*! - * Read a register (32 bits) - * \param addr the address - * \return the 32bit data - */ - 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; - -}; - -}} - -#endif //INCLUDED_UHD_USRP_DBOARD_IFACE_HPP diff --git a/host/include/uhd/usrp/multi_usrp.hpp b/host/include/uhd/usrp/multi_usrp.hpp index 00d5dfe7c..5ea00565a 100644 --- a/host/include/uhd/usrp/multi_usrp.hpp +++ b/host/include/uhd/usrp/multi_usrp.hpp @@ -18,19 +18,27 @@ #ifndef INCLUDED_UHD_USRP_MULTI_USRP_HPP #define INCLUDED_UHD_USRP_MULTI_USRP_HPP +//define API capabilities for compile time detection of new features +#define UHD_USRP_MULTI_USRP_REF_SOURCES_API +#define UHD_USRP_MULTI_USRP_GET_RATES_API +#define UHD_USRP_MULTI_USRP_FRONTEND_CAL_API +#define UHD_USRP_MULTI_USRP_COMMAND_TIME_API +#define UHD_USRP_MULTI_USRP_BW_RANGE_API + #include <uhd/config.hpp> #include <uhd/device.hpp> +#include <uhd/deprecated.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/types/sensors.hpp> #include <uhd/usrp/subdev_spec.hpp> #include <uhd/usrp/dboard_iface.hpp> -#include <uhd/usrp/mboard_iface.hpp> #include <boost/shared_ptr.hpp> #include <boost/utility.hpp> +#include <complex> +#include <string> #include <vector> namespace uhd{ namespace usrp{ @@ -47,7 +55,7 @@ namespace uhd{ namespace usrp{ * In the single device, single channel case, these parameters can be unspecified. * * When using a single device with multiple channels: - * - Channel mapping is determined by the subdevice specifications + * - Channel mapping is determined by the frontend specifications * - All channels share a common RX sample rate * - All channels share a common TX sample rate * @@ -55,8 +63,8 @@ namespace uhd{ namespace usrp{ * - Channel mapping is determined by the device address arguments * - 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 share a common RX frontend specification size + * - All boards share a common TX frontend specification size * - All boards must have synchronized times (see the set_time_*() calls) * * Example to setup channel mapping for multiple devices: @@ -68,13 +76,13 @@ namespace uhd{ namespace usrp{ * dev_addr["addr1"] = "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) + * //set the board on 10.2 to use the A RX frontend (RX channel 0) * dev->set_rx_subdev_spec("A:A", 0); * - * //set the board on 10.3 to use the B RX subdevice (RX channel 1) + * //set the board on 10.3 to use the B RX frontend (RX channel 1) * dev->set_rx_subdev_spec("A:B", 1); * - * //set both boards to use the AB TX subdevice (TX channels 0 and 1) + * //set both boards to use the AB TX frontend (TX channels 0 and 1) * dev->set_tx_subdev_spec("A:AB", multi_usrp::ALL_MBOARDS); * * //now that all the channels are mapped, continue with configuration... @@ -108,6 +116,16 @@ public: */ virtual device::sptr get_device(void) = 0; + //! Convenience method to get a RX streamer + rx_streamer::sptr get_rx_stream(const stream_args_t &args){ + return this->get_device()->get_rx_stream(args); + } + + //! Convenience method to get a TX streamer + tx_streamer::sptr get_tx_stream(const stream_args_t &args){ + return this->get_device()->get_tx_stream(args); + } + /******************************************************************* * Mboard methods ******************************************************************/ @@ -210,6 +228,25 @@ public: virtual bool get_time_synchronized(void) = 0; /*! + * Set the time at which the control commands will take effect. + * + * A timed command will back-pressure all subsequent timed commands, + * assuming that the subsequent commands occur within the time-window. + * If the time spec is late, the command will be activated upon arrival. + * + * \param time_spec the time at which the next command will activate + * \param mboard which motherboard to set the config + */ + virtual void set_command_time(const uhd::time_spec_t &time_spec, size_t mboard = ALL_MBOARDS) = 0; + + /*! + * Clear the command time so future commands are sent ASAP. + * + * \param mboard which motherboard to set the config + */ + virtual void clear_command_time(size_t mboard = ALL_MBOARDS) = 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. @@ -225,6 +262,7 @@ public: /*! * Set the clock configuration for the usrp device. + * DEPRECATED in favor of set time and clock source calls. * 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 @@ -233,6 +271,53 @@ public: virtual void set_clock_config(const clock_config_t &clock_config, size_t mboard = ALL_MBOARDS) = 0; /*! + * Set the time source for the usrp device. + * This sets the method of time synchronization, + * typically a pulse per second or an encoded time. + * Typical options for source: external, MIMO. + * \param source a string representing the time source + * \param mboard which motherboard to set the config + */ + virtual void set_time_source(const std::string &source, const size_t mboard = ALL_MBOARDS) = 0; + + /*! + * Get the currently set time source. + * \param mboard which motherboard to get the config + * \return the string representing the time source + */ + virtual std::string get_time_source(const size_t mboard) = 0; + + /*! + * Get a list of possible time sources. + * \param mboard which motherboard to get the list + * \return a vector of strings for possible settings + */ + virtual std::vector<std::string> get_time_sources(const size_t mboard) = 0; + + /*! + * Set the clock source for the usrp device. + * This sets the source for a 10 Mhz reference clock. + * Typical options for source: internal, external, MIMO. + * \param source a string representing the clock source + * \param mboard which motherboard to set the config + */ + virtual void set_clock_source(const std::string &source, const size_t mboard = ALL_MBOARDS) = 0; + + /*! + * Get the currently set clock source. + * \param mboard which motherboard to get the config + * \return the string representing the clock source + */ + virtual std::string get_clock_source(const size_t mboard) = 0; + + /*! + * Get a list of possible clock sources. + * \param mboard which motherboard to get the list + * \return a vector of strings for possible settings + */ + virtual std::vector<std::string> get_clock_sources(const size_t mboard) = 0; + + /*! * Get the number of USRP motherboards in this configuration. */ virtual size_t get_num_mboards(void) = 0; @@ -251,30 +336,24 @@ public: * \return a vector of sensor names */ virtual std::vector<std::string> get_mboard_sensor_names(size_t mboard = 0) = 0; - - /*! - * Get a handle to the mboard_iface object which controls peripheral access. - * \return a mboard_iface::sptr object - */ - virtual mboard_iface::sptr get_mboard_iface(size_t mboard) = 0; /******************************************************************* * RX methods ******************************************************************/ /*! - * Set the RX subdevice specification: + * Set the RX frontend 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 spec the new frontend 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 = ALL_MBOARDS) = 0; /*! - * Get the RX subdevice specification. + * Get the RX frontend specification. * \param mboard the motherboard index 0 to M-1 - * \return the subdevice specification in use + * \return the frontend specification in use */ virtual uhd::usrp::subdev_spec_t get_rx_subdev_spec(size_t mboard = 0) = 0; @@ -286,9 +365,9 @@ public: virtual size_t get_rx_num_channels(void) = 0; /*! - * Get the name of the RX subdevice. + * Get the name of the RX frontend. * \param chan the channel index 0 to N-1 - * \return the subdevice name + * \return the frontend name */ virtual std::string get_rx_subdev_name(size_t chan = 0) = 0; @@ -307,6 +386,13 @@ public: virtual double get_rx_rate(size_t chan = 0) = 0; /*! + * Get a range of possible RX rates. + * \param chan the channel index 0 to N-1 + * \return the meta range of rates + */ + virtual meta_range_t get_rx_rates(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 @@ -381,28 +467,28 @@ public: virtual std::vector<std::string> get_rx_gain_names(size_t chan = 0) = 0; /*! - * Select the RX antenna on the subdevice. + * Select the RX antenna on the frontend. * \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. + * Get the selected RX antenna on the frontend. * \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. + * Get a list of possible RX antennas on the frontend. * \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. + * Get the locked status of the LO on the frontend. * \param chan the channel index 0 to N-1 * \return true for locked */ @@ -411,21 +497,28 @@ public: } /*! - * Set the RX bandwidth on the subdevice. + * Set the RX bandwidth on the frontend. * \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. + * Get the RX bandwidth on the frontend. * \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. + * Get the range of the possible RX bandwidth settings. + * \param chan the channel index 0 to N-1 + * \return a range of bandwidths in Hz + */ + virtual meta_range_t get_rx_bandwidth_range(size_t chan = 0) = 0; + + /*! + * Read the RSSI value on the RX frontend. * \param chan the channel index 0 to N-1 * \return the rssi in dB * \throw exception if RSSI readback not supported @@ -435,7 +528,7 @@ public: } /*! - * Get the dboard interface object for the RX subdevice. + * Get the dboard interface object for the RX frontend. * 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 @@ -444,7 +537,7 @@ public: virtual dboard_iface::sptr get_rx_dboard_iface(size_t chan = 0) = 0; /*! - * Get an RX subdevice sensor value. + * Get an RX frontend sensor value. * \param name the name of the sensor * \param chan the channel index 0 to N-1 * \return a sensor value object @@ -452,29 +545,61 @@ public: virtual sensor_value_t get_rx_sensor(const std::string &name, size_t chan = 0) = 0; /*! - * Get a list of possible RX subdevice sensor names. + * Get a list of possible RX frontend sensor names. * \param chan the channel index 0 to N-1 * \return a vector of sensor names */ virtual std::vector<std::string> get_rx_sensor_names(size_t chan = 0) = 0; + /*! + * Enable/disable the automatic RX DC offset correction. + * The automatic correction subtracts out the long-run average. + * + * When disabled, the averaging option operation is halted. + * Once halted, the average value will be held constant + * until the user re-enables the automatic correction + * or overrides the value by manually setting the offset. + * + * \param enb true to enable automatic DC offset correction + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_dc_offset(const bool enb, size_t chan = ALL_CHANS) = 0; + + /*! + * Set a constant RX DC offset value. + * The value is complex to control both I and Q. + * Only set this when automatic correction is disabled. + * \param offset the dc offset (1.0 is full-scale) + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_dc_offset(const std::complex<double> &offset, size_t chan = ALL_CHANS) = 0; + + /*! + * Set the RX frontend IQ imbalance correction. + * Use this to adjust the magnitude and phase of I and Q. + * + * \param correction the complex correction (1.0 is full-scale) + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_iq_balance(const std::complex<double> &correction, size_t chan = ALL_CHANS) = 0; + /******************************************************************* * TX methods ******************************************************************/ /*! - * Set the TX subdevice specification: + * Set the TX frontend 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 spec the new frontend 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 = ALL_MBOARDS) = 0; /*! - * Get the TX subdevice specification. + * Get the TX frontend specification. * \param mboard the motherboard index 0 to M-1 - * \return the subdevice specification in use + * \return the frontend specification in use */ virtual uhd::usrp::subdev_spec_t get_tx_subdev_spec(size_t mboard = 0) = 0; @@ -486,9 +611,9 @@ public: virtual size_t get_tx_num_channels(void) = 0; /*! - * Get the name of the TX subdevice. + * Get the name of the TX frontend. * \param chan the channel index 0 to N-1 - * \return the subdevice name + * \return the frontend name */ virtual std::string get_tx_subdev_name(size_t chan = 0) = 0; @@ -507,6 +632,13 @@ public: virtual double get_tx_rate(size_t chan = 0) = 0; /*! + * Get a range of possible TX rates. + * \param chan the channel index 0 to N-1 + * \return the meta range of rates + */ + virtual meta_range_t get_tx_rates(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 @@ -581,28 +713,28 @@ public: virtual std::vector<std::string> get_tx_gain_names(size_t chan = 0) = 0; /*! - * Select the TX antenna on the subdevice. + * Select the TX antenna on the frontend. * \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. + * Get the selected TX antenna on the frontend. * \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. + * Get a list of possible TX antennas on the frontend. * \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. + * Get the locked status of the LO on the frontend. * \param chan the channel index 0 to N-1 * \return true for locked */ @@ -611,21 +743,28 @@ public: } /*! - * Set the TX bandwidth on the subdevice. + * Set the TX bandwidth on the frontend. * \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. + * Get the TX bandwidth on the frontend. * \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. + * Get the range of the possible TX bandwidth settings. + * \param chan the channel index 0 to N-1 + * \return a range of bandwidths in Hz + */ + virtual meta_range_t get_tx_bandwidth_range(size_t chan = 0) = 0; + + /*! + * Get the dboard interface object for the TX frontend. * 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 @@ -634,7 +773,7 @@ public: virtual dboard_iface::sptr get_tx_dboard_iface(size_t chan = 0) = 0; /*! - * Get an TX subdevice sensor value. + * Get an TX frontend sensor value. * \param name the name of the sensor * \param chan the channel index 0 to N-1 * \return a sensor value object @@ -642,11 +781,29 @@ public: virtual sensor_value_t get_tx_sensor(const std::string &name, size_t chan = 0) = 0; /*! - * Get a list of possible TX subdevice sensor names. + * Get a list of possible TX frontend sensor names. * \param chan the channel index 0 to N-1 * \return a vector of sensor names */ virtual std::vector<std::string> get_tx_sensor_names(size_t chan = 0) = 0; + + /*! + * Set a constant TX DC offset value. + * The value is complex to control both I and Q. + * \param offset the dc offset (1.0 is full-scale) + * \param chan the channel index 0 to N-1 + */ + virtual void set_tx_dc_offset(const std::complex<double> &offset, size_t chan = ALL_CHANS) = 0; + + /*! + * Set the TX frontend IQ imbalance correction. + * Use this to adjust the magnitude and phase of I and Q. + * + * \param correction the complex correction (1.0 is full-scale) + * \param chan the channel index 0 to N-1 + */ + virtual void set_tx_iq_balance(const std::complex<double> &correction, size_t chan = ALL_CHANS) = 0; + }; }} diff --git a/host/include/uhd/utils/CMakeLists.txt b/host/include/uhd/utils/CMakeLists.txt index 0bf98fb67..48b8db885 100644 --- a/host/include/uhd/utils/CMakeLists.txt +++ b/host/include/uhd/utils/CMakeLists.txt @@ -26,7 +26,6 @@ INSTALL(FILES log.hpp msg.hpp pimpl.hpp - props.hpp safe_call.hpp safe_main.hpp static.hpp diff --git a/host/include/uhd/utils/props.hpp b/host/include/uhd/utils/props.hpp deleted file mode 100644 index 81737423a..000000000 --- a/host/include/uhd/utils/props.hpp +++ /dev/null @@ -1,81 +0,0 @@ -// -// Copyright 2010-2011 Ettus Research LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// - -#ifndef INCLUDED_UHD_UTILS_PROPS_HPP -#define INCLUDED_UHD_UTILS_PROPS_HPP - -#include <uhd/config.hpp> -#include <uhd/wax.hpp> -#include <uhd/exception.hpp> -#include <vector> -#include <string> - -namespace uhd{ - - //! The type for a vector of property names - typedef std::vector<std::string> prop_names_t; - - /*! - * A named prop struct holds a key and a name. - * Allows properties to be sub-sectioned by name. - */ - struct UHD_API named_prop_t{ - const wax::obj key; - const std::string name; - - //! Convert the key to the specified type - template<typename T> inline T as(void){ - return key.as<T>(); - } - - /*! - * Utility function to convert generic key into a named prop. - * If the key was already a named prop, the prop will be split. - * Otherwise, the key will be the key, and the name will be used. - * \param key a reference to the prop object - * \param name a reference to the name object - * \return a named property struct with key and name - */ - static named_prop_t extract( - const wax::obj &key, const std::string &name = "" - ); - - /*! - * Create a new named prop from key and name. - * \param key the property key - * \param name the string name - */ - named_prop_t(const wax::obj &key, const std::string &name); - }; - - /*! - * Throw when getting a not-implemented or write-only property. - * Throw-site information will be included with this error. - */ - #define UHD_THROW_PROP_GET_ERROR() \ - throw uhd::key_error(UHD_THROW_SITE_INFO("cannot get this property")) - - /*! - * Throw when setting a not-implemented or read-only property. - * Throw-site information will be included with this error. - */ - #define UHD_THROW_PROP_SET_ERROR() \ - throw uhd::key_error(UHD_THROW_SITE_INFO("cannot set this property")) - -} //namespace uhd - -#endif /* INCLUDED_UHD_UTILS_PROPS_HPP */ diff --git a/host/include/uhd/wax.hpp b/host/include/uhd/wax.hpp deleted file mode 100644 index 6fd2b8652..000000000 --- a/host/include/uhd/wax.hpp +++ /dev/null @@ -1,169 +0,0 @@ -// -// Copyright 2010-2011 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_WAX_HPP -#define INCLUDED_WAX_HPP - -#include <uhd/config.hpp> -#include <uhd/exception.hpp> -#include <boost/any.hpp> -#include <typeinfo> -#include <string> - -/*! - * WAX - it's a metaphor! - * - * The WAX framework allows an object to have generic/anyobj properties. - * These properties can be addressed through generic/anyobj identifiers. - * - * The WAX object itself is an anytype container much like boost::any. - * To retrieve the value of the appropriate type, use my_obj.as<type>(). - * - * Proprties may be referenced though the [] overloaded operator. - * The [] operator returns a special proxy that allows for assigment. - * Also, the [] operators may be chained as in the folowing examples: - * my_obj[prop1][prop2][prop3] = value; - * value = my_obj[prop1][prop2][prop3].as<type>(); - * - * Property nesting occurs when a WAX object gets another object's link. - * This special link is obtained through a call to my_obj.get_link(). - * - * Note: Do not put a class derived from wax::obj into an stl container. - * MSVC will compile the code, but the binaries will crash at runtime. - * Rather, use pointers or smart pointers to instances of the derived class. - */ - -namespace wax{ - - /*! - * WAX object base class: - * - * A wax obj has two major purposes: - * 1) to act as a polymorphic container, just like boost any - * 2) to provide a nested set/get properties interface - * - * Internally, the polymorphic container is handled by a boost any. - * For properties, a subclass should override the set and get methods. - * For property nesting, wax obj subclasses return special links - * to other wax obj subclasses, and the api handles the magic. - */ - class UHD_API obj{ - public: - - /*! - * Default constructor: - * The contents will be empty. - */ - obj(void); - - /*! - * Copy constructor: - * The contents will be cloned. - * \param o another wax::obj - */ - obj(const obj &o); - - /*! - * Templated any type constructor: - * The contents can be anything. - * Uses the boost::any to handle the magic. - * \param o an object of any type - */ - template<class T> obj(const T &o){ - _contents = o; - } - - /*! - * Destructor. - */ - virtual ~obj(void); - - /*! - * The chaining operator: - * This operator allows access objs with properties. - * A call to the [] operator will return a new proxy obj. - * The proxy object is an obj with special proxy contents. - * Assignment and casting can be used on this special object - * to access the property referenced by the obj key. - * \param key a key to identify a property within this obj - * \return a special wax obj that proxies the obj and key - */ - obj operator[](const obj &key); - - /*! - * The assignment operator: - * This operator allows for assignment of new contents. - * In the special case where this obj contains a proxy, - * the value will be set to the proxy's property reference. - * \param val the new value to assign to the wax obj - * \return a reference to this obj (*this) - */ - obj & operator=(const obj &val); - - /*! - * Get a link in the chain: - * When a wax obj returns another wax obj as part of a get call, - * the return value should be set to the result of this method. - * Doing so will ensure chain-ability of the returned object. - * \return an obj containing a valid link to a wax obj - */ - obj get_link(void) const; - - /*! - * Get the type of the contents of this obj. - * \return a reference to the type_info - */ - const std::type_info & type(void) const; - - /*! - * Cast this obj into the desired type. - * Usage: myobj.as<type>() - * - * \return an object of the desired type - * \throw wax::bad_cast when the cast fails - */ - template<class T> T as(void) const{ - try{ - return boost::any_cast<T>(resolve()); - } - catch(const boost::bad_any_cast &e){ - throw uhd::type_error(std::string("") + "Cannot wax cast " + type().name() + " to " + typeid(T).name() + " " + e.what()); - } - } - - private: - //private interface (override in subclasses) - virtual void get(const obj &, obj &); - virtual void set(const obj &, const obj &); - - /*! - * Resolve the contents of this obj. - * In the case where this obj is a proxy, - * the referenced property will be resolved. - * Otherwise, just get the private contents. - * \return a boost any type with contents - */ - boost::any resolve(void) const; - - //private contents of this obj - boost::any _contents; - - }; - -} //namespace wax - -#endif /* INCLUDED_WAX_HPP */ diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt index 2b0210e6e..7a76ebd53 100644 --- a/host/lib/CMakeLists.txt +++ b/host/lib/CMakeLists.txt @@ -86,11 +86,11 @@ CONFIGURE_FILE( # Append to the list of sources for lib uhd ######################################################################## LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/deprecated.cpp ${CMAKE_CURRENT_SOURCE_DIR}/device.cpp ${CMAKE_CURRENT_SOURCE_DIR}/exception.cpp ${CMAKE_CURRENT_SOURCE_DIR}/property_tree.cpp ${CMAKE_CURRENT_BINARY_DIR}/version.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/wax.cpp ) ######################################################################## diff --git a/host/lib/convert/CMakeLists.txt b/host/lib/convert/CMakeLists.txt index b260cb247..4cc421884 100644 --- a/host/lib/convert/CMakeLists.txt +++ b/host/lib/convert/CMakeLists.txt @@ -112,17 +112,6 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) LIBUHD_PYTHON_GEN_SOURCE( - ${CMAKE_CURRENT_SOURCE_DIR}/gen_convert_pred.py - ${CMAKE_CURRENT_BINARY_DIR}/convert_pred.hpp -) - -INCLUDE(AddFileDependencies) -ADD_FILE_DEPENDENCIES( - ${CMAKE_CURRENT_SOURCE_DIR}/convert_impl.cpp - ${CMAKE_CURRENT_BINARY_DIR}/convert_pred.hpp -) - -LIBUHD_PYTHON_GEN_SOURCE( ${CMAKE_CURRENT_SOURCE_DIR}/gen_convert_general.py ${CMAKE_CURRENT_BINARY_DIR}/convert_general.cpp ) diff --git a/host/lib/convert/convert_common.hpp b/host/lib/convert/convert_common.hpp index 7f513b124..34fb848c3 100644 --- a/host/lib/convert/convert_common.hpp +++ b/host/lib/convert/convert_common.hpp @@ -23,43 +23,58 @@ #include <boost/cstdint.hpp> #include <complex> -#define DECLARE_CONVERTER(fcn, prio) \ +#define _DECLARE_CONVERTER(fcn, in_form, num_in, out_form, num_out, prio) \ static void fcn( \ const uhd::convert::input_type &inputs, \ const uhd::convert::output_type &outputs, \ - size_t nsamps, double scale_factor \ + const size_t nsamps, const double scale_factor \ ); \ - UHD_STATIC_BLOCK(register_##fcn##_##prio){ \ - uhd::convert::register_converter(#fcn, fcn, prio); \ + UHD_STATIC_BLOCK(__register_##fcn##_##prio){ \ + uhd::convert::id_type id; \ + id.input_format = #in_form; \ + id.num_inputs = num_in; \ + id.output_format = #out_form; \ + id.num_outputs = num_out; \ + uhd::convert::register_converter(id, fcn, prio); \ } \ static void fcn( \ const uhd::convert::input_type &inputs, \ const uhd::convert::output_type &outputs, \ - size_t nsamps, double scale_factor \ + const size_t nsamps, const double scale_factor \ ) +#define DECLARE_CONVERTER(in_form, num_in, out_form, num_out, prio) \ + _DECLARE_CONVERTER(__convert_##in_form##_##num_in##_##out_form##_##num_out, in_form, num_in, out_form, num_out, prio) + /*********************************************************************** * Typedefs **********************************************************************/ typedef std::complex<double> fc64_t; typedef std::complex<float> fc32_t; +typedef std::complex<boost::int32_t> sc32_t; typedef std::complex<boost::int16_t> sc16_t; typedef std::complex<boost::int8_t> sc8_t; +typedef double f64_t; +typedef float f32_t; +typedef boost::int32_t s32_t; +typedef boost::int16_t s16_t; +typedef boost::int8_t s8_t; + typedef boost::uint32_t item32_t; /*********************************************************************** - * Convert complex short buffer to items32 + * Convert complex short buffer to items32 sc16 **********************************************************************/ -static UHD_INLINE item32_t sc16_to_item32(sc16_t num, double){ +static UHD_INLINE item32_t sc16_to_item32_sc16(sc16_t num, double){ boost::uint16_t real = num.real(); boost::uint16_t imag = num.imag(); return (item32_t(real) << 16) | (item32_t(imag) << 0); } /*********************************************************************** - * Convert items32 buffer to complex short + * Convert items32 sc16 buffer to complex short **********************************************************************/ -static UHD_INLINE sc16_t item32_to_sc16(item32_t item, double){ +static UHD_INLINE sc16_t item32_sc16_to_sc16(item32_t item, double){ return sc16_t( boost::int16_t(item >> 16), boost::int16_t(item >> 0) @@ -67,41 +82,83 @@ static UHD_INLINE sc16_t item32_to_sc16(item32_t item, double){ } /*********************************************************************** - * Convert complex float buffer to items32 (no swap) + * Convert complex float buffer to items32 sc16 **********************************************************************/ -static UHD_INLINE item32_t fc32_to_item32(fc32_t num, float scale_factor){ - boost::uint16_t real = boost::int16_t(num.real()*scale_factor); - boost::uint16_t imag = boost::int16_t(num.imag()*scale_factor); +static UHD_INLINE item32_t fc32_to_item32_sc16(fc32_t num, double scale_factor){ + boost::uint16_t real = boost::int16_t(num.real()*float(scale_factor)); + boost::uint16_t imag = boost::int16_t(num.imag()*float(scale_factor)); return (item32_t(real) << 16) | (item32_t(imag) << 0); } /*********************************************************************** - * Convert items32 buffer to complex float + * Convert items32 sc16 buffer to complex float **********************************************************************/ -static UHD_INLINE fc32_t item32_to_fc32(item32_t item, float scale_factor){ +static UHD_INLINE fc32_t item32_sc16_to_fc32(item32_t item, double scale_factor){ return fc32_t( - float(boost::int16_t(item >> 16)*scale_factor), - float(boost::int16_t(item >> 0)*scale_factor) + float(boost::int16_t(item >> 16)*float(scale_factor)), + float(boost::int16_t(item >> 0)*float(scale_factor)) ); } /*********************************************************************** - * Convert complex double buffer to items32 (no swap) + * Convert complex double buffer to items32 sc16 **********************************************************************/ -static UHD_INLINE item32_t fc64_to_item32(fc64_t num, double scale_factor){ +static UHD_INLINE item32_t fc64_to_item32_sc16(fc64_t num, double scale_factor){ boost::uint16_t real = boost::int16_t(num.real()*scale_factor); boost::uint16_t imag = boost::int16_t(num.imag()*scale_factor); return (item32_t(real) << 16) | (item32_t(imag) << 0); } /*********************************************************************** - * Convert items32 buffer to complex double + * Convert items32 sc16 buffer to complex double **********************************************************************/ -static UHD_INLINE fc64_t item32_to_fc64(item32_t item, double scale_factor){ +static UHD_INLINE fc64_t item32_sc16_to_fc64(item32_t item, double scale_factor){ return fc64_t( float(boost::int16_t(item >> 16)*scale_factor), float(boost::int16_t(item >> 0)*scale_factor) ); } +/*********************************************************************** + * Convert items32 sc8 buffer to complex short + **********************************************************************/ +static UHD_INLINE void item32_sc8_to_sc16(item32_t item, sc16_t &out0, sc16_t &out1, double){ + out0 = sc16_t( + boost::int8_t(item >> 8), + boost::int8_t(item >> 0) + ); + out1 = sc16_t( + boost::int8_t(item >> 24), + boost::int8_t(item >> 16) + ); +} + +/*********************************************************************** + * Convert items32 sc8 buffer to complex float + **********************************************************************/ +static UHD_INLINE void item32_sc8_to_fc32(item32_t item, fc32_t &out0, fc32_t &out1, double scale_factor){ + out0 = fc32_t( + float(boost::int8_t(item >> 8)*float(scale_factor)), + float(boost::int8_t(item >> 0)*float(scale_factor)) + ); + out1 = fc32_t( + float(boost::int8_t(item >> 24)*float(scale_factor)), + float(boost::int8_t(item >> 16)*float(scale_factor)) + ); +} + +/*********************************************************************** + * Convert items32 sc8 buffer to complex double + **********************************************************************/ +static UHD_INLINE void item32_sc8_to_fc64(item32_t item, fc64_t &out0, fc64_t &out1, double scale_factor){ + out0 = fc64_t( + float(boost::int8_t(item >> 8)*scale_factor), + float(boost::int8_t(item >> 0)*scale_factor) + ); + out1 = fc64_t( + float(boost::int8_t(item >> 24)*scale_factor), + float(boost::int8_t(item >> 16)*scale_factor) + ); +} + #endif /* INCLUDED_LIBUHD_CONVERT_COMMON_HPP */ diff --git a/host/lib/convert/convert_fc32_with_sse2.cpp b/host/lib/convert/convert_fc32_with_sse2.cpp index 676e1561c..24a939d6c 100644 --- a/host/lib/convert/convert_fc32_with_sse2.cpp +++ b/host/lib/convert/convert_fc32_with_sse2.cpp @@ -21,7 +21,7 @@ using namespace uhd::convert; -DECLARE_CONVERTER(convert_fc32_1_to_item32_1_nswap, PRIORITY_CUSTOM){ +DECLARE_CONVERTER(fc32, 1, sc16_item32_le, 1, PRIORITY_SIMD){ const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]); item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); @@ -51,7 +51,7 @@ DECLARE_CONVERTER(convert_fc32_1_to_item32_1_nswap, PRIORITY_CUSTOM){ //dispatch according to alignment switch (size_t(input) & 0xf){ case 0x8: - output[i] = fc32_to_item32(input[i], float(scale_factor)); i++; + output[i] = fc32_to_item32_sc16(input[i], float(scale_factor)); i++; case 0x0: convert_fc32_1_to_item32_1_nswap_guts(_) break; @@ -60,11 +60,11 @@ DECLARE_CONVERTER(convert_fc32_1_to_item32_1_nswap, PRIORITY_CUSTOM){ //convert remainder for (; i < nsamps; i++){ - output[i] = fc32_to_item32(input[i], float(scale_factor)); + output[i] = fc32_to_item32_sc16(input[i], float(scale_factor)); } } -DECLARE_CONVERTER(convert_fc32_1_to_item32_1_bswap, PRIORITY_CUSTOM){ +DECLARE_CONVERTER(fc32, 1, sc16_item32_be, 1, PRIORITY_SIMD){ const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]); item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); @@ -93,7 +93,7 @@ DECLARE_CONVERTER(convert_fc32_1_to_item32_1_bswap, PRIORITY_CUSTOM){ //dispatch according to alignment switch (size_t(input) & 0xf){ case 0x8: - output[i] = uhd::byteswap(fc32_to_item32(input[i], float(scale_factor))); i++; + output[i] = uhd::byteswap(fc32_to_item32_sc16(input[i], float(scale_factor))); i++; case 0x0: convert_fc32_1_to_item32_1_bswap_guts(_) break; @@ -102,11 +102,11 @@ DECLARE_CONVERTER(convert_fc32_1_to_item32_1_bswap, PRIORITY_CUSTOM){ //convert remainder for (; i < nsamps; i++){ - output[i] = uhd::byteswap(fc32_to_item32(input[i], float(scale_factor))); + output[i] = uhd::byteswap(fc32_to_item32_sc16(input[i], float(scale_factor))); } } -DECLARE_CONVERTER(convert_item32_1_to_fc32_1_nswap, PRIORITY_CUSTOM){ +DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_SIMD){ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); fc32_t *output = reinterpret_cast<fc32_t *>(outputs[0]); @@ -138,7 +138,7 @@ DECLARE_CONVERTER(convert_item32_1_to_fc32_1_nswap, PRIORITY_CUSTOM){ //dispatch according to alignment switch (size_t(output) & 0xf){ case 0x8: - output[i] = item32_to_fc32(input[i], float(scale_factor)); i++; + output[i] = item32_sc16_to_fc32(input[i], float(scale_factor)); i++; case 0x0: convert_item32_1_to_fc32_1_nswap_guts(_) break; @@ -147,11 +147,11 @@ DECLARE_CONVERTER(convert_item32_1_to_fc32_1_nswap, PRIORITY_CUSTOM){ //convert remainder for (; i < nsamps; i++){ - output[i] = item32_to_fc32(input[i], float(scale_factor)); + output[i] = item32_sc16_to_fc32(input[i], float(scale_factor)); } } -DECLARE_CONVERTER(convert_item32_1_to_fc32_1_bswap, PRIORITY_CUSTOM){ +DECLARE_CONVERTER(sc16_item32_be, 1, fc32, 1, PRIORITY_SIMD){ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); fc32_t *output = reinterpret_cast<fc32_t *>(outputs[0]); @@ -182,7 +182,7 @@ DECLARE_CONVERTER(convert_item32_1_to_fc32_1_bswap, PRIORITY_CUSTOM){ //dispatch according to alignment switch (size_t(output) & 0xf){ case 0x8: - output[i] = item32_to_fc32(uhd::byteswap(input[i]), float(scale_factor)); i++; + output[i] = item32_sc16_to_fc32(uhd::byteswap(input[i]), float(scale_factor)); i++; case 0x0: convert_item32_1_to_fc32_1_bswap_guts(_) break; @@ -191,6 +191,6 @@ DECLARE_CONVERTER(convert_item32_1_to_fc32_1_bswap, PRIORITY_CUSTOM){ //convert remainder for (; i < nsamps; i++){ - output[i] = item32_to_fc32(uhd::byteswap(input[i]), float(scale_factor)); + output[i] = item32_sc16_to_fc32(uhd::byteswap(input[i]), float(scale_factor)); } } diff --git a/host/lib/convert/convert_fc64_with_sse2.cpp b/host/lib/convert/convert_fc64_with_sse2.cpp index 4d28396a4..837bb584e 100644 --- a/host/lib/convert/convert_fc64_with_sse2.cpp +++ b/host/lib/convert/convert_fc64_with_sse2.cpp @@ -21,7 +21,7 @@ using namespace uhd::convert; -DECLARE_CONVERTER(convert_fc64_1_to_item32_1_nswap, PRIORITY_CUSTOM){ +DECLARE_CONVERTER(fc64, 1, sc16_item32_le, 1, PRIORITY_SIMD){ const fc64_t *input = reinterpret_cast<const fc64_t *>(inputs[0]); item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); @@ -64,11 +64,11 @@ DECLARE_CONVERTER(convert_fc64_1_to_item32_1_nswap, PRIORITY_CUSTOM){ //convert remainder for (; i < nsamps; i++){ - output[i] = fc64_to_item32(input[i], scale_factor); + output[i] = fc64_to_item32_sc16(input[i], scale_factor); } } -DECLARE_CONVERTER(convert_fc64_1_to_item32_1_bswap, PRIORITY_CUSTOM){ +DECLARE_CONVERTER(fc64, 1, sc16_item32_be, 1, PRIORITY_SIMD){ const fc64_t *input = reinterpret_cast<const fc64_t *>(inputs[0]); item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); @@ -110,11 +110,11 @@ DECLARE_CONVERTER(convert_fc64_1_to_item32_1_bswap, PRIORITY_CUSTOM){ //convert remainder for (; i < nsamps; i++){ - output[i] = uhd::byteswap(fc64_to_item32(input[i], scale_factor)); + output[i] = uhd::byteswap(fc64_to_item32_sc16(input[i], scale_factor)); } } -DECLARE_CONVERTER(convert_item32_1_to_fc64_1_nswap, PRIORITY_CUSTOM){ +DECLARE_CONVERTER(sc16_item32_le, 1, fc64, 1, PRIORITY_SIMD){ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); fc64_t *output = reinterpret_cast<fc64_t *>(outputs[0]); @@ -159,11 +159,11 @@ DECLARE_CONVERTER(convert_item32_1_to_fc64_1_nswap, PRIORITY_CUSTOM){ //convert remainder for (; i < nsamps; i++){ - output[i] = item32_to_fc64(input[i], scale_factor); + output[i] = item32_sc16_to_fc64(input[i], scale_factor); } } -DECLARE_CONVERTER(convert_item32_1_to_fc64_1_bswap, PRIORITY_CUSTOM){ +DECLARE_CONVERTER(sc16_item32_be, 1, fc64, 1, PRIORITY_SIMD){ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); fc64_t *output = reinterpret_cast<fc64_t *>(outputs[0]); @@ -207,6 +207,6 @@ DECLARE_CONVERTER(convert_item32_1_to_fc64_1_bswap, PRIORITY_CUSTOM){ //convert remainder for (; i < nsamps; i++){ - output[i] = item32_to_fc64(uhd::byteswap(input[i]), scale_factor); + output[i] = item32_sc16_to_fc64(uhd::byteswap(input[i]), scale_factor); } } diff --git a/host/lib/convert/convert_impl.cpp b/host/lib/convert/convert_impl.cpp index 9b2cdcdc9..df03b44f9 100644 --- a/host/lib/convert/convert_impl.cpp +++ b/host/lib/convert/convert_impl.cpp @@ -18,11 +18,37 @@ #include <uhd/convert.hpp> #include <uhd/utils/log.hpp> #include <uhd/utils/static.hpp> +#include <uhd/types/dict.hpp> #include <uhd/exception.hpp> +#include <boost/cstdint.hpp> +#include <boost/format.hpp> +#include <complex> using namespace uhd; -#include "convert_pred.hpp" +bool convert::operator==(const convert::id_type &lhs, const convert::id_type &rhs){ + return true + and (lhs.input_format == rhs.input_format) + and (lhs.num_inputs == rhs.num_inputs) + and (lhs.output_format == rhs.output_format) + and (lhs.num_outputs == rhs.num_outputs) + ; +} + +std::string convert::id_type::to_pp_string(void) const{ + return str(boost::format( + "conversion ID\n" + " Input format: %s\n" + " Num inputs: %d\n" + " Output format: %s\n" + " Num outputs: %d\n" + ) + % this->input_format + % this->num_inputs + % this->output_format + % this->num_outputs + ); +} /*********************************************************************** * Define types for the function tables @@ -30,56 +56,34 @@ using namespace uhd; struct fcn_table_entry_type{ convert::priority_type prio; convert::function_type fcn; - fcn_table_entry_type(void) - : prio(convert::PRIORITY_EMPTY), fcn(NULL){ - /* NOP */ - } }; -typedef std::vector<fcn_table_entry_type> fcn_table_type; /*********************************************************************** * Setup the table registry **********************************************************************/ -UHD_SINGLETON_FCN(fcn_table_type, get_cpu_to_otw_table); -UHD_SINGLETON_FCN(fcn_table_type, get_otw_to_cpu_table); - -fcn_table_type &get_table(dir_type dir){ - switch(dir){ - case DIR_OTW_TO_CPU: return get_otw_to_cpu_table(); - case DIR_CPU_TO_OTW: return get_cpu_to_otw_table(); - } - UHD_THROW_INVALID_CODE_PATH(); -} +typedef uhd::dict<convert::id_type, fcn_table_entry_type> fcn_table_type; +UHD_SINGLETON_FCN(fcn_table_type, get_table); /*********************************************************************** * The registry functions **********************************************************************/ void uhd::convert::register_converter( - const std::string &markup, + const id_type &id, function_type fcn, priority_type prio ){ - //extract the predicate and direction from the markup - dir_type dir; - pred_type pred = make_pred(markup, dir); - //get a reference to the function table - fcn_table_type &table = get_table(dir); - - //resize the table so that its at least pred+1 - if (table.size() <= pred) table.resize(pred+1); + fcn_table_type &table = get_table(); //register the function if higher priority - if (table[pred].prio < prio){ - table[pred].fcn = fcn; - table[pred].prio = prio; + if (not table.has_key(id) or table[id].prio < prio){ + table[id].fcn = fcn; + table[id].prio = prio; } //----------------------------------------------------------------// - UHD_LOGV(always) << "register_converter: " << markup << std::endl + UHD_LOGV(always) << "register_converter: " << id.to_pp_string() << std::endl << " prio: " << prio << std::endl - << " pred: " << pred << std::endl - << " dir: " << dir << std::endl << std::endl ; //----------------------------------------------------------------// @@ -88,22 +92,50 @@ void uhd::convert::register_converter( /*********************************************************************** * The converter functions **********************************************************************/ -const convert::function_type &convert::get_converter_cpu_to_otw( - const io_type_t &io_type, - const otw_type_t &otw_type, - size_t num_input_buffs, - size_t num_output_buffs -){ - pred_type pred = make_pred(io_type, otw_type, num_input_buffs, num_output_buffs); - return get_cpu_to_otw_table().at(pred).fcn; +convert::function_type convert::get_converter(const id_type &id){ + if (get_table().has_key(id)) return get_table()[id].fcn; + throw uhd::key_error("Cannot find a conversion routine for " + id.to_pp_string()); } -const convert::function_type &convert::get_converter_otw_to_cpu( - const io_type_t &io_type, - const otw_type_t &otw_type, - size_t num_input_buffs, - size_t num_output_buffs +/*********************************************************************** + * Mappings for item format to byte size for all items we can + **********************************************************************/ +typedef uhd::dict<std::string, size_t> item_size_type; +UHD_SINGLETON_FCN(item_size_type, get_item_size_table); + +void register_bytes_per_item( + const std::string &format, const size_t size ){ - pred_type pred = make_pred(io_type, otw_type, num_input_buffs, num_output_buffs); - return get_otw_to_cpu_table().at(pred).fcn; + get_item_size_table()[format] = size; +} + +size_t convert::get_bytes_per_item(const std::string &format){ + if (get_item_size_table().has_key(format)) return get_item_size_table()[format]; + + //OK. I am sorry about this. + //We didnt find a match, so lets find a match for the first term. + //This is partially a hack because of the way I append strings. + //But as long as life is kind, we can keep this. + const size_t pos = format.find("_"); + if (pos != std::string::npos){ + return get_bytes_per_item(format.substr(0, pos)); + } + + throw uhd::key_error("Cannot find an item size:\n" + format); +} + +UHD_STATIC_BLOCK(convert_register_item_sizes){ + //register standard complex types + get_item_size_table()["fc64"] = sizeof(std::complex<double>); + get_item_size_table()["fc32"] = sizeof(std::complex<float>); + get_item_size_table()["sc32"] = sizeof(std::complex<boost::int32_t>); + get_item_size_table()["sc16"] = sizeof(std::complex<boost::int16_t>); + get_item_size_table()["sc8"] = sizeof(std::complex<boost::int8_t>); + + //register standard real types + get_item_size_table()["f64"] = sizeof(double); + get_item_size_table()["f32"] = sizeof(float); + get_item_size_table()["s32"] = sizeof(boost::int32_t); + get_item_size_table()["s16"] = sizeof(boost::int16_t); + get_item_size_table()["s8"] = sizeof(boost::int8_t); } diff --git a/host/lib/convert/convert_with_neon.cpp b/host/lib/convert/convert_with_neon.cpp index e5f08cad9..c7ad62104 100644 --- a/host/lib/convert/convert_with_neon.cpp +++ b/host/lib/convert/convert_with_neon.cpp @@ -20,7 +20,7 @@ using namespace uhd::convert; -DECLARE_CONVERTER(convert_fc32_1_to_item32_1_nswap, PRIORITY_CUSTOM){ +DECLARE_CONVERTER(fc32, 1, sc16_item32_le, 1, PRIORITY_SIMD){ const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]); item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); @@ -37,10 +37,10 @@ DECLARE_CONVERTER(convert_fc32_1_to_item32_1_nswap, PRIORITY_CUSTOM){ } for (; i < nsamps; i++) - output[i] = fc32_to_item32(input[i], float(scale_factor)); + output[i] = fc32_to_item32_sc16(input[i], scale_factor); } -DECLARE_CONVERTER(convert_item32_1_to_fc32_1_nswap, PRIORITY_CUSTOM){ +DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_SIMD){ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); fc32_t *output = reinterpret_cast<fc32_t *>(outputs[0]); @@ -57,5 +57,5 @@ DECLARE_CONVERTER(convert_item32_1_to_fc32_1_nswap, PRIORITY_CUSTOM){ } for (; i < nsamps; i++) - output[i] = item32_to_fc32(input[i], float(scale_factor)); + output[i] = item32_sc16_to_fc32(input[i], scale_factor); } diff --git a/host/lib/convert/convert_with_orc.cpp b/host/lib/convert/convert_with_orc.cpp index 844c2595c..0c46bcf1e 100644 --- a/host/lib/convert/convert_with_orc.cpp +++ b/host/lib/convert/convert_with_orc.cpp @@ -29,26 +29,26 @@ extern void _convert_sc16_1_to_item32_1_nswap_orc(void *, const void *, float, i extern void _convert_item32_1_to_sc16_1_nswap_orc(void *, const void *, float, int); } -DECLARE_CONVERTER(convert_fc32_1_to_item32_1_nswap, PRIORITY_LIBORC){ +DECLARE_CONVERTER(fc32, 1, sc16_item32_le, 1, PRIORITY_LIBORC){ _convert_fc32_1_to_item32_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps); } -DECLARE_CONVERTER(convert_fc32_1_to_item32_1_bswap, PRIORITY_LIBORC){ +DECLARE_CONVERTER(fc32, 1, sc16_item32_be, 1, PRIORITY_LIBORC){ _convert_fc32_1_to_item32_1_bswap_orc(outputs[0], inputs[0], scale_factor, nsamps); } -DECLARE_CONVERTER(convert_item32_1_to_fc32_1_nswap, PRIORITY_LIBORC){ +DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_LIBORC){ _convert_item32_1_to_fc32_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps); } -DECLARE_CONVERTER(convert_item32_1_to_fc32_1_bswap, PRIORITY_LIBORC){ +DECLARE_CONVERTER(sc16_item32_be, 1, fc32, 1, PRIORITY_LIBORC){ _convert_item32_1_to_fc32_1_bswap_orc(outputs[0], inputs[0], scale_factor, nsamps); } -DECLARE_CONVERTER(convert_sc16_1_to_item32_1_nswap, PRIORITY_LIBORC){ +DECLARE_CONVERTER(sc16, 1, sc16_item32_le, 1, PRIORITY_LIBORC){ _convert_sc16_1_to_item32_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps); } -DECLARE_CONVERTER(convert_item32_1_to_sc16_1_nswap, PRIORITY_LIBORC){ +DECLARE_CONVERTER(sc16_item32_le, 1, sc16, 1, PRIORITY_LIBORC){ _convert_item32_1_to_sc16_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps); } diff --git a/host/lib/convert/gen_convert_general.py b/host/lib/convert/gen_convert_general.py index 8c3138bda..52b4212b4 100644 --- a/host/lib/convert/gen_convert_general.py +++ b/host/lib/convert/gen_convert_general.py @@ -28,48 +28,127 @@ TMPL_HEADER = """ using namespace uhd::convert; """ -TMPL_CONV_TO_FROM_ITEM32_1 = """ -DECLARE_CONVERTER(convert_$(cpu_type)_1_to_item32_1_$(swap), PRIORITY_GENERAL){ +TMPL_CONV_GEN2_ITEM32 = """ +DECLARE_CONVERTER(item32, 1, sc16_item32_$(end), 1, PRIORITY_GENERAL){ + const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); + item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + + if (scale_factor == 0){} //avoids unused warning + + for (size_t i = 0; i < nsamps; i++){ + output[i] = $(to_wire)(input[i]); + } +} + +DECLARE_CONVERTER(sc16_item32_$(end), 1, item32, 1, PRIORITY_GENERAL){ + const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); + item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + + if (scale_factor == 0){} //avoids unused warning + + for (size_t i = 0; i < nsamps; i++){ + output[i] = $(to_host)(input[i]); + } +} +""" + +TMPL_CONV_GEN2_COMPLEX = """ +DECLARE_CONVERTER($(cpu_type), 1, sc16_item32_$(end), 1, PRIORITY_GENERAL){ const $(cpu_type)_t *input = reinterpret_cast<const $(cpu_type)_t *>(inputs[0]); item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); for (size_t i = 0; i < nsamps; i++){ - output[i] = $(swap_fcn)($(cpu_type)_to_item32(input[i], float(scale_factor))); + output[i] = $(to_wire)($(cpu_type)_to_item32_sc16(input[i], scale_factor)); } } -DECLARE_CONVERTER(convert_item32_1_to_$(cpu_type)_1_$(swap), PRIORITY_GENERAL){ +DECLARE_CONVERTER(sc16_item32_$(end), 1, $(cpu_type), 1, PRIORITY_GENERAL){ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); $(cpu_type)_t *output = reinterpret_cast<$(cpu_type)_t *>(outputs[0]); for (size_t i = 0; i < nsamps; i++){ - output[i] = item32_to_$(cpu_type)($(swap_fcn)(input[i]), float(scale_factor)); + output[i] = item32_sc16_to_$(cpu_type)($(to_host)(input[i]), scale_factor); + } +} + +DECLARE_CONVERTER(sc8_item32_$(end), 1, $(cpu_type), 1, PRIORITY_GENERAL){ + const item32_t *input = reinterpret_cast<const item32_t *>(size_t(inputs[0]) & ~0x3); + $(cpu_type)_t *output = reinterpret_cast<$(cpu_type)_t *>(outputs[0]); + $(cpu_type)_t dummy; + size_t num_samps = nsamps; + + if ((size_t(inputs[0]) & 0x3) != 0){ + const item32_t item0 = $(to_host)(*input++); + item32_sc8_to_$(cpu_type)(item0, dummy, *output++, scale_factor); + num_samps--; + } + + const size_t num_pairs = num_samps/2; + for (size_t i = 0, j = 0; i < num_pairs; i++, j+=2){ + const item32_t item_i = $(to_host)(input[i]); + item32_sc8_to_$(cpu_type)(item_i, output[j], output[j+1], scale_factor); + } + + if (num_samps != num_pairs*2){ + const item32_t item_n = $(to_host)(input[num_pairs]); + item32_sc8_to_$(cpu_type)(item_n, output[num_samps-1], dummy, scale_factor); } } """ -TMPL_CONV_TO_FROM_ITEM32_X = """ -DECLARE_CONVERTER(convert_$(cpu_type)_$(width)_to_item32_1_$(swap), PRIORITY_GENERAL){ + +TMPL_CONV_USRP1_COMPLEX = """ +DECLARE_CONVERTER($(cpu_type), $(width), sc16_item16_usrp1, 1, PRIORITY_GENERAL){ #for $w in range($width) const $(cpu_type)_t *input$(w) = reinterpret_cast<const $(cpu_type)_t *>(inputs[$(w)]); #end for - item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + boost::uint16_t *output = reinterpret_cast<boost::uint16_t *>(outputs[0]); + + if (scale_factor == 0){} //avoids unused warning for (size_t i = 0, j = 0; i < nsamps; i++){ #for $w in range($width) - output[j++] = $(swap_fcn)($(cpu_type)_to_item32(input$(w)[i], float(scale_factor))); + output[j++] = $(to_wire)(boost::int16_t(input$(w)[i].real()$(do_scale))); + output[j++] = $(to_wire)(boost::int16_t(input$(w)[i].imag()$(do_scale))); #end for } } -DECLARE_CONVERTER(convert_item32_1_to_$(cpu_type)_$(width)_$(swap), PRIORITY_GENERAL){ - const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); +DECLARE_CONVERTER(sc16_item16_usrp1, 1, $(cpu_type), $(width), PRIORITY_GENERAL){ + const boost::uint16_t *input = reinterpret_cast<const boost::uint16_t *>(inputs[0]); + #for $w in range($width) + $(cpu_type)_t *output$(w) = reinterpret_cast<$(cpu_type)_t *>(outputs[$(w)]); + #end for + + if (scale_factor == 0){} //avoids unused warning + + for (size_t i = 0, j = 0; i < nsamps; i++){ + #for $w in range($width) + output$(w)[i] = $(cpu_type)_t( + boost::int16_t($(to_host)(input[j+0]))$(do_scale), + boost::int16_t($(to_host)(input[j+1]))$(do_scale) + ); + j += 2; + #end for + } +} + +DECLARE_CONVERTER(sc8_item16_usrp1, 1, $(cpu_type), $(width), PRIORITY_GENERAL){ + const boost::uint16_t *input = reinterpret_cast<const boost::uint16_t *>(inputs[0]); #for $w in range($width) $(cpu_type)_t *output$(w) = reinterpret_cast<$(cpu_type)_t *>(outputs[$(w)]); #end for + if (scale_factor == 0){} //avoids unused warning + for (size_t i = 0, j = 0; i < nsamps; i++){ #for $w in range($width) - output$(w)[i] = item32_to_$(cpu_type)($(swap_fcn)(input[j++]), float(scale_factor)); + { + const boost::uint16_t num = $(to_host)(input[j++]); + output$(w)[i] = $(cpu_type)_t( + boost::int8_t(num)$(do_scale), + boost::int8_t(num >> 8)$(do_scale) + ); + } #end for } } @@ -83,11 +162,32 @@ if __name__ == '__main__': import sys, os file = os.path.basename(__file__) output = parse_tmpl(TMPL_HEADER, file=file) - for width in 1, 2, 3, 4: - for swap, swap_fcn in (('nswap', ''), ('bswap', 'uhd::byteswap')): - for cpu_type in 'fc64', 'fc32', 'sc16': - output += parse_tmpl( - TMPL_CONV_TO_FROM_ITEM32_1 if width == 1 else TMPL_CONV_TO_FROM_ITEM32_X, - width=width, swap=swap, swap_fcn=swap_fcn, cpu_type=cpu_type - ) + + #generate complex converters for all gen2 platforms + for end, to_host, to_wire in ( + ('be', 'uhd::ntohx', 'uhd::htonx'), + ('le', 'uhd::wtohx', 'uhd::htowx'), + ): + for cpu_type in 'fc64', 'fc32', 'sc16': + output += parse_tmpl( + TMPL_CONV_GEN2_COMPLEX, + end=end, to_host=to_host, to_wire=to_wire, cpu_type=cpu_type + ) + output += parse_tmpl( + TMPL_CONV_GEN2_ITEM32, + end=end, to_host=to_host, to_wire=to_wire + ) + + #generate complex converters for usrp1 format + for width in 1, 2, 4: + for cpu_type, do_scale in ( + ('fc64', '*scale_factor'), + ('fc32', '*float(scale_factor)'), + ('sc16', ''), + ): + output += parse_tmpl( + TMPL_CONV_USRP1_COMPLEX, + width=width, to_host='uhd::wtohx', to_wire='uhd::htowx', + cpu_type=cpu_type, do_scale=do_scale + ) open(sys.argv[1], 'w').write(output) diff --git a/host/lib/convert/gen_convert_pred.py b/host/lib/convert/gen_convert_pred.py deleted file mode 100644 index 360fbcf44..000000000 --- a/host/lib/convert/gen_convert_pred.py +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2011-2011 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/>. -# - -TMPL_TEXT = """ -#import time -/*********************************************************************** - * This file was generated by $file on $time.strftime("%c") - **********************************************************************/ -\#include <uhd/exception.hpp> -\#include <boost/tokenizer.hpp> -\#include <boost/lexical_cast.hpp> -\#include <boost/detail/endian.hpp> -\#include <boost/cstdint.hpp> -\#include <string> -\#include <vector> - -typedef size_t pred_type; -typedef std::vector<pred_type> pred_vector_type; - -enum dir_type{ - DIR_OTW_TO_CPU = 0, - DIR_CPU_TO_OTW = 1 -}; - -struct pred_error : uhd::value_error{ - pred_error(const std::string &what): - uhd::value_error("convert::make_pred: " + what) - { - /* NOP */ - } -}; - -pred_type make_pred(const std::string &markup, dir_type &dir){ - pred_type pred = 0; - - try{ - boost::tokenizer<boost::char_separator<char> > tokenizer(markup, boost::char_separator<char>("_")); - std::vector<std::string> tokens(tokenizer.begin(), tokenizer.end()); - //token 0 is <convert> - std::string inp_type = tokens.at(1); - std::string num_inps = tokens.at(2); - //token 3 is <to> - std::string out_type = tokens.at(4); - std::string num_outs = tokens.at(5); - std::string swap_type = tokens.at(6); - - std::string cpu_type, otw_type; - if (inp_type.find("item") == std::string::npos){ - cpu_type = inp_type; - otw_type = out_type; - dir = DIR_CPU_TO_OTW; - } - else{ - cpu_type = out_type; - otw_type = inp_type; - dir = DIR_OTW_TO_CPU; - } - - if (cpu_type == "fc64") pred |= $ph.fc64_p; - else if (cpu_type == "fc32") pred |= $ph.fc32_p; - else if (cpu_type == "sc16") pred |= $ph.sc16_p; - else if (cpu_type == "sc8") pred |= $ph.sc8_p; - else throw pred_error("unhandled io type " + cpu_type); - - if (otw_type == "item32") pred |= $ph.item32_p; - else throw pred_error("unhandled otw type " + otw_type); - - int num_inputs = boost::lexical_cast<int>(num_inps); - int num_outputs = boost::lexical_cast<int>(num_outs); - - switch(num_inputs*num_outputs){ //FIXME treated as one value - case 1: pred |= $ph.chan1_p; break; - case 2: pred |= $ph.chan2_p; break; - case 3: pred |= $ph.chan3_p; break; - case 4: pred |= $ph.chan4_p; break; - default: throw pred_error("unhandled number of channels"); - } - - if (swap_type == "bswap") pred |= $ph.bswap_p; - else if (swap_type == "nswap") pred |= $ph.nswap_p; - else throw pred_error("unhandled swap type"); - - } - catch(...){ - throw pred_error("could not parse markup: " + markup); - } - - return pred; -} - -#define pred_table_wildcard pred_type(~0) -#define pred_table_max_size size_t(128) -#define pred_table_index(e) (pred_type(e) & 0x7f) - -static pred_vector_type get_pred_byte_order_table(void){ - pred_vector_type table(pred_table_max_size, pred_table_wildcard); - \#ifdef BOOST_BIG_ENDIAN - table[pred_table_index(otw_type_t::BO_BIG_ENDIAN)] = $ph.nswap_p; - table[pred_table_index(otw_type_t::BO_LITTLE_ENDIAN)] = $ph.bswap_p; - \#else - table[pred_table_index(otw_type_t::BO_BIG_ENDIAN)] = $ph.bswap_p; - table[pred_table_index(otw_type_t::BO_LITTLE_ENDIAN)] = $ph.nswap_p; - \#endif - table[pred_table_index(otw_type_t::BO_NATIVE)] = $ph.nswap_p; - return table; -} - -static pred_vector_type get_pred_io_type_table(void){ - pred_vector_type table(pred_table_max_size, pred_table_wildcard); - table[pred_table_index(io_type_t::COMPLEX_FLOAT64)] = $ph.fc64_p; - table[pred_table_index(io_type_t::COMPLEX_FLOAT32)] = $ph.fc32_p; - table[pred_table_index(io_type_t::COMPLEX_INT16)] = $ph.sc16_p; - return table; -} - -static pred_vector_type get_pred_num_io_table(void){ - pred_vector_type table(pred_table_max_size, pred_table_wildcard); - table[1] = $ph.chan1_p; - table[2] = $ph.chan2_p; - table[3] = $ph.chan3_p; - table[4] = $ph.chan4_p; - return table; -} - -UHD_INLINE pred_type make_pred( - const io_type_t &io_type, - const otw_type_t &otw_type, - size_t num_inputs, - size_t num_outputs -){ - pred_type pred = $ph.item32_p; //only item32 supported as of now - - static const pred_vector_type pred_byte_order_table(get_pred_byte_order_table()); - pred |= pred_byte_order_table[pred_table_index(otw_type.byteorder)]; - - static const pred_vector_type pred_io_type_table(get_pred_io_type_table()); - pred |= pred_io_type_table[pred_table_index(io_type.tid)]; - - static const pred_vector_type pred_num_io_table(get_pred_num_io_table()); - pred |= pred_num_io_table[pred_table_index(num_inputs*num_outputs)]; - - if (pred == pred_table_wildcard) throw pred_error( - "unhanded input combination for make_pred()" - ); - - return pred; -} -""" - -def parse_tmpl(_tmpl_text, **kwargs): - from Cheetah.Template import Template - return str(Template(_tmpl_text, kwargs)) - -class ph: - bswap_p = 0b00001 - nswap_p = 0b00000 - item32_p = 0b00000 - sc8_p = 0b00000 - sc16_p = 0b00010 - fc32_p = 0b00100 - fc64_p = 0b00110 - chan1_p = 0b00000 - chan2_p = 0b01000 - chan3_p = 0b10000 - chan4_p = 0b11000 - -if __name__ == '__main__': - import sys, os - file = os.path.basename(__file__) - open(sys.argv[1], 'w').write(parse_tmpl(TMPL_TEXT, file=file, ph=ph)) diff --git a/host/lib/deprecated.cpp b/host/lib/deprecated.cpp new file mode 100644 index 000000000..0fc751eeb --- /dev/null +++ b/host/lib/deprecated.cpp @@ -0,0 +1,81 @@ +//---------------------------------------------------------------------- +//-- deprecated interfaces below, to be removed when the API is changed +//---------------------------------------------------------------------- + +#include <uhd/types/otw_type.hpp> +#include <uhd/types/io_type.hpp> +#include <boost/cstdint.hpp> +#include <stdexcept> +#include <complex> +#include <vector> + +using namespace uhd; + +/*********************************************************************** + * otw type + **********************************************************************/ +size_t otw_type_t::get_sample_size(void) const{ + return (this->width * 2) / 8; +} + +otw_type_t::otw_type_t(void): + width(0), + shift(0), + byteorder(BO_NATIVE) +{ + /* NOP */ +} + +/*********************************************************************** + * io type + **********************************************************************/ +static std::vector<size_t> get_tid_size_table(void){ + std::vector<size_t> table(128, 0); + table[size_t(io_type_t::COMPLEX_FLOAT64)] = sizeof(std::complex<double>); + table[size_t(io_type_t::COMPLEX_FLOAT32)] = sizeof(std::complex<float>); + table[size_t(io_type_t::COMPLEX_INT16)] = sizeof(std::complex<boost::int16_t>); + table[size_t(io_type_t::COMPLEX_INT8)] = sizeof(std::complex<boost::int8_t>); + return table; +} + +static const std::vector<size_t> tid_size_table(get_tid_size_table()); + +io_type_t::io_type_t(tid_t tid): + size(tid_size_table[size_t(tid) & 0x7f]), tid(tid) +{ + /* NOP */ +} + +io_type_t::io_type_t(size_t size): + size(size), tid(CUSTOM_TYPE) +{ + /* NOP */ +} + +#include <uhd/types/clock_config.hpp> + +using namespace uhd; + +clock_config_t clock_config_t::external(void){ + clock_config_t clock_config; + clock_config.ref_source = clock_config_t::REF_SMA; + clock_config.pps_source = clock_config_t::PPS_SMA; + clock_config.pps_polarity = clock_config_t::PPS_POS; + return clock_config; +} + +clock_config_t clock_config_t::internal(void){ + clock_config_t clock_config; + clock_config.ref_source = clock_config_t::REF_INT; + clock_config.pps_source = clock_config_t::PPS_SMA; + clock_config.pps_polarity = clock_config_t::PPS_POS; + return clock_config; +} + +clock_config_t::clock_config_t(void): + ref_source(REF_INT), + pps_source(PPS_SMA), + pps_polarity(PPS_POS) +{ + /* NOP */ +} diff --git a/host/lib/ic_reg_maps/CMakeLists.txt b/host/lib/ic_reg_maps/CMakeLists.txt index 46cf5a06a..dc2ab7847 100644 --- a/host/lib/ic_reg_maps/CMakeLists.txt +++ b/host/lib/ic_reg_maps/CMakeLists.txt @@ -28,6 +28,11 @@ LIBUHD_PYTHON_GEN_SOURCE( ) LIBUHD_PYTHON_GEN_SOURCE( + ${CMAKE_CURRENT_SOURCE_DIR}/gen_adf4351_regs.py + ${CMAKE_CURRENT_BINARY_DIR}/adf4351_regs.hpp +) + +LIBUHD_PYTHON_GEN_SOURCE( ${CMAKE_CURRENT_SOURCE_DIR}/gen_adf4360_regs.py ${CMAKE_CURRENT_BINARY_DIR}/adf4360_regs.hpp ) diff --git a/host/lib/ic_reg_maps/gen_adf4350_regs.py b/host/lib/ic_reg_maps/gen_adf4350_regs.py index bccdb2edf..fce2f569b 100755 --- a/host/lib/ic_reg_maps/gen_adf4350_regs.py +++ b/host/lib/ic_reg_maps/gen_adf4350_regs.py @@ -51,6 +51,7 @@ reference_divide_by_2 2[24] 1 disabled, enabled reference_doubler 2[25] 0 disabled, enabled muxout 2[26:28] 1 3state, dvdd, dgnd, rdiv, ndiv, analog_ld, dld, reserved low_noise_and_spur 2[29:30] 3 low_noise, reserved0, reserved1, low_spur +##reserved 2[31] 0 ######################################################################## ## address 3 ######################################################################## diff --git a/host/lib/ic_reg_maps/gen_adf4351_regs.py b/host/lib/ic_reg_maps/gen_adf4351_regs.py new file mode 100755 index 000000000..607b2979d --- /dev/null +++ b/host/lib/ic_reg_maps/gen_adf4351_regs.py @@ -0,0 +1,138 @@ +#!/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="""\ +######################################################################## +## address 0 +######################################################################## +frac_12_bit 0[3:14] 0 +int_16_bit 0[15:30] 0x23 +##reserved 0[31] 0 +######################################################################## +## address 1 +######################################################################## +mod_12_bit 1[3:14] 0xfff +phase_12_bit 1[15:26] 0 +prescaler 1[27] 0 4_5, 8_9 +phase_adjust 1[28] 0 +##reserved 1[29:31] 0 +######################################################################## +## address 2 +######################################################################## +counter_reset 2[3] 0 disabled, enabled +cp_three_state 2[4] 0 disabled, enabled +power_down 2[5] 0 disabled, enabled +pd_polarity 2[6] 1 negative, positive +ldp 2[7] 0 10ns, 6ns +ldf 2[8] 0 frac_n, int_n +#set $current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(round(x*31.27 + 31.27)/100)).split('.')), range(0,16))) +charge_pump_current 2[9:12] 5 $current_setting_enums +double_buffer 2[13] 0 disabled, enabled +r_counter_10_bit 2[14:23] 0 +reference_divide_by_2 2[24] 1 disabled, enabled +reference_doubler 2[25] 0 disabled, enabled +muxout 2[26:28] 1 3state, dvdd, dgnd, rdiv, ndiv, analog_ld, dld, reserved +low_noise_and_spur 2[29:30] 3 low_noise, reserved0, reserved1, low_spur +##reserved 2[31] 0 +######################################################################## +## address 3 +######################################################################## +clock_divider_12_bit 3[3:14] 0 +clock_div_mode 3[15:16] 0 clock_divider_off, fast_lock, resync_enable, reserved +##reserved 3[17] 0 +cycle_slip_reduction 3[18] 0 disabled, enabled +##reserved 3[19:20] 0 +charge_cancel 3[21] 0 +anti_backlash_pulse 3[22] 0 6ns, 3ns +band_select_mode 3[23] 0 low, high +##reserved 3[24:31] 0 +######################################################################## +## address 4 +######################################################################## +output_power 4[3:4] 3 m4dbm, m1dbm, 2dbm, 5dbm +rf_output_enable 4[5] 1 disabled, enabled +aux_output_power 4[6:7] 0 m4dbm, m1dbm, 2dbm, 5dbm +aux_output_enable 4[8] 0 disabled, enabled +aux_output_select 4[9] 1 divided, fundamental +mute_till_lock_detect 4[10] 0 mute_disabled, mute_enabled +vco_power_down 4[11] 0 vco_powered_up, vco_powered_down +band_select_clock_div 4[12:19] 0 +rf_divider_select 4[20:22] 5 div1, div2, div4, div8, div16, div32, div64 +feedback_select 4[23] 1 divided, fundamental +##reserved 4[24:31] 0 +######################################################################## +## address 5 +######################################################################## +##reserved 5[3:18] 0 +##reserved 5[19:20] 0 +##reserved 5[21] 0 +ld_pin_mode 5[22:23] 1 low0, dld, low, high +##reserved 5[24:31] 0 +""" + +######################################################################## +# Template for methods in the body of the struct +######################################################################## +BODY_TMPL="""\ +enum addr_t{ + ADDR_R0 = 0, + ADDR_R1 = 1, + ADDR_R2 = 2, + ADDR_R3 = 3, + ADDR_R4 = 4, + ADDR_R5 = 5 +}; + +boost::uint32_t get_reg(boost::uint8_t addr){ + boost::uint32_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::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); + #end for + break; + #end for + } + return reg; +} + +void set_reg(boost::uint8_t addr, boost::uint32_t reg){ + 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.get_name() = $(reg.get_type())((reg >> $reg.get_shift()) & $reg.get_mask()); + #end for + break; + #end for + } +} +""" + +if __name__ == '__main__': + import common; common.generate( + name='adf4351_regs', + regs_tmpl=REGS_TMPL, + body_tmpl=BODY_TMPL, + file=__file__, + ) diff --git a/host/lib/transport/gen_vrt_if_packet.py b/host/lib/transport/gen_vrt_if_packet.py index 5f048d8c7..245a7ddbd 100755 --- a/host/lib/transport/gen_vrt_if_packet.py +++ b/host/lib/transport/gen_vrt_if_packet.py @@ -70,6 +70,10 @@ static pred_table_type get_pred_unpack_table(void){ static const pred_table_type pred_unpack_table(get_pred_unpack_table()); +//maps trailer bits to num empty bytes +//maps num empty bytes to trailer bits +static const size_t occ_table[] = {0, 2, 1, 3}; + ######################################################################## #def gen_code($XE_MACRO, $suffix) ######################################################################## @@ -122,14 +126,6 @@ void vrt::if_hdr_pack_$(suffix)( #set $num_header_words += 1 #set $flags |= (0x1 << 20); #end if - ########## Trailer ########## - #if $pred & $tlr_p - //packet_buff[$num_header_words+if_packet_info.num_payload_words32] = $(XE_MACRO)(if_packet_info.tlr); - #set $flags |= (0x1 << 26); - #set $num_trailer_words = 1; - #else - #set $num_trailer_words = 0; - #end if ########## Burst Flags ########## #if $pred & $eob_p #set $flags |= (0x1 << 24); @@ -137,6 +133,18 @@ void vrt::if_hdr_pack_$(suffix)( #if $pred & $sob_p #set $flags |= (0x1 << 25); #end if + ########## Trailer ########## + #if $pred & $tlr_p + { + const size_t empty_bytes = if_packet_info.num_payload_words32*sizeof(boost::uint32_t) - if_packet_info.num_payload_bytes; + if_packet_info.tlr |= (0x3 << 22) | (occ_table[empty_bytes & 0x3] << 10); + } + packet_buff[$num_header_words+if_packet_info.num_payload_words32] = $(XE_MACRO)(if_packet_info.tlr); + #set $flags |= (0x1 << 26); + #set $num_trailer_words = 1; + #else + #set $num_trailer_words = 0; + #end if ########## Variables ########## if_packet_info.num_header_words32 = $num_header_words; if_packet_info.num_packet_words32 = $($num_header_words + $num_trailer_words) + if_packet_info.num_payload_words32; @@ -172,6 +180,8 @@ void vrt::if_hdr_unpack_$(suffix)( const pred_type pred = pred_unpack_table[pred_table_index(vrt_hdr_word)]; + size_t empty_bytes = 0; + switch(pred){ #for $pred in range(2**7) case $pred: @@ -211,15 +221,6 @@ void vrt::if_hdr_unpack_$(suffix)( #else if_packet_info.has_tsf = false; #end if - ########## Trailer ########## - #if $pred & $tlr_p - if_packet_info.has_tlr = true; - if_packet_info.tlr = $(XE_MACRO)(packet_buff[packet_words32-1]); - #set $num_trailer_words = 1; - #else - if_packet_info.has_tlr = false; - #set $num_trailer_words = 0; - #end if ########## Burst Flags ########## #if $pred & $eob_p if_packet_info.eob = true; @@ -231,12 +232,28 @@ void vrt::if_hdr_unpack_$(suffix)( #else if_packet_info.sob = false; #end if + ########## Trailer ########## + #if $pred & $tlr_p + if_packet_info.has_tlr = true; + if_packet_info.tlr = $(XE_MACRO)(packet_buff[packet_words32-1]); + #set $num_trailer_words = 1; + { + const int indicators = (if_packet_info.tlr >> 20) & (if_packet_info.tlr >> 8); + if ((indicators & (1 << 0)) != 0) if_packet_info.eob = true; + if ((indicators & (1 << 1)) != 0) if_packet_info.sob = true; + empty_bytes = occ_table[(indicators >> 2) & 0x3]; + } + #else + if_packet_info.has_tlr = false; + #set $num_trailer_words = 0; + #end if ########## Variables ########## //another failure case if (packet_words32 < $($num_header_words + $num_trailer_words)) throw uhd::value_error("bad vrt header or invalid packet length"); if_packet_info.num_header_words32 = $num_header_words; if_packet_info.num_payload_words32 = packet_words32 - $($num_header_words + $num_trailer_words); + if_packet_info.num_payload_bytes = if_packet_info.num_payload_words32*sizeof(boost::uint32_t) - empty_bytes; break; #end for } diff --git a/host/lib/transport/super_recv_packet_handler.hpp b/host/lib/transport/super_recv_packet_handler.hpp index 541c588e6..57aae96b1 100644 --- a/host/lib/transport/super_recv_packet_handler.hpp +++ b/host/lib/transport/super_recv_packet_handler.hpp @@ -21,15 +21,12 @@ #include <uhd/config.hpp> #include <uhd/exception.hpp> #include <uhd/convert.hpp> -#include <uhd/device.hpp> +#include <uhd/stream.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/byteswap.hpp> -#include <uhd/types/io_type.hpp> -#include <uhd/types/otw_type.hpp> #include <uhd/types/metadata.hpp> #include <uhd/transport/vrt_if_packet.hpp> #include <uhd/transport/zero_copy.hpp> -#include <boost/thread/mutex.hpp> #include <boost/dynamic_bitset.hpp> #include <boost/foreach.hpp> #include <boost/function.hpp> @@ -124,24 +121,12 @@ public: _props.at(xport_chan).get_buff = get_buff; } - /*! - * Setup the conversion functions (homogeneous across transports). - * Here, we load a table of converters for all possible io types. - * This makes the converter look-up an O(1) operation. - * \param otw_type the channel data type - * \param width the streams per channel (usually 1) - */ - void set_converter(const uhd::otw_type_t &otw_type, const size_t width = 1){ - _io_buffs.resize(width); - _converters.resize(128); - for (size_t io_type = 0; io_type < _converters.size(); io_type++){ - try{ - _converters[io_type] = uhd::convert::get_converter_otw_to_cpu( - io_type_t::tid_t(io_type), otw_type, 1, width - ); - }catch(const uhd::value_error &){} //we expect this, not all io_types valid... - } - _bytes_per_item = otw_type.get_sample_size(); + //! Set the conversion routine for all channels + void set_converter(const uhd::convert::id_type &id){ + _io_buffs.resize(id.num_outputs); + _converter = uhd::convert::get_converter(id); + _bytes_per_otw_item = uhd::convert::get_bytes_per_item(id.input_format); + _bytes_per_cpu_item = uhd::convert::get_bytes_per_item(id.output_format); } //! Set the transport channel's overflow handler @@ -149,11 +134,6 @@ public: _props.at(xport_chan).handle_overflow = handle_overflow; } - //! Get a scoped lock object for this instance - boost::mutex::scoped_lock get_scoped_lock(void){ - return boost::mutex::scoped_lock(_mutex); - } - //! Set the scale factor used in float conversion void set_scale_factor(const double scale_factor){ _scale_factor = scale_factor; @@ -165,15 +145,12 @@ public: * Dispatch into combinations of single packet receive calls. ******************************************************************/ UHD_INLINE size_t recv( - const uhd::device::recv_buffs_type &buffs, + const uhd::rx_streamer::buffs_type &buffs, const size_t nsamps_per_buff, uhd::rx_metadata_t &metadata, - const uhd::io_type_t &io_type, - uhd::device::recv_mode_t recv_mode, - double timeout + const double timeout, + const bool one_packet ){ - boost::mutex::scoped_lock lock(_mutex); - //handle metadata queued from a previous receive if (_queue_error_for_next_call){ _queue_error_for_next_call = false; @@ -183,48 +160,34 @@ public: if (_queue_metadata.error_code != rx_metadata_t::ERROR_CODE_TIMEOUT) return 0; } - switch(recv_mode){ + size_t accum_num_samps = recv_one_packet( + buffs, nsamps_per_buff, metadata, timeout + ); - //////////////////////////////////////////////////////////////// - case uhd::device::RECV_MODE_ONE_PACKET:{ - //////////////////////////////////////////////////////////////// - return recv_one_packet(buffs, nsamps_per_buff, metadata, io_type, timeout); - } - - //////////////////////////////////////////////////////////////// - case uhd::device::RECV_MODE_FULL_BUFF:{ - //////////////////////////////////////////////////////////////// - size_t accum_num_samps = recv_one_packet( - buffs, nsamps_per_buff, metadata, io_type, timeout - ); + if (one_packet) return accum_num_samps; - //first recv had an error code set, return immediately - if (metadata.error_code != rx_metadata_t::ERROR_CODE_NONE) return accum_num_samps; + //first recv had an error code set, return immediately + if (metadata.error_code != rx_metadata_t::ERROR_CODE_NONE) return accum_num_samps; - //loop until buffer is filled or error code - while(accum_num_samps < nsamps_per_buff){ - size_t num_samps = recv_one_packet( - buffs, nsamps_per_buff - accum_num_samps, _queue_metadata, - io_type, timeout, accum_num_samps*io_type.size - ); + //loop until buffer is filled or error code + while(accum_num_samps < nsamps_per_buff){ + size_t num_samps = recv_one_packet( + buffs, nsamps_per_buff - accum_num_samps, _queue_metadata, + timeout, accum_num_samps*_bytes_per_cpu_item + ); - //metadata had an error code set, store for next call and return - if (_queue_metadata.error_code != rx_metadata_t::ERROR_CODE_NONE){ - _queue_error_for_next_call = true; - break; - } - accum_num_samps += num_samps; + //metadata had an error code set, store for next call and return + if (_queue_metadata.error_code != rx_metadata_t::ERROR_CODE_NONE){ + _queue_error_for_next_call = true; + break; } - return accum_num_samps; + accum_num_samps += num_samps; } - - default: throw uhd::value_error("unknown recv mode"); - }//switch(recv_mode) + return accum_num_samps; } private: - boost::mutex _mutex; vrt_unpacker_type _vrt_unpacker; size_t _header_offset_words32; double _tick_rate, _samp_rate; @@ -242,8 +205,9 @@ private: }; std::vector<xport_chan_props_type> _props; std::vector<void *> _io_buffs; //used in conversion - size_t _bytes_per_item; //used in conversion - std::vector<uhd::convert::function_type> _converters; //used in conversion + size_t _bytes_per_otw_item; //used in conversion + size_t _bytes_per_cpu_item; //used in conversion + uhd::convert::function_type _converter; //used in conversion double _scale_factor; //! information stored for a received buffer @@ -363,7 +327,7 @@ private: info.alignment_time = info[index].time; info.indexes_todo.set(); info.indexes_todo.reset(index); - info.data_bytes_to_copy = info[index].ifpi.num_payload_words32*sizeof(boost::uint32_t); + info.data_bytes_to_copy = info[index].ifpi.num_payload_bytes; } //if the sequence id matches: @@ -471,7 +435,7 @@ private: std::swap(curr_info, next_info); //save progress from curr -> next curr_info.metadata.has_time_spec = prev_info.metadata.has_time_spec; curr_info.metadata.time_spec = prev_info.metadata.time_spec + time_spec_t(0, - prev_info[index].ifpi.num_payload_words32*sizeof(boost::uint32_t)/_bytes_per_item, _samp_rate); + prev_info[index].ifpi.num_payload_words32*sizeof(boost::uint32_t)/_bytes_per_otw_item, _samp_rate); curr_info.metadata.more_fragments = false; curr_info.metadata.fragment_offset = 0; curr_info.metadata.start_of_burst = false; @@ -507,13 +471,8 @@ private: curr_info.metadata.time_spec = curr_info[0].time; curr_info.metadata.more_fragments = false; curr_info.metadata.fragment_offset = 0; - /* TODO SOB on RX not supported in hardware - static const int tlr_sob_flags = (1 << 21) | (1 << 9); //enable and indicator bits - curr_info.metadata.start_of_burst = curr_info[0].ifpi.has_tlr and (int(curr_info[0].ifpi.tlr & tlr_sob_flags) != 0); - */ - curr_info.metadata.start_of_burst = false; - static const int tlr_eob_flags = (1 << 20) | (1 << 8); //enable and indicator bits - curr_info.metadata.end_of_burst = curr_info[0].ifpi.has_tlr and (int(curr_info[0].ifpi.tlr & tlr_eob_flags) != 0); + curr_info.metadata.start_of_burst = curr_info[0].ifpi.sob; + curr_info.metadata.end_of_burst = curr_info[0].ifpi.eob; curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_NONE; } @@ -525,11 +484,10 @@ private: * Then copy-convert available data into the user's IO buffers. ******************************************************************/ UHD_INLINE size_t recv_one_packet( - const uhd::device::recv_buffs_type &buffs, + const uhd::rx_streamer::buffs_type &buffs, const size_t nsamps_per_buff, uhd::rx_metadata_t &metadata, - const uhd::io_type_t &io_type, - double timeout, + const double timeout, const size_t buffer_offset_bytes = 0 ){ //get the next buffer if the current one has expired @@ -551,9 +509,9 @@ private: metadata.time_spec += time_spec_t(0, info.fragment_offset_in_samps, _samp_rate); //extract the number of samples available to copy - const size_t nsamps_available = info.data_bytes_to_copy/_bytes_per_item; + const size_t nsamps_available = info.data_bytes_to_copy/_bytes_per_otw_item; const size_t nsamps_to_copy = std::min(nsamps_per_buff*_io_buffs.size(), nsamps_available); - const size_t bytes_to_copy = nsamps_to_copy*_bytes_per_item; + const size_t bytes_to_copy = nsamps_to_copy*_bytes_per_otw_item; const size_t nsamps_to_copy_per_io_buff = nsamps_to_copy/_io_buffs.size(); size_t buff_index = 0; @@ -565,7 +523,9 @@ private: } //copy-convert the samples from the recv buffer - _converters[io_type.tid](buff_info.copy_buff, _io_buffs, nsamps_to_copy_per_io_buff, _scale_factor); + if (nsamps_to_copy_per_io_buff != 0) _converter( + buff_info.copy_buff, _io_buffs, nsamps_to_copy_per_io_buff, _scale_factor + ); //update the rx copy buffer to reflect the bytes copied buff_info.copy_buff += bytes_to_copy; @@ -589,6 +549,34 @@ private: } }; +class recv_packet_streamer : public recv_packet_handler, public rx_streamer{ +public: + recv_packet_streamer(const size_t max_num_samps){ + _max_num_samps = max_num_samps; + } + + size_t get_num_channels(void) const{ + return this->size(); + } + + size_t get_max_num_samps(void) const{ + return _max_num_samps; + } + + size_t recv( + const rx_streamer::buffs_type &buffs, + const size_t nsamps_per_buff, + uhd::rx_metadata_t &metadata, + const double timeout, + const bool one_packet + ){ + return recv_packet_handler::recv(buffs, nsamps_per_buff, metadata, timeout, one_packet); + } + +private: + size_t _max_num_samps; +}; + }}} //namespace #endif /* INCLUDED_LIBUHD_TRANSPORT_SUPER_RECV_PACKET_HANDLER_HPP */ diff --git a/host/lib/transport/super_send_packet_handler.hpp b/host/lib/transport/super_send_packet_handler.hpp index a99adcb5f..c3ffcc861 100644 --- a/host/lib/transport/super_send_packet_handler.hpp +++ b/host/lib/transport/super_send_packet_handler.hpp @@ -21,16 +21,13 @@ #include <uhd/config.hpp> #include <uhd/exception.hpp> #include <uhd/convert.hpp> -#include <uhd/device.hpp> +#include <uhd/stream.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/byteswap.hpp> -#include <uhd/types/io_type.hpp> -#include <uhd/types/otw_type.hpp> #include <uhd/types/metadata.hpp> #include <uhd/transport/vrt_if_packet.hpp> #include <uhd/transport/zero_copy.hpp> #include <boost/thread/thread_time.hpp> -#include <boost/thread/mutex.hpp> #include <boost/foreach.hpp> #include <boost/function.hpp> #include <iostream> @@ -100,24 +97,12 @@ public: _props.at(xport_chan).get_buff = get_buff; } - /*! - * Setup the conversion functions (homogeneous across transports). - * Here, we load a table of converters for all possible io types. - * This makes the converter look-up an O(1) operation. - * \param otw_type the channel data type - * \param width the streams per channel (usually 1) - */ - void set_converter(const uhd::otw_type_t &otw_type, const size_t width = 1){ - _io_buffs.resize(width); - _converters.resize(128); - for (size_t io_type = 0; io_type < _converters.size(); io_type++){ - try{ - _converters[io_type] = uhd::convert::get_converter_cpu_to_otw( - io_type_t::tid_t(io_type), otw_type, 1, width - ); - }catch(const uhd::value_error &){} //we expect this, not all io_types valid... - } - _bytes_per_item = otw_type.get_sample_size(); + //! Set the conversion routine for all channels + void set_converter(const uhd::convert::id_type &id){ + _io_buffs.resize(id.num_inputs); + _converter = uhd::convert::get_converter(id); + _bytes_per_otw_item = uhd::convert::get_bytes_per_item(id.output_format); + _bytes_per_cpu_item = uhd::convert::get_bytes_per_item(id.input_format); } /*! @@ -129,11 +114,6 @@ public: _max_samples_per_packet = num_samps; } - //! Get a scoped lock object for this instance - boost::mutex::scoped_lock get_scoped_lock(void){ - return boost::mutex::scoped_lock(_mutex); - } - //! Set the scale factor used in float conversion void set_scale_factor(const double scale_factor){ _scale_factor = scale_factor; @@ -145,15 +125,11 @@ public: * Dispatch into combinations of single packet send calls. ******************************************************************/ UHD_INLINE size_t send( - const uhd::device::send_buffs_type &buffs, + const uhd::tx_streamer::buffs_type &buffs, const size_t nsamps_per_buff, const uhd::tx_metadata_t &metadata, - const uhd::io_type_t &io_type, - uhd::device::send_mode_t send_mode, - double timeout + const double timeout ){ - boost::mutex::scoped_lock lock(_mutex); - //translate the metadata to vrt if packet info vrt::if_packet_info_t if_packet_info; if_packet_info.has_sid = false; @@ -166,74 +142,56 @@ public: if_packet_info.sob = metadata.start_of_burst; if_packet_info.eob = metadata.end_of_burst; - if (nsamps_per_buff <= _max_samples_per_packet) send_mode = uhd::device::SEND_MODE_ONE_PACKET; - switch(send_mode){ - - //////////////////////////////////////////////////////////////// - case uhd::device::SEND_MODE_ONE_PACKET:{ - //////////////////////////////////////////////////////////////// + if (nsamps_per_buff <= _max_samples_per_packet){ //TODO remove this code when sample counts of zero are supported by hardware #ifndef SSPH_DONT_PAD_TO_ONE if (nsamps_per_buff == 0) return send_one_packet( - _zero_buffs, 1, if_packet_info, io_type, timeout + _zero_buffs, 1, if_packet_info, timeout ) & 0x0; #endif - return send_one_packet( - buffs, - std::min(nsamps_per_buff, _max_samples_per_packet), - if_packet_info, io_type, timeout - ); + return send_one_packet(buffs, nsamps_per_buff, if_packet_info, timeout); } + size_t total_num_samps_sent = 0; - //////////////////////////////////////////////////////////////// - case uhd::device::SEND_MODE_FULL_BUFF:{ - //////////////////////////////////////////////////////////////// - size_t total_num_samps_sent = 0; + //false until final fragment + if_packet_info.eob = false; - //false until final fragment - if_packet_info.eob = false; + const size_t num_fragments = (nsamps_per_buff-1)/_max_samples_per_packet; + const size_t final_length = ((nsamps_per_buff-1)%_max_samples_per_packet)+1; - const size_t num_fragments = (nsamps_per_buff-1)/_max_samples_per_packet; - const size_t final_length = ((nsamps_per_buff-1)%_max_samples_per_packet)+1; + //loop through the following fragment indexes + for (size_t i = 0; i < num_fragments; i++){ - //loop through the following fragment indexes - for (size_t i = 0; i < num_fragments; i++){ - - //send a fragment with the helper function - const size_t num_samps_sent = send_one_packet( - buffs, _max_samples_per_packet, - if_packet_info, io_type, timeout, - total_num_samps_sent*io_type.size - ); - total_num_samps_sent += num_samps_sent; - if (num_samps_sent == 0) return total_num_samps_sent; - - //setup metadata for the next fragment - const time_spec_t time_spec = metadata.time_spec + time_spec_t(0, total_num_samps_sent, _samp_rate); - if_packet_info.tsi = boost::uint32_t(time_spec.get_full_secs()); - if_packet_info.tsf = boost::uint64_t(time_spec.get_tick_count(_tick_rate)); - if_packet_info.sob = false; + //send a fragment with the helper function + const size_t num_samps_sent = send_one_packet( + buffs, _max_samples_per_packet, + if_packet_info, timeout, + total_num_samps_sent*_bytes_per_cpu_item + ); + total_num_samps_sent += num_samps_sent; + if (num_samps_sent == 0) return total_num_samps_sent; - } + //setup metadata for the next fragment + const time_spec_t time_spec = metadata.time_spec + time_spec_t(0, total_num_samps_sent, _samp_rate); + if_packet_info.tsi = boost::uint32_t(time_spec.get_full_secs()); + if_packet_info.tsf = boost::uint64_t(time_spec.get_tick_count(_tick_rate)); + if_packet_info.sob = false; - //send the final fragment with the helper function - if_packet_info.eob = metadata.end_of_burst; - return total_num_samps_sent + send_one_packet( - buffs, final_length, - if_packet_info, io_type, timeout, - total_num_samps_sent*io_type.size - ); } - default: throw uhd::value_error("unknown send mode"); - }//switch(send_mode) + //send the final fragment with the helper function + if_packet_info.eob = metadata.end_of_burst; + return total_num_samps_sent + send_one_packet( + buffs, final_length, + if_packet_info, timeout, + total_num_samps_sent*_bytes_per_cpu_item + ); } private: - boost::mutex _mutex; vrt_packer_type _vrt_packer; size_t _header_offset_words32; double _tick_rate, _samp_rate; @@ -242,8 +200,9 @@ private: }; std::vector<xport_chan_props_type> _props; std::vector<const void *> _io_buffs; //used in conversion - size_t _bytes_per_item; //used in conversion - std::vector<uhd::convert::function_type> _converters; //used in conversion + size_t _bytes_per_otw_item; //used in conversion + size_t _bytes_per_cpu_item; //used in conversion + uhd::convert::function_type _converter; //used in conversion size_t _max_samples_per_packet; std::vector<const void *> _zero_buffs; size_t _next_packet_seq; @@ -253,15 +212,15 @@ private: * Send a single packet: ******************************************************************/ UHD_INLINE size_t send_one_packet( - const uhd::device::send_buffs_type &buffs, + const uhd::tx_streamer::buffs_type &buffs, const size_t nsamps_per_buff, vrt::if_packet_info_t &if_packet_info, - const uhd::io_type_t &io_type, - double timeout, + const double timeout, const size_t buffer_offset_bytes = 0 ){ //load the rest of the if_packet_info in here - if_packet_info.num_payload_words32 = (nsamps_per_buff*_io_buffs.size()*_bytes_per_item)/sizeof(boost::uint32_t); + if_packet_info.num_payload_bytes = nsamps_per_buff*_io_buffs.size()*_bytes_per_otw_item; + if_packet_info.num_payload_words32 = (if_packet_info.num_payload_bytes + 3/*round up*/)/sizeof(boost::uint32_t); if_packet_info.packet_count = _next_packet_seq; size_t buff_index = 0; @@ -280,7 +239,9 @@ private: otw_mem += if_packet_info.num_header_words32; //copy-convert the samples into the send buffer - _converters[io_type.tid](_io_buffs, otw_mem, nsamps_per_buff, _scale_factor); + if (nsamps_per_buff != 0) _converter( + _io_buffs, otw_mem, nsamps_per_buff, _scale_factor + ); //commit the samples to the zero-copy interface size_t num_bytes_total = (_header_offset_words32+if_packet_info.num_packet_words32)*sizeof(boost::uint32_t); @@ -292,6 +253,34 @@ private: } }; +class send_packet_streamer : public send_packet_handler, public tx_streamer{ +public: + send_packet_streamer(const size_t max_num_samps){ + _max_num_samps = max_num_samps; + this->set_max_samples_per_packet(_max_num_samps); + } + + size_t get_num_channels(void) const{ + return this->size(); + } + + size_t get_max_num_samps(void) const{ + return _max_num_samps; + } + + size_t send( + const tx_streamer::buffs_type &buffs, + const size_t nsamps_per_buff, + const uhd::tx_metadata_t &metadata, + const double timeout + ){ + return send_packet_handler::send(buffs, nsamps_per_buff, metadata, timeout); + } + +private: + size_t _max_num_samps; +}; + }}} //namespace #endif /* INCLUDED_LIBUHD_TRANSPORT_SUPER_SEND_PACKET_HANDLER_HPP */ diff --git a/host/lib/types/CMakeLists.txt b/host/lib/types/CMakeLists.txt index 957dfd345..2ca0faef7 100644 --- a/host/lib/types/CMakeLists.txt +++ b/host/lib/types/CMakeLists.txt @@ -80,7 +80,6 @@ SET_SOURCE_FILES_PROPERTIES( # This file included, use CMake directory variables ######################################################################## LIBUHD_APPEND_SOURCES( - ${CMAKE_CURRENT_SOURCE_DIR}/clock_config.cpp ${CMAKE_CURRENT_SOURCE_DIR}/device_addr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mac_addr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ranges.cpp diff --git a/host/lib/types/clock_config.cpp b/host/lib/types/clock_config.cpp deleted file mode 100644 index c150c5cc3..000000000 --- a/host/lib/types/clock_config.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// -// Copyright 2011 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/types/clock_config.hpp> - -using namespace uhd; - -clock_config_t clock_config_t::external(void){ - clock_config_t clock_config; - clock_config.ref_source = clock_config_t::REF_SMA; - clock_config.pps_source = clock_config_t::PPS_SMA; - clock_config.pps_polarity = clock_config_t::PPS_POS; - return clock_config; -} - -clock_config_t clock_config_t::internal(void){ - clock_config_t clock_config; - clock_config.ref_source = clock_config_t::REF_INT; - clock_config.pps_source = clock_config_t::PPS_SMA; - clock_config.pps_polarity = clock_config_t::PPS_POS; - return clock_config; -} - -clock_config_t::clock_config_t(void): - ref_source(REF_INT), - pps_source(PPS_SMA), - pps_polarity(PPS_POS) -{ - /* NOP */ -} diff --git a/host/lib/types/types.cpp b/host/lib/types/types.cpp index 7c65d2997..1a3e7e860 100644 --- a/host/lib/types/types.cpp +++ b/host/lib/types/types.cpp @@ -17,12 +17,6 @@ #include <uhd/types/stream_cmd.hpp> #include <uhd/types/metadata.hpp> -#include <uhd/types/otw_type.hpp> -#include <uhd/types/io_type.hpp> -#include <boost/cstdint.hpp> -#include <stdexcept> -#include <complex> -#include <vector> using namespace uhd; @@ -48,44 +42,3 @@ tx_metadata_t::tx_metadata_t(void): { /* NOP */ } - -/*********************************************************************** - * otw type - **********************************************************************/ -size_t otw_type_t::get_sample_size(void) const{ - return (this->width * 2) / 8; -} - -otw_type_t::otw_type_t(void): - width(0), - shift(0), - byteorder(BO_NATIVE) -{ - /* NOP */ -} - -/*********************************************************************** - * io type - **********************************************************************/ -static std::vector<size_t> get_tid_size_table(void){ - std::vector<size_t> table(128, 0); - table[size_t(io_type_t::COMPLEX_FLOAT64)] = sizeof(std::complex<double>); - table[size_t(io_type_t::COMPLEX_FLOAT32)] = sizeof(std::complex<float>); - table[size_t(io_type_t::COMPLEX_INT16)] = sizeof(std::complex<boost::int16_t>); - table[size_t(io_type_t::COMPLEX_INT8)] = sizeof(std::complex<boost::int8_t>); - return table; -} - -static const std::vector<size_t> tid_size_table(get_tid_size_table()); - -io_type_t::io_type_t(tid_t tid): - size(tid_size_table[size_t(tid) & 0x7f]), tid(tid) -{ - /* NOP */ -} - -io_type_t::io_type_t(size_t size): - size(size), tid(CUSTOM_TYPE) -{ - /* NOP */ -} diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp index 98c5cab8c..c506559be 100644 --- a/host/lib/usrp/b100/b100_impl.cpp +++ b/host/lib/usrp/b100/b100_impl.cpp @@ -139,6 +139,7 @@ UHD_STATIC_BLOCK(register_b100_device){ * Structors **********************************************************************/ b100_impl::b100_impl(const device_addr_t &device_addr){ + _tree = property_tree::make(); //extract the FPGA path for the B100 std::string b100_fpga_image = find_image_path( @@ -226,7 +227,6 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // Initialize the properties tree //////////////////////////////////////////////////////////////////// - _tree = property_tree::make(); _tree->create<std::string>("/name").set("B-Series Device"); const fs_path mb_path = "/mboards/0"; _tree->create<std::string>(mb_path / "name").set("B100 (B-Hundo)"); @@ -276,12 +276,31 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// _rx_fe = rx_frontend_core_200::make(_fpga_ctrl, B100_REG_SR_ADDR(B100_SR_RX_FRONT)); _tx_fe = tx_frontend_core_200::make(_fpga_ctrl, B100_REG_SR_ADDR(B100_SR_TX_FRONT)); - //TODO lots of properties to expose here for frontends + _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec") .subscribe(boost::bind(&b100_impl::update_rx_subdev_spec, this, _1)); _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec") .subscribe(boost::bind(&b100_impl::update_tx_subdev_spec, this, _1)); + const fs_path rx_fe_path = mb_path / "rx_frontends" / "A"; + const fs_path tx_fe_path = mb_path / "tx_frontends" / "A"; + + _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value") + .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, _rx_fe, _1)) + .set(std::complex<double>(0.0, 0.0)); + _tree->create<bool>(rx_fe_path / "dc_offset" / "enable") + .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, _rx_fe, _1)) + .set(true); + _tree->create<std::complex<double> >(rx_fe_path / "iq_balance" / "value") + .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, _rx_fe, _1)) + .set(std::complex<double>(0.0, 0.0)); + _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value") + .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, _tx_fe, _1)) + .set(std::complex<double>(0.0, 0.0)); + _tree->create<std::complex<double> >(tx_fe_path / "iq_balance" / "value") + .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, _tx_fe, _1)) + .set(std::complex<double>(0.0, 0.0)); + //////////////////////////////////////////////////////////////////// // create rx dsp control objects //////////////////////////////////////////////////////////////////// @@ -296,9 +315,12 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&rx_dsp_core_200::set_tick_rate, _rx_dsps[dspno], _1)); fs_path rx_dsp_path = mb_path / str(boost::format("rx_dsps/%u") % dspno); + _tree->create<meta_range_t>(rx_dsp_path / "rate/range") + .publish(boost::bind(&rx_dsp_core_200::get_host_rates, _rx_dsps[dspno])); _tree->create<double>(rx_dsp_path / "rate/value") + .set(1e6) //some default .coerce(boost::bind(&rx_dsp_core_200::set_host_rate, _rx_dsps[dspno], _1)) - .subscribe(boost::bind(&b100_impl::update_rx_samp_rate, this, _1)); + .subscribe(boost::bind(&b100_impl::update_rx_samp_rate, this, dspno, _1)); _tree->create<double>(rx_dsp_path / "freq/value") .coerce(boost::bind(&rx_dsp_core_200::set_freq, _rx_dsps[dspno], _1)); _tree->create<meta_range_t>(rx_dsp_path / "freq/range") @@ -316,9 +338,12 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ _tx_dsp->set_link_rate(B100_LINK_RATE_BPS); _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&tx_dsp_core_200::set_tick_rate, _tx_dsp, _1)); + _tree->create<meta_range_t>(mb_path / "tx_dsps/0/rate/range") + .publish(boost::bind(&tx_dsp_core_200::get_host_rates, _tx_dsp)); _tree->create<double>(mb_path / "tx_dsps/0/rate/value") + .set(1e6) //some default .coerce(boost::bind(&tx_dsp_core_200::set_host_rate, _tx_dsp, _1)) - .subscribe(boost::bind(&b100_impl::update_tx_samp_rate, this, _1)); + .subscribe(boost::bind(&b100_impl::update_tx_samp_rate, this, 0, _1)); _tree->create<double>(mb_path / "tx_dsps/0/freq/value") .coerce(boost::bind(&tx_dsp_core_200::set_freq, _tx_dsp, _1)); _tree->create<meta_range_t>(mb_path / "tx_dsps/0/freq/range") @@ -379,22 +404,9 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ _dboard_iface = make_b100_dboard_iface(_fpga_ctrl, _fpga_i2c_ctrl, _fpga_spi_ctrl, _clock_ctrl, _codec_ctrl); _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_dboard_iface); _dboard_manager = dboard_manager::make( - rx_db_eeprom.id, - ((gdb_eeprom.id == dboard_id_t::none())? tx_db_eeprom : gdb_eeprom).id, - _dboard_iface + rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id, + _dboard_iface, _tree->subtree(mb_path / "dboards/A") ); - BOOST_FOREACH(const std::string &name, _dboard_manager->get_rx_subdev_names()){ - dboard_manager::populate_prop_tree_from_subdev( - _tree->subtree(mb_path / "dboards/A/rx_frontends" / name), - _dboard_manager->get_rx_subdev(name) - ); - } - BOOST_FOREACH(const std::string &name, _dboard_manager->get_tx_subdev_names()){ - dboard_manager::populate_prop_tree_from_subdev( - _tree->subtree(mb_path / "dboards/A/tx_frontends" / name), - _dboard_manager->get_tx_subdev(name) - ); - } //initialize io handling this->io_init(); @@ -402,19 +414,13 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // do some post-init tasks //////////////////////////////////////////////////////////////////// - _tree->access<double>(mb_path / "tick_rate").update() //update and then subscribe the clock callback - .subscribe(boost::bind(&b100_clock_ctrl::set_fpga_clock_rate, _clock_ctrl, _1)); + this->update_rates(); - //and now that the tick rate is set, init the host rates to something - BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){ - _tree->access<double>(mb_path / "rx_dsps" / name / "rate" / "value").set(1e6); - } - BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "tx_dsps")){ - _tree->access<double>(mb_path / "tx_dsps" / name / "rate" / "value").set(1e6); - } + _tree->access<double>(mb_path / "tick_rate") //now subscribe the clock rate setter + .subscribe(boost::bind(&b100_clock_ctrl::set_fpga_clock_rate, _clock_ctrl, _1)); - _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(subdev_spec_t("A:"+_dboard_manager->get_rx_subdev_names()[0])); - _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(subdev_spec_t("A:"+_dboard_manager->get_tx_subdev_names()[0])); + _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(mb_path / "dboards/A/rx_frontends").at(0))); + _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(mb_path / "dboards/A/tx_frontends").at(0))); _tree->access<std::string>(mb_path / "clock_source/value").set("internal"); _tree->access<std::string>(mb_path / "time_source/value").set("none"); } @@ -438,13 +444,19 @@ void b100_impl::check_fw_compat(void){ } void b100_impl::check_fpga_compat(void){ - const boost::uint16_t fpga_compat_num = _fpga_ctrl->peek16(B100_REG_MISC_COMPAT); - if (fpga_compat_num != B100_FPGA_COMPAT_NUM){ + const boost::uint32_t fpga_compat_num = _fpga_ctrl->peek32(B100_REG_RB_COMPAT); + boost::uint16_t fpga_major = fpga_compat_num >> 16, fpga_minor = fpga_compat_num & 0xffff; + if (fpga_major == 0){ //old version scheme + fpga_major = fpga_minor; + fpga_minor = 0; + } + if (fpga_major != B100_FPGA_COMPAT_NUM){ throw uhd::runtime_error(str(boost::format( - "Expected FPGA compatibility number 0x%x, but got 0x%x:\n" + "Expected FPGA compatibility number %d, but got %d:\n" "The FPGA build is not compatible with the host code build." - ) % B100_FPGA_COMPAT_NUM % fpga_compat_num)); + ) % int(B100_FPGA_COMPAT_NUM) % fpga_major)); } + _tree->create<std::string>("/mboards/0/fpga_version").set(str(boost::format("%u.%u") % fpga_major % fpga_minor)); } double b100_impl::update_rx_codec_gain(const double gain){ diff --git a/host/lib/usrp/b100/b100_impl.hpp b/host/lib/usrp/b100/b100_impl.hpp index 5ec3dde7b..0984260be 100644 --- a/host/lib/usrp/b100/b100_impl.hpp +++ b/host/lib/usrp/b100/b100_impl.hpp @@ -33,7 +33,6 @@ #include <uhd/property_tree.hpp> #include <uhd/utils/pimpl.hpp> #include <uhd/types/dict.hpp> -#include <uhd/types/otw_type.hpp> #include <uhd/types/sensors.hpp> #include <uhd/types/clock_config.hpp> #include <uhd/types/stream_cmd.hpp> @@ -42,12 +41,13 @@ #include <uhd/usrp/dboard_eeprom.hpp> #include <uhd/usrp/dboard_manager.hpp> #include <uhd/transport/usb_zero_copy.hpp> +#include <boost/weak_ptr.hpp> static const double B100_LINK_RATE_BPS = 256e6/8; //pratical link rate (< 480 Mbps) static const std::string B100_FW_FILE_NAME = "usrp_b100_fw.ihx"; static const std::string B100_FPGA_FILE_NAME = "usrp_b100_fpga.bin"; static const boost::uint16_t B100_FW_COMPAT_NUM = 0x02; -static const boost::uint16_t B100_FPGA_COMPAT_NUM = 0x05; +static const boost::uint16_t B100_FPGA_COMPAT_NUM = 0x08; static const boost::uint32_t B100_RX_SID_BASE = 2; static const boost::uint32_t B100_TX_ASYNC_SID = 1; static const double B100_DEFAULT_TICK_RATE = 64e6; @@ -69,17 +69,8 @@ public: ~b100_impl(void); //the io interface - size_t send(const send_buffs_type &, - size_t, - const uhd::tx_metadata_t &, - const uhd::io_type_t &, - send_mode_t, double); - size_t recv(const recv_buffs_type &, - size_t, uhd::rx_metadata_t &, - const uhd::io_type_t &, - recv_mode_t, double); - size_t get_max_send_samps_per_packet(void) const; - size_t get_max_recv_samps_per_packet(void) const; + uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &args); + uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args); bool recv_async_msg(uhd::async_metadata_t &, double); private: @@ -106,7 +97,6 @@ private: uhd::usrp::dboard_iface::sptr _dboard_iface; //handle io stuff - uhd::otw_type_t _rx_otw_type, _tx_otw_type; UHD_PIMPL_DECL(io_impl) _io_impl; void io_init(void); @@ -115,14 +105,18 @@ private: return _tree; } + std::vector<boost::weak_ptr<uhd::rx_streamer> > _rx_streamers; + std::vector<boost::weak_ptr<uhd::tx_streamer> > _tx_streamers; + void check_fw_compat(void); void check_fpga_compat(void); double update_rx_codec_gain(const double); //sets A and B at once void set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &); void set_db_eeprom(const std::string &, const uhd::usrp::dboard_eeprom_t &); void update_tick_rate(const double rate); - void update_rx_samp_rate(const double rate); - void update_tx_samp_rate(const double rate); + void update_rx_samp_rate(const size_t, const double rate); + void update_tx_samp_rate(const size_t, const double rate); + void update_rates(void); void update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &); void update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &); void update_clock_source(const std::string &); diff --git a/host/lib/usrp/b100/b100_regs.hpp b/host/lib/usrp/b100/b100_regs.hpp index 5e24f9937..491e16eef 100644 --- a/host/lib/usrp/b100/b100_regs.hpp +++ b/host/lib/usrp/b100/b100_regs.hpp @@ -31,7 +31,6 @@ #define B100_REG_MISC_RX_LEN B100_REG_MISC_BASE + 10 #define B100_REG_MISC_TX_LEN B100_REG_MISC_BASE + 12 #define B100_REG_MISC_XFER_RATE B100_REG_MISC_BASE + 14 -#define B100_REG_MISC_COMPAT B100_REG_MISC_BASE + 16 ///////////////////////////////////////////////////// // Slave 1 -- UART @@ -61,43 +60,6 @@ #define B100_REG_I2C_BASE B100_REG_SLAVE(3) -//////////////////////////////////////////////// -// Slave 4 -- GPIO - -#define B100_REG_GPIO_BASE B100_REG_SLAVE(4) - -#define B100_REG_GPIO_RX_IO B100_REG_GPIO_BASE + 0 -#define B100_REG_GPIO_TX_IO B100_REG_GPIO_BASE + 2 -#define B100_REG_GPIO_RX_DDR B100_REG_GPIO_BASE + 4 -#define B100_REG_GPIO_TX_DDR B100_REG_GPIO_BASE + 6 -#define B100_REG_GPIO_RX_SEL B100_REG_GPIO_BASE + 8 -#define B100_REG_GPIO_TX_SEL B100_REG_GPIO_BASE + 10 -#define B100_REG_GPIO_RX_DBG B100_REG_GPIO_BASE + 12 -#define B100_REG_GPIO_TX_DBG B100_REG_GPIO_BASE + 14 - -//possible bit values for sel when dbg is 0: -#define GPIO_SEL_SW 0 // if pin is an output, set by software in the io reg -#define GPIO_SEL_ATR 1 // if pin is an output, set by ATR logic - -//possible bit values for sel when dbg is 1: -#define GPIO_SEL_DEBUG_0 0 // if pin is an output, debug lines from FPGA fabric -#define GPIO_SEL_DEBUG_1 1 // if pin is an output, debug lines from FPGA fabric - -/////////////////////////////////////////////////// -// Slave 6 -- ATR Controller -// 16 regs - -#define B100_REG_ATR_BASE B100_REG_SLAVE(6) - -#define B100_REG_ATR_IDLE_RXSIDE B100_REG_ATR_BASE + 0 -#define B100_REG_ATR_IDLE_TXSIDE B100_REG_ATR_BASE + 2 -#define B100_REG_ATR_INTX_RXSIDE B100_REG_ATR_BASE + 4 -#define B100_REG_ATR_INTX_TXSIDE B100_REG_ATR_BASE + 6 -#define B100_REG_ATR_INRX_RXSIDE B100_REG_ATR_BASE + 8 -#define B100_REG_ATR_INRX_TXSIDE B100_REG_ATR_BASE + 10 -#define B100_REG_ATR_FULL_RXSIDE B100_REG_ATR_BASE + 12 -#define B100_REG_ATR_FULL_TXSIDE B100_REG_ATR_BASE + 14 - /////////////////////////////////////////////////// // Slave 7 -- Readback Mux 32 @@ -108,6 +70,8 @@ #define B100_REG_RB_TIME_PPS_SECS B100_REG_RB_MUX_32_BASE + 8 #define B100_REG_RB_TIME_PPS_TICKS B100_REG_RB_MUX_32_BASE + 12 #define B100_REG_RB_MISC_TEST32 B100_REG_RB_MUX_32_BASE + 16 +#define B100_REG_RB_COMPAT B100_REG_RB_MUX_32_BASE + 24 +#define B100_REG_RB_GPIO B100_REG_RB_MUX_32_BASE + 28 //////////////////////////////////////////////////// // Slaves 8 & 9 -- Settings Bus @@ -132,6 +96,8 @@ #define B100_SR_CLEAR_TX_FIFO 62 // 1 reg #define B100_SR_GLOBAL_RESET 63 // 1 reg +#define B100_SR_GPIO 128 + #define B100_REG_SR_ADDR(n) (B100_REG_SLAVE(8) + (4*(n))) #define B100_REG_SR_MISC_TEST32 B100_REG_SR_ADDR(B100_SR_REG_TEST32) diff --git a/host/lib/usrp/b100/dboard_iface.cpp b/host/lib/usrp/b100/dboard_iface.cpp index 229215a4e..39ad5c5ac 100644 --- a/host/lib/usrp/b100/dboard_iface.cpp +++ b/host/lib/usrp/b100/dboard_iface.cpp @@ -15,7 +15,7 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include "wb_iface.hpp" +#include "gpio_core_200.hpp" #include <uhd/types/serial.hpp> #include "b100_regs.hpp" #include "clock_ctrl.hpp" @@ -45,13 +45,11 @@ public: _spi_iface = spi_iface; _clock = clock; _codec = codec; + _gpio = gpio_core_200::make(_wb_iface, B100_REG_SR_ADDR(B100_SR_GPIO), B100_REG_RB_GPIO); //init the clock rate shadows this->set_clock_rate(UNIT_RX, _clock->get_fpga_clock_rate()); this->set_clock_rate(UNIT_TX, _clock->get_fpga_clock_rate()); - - _wb_iface->poke16(B100_REG_GPIO_RX_DBG, 0); - _wb_iface->poke16(B100_REG_GPIO_TX_DBG, 0); } ~b100_dboard_iface(void){ @@ -104,6 +102,7 @@ private: spi_iface::sptr _spi_iface; b100_clock_ctrl::sptr _clock; b100_codec_ctrl::sptr _codec; + gpio_core_200::sptr _gpio; }; /*********************************************************************** @@ -160,77 +159,27 @@ double b100_dboard_iface::get_codec_rate(unit_t){ * GPIO **********************************************************************/ void b100_dboard_iface::_set_pin_ctrl(unit_t unit, boost::uint16_t value){ - UHD_ASSERT_THROW(GPIO_SEL_ATR == 1); //make this assumption - switch(unit){ - case UNIT_RX: _wb_iface->poke16(B100_REG_GPIO_RX_SEL, value); return; - case UNIT_TX: _wb_iface->poke16(B100_REG_GPIO_TX_SEL, value); return; - } + return _gpio->set_pin_ctrl(unit, value); } void b100_dboard_iface::_set_gpio_ddr(unit_t unit, boost::uint16_t value){ - switch(unit){ - case UNIT_RX: _wb_iface->poke16(B100_REG_GPIO_RX_DDR, value); return; - case UNIT_TX: _wb_iface->poke16(B100_REG_GPIO_TX_DDR, value); return; - } + return _gpio->set_gpio_ddr(unit, value); } void b100_dboard_iface::_set_gpio_out(unit_t unit, boost::uint16_t value){ - switch(unit){ - case UNIT_RX: _wb_iface->poke16(B100_REG_GPIO_RX_IO, value); return; - case UNIT_TX: _wb_iface->poke16(B100_REG_GPIO_TX_IO, value); return; - } + return _gpio->set_gpio_out(unit, value); } boost::uint16_t b100_dboard_iface::read_gpio(unit_t unit){ - switch(unit){ - case UNIT_RX: return _wb_iface->peek16(B100_REG_GPIO_RX_IO); - case UNIT_TX: return _wb_iface->peek16(B100_REG_GPIO_TX_IO); - default: UHD_THROW_INVALID_CODE_PATH(); - } + return _gpio->read_gpio(unit); } void b100_dboard_iface::_set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value){ - //define mapping of unit to atr regs to register address - static const uhd::dict< - unit_t, uhd::dict<atr_reg_t, boost::uint32_t> - > unit_to_atr_to_addr = map_list_of - (UNIT_RX, map_list_of - (ATR_REG_IDLE, B100_REG_ATR_IDLE_RXSIDE) - (ATR_REG_TX_ONLY, B100_REG_ATR_INTX_RXSIDE) - (ATR_REG_RX_ONLY, B100_REG_ATR_INRX_RXSIDE) - (ATR_REG_FULL_DUPLEX, B100_REG_ATR_FULL_RXSIDE) - ) - (UNIT_TX, map_list_of - (ATR_REG_IDLE, B100_REG_ATR_IDLE_TXSIDE) - (ATR_REG_TX_ONLY, B100_REG_ATR_INTX_TXSIDE) - (ATR_REG_RX_ONLY, B100_REG_ATR_INRX_TXSIDE) - (ATR_REG_FULL_DUPLEX, B100_REG_ATR_FULL_TXSIDE) - ) - ; - _wb_iface->poke16(unit_to_atr_to_addr[unit][atr], value); + return _gpio->set_atr_reg(unit, atr, value); } -void b100_dboard_iface::set_gpio_debug(unit_t unit, int which){ - //set this unit to all outputs - this->set_gpio_ddr(unit, 0xffff); - - //calculate the debug selections - boost::uint32_t dbg_sels = 0x0; - int sel = (which == 0)? GPIO_SEL_DEBUG_0 : GPIO_SEL_DEBUG_1; - for(size_t i = 0; i < 16; i++) dbg_sels |= sel << i; - - //set the debug on and which debug selection - switch(unit){ - case UNIT_RX: - _wb_iface->poke16(B100_REG_GPIO_RX_DBG, 0xffff); - _wb_iface->poke16(B100_REG_GPIO_RX_SEL, dbg_sels); - return; - - case UNIT_TX: - _wb_iface->poke16(B100_REG_GPIO_TX_DBG, 0xffff); - _wb_iface->poke16(B100_REG_GPIO_TX_SEL, dbg_sels); - return; - } +void b100_dboard_iface::set_gpio_debug(unit_t, int){ + throw uhd::not_implemented_error("no set_gpio_debug implemented"); } /*********************************************************************** diff --git a/host/lib/usrp/b100/io_impl.cpp b/host/lib/usrp/b100/io_impl.cpp index d2eee4f7c..abbd9864f 100644 --- a/host/lib/usrp/b100/io_impl.cpp +++ b/host/lib/usrp/b100/io_impl.cpp @@ -30,6 +30,7 @@ #include <boost/thread.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/log.hpp> +#include <boost/make_shared.hpp> using namespace uhd; using namespace uhd::usrp; @@ -46,8 +47,6 @@ struct b100_impl::io_impl{ zero_copy_if::sptr data_transport; bounded_buffer<async_metadata_t> async_msg_fifo; recv_packet_demuxer::sptr demuxer; - sph::recv_packet_handler recv_handler; - sph::send_packet_handler send_handler; }; /*********************************************************************** @@ -55,16 +54,6 @@ struct b100_impl::io_impl{ **********************************************************************/ void b100_impl::io_init(void){ - //setup rx otw type - _rx_otw_type.width = 16; - _rx_otw_type.shift = 0; - _rx_otw_type.byteorder = uhd::otw_type_t::BO_LITTLE_ENDIAN; - - //setup tx otw type - _tx_otw_type.width = 16; - _tx_otw_type.shift = 0; - _tx_otw_type.byteorder = uhd::otw_type_t::BO_LITTLE_ENDIAN; - //clear state machines _fpga_ctrl->poke32(B100_REG_CLEAR_RX, 0); _fpga_ctrl->poke32(B100_REG_CLEAR_TX, 0); @@ -72,19 +61,16 @@ void b100_impl::io_init(void){ //set the expected packet size in USB frames _fpga_ctrl->poke32(B100_REG_MISC_RX_LEN, 4); + //allocate streamer weak ptrs containers + _rx_streamers.resize(_rx_dsps.size()); + _tx_streamers.resize(1/*known to be 1 dsp*/); + //create new io impl _io_impl = UHD_PIMPL_MAKE(io_impl, ()); _io_impl->demuxer = recv_packet_demuxer::make(_data_transport, _rx_dsps.size(), B100_RX_SID_BASE); //now its safe to register the async callback _fpga_ctrl->set_async_cb(boost::bind(&b100_impl::handle_async_message, this, _1)); - - //init some handler stuff - _io_impl->recv_handler.set_vrt_unpacker(&vrt::if_hdr_unpack_le); - _io_impl->recv_handler.set_converter(_rx_otw_type); - _io_impl->send_handler.set_vrt_packer(&vrt::if_hdr_pack_le); - _io_impl->send_handler.set_converter(_tx_otw_type); - _io_impl->send_handler.set_max_samples_per_packet(get_max_send_samps_per_packet()); } void b100_impl::handle_async_message(managed_recv_buffer::sptr rbuf){ @@ -120,27 +106,54 @@ void b100_impl::handle_async_message(managed_recv_buffer::sptr rbuf){ else UHD_MSG(error) << "Unknown async packet" << std::endl; } +void b100_impl::update_rates(void){ + const fs_path mb_path = "/mboards/0"; + _tree->access<double>(mb_path / "tick_rate").update(); + + //and now that the tick rate is set, init the host rates to something + BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){ + _tree->access<double>(mb_path / "rx_dsps" / name / "rate" / "value").update(); + } + BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "tx_dsps")){ + _tree->access<double>(mb_path / "tx_dsps" / name / "rate" / "value").update(); + } +} + void b100_impl::update_tick_rate(const double rate){ - boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); - _io_impl->recv_handler.set_tick_rate(rate); - boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); - _io_impl->send_handler.set_tick_rate(rate); + //update the tick rate on all existing streamers -> thread safe + for (size_t i = 0; i < _rx_streamers.size(); i++){ + boost::shared_ptr<sph::recv_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_rx_streamers[i].lock()); + if (my_streamer.get() == NULL) continue; + my_streamer->set_tick_rate(rate); + } + for (size_t i = 0; i < _tx_streamers.size(); i++){ + boost::shared_ptr<sph::send_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::send_packet_streamer>(_tx_streamers[i].lock()); + if (my_streamer.get() == NULL) continue; + my_streamer->set_tick_rate(rate); + } } -void b100_impl::update_rx_samp_rate(const double rate){ - boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); - _io_impl->recv_handler.set_samp_rate(rate); - const double adj = _rx_dsps.front()->get_scaling_adjustment(); - _io_impl->recv_handler.set_scale_factor(adj/32767.); +void b100_impl::update_rx_samp_rate(const size_t dspno, const double rate){ + boost::shared_ptr<sph::recv_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_rx_streamers[dspno].lock()); + if (my_streamer.get() == NULL) return; + + my_streamer->set_samp_rate(rate); + const double adj = _rx_dsps[dspno]->get_scaling_adjustment(); + my_streamer->set_scale_factor(adj); } -void b100_impl::update_tx_samp_rate(const double rate){ - boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); - _io_impl->send_handler.set_samp_rate(rate); +void b100_impl::update_tx_samp_rate(const size_t dspno, const double rate){ + boost::shared_ptr<sph::send_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::send_packet_streamer>(_tx_streamers[dspno].lock()); + if (my_streamer.get() == NULL) return; + + my_streamer->set_samp_rate(rate); } void b100_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ - boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); fs_path root = "/mboards/0/dboards"; //sanity checking @@ -154,22 +167,9 @@ void b100_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ _rx_dsps[i]->set_mux(conn, fe_swapped); } _rx_fe->set_mux(fe_swapped); - - //resize for the new occupancy - _io_impl->recv_handler.resize(spec.size()); - - //bind new callbacks for the handler - for (size_t i = 0; i < _io_impl->recv_handler.size(); i++){ - _rx_dsps[i]->set_nsamps_per_packet(get_max_recv_samps_per_packet()); //seems to be a good place to set this - _io_impl->recv_handler.set_xport_chan_get_buff(i, boost::bind( - &recv_packet_demuxer::get_recv_buff, _io_impl->demuxer, i, _1 - )); - _io_impl->recv_handler.set_overflow_handler(i, boost::bind(&rx_dsp_core_200::handle_overflow, _rx_dsps[i])); - } } void b100_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ - boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); fs_path root = "/mboards/0/dboards"; //sanity checking @@ -178,16 +178,6 @@ void b100_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ //set the mux for this spec const std::string conn = _tree->access<std::string>(root / spec[0].db_name / "tx_frontends" / spec[0].sd_name / "connection").get(); _tx_fe->set_mux(conn); - - //resize for the new occupancy - _io_impl->send_handler.resize(spec.size()); - - //bind new callbacks for the handler - for (size_t i = 0; i < _io_impl->send_handler.size(); i++){ - _io_impl->send_handler.set_xport_chan_get_buff(i, boost::bind( - &zero_copy_if::get_send_buff, _data_transport, _1 - )); - } } /*********************************************************************** @@ -201,50 +191,109 @@ bool b100_impl::recv_async_msg( } /*********************************************************************** - * Send Data + * Receive streamer **********************************************************************/ -size_t b100_impl::get_max_send_samps_per_packet(void) const { +rx_streamer::sptr b100_impl::get_rx_stream(const uhd::stream_args_t &args_){ + stream_args_t args = args_; + + //setup defaults for unspecified values + args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; + args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + const unsigned sc8_scalar = unsigned(args.args.cast<double>("scalar", 0x400)); + + //calculate packet size 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 ; - static const size_t bpp = 2048 - hdr_size; - return bpp / _tx_otw_type.get_sample_size(); -} + const size_t bpp = 2048 - hdr_size; //limited by FPGA pkt buffer size + const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format); + + //make the new streamer given the samples per packet + boost::shared_ptr<sph::recv_packet_streamer> my_streamer = boost::make_shared<sph::recv_packet_streamer>(spp); + + //init some streamer stuff + my_streamer->resize(args.channels.size()); + my_streamer->set_vrt_unpacker(&vrt::if_hdr_unpack_le); + + //set the converter + uhd::convert::id_type id; + id.input_format = args.otw_format + "_item32_le"; + id.num_inputs = 1; + id.output_format = args.cpu_format; + id.num_outputs = 1; + my_streamer->set_converter(id); + + //bind callbacks for the handler + for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){ + const size_t dsp = args.channels[chan_i]; + _rx_dsps[dsp]->set_nsamps_per_packet(spp); //seems to be a good place to set this + _rx_dsps[dsp]->set_format(args.otw_format, sc8_scalar); + my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( + &recv_packet_demuxer::get_recv_buff, _io_impl->demuxer, dsp, _1 + )); + my_streamer->set_overflow_handler(chan_i, boost::bind( + &rx_dsp_core_200::handle_overflow, _rx_dsps[dsp] + )); + _rx_streamers[dsp] = my_streamer; //store weak pointer + } -size_t b100_impl::send( - const send_buffs_type &buffs, size_t nsamps_per_buff, - const tx_metadata_t &metadata, const io_type_t &io_type, - send_mode_t send_mode, double timeout -){ - return _io_impl->send_handler.send( - buffs, nsamps_per_buff, - metadata, io_type, - send_mode, timeout - ); + //sets all tick and samp rates on this streamer + this->update_rates(); + + return my_streamer; } /*********************************************************************** - * Receive Data + * Transmit streamer **********************************************************************/ -size_t b100_impl::get_max_recv_samps_per_packet(void) const { +tx_streamer::sptr b100_impl::get_tx_stream(const uhd::stream_args_t &args_){ + stream_args_t args = args_; + + //setup defaults for unspecified values + args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; + args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + + if (args.otw_format != "sc16"){ + throw uhd::value_error("USRP TX cannot handle requested wire format: " + args.otw_format); + } + + //calculate packet size 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 = 2048 - hdr_size; //limited by FPGA pkt buffer size - return bpp/_rx_otw_type.get_sample_size(); -} + static const size_t bpp = 2048 - hdr_size; + const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format); + + //make the new streamer given the samples per packet + boost::shared_ptr<sph::send_packet_streamer> my_streamer = boost::make_shared<sph::send_packet_streamer>(spp); + + //init some streamer stuff + my_streamer->resize(args.channels.size()); + my_streamer->set_vrt_packer(&vrt::if_hdr_pack_le); + + //set the converter + uhd::convert::id_type id; + id.input_format = args.cpu_format; + id.num_inputs = 1; + id.output_format = args.otw_format + "_item32_le"; + id.num_outputs = 1; + my_streamer->set_converter(id); + + //bind callbacks for the handler + for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){ + const size_t dsp = args.channels[chan_i]; + UHD_ASSERT_THROW(dsp == 0); //always 0 + my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( + &zero_copy_if::get_send_buff, _data_transport, _1 + )); + _tx_streamers[dsp] = my_streamer; //store weak pointer + } -size_t b100_impl::recv( - const recv_buffs_type &buffs, size_t nsamps_per_buff, - rx_metadata_t &metadata, const io_type_t &io_type, - recv_mode_t recv_mode, double timeout -){ - return _io_impl->recv_handler.recv( - buffs, nsamps_per_buff, - metadata, io_type, - recv_mode, timeout - ); + //sets all tick and samp rates on this streamer + this->update_rates(); + + return my_streamer; } diff --git a/host/lib/usrp/cores/gpio_core_200.cpp b/host/lib/usrp/cores/gpio_core_200.cpp index 8639b1851..d756097ff 100644 --- a/host/lib/usrp/cores/gpio_core_200.cpp +++ b/host/lib/usrp/cores/gpio_core_200.cpp @@ -23,15 +23,14 @@ #define REG_GPIO_TX_ONLY _base + 8 #define REG_GPIO_BOTH _base + 12 #define REG_GPIO_DDR _base + 16 -#define REG_GPIO_READ _base + 0 //any address will readback using namespace uhd; using namespace usrp; class gpio_core_200_impl : public gpio_core_200{ public: - gpio_core_200_impl(wb_iface::sptr iface, const size_t base): - _iface(iface), _base(base) { /* NOP */ } + gpio_core_200_impl(wb_iface::sptr iface, const size_t base, const size_t rb_addr): + _iface(iface), _base(base), _rb_addr(rb_addr) { /* NOP */ } void set_pin_ctrl(const unit_t unit, const boost::uint16_t value){ _pin_ctrl[unit] = value; //shadow @@ -57,12 +56,13 @@ public: } boost::uint16_t read_gpio(const unit_t unit){ - return boost::uint16_t(_iface->peek32(REG_GPIO_READ) >> unit2shit(unit)); + return boost::uint16_t(_iface->peek32(_rb_addr) >> unit2shit(unit)); } private: wb_iface::sptr _iface; const size_t _base; + const size_t _rb_addr; uhd::dict<unit_t, boost::uint16_t> _pin_ctrl, _gpio_out, _gpio_ddr; uhd::dict<unit_t, uhd::dict<atr_reg_t, boost::uint16_t> > _atr_regs; @@ -95,6 +95,6 @@ private: }; -gpio_core_200::sptr gpio_core_200::make(wb_iface::sptr iface, const size_t base){ - return sptr(new gpio_core_200_impl(iface, base)); +gpio_core_200::sptr gpio_core_200::make(wb_iface::sptr iface, const size_t base, const size_t rb_addr){ + return sptr(new gpio_core_200_impl(iface, base, rb_addr)); } diff --git a/host/lib/usrp/cores/gpio_core_200.hpp b/host/lib/usrp/cores/gpio_core_200.hpp index 7ff2af649..278575874 100644 --- a/host/lib/usrp/cores/gpio_core_200.hpp +++ b/host/lib/usrp/cores/gpio_core_200.hpp @@ -33,7 +33,7 @@ public: typedef uhd::usrp::dboard_iface::atr_reg_t atr_reg_t; //! makes a new GPIO core from iface and slave base - static sptr make(wb_iface::sptr iface, const size_t base); + static sptr make(wb_iface::sptr iface, const size_t base, const size_t rb_addr); //! 1 = ATR virtual void set_pin_ctrl(const unit_t unit, const boost::uint16_t value) = 0; diff --git a/host/lib/usrp/cores/rx_dsp_core_200.cpp b/host/lib/usrp/cores/rx_dsp_core_200.cpp index d562c64db..b97f9c58e 100644 --- a/host/lib/usrp/cores/rx_dsp_core_200.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_200.cpp @@ -42,6 +42,7 @@ #define REG_RX_CTRL_VRT_TLR _ctrl_base + 24 #define REG_RX_CTRL_NSAMPS_PP _ctrl_base + 28 #define REG_RX_CTRL_NCHANNELS _ctrl_base + 32 +#define REG_RX_CTRL_FORMAT _ctrl_base + 36 template <class T> T ceil_log2(T num){ return std::ceil(std::log(num)/std::log(T(2))); @@ -129,15 +130,26 @@ public: } void set_link_rate(const double rate){ - _link_rate = rate/sizeof(boost::uint32_t); //in samps/s + //_link_rate = rate/sizeof(boost::uint32_t); //in samps/s + _link_rate = rate/sizeof(boost::uint16_t); //in samps/s (allows for 8sc) + } + + uhd::meta_range_t get_host_rates(void){ + meta_range_t range; + for (int rate = 512; rate > 256; rate -= 4){ + range.push_back(range_t(_tick_rate/rate)); + } + for (int rate = 256; rate > 128; rate -= 2){ + range.push_back(range_t(_tick_rate/rate)); + } + for (int rate = 128; rate >= int(std::ceil(_tick_rate/_link_rate)); rate -= 1){ + range.push_back(range_t(_tick_rate/rate)); + } + return range; } double set_host_rate(const double rate){ - size_t decim_rate = uhd::clip<size_t>( - boost::math::iround(_tick_rate/rate), size_t(std::ceil(_tick_rate/_link_rate)), 512 - ); - if (decim_rate > 128) decim_rate &= ~0x1; //CIC up to 128, have to use 1 HB - if (decim_rate > 256) decim_rate &= ~0x3; //CIC up to 128, have to use 2 HB + const size_t decim_rate = boost::math::iround(_tick_rate/this->get_host_rates().clip(rate, true)); size_t decim = decim_rate; //determine which half-band filters are activated @@ -162,7 +174,7 @@ public: } double get_scaling_adjustment(void){ - return _scaling_adjustment; + return _scaling_adjustment/_fxpt_scale_adj; } double set_freq(const double freq_){ @@ -192,12 +204,28 @@ public: if (_continuous_streaming) issue_stream_command(stream_cmd_t::STREAM_MODE_START_CONTINUOUS); } + void set_format(const std::string &format, const unsigned scale){ + unsigned format_word = 0; + if (format == "sc16"){ + format_word = 0; + _fxpt_scale_adj = 32767.; + } + else if (format == "sc8"){ + format_word = (1 << 18); + _fxpt_scale_adj = 32767./scale; + } + else throw uhd::value_error("USRP RX cannot handle requested wire format: " + format); + + const unsigned scale_word = scale & 0x3ffff; //18 bits; + _iface->poke32(REG_RX_CTRL_FORMAT, format_word | scale_word); + } + private: wb_iface::sptr _iface; const size_t _dsp_base, _ctrl_base; double _tick_rate, _link_rate; bool _continuous_streaming; - double _scaling_adjustment; + double _scaling_adjustment, _fxpt_scale_adj; }; rx_dsp_core_200::sptr rx_dsp_core_200::make(wb_iface::sptr iface, const size_t dsp_base, const size_t ctrl_base, const boost::uint32_t sid, const bool lingering_packet){ diff --git a/host/lib/usrp/cores/rx_dsp_core_200.hpp b/host/lib/usrp/cores/rx_dsp_core_200.hpp index 391cc8441..89b8c1f51 100644 --- a/host/lib/usrp/cores/rx_dsp_core_200.hpp +++ b/host/lib/usrp/cores/rx_dsp_core_200.hpp @@ -48,6 +48,8 @@ public: virtual double set_host_rate(const double rate) = 0; + virtual uhd::meta_range_t get_host_rates(void) = 0; + virtual double get_scaling_adjustment(void) = 0; virtual uhd::meta_range_t get_freq_range(void) = 0; @@ -56,6 +58,7 @@ public: virtual void handle_overflow(void) = 0; + virtual void set_format(const std::string &format, const unsigned scale) = 0; }; #endif /* INCLUDED_LIBUHD_USRP_RX_DSP_CORE_200_HPP */ diff --git a/host/lib/usrp/cores/rx_frontend_core_200.cpp b/host/lib/usrp/cores/rx_frontend_core_200.cpp index 0e8220b49..d42022947 100644 --- a/host/lib/usrp/cores/rx_frontend_core_200.cpp +++ b/host/lib/usrp/cores/rx_frontend_core_200.cpp @@ -24,6 +24,9 @@ #define REG_RX_FE_OFFSET_I _base + 12 //18 bits #define REG_RX_FE_OFFSET_Q _base + 16 //18 bits +#define OFFSET_FIXED (1ul << 31) +#define OFFSET_SET (1ul << 30) + static boost::uint32_t fs_to_bits(const double num, const size_t bits){ return boost::int32_t(boost::math::round(num * (1 << (bits-1)))); } @@ -41,17 +44,32 @@ public: _iface->poke32(REG_RX_FE_SWAP_IQ, swap? 1 : 0); } - void set_offset(const std::complex<double> &off){ - _iface->poke32(REG_RX_FE_OFFSET_I, fs_to_bits(off.real(), 24)); - _iface->poke32(REG_RX_FE_OFFSET_Q, fs_to_bits(off.imag(), 24)); + void set_dc_offset_auto(const bool enb){ + this->set_dc_offset(enb? 0 : OFFSET_FIXED); + } + + std::complex<double> set_dc_offset(const std::complex<double> &off){ + static const double scaler = double(1ul << 29); + _i_dc_off = boost::math::iround(off.real()*scaler); + _q_dc_off = boost::math::iround(off.imag()*scaler); + + this->set_dc_offset(OFFSET_SET | OFFSET_FIXED); + + return std::complex<double>(_i_dc_off/scaler, _q_dc_off/scaler); + } + + void set_dc_offset(const boost::uint32_t flags){ + _iface->poke32(REG_RX_FE_OFFSET_I, flags | _i_dc_off); + _iface->poke32(REG_RX_FE_OFFSET_Q, flags | _q_dc_off); } - void set_correction(const std::complex<double> &cor){ + void set_iq_balance(const std::complex<double> &cor){ _iface->poke32(REG_RX_FE_MAG_CORRECTION, fs_to_bits(std::abs(cor), 18)); _iface->poke32(REG_RX_FE_PHASE_CORRECTION, fs_to_bits(std::atan2(cor.real(), cor.imag()), 18)); } private: + boost::int32_t _i_dc_off, _q_dc_off; wb_iface::sptr _iface; const size_t _base; }; diff --git a/host/lib/usrp/cores/rx_frontend_core_200.hpp b/host/lib/usrp/cores/rx_frontend_core_200.hpp index a950e2bb7..5755424c8 100644 --- a/host/lib/usrp/cores/rx_frontend_core_200.hpp +++ b/host/lib/usrp/cores/rx_frontend_core_200.hpp @@ -33,9 +33,11 @@ public: virtual void set_mux(const bool swap) = 0; - virtual void set_offset(const std::complex<double> &off) = 0; + virtual void set_dc_offset_auto(const bool enb) = 0; - virtual void set_correction(const std::complex<double> &cor) = 0; + virtual std::complex<double> set_dc_offset(const std::complex<double> &off) = 0; + + virtual void set_iq_balance(const std::complex<double> &cor) = 0; }; diff --git a/host/lib/usrp/cores/tx_dsp_core_200.cpp b/host/lib/usrp/cores/tx_dsp_core_200.cpp index 04e9f5da4..9d90d30cc 100644 --- a/host/lib/usrp/cores/tx_dsp_core_200.cpp +++ b/host/lib/usrp/cores/tx_dsp_core_200.cpp @@ -70,15 +70,26 @@ public: } void set_link_rate(const double rate){ - _link_rate = rate/sizeof(boost::uint32_t); //in samps/s + //_link_rate = rate/sizeof(boost::uint32_t); //in samps/s + _link_rate = rate/sizeof(boost::uint16_t); //in samps/s (allows for 8sc) + } + + uhd::meta_range_t get_host_rates(void){ + meta_range_t range; + for (int rate = 512; rate > 256; rate -= 4){ + range.push_back(range_t(_tick_rate/rate)); + } + for (int rate = 256; rate > 128; rate -= 2){ + range.push_back(range_t(_tick_rate/rate)); + } + for (int rate = 128; rate >= int(std::ceil(_tick_rate/_link_rate)); rate -= 1){ + range.push_back(range_t(_tick_rate/rate)); + } + return range; } double set_host_rate(const double rate){ - size_t interp_rate = uhd::clip<size_t>( - boost::math::iround(_tick_rate/rate), size_t(std::ceil(_tick_rate/_link_rate)), 512 - ); - if (interp_rate > 128) interp_rate &= ~0x1; //CIC up to 128, have to use 1 HB - if (interp_rate > 256) interp_rate &= ~0x3; //CIC up to 128, have to use 2 HB + const size_t interp_rate = boost::math::iround(_tick_rate/this->get_host_rates().clip(rate, true)); size_t interp = interp_rate; //determine which half-band filters are activated diff --git a/host/lib/usrp/cores/tx_dsp_core_200.hpp b/host/lib/usrp/cores/tx_dsp_core_200.hpp index 65f822558..e6be63557 100644 --- a/host/lib/usrp/cores/tx_dsp_core_200.hpp +++ b/host/lib/usrp/cores/tx_dsp_core_200.hpp @@ -40,6 +40,8 @@ public: virtual double set_host_rate(const double rate) = 0; + virtual uhd::meta_range_t get_host_rates(void) = 0; + virtual uhd::meta_range_t get_freq_range(void) = 0; virtual double set_freq(const double freq) = 0; diff --git a/host/lib/usrp/cores/tx_frontend_core_200.cpp b/host/lib/usrp/cores/tx_frontend_core_200.cpp index a7568a81e..327e8d344 100644 --- a/host/lib/usrp/cores/tx_frontend_core_200.cpp +++ b/host/lib/usrp/cores/tx_frontend_core_200.cpp @@ -50,12 +50,18 @@ public: _iface->poke32(REG_TX_FE_MUX, mode_to_mux[mode]); } - void set_dc_offset(const std::complex<double> &off){ - _iface->poke32(REG_TX_FE_DC_OFFSET_I, fs_to_bits(off.real(), 24)); - _iface->poke32(REG_TX_FE_DC_OFFSET_Q, fs_to_bits(off.imag(), 24)); + std::complex<double> set_dc_offset(const std::complex<double> &off){ + static const double scaler = double(1ul << 23); + const boost::int32_t i_dc_off = boost::math::iround(off.real()*scaler); + const boost::int32_t q_dc_off = boost::math::iround(off.imag()*scaler); + + _iface->poke32(REG_TX_FE_DC_OFFSET_I, i_dc_off); + _iface->poke32(REG_TX_FE_DC_OFFSET_Q, q_dc_off); + + return std::complex<double>(i_dc_off/scaler, q_dc_off/scaler); } - void set_correction(const std::complex<double> &cor){ + void set_iq_balance(const std::complex<double> &cor){ _iface->poke32(REG_TX_FE_MAG_CORRECTION, fs_to_bits(std::abs(cor), 18)); _iface->poke32(REG_TX_FE_PHASE_CORRECTION, fs_to_bits(std::atan2(cor.real(), cor.imag()), 18)); } diff --git a/host/lib/usrp/cores/tx_frontend_core_200.hpp b/host/lib/usrp/cores/tx_frontend_core_200.hpp index 9e4a7bc79..8ee0f3e6d 100644 --- a/host/lib/usrp/cores/tx_frontend_core_200.hpp +++ b/host/lib/usrp/cores/tx_frontend_core_200.hpp @@ -33,9 +33,9 @@ public: virtual void set_mux(const std::string &mode) = 0; - virtual void set_dc_offset(const std::complex<double> &off) = 0; + virtual std::complex<double> set_dc_offset(const std::complex<double> &off) = 0; - virtual void set_correction(const std::complex<double> &cor) = 0; + virtual void set_iq_balance(const std::complex<double> &cor) = 0; }; diff --git a/host/lib/usrp/dboard/CMakeLists.txt b/host/lib/usrp/dboard/CMakeLists.txt index c7b46e7c4..b000c7f33 100644 --- a/host/lib/usrp/dboard/CMakeLists.txt +++ b/host/lib/usrp/dboard/CMakeLists.txt @@ -23,8 +23,13 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/db_basic_and_lf.cpp ${CMAKE_CURRENT_SOURCE_DIR}/db_rfx.cpp ${CMAKE_CURRENT_SOURCE_DIR}/db_xcvr2450.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/db_sbx.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/db_sbx_common.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/db_sbx_version3.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/db_sbx_version4.cpp ${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_common.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_version2.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_version3.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_version4.cpp ${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_simple.cpp ${CMAKE_CURRENT_SOURCE_DIR}/db_dbsrx.cpp ${CMAKE_CURRENT_SOURCE_DIR}/db_unknown.cpp diff --git a/host/lib/usrp/dboard/db_basic_and_lf.cpp b/host/lib/usrp/dboard/db_basic_and_lf.cpp index 86d86dda0..53429a8c7 100644 --- a/host/lib/usrp/dboard/db_basic_and_lf.cpp +++ b/host/lib/usrp/dboard/db_basic_and_lf.cpp @@ -23,6 +23,7 @@ #include <uhd/usrp/dboard_base.hpp> #include <uhd/usrp/dboard_manager.hpp> #include <boost/assign/list_of.hpp> +#include <boost/bind.hpp> #include <boost/format.hpp> using namespace uhd; @@ -48,9 +49,6 @@ public: basic_rx(ctor_args_t args, double max_freq); ~basic_rx(void); - void rx_get(const wax::obj &key, wax::obj &val); - void rx_set(const wax::obj &key, const wax::obj &val); - private: double _max_freq; }; @@ -60,18 +58,15 @@ public: basic_tx(ctor_args_t args, double max_freq); ~basic_tx(void); - void tx_get(const wax::obj &key, wax::obj &val); - void tx_set(const wax::obj &key, const wax::obj &val); - private: double _max_freq; }; -static const uhd::dict<std::string, subdev_conn_t> sd_name_to_conn = map_list_of - ("AB", SUBDEV_CONN_COMPLEX_IQ) - ("BA", SUBDEV_CONN_COMPLEX_QI) - ("A", SUBDEV_CONN_REAL_I) - ("B", SUBDEV_CONN_REAL_Q) +static const uhd::dict<std::string, std::string> sd_name_to_conn = map_list_of + ("AB", "IQ") + ("BA", "QI") + ("A", "I") + ("B", "Q") ; /*********************************************************************** @@ -105,222 +100,95 @@ UHD_STATIC_BLOCK(reg_basic_and_lf_dboards){ **********************************************************************/ basic_rx::basic_rx(ctor_args_t args, double max_freq) : rx_dboard_base(args){ _max_freq = max_freq; + //this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); + //////////////////////////////////////////////////////////////////// + // Register properties + //////////////////////////////////////////////////////////////////// + this->get_rx_subtree()->create<std::string>("name").set( + std::string(str(boost::format("%s - %s") + % get_rx_id().to_pp_string() + % get_subdev_name() + ))); + this->get_rx_subtree()->create<int>("gains"); //phony property so this dir exists + this->get_rx_subtree()->create<double>("freq/value") + .set(double(0.0)); + this->get_rx_subtree()->create<meta_range_t>("freq/range") + .set(freq_range_t(-_max_freq, +_max_freq)); + this->get_rx_subtree()->create<std::string>("antenna/value") + .set(""); + this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options") + .set(list_of("")); + this->get_rx_subtree()->create<int>("sensors"); //phony property so this dir exists + this->get_rx_subtree()->create<std::string>("connection") + .set(sd_name_to_conn[get_subdev_name()]); + this->get_rx_subtree()->create<bool>("enabled") + .set(true); //always enabled + this->get_rx_subtree()->create<bool>("use_lo_offset") + .set(false); + this->get_rx_subtree()->create<double>("bandwidth/value") + .set(subdev_bandwidth_scalar[get_subdev_name()]*_max_freq); + this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") + .set(freq_range_t(subdev_bandwidth_scalar[get_subdev_name()]*_max_freq, subdev_bandwidth_scalar[get_subdev_name()]*_max_freq)); + + //disable RX dboard clock by default + this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, false); + //set GPIOs to output 0x0000 to decrease noise pickup this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0x0000); this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0xFFFF); this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0x0000); - //this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); } basic_rx::~basic_rx(void){ /* NOP */ } -void basic_rx::rx_get(const wax::obj &key_, 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_NAME: - val = std::string(str(boost::format("%s - %s") - % get_rx_id().to_pp_string() - % get_subdev_name() - )); - return; - - case SUBDEV_PROP_OTHERS: - val = prop_names_t(); //empty - return; - - case SUBDEV_PROP_GAIN: - val = double(0); - return; - - case SUBDEV_PROP_GAIN_RANGE: - val = gain_range_t(0, 0, 0); - return; - - case SUBDEV_PROP_GAIN_NAMES: - val = prop_names_t(); //empty - return; - - case SUBDEV_PROP_FREQ: - val = double(0); - return; - - case SUBDEV_PROP_FREQ_RANGE: - val = freq_range_t(-_max_freq, +_max_freq); - return; - - case SUBDEV_PROP_ANTENNA: - val = std::string(""); - return; - - case SUBDEV_PROP_ANTENNA_NAMES: - val = prop_names_t(1, ""); //vector of 1 empty string - return; - - case SUBDEV_PROP_SENSOR_NAMES: - val = std::vector<std::string>(); //empty - return; - - case SUBDEV_PROP_CONNECTION: - 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; - - case SUBDEV_PROP_BANDWIDTH: - val = subdev_bandwidth_scalar[get_subdev_name()]*_max_freq; - return; - - default: UHD_THROW_PROP_GET_ERROR(); - } -} - -void basic_rx::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: - UHD_ASSERT_THROW(val.as<double>() == double(0)); - return; - - case SUBDEV_PROP_ANTENNA: - if (val.as<std::string>().empty()) return; - throw uhd::value_error("no selectable antennas on this board"); - - 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_MSG(warning) << 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(); - } -} - /*********************************************************************** * Basic and LF TX dboard **********************************************************************/ basic_tx::basic_tx(ctor_args_t args, double max_freq) : tx_dboard_base(args){ _max_freq = max_freq; //this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); -} -basic_tx::~basic_tx(void){ - /* NOP */ -} - -void basic_tx::tx_get(const wax::obj &key_, 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_NAME: - val = std::string(str(boost::format("%s - %s") - % get_tx_id().to_pp_string() + //////////////////////////////////////////////////////////////////// + // Register properties + //////////////////////////////////////////////////////////////////// + this->get_tx_subtree()->create<std::string>("name").set( + std::string(str(boost::format("%s - %s") + % get_rx_id().to_pp_string() % get_subdev_name() - )); - return; - - case SUBDEV_PROP_OTHERS: - val = prop_names_t(); //empty - return; - - case SUBDEV_PROP_GAIN: - val = double(0); - return; - - case SUBDEV_PROP_GAIN_RANGE: - val = gain_range_t(0, 0, 0); - return; - - case SUBDEV_PROP_GAIN_NAMES: - val = prop_names_t(); //empty - return; - - case SUBDEV_PROP_FREQ: - val = double(0); - return; - - case SUBDEV_PROP_FREQ_RANGE: - val = freq_range_t(-_max_freq, +_max_freq); - return; - - case SUBDEV_PROP_ANTENNA: - val = std::string(""); - return; - - case SUBDEV_PROP_ANTENNA_NAMES: - val = prop_names_t(1, ""); //vector of 1 empty string - return; - - case SUBDEV_PROP_SENSOR_NAMES: - val = std::vector<std::string>(); //empty - return; - - case SUBDEV_PROP_CONNECTION: - 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; - - case SUBDEV_PROP_BANDWIDTH: - val = subdev_bandwidth_scalar[get_subdev_name()]*_max_freq; - return; + ))); + this->get_tx_subtree()->create<int>("gains"); //phony property so this dir exists + this->get_tx_subtree()->create<double>("freq/value") + .set(double(0.0)); + this->get_tx_subtree()->create<meta_range_t>("freq/range") + .set(freq_range_t(-_max_freq, +_max_freq)); + this->get_tx_subtree()->create<std::string>("antenna/value") + .set(""); + this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options") + .set(list_of("")); + this->get_tx_subtree()->create<int>("sensors"); //phony property so this dir exists + this->get_tx_subtree()->create<std::string>("connection") + .set(sd_name_to_conn[get_subdev_name()]); + this->get_tx_subtree()->create<bool>("enabled") + .set(true); //always enabled + this->get_tx_subtree()->create<bool>("use_lo_offset") + .set(false); + this->get_tx_subtree()->create<double>("bandwidth/value") + .set(subdev_bandwidth_scalar[get_subdev_name()]*_max_freq); + this->get_tx_subtree()->create<meta_range_t>("bandwidth/range") + .set(freq_range_t(subdev_bandwidth_scalar[get_subdev_name()]*_max_freq, subdev_bandwidth_scalar[get_subdev_name()]*_max_freq)); + + //disable TX dboard clock by default + this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, false); - default: UHD_THROW_PROP_GET_ERROR(); - } + //set GPIOs to output 0x0000 to decrease noise pickup + this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, 0x0000); + this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, 0xFFFF); + this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0x0000); } -void basic_tx::tx_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: - UHD_ASSERT_THROW(val.as<double>() == double(0)); - return; - - case SUBDEV_PROP_ANTENNA: - if (val.as<std::string>().empty()) return; - throw uhd::value_error("no selectable antennas on this board"); - - 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_MSG(warning) << 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(); - } +basic_tx::~basic_tx(void){ + /* NOP */ } diff --git a/host/lib/usrp/dboard/db_dbsrx.cpp b/host/lib/usrp/dboard/db_dbsrx.cpp index c65c52590..7a90467ef 100644 --- a/host/lib/usrp/dboard/db_dbsrx.cpp +++ b/host/lib/usrp/dboard/db_dbsrx.cpp @@ -46,9 +46,12 @@ using namespace boost::assign; **********************************************************************/ static const freq_range_t dbsrx_freq_range(0.8e9, 2.4e9); +//Multiplied by 2.0 for conversion to complex bandpass from lowpass +static const freq_range_t dbsrx_bandwidth_range(2.0*4.0e6, 2.0*33.0e6); + static const freq_range_t dbsrx_pfd_freq_range(0.15e6, 2.01e6); -static const prop_names_t dbsrx_antennas = list_of("J3"); +static const std::vector<std::string> dbsrx_antennas = list_of("J3"); static const uhd::dict<std::string, gain_range_t> dbsrx_gain_ranges = map_list_of ("GC1", gain_range_t(0, 56, 0.5)) @@ -63,9 +66,6 @@ public: dbsrx(ctor_args_t args); ~dbsrx(void); - void rx_get(const wax::obj &key, wax::obj &val); - void rx_set(const wax::obj &key, const wax::obj &val); - private: double _lo_freq; double _bandwidth; @@ -76,9 +76,9 @@ private: return (this->get_iface()->get_special_props().mangle_i2c_addrs)? 0x65 : 0x67; }; - void set_lo_freq(double target_freq); - void set_gain(double gain, const std::string &name); - void set_bandwidth(double bandwidth); + double set_lo_freq(double target_freq); + double set_gain(double gain, const std::string &name); + double set_bandwidth(double bandwidth); void send_reg(boost::uint8_t start_reg, boost::uint8_t stop_reg){ start_reg = boost::uint8_t(uhd::clip(int(start_reg), 0x0, 0x5)); @@ -136,10 +136,10 @@ private: } /*! - * Is the LO locked? - * \return true for locked + * Get the lock detect status of the LO. + * \return sensor for locked */ - bool get_locked(void){ + sensor_value_t get_locked(void){ read_reg(0x0, 0x0); //mask and return lock detect @@ -149,9 +149,8 @@ private: "DBSRX: locked %d" ) % locked << std::endl; - return locked; + return sensor_value_t("LO", locked, "locked", "unlocked"); } - }; /*********************************************************************** @@ -190,30 +189,58 @@ dbsrx::dbsrx(ctor_args_t args) : rx_dboard_base(args){ "Please see the daughterboard app notes" ) % this->get_rx_id().to_pp_string(); + //send initial register settings + this->send_reg(0x0, 0x5); + + //set defaults for LO, gains, and filter bandwidth + _bandwidth = 33e6; + + //////////////////////////////////////////////////////////////////// + // Register properties + //////////////////////////////////////////////////////////////////// + this->get_rx_subtree()->create<std::string>("name") + .set(get_rx_id().to_pp_string()); + this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") + .publish(boost::bind(&dbsrx::get_locked, this)); + BOOST_FOREACH(const std::string &name, dbsrx_gain_ranges.keys()){ + this->get_rx_subtree()->create<double>("gains/"+name+"/value") + .coerce(boost::bind(&dbsrx::set_gain, this, _1, name)) + .set(dbsrx_gain_ranges[name].start()); + this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range") + .set(dbsrx_gain_ranges[name]); + } + this->get_rx_subtree()->create<double>("freq/value") + .coerce(boost::bind(&dbsrx::set_lo_freq, this, _1)) + .set(dbsrx_freq_range.start()); + this->get_rx_subtree()->create<meta_range_t>("freq/range") + .set(dbsrx_freq_range); + this->get_rx_subtree()->create<std::string>("antenna/value") + .set(dbsrx_antennas.at(0)); + this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options") + .set(dbsrx_antennas); + this->get_rx_subtree()->create<std::string>("connection") + .set("IQ"); + this->get_rx_subtree()->create<bool>("enabled") + .set(true); //always enabled + this->get_rx_subtree()->create<bool>("use_lo_offset") + .set(false); + this->get_rx_subtree()->create<double>("bandwidth/value") + .coerce(boost::bind(&dbsrx::set_bandwidth, this, _1)) + .set(2.0*_bandwidth); //_bandwidth in lowpass, convert to complex bandpass + this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") + .set(dbsrx_bandwidth_range); + //enable only the clocks we need 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 + this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x1); // GPIO0 is clock when on USRP1 } else{ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x0); // All Inputs } - - //send initial register settings - this->send_reg(0x0, 0x5); - - //set defaults for LO, gains, and filter bandwidth - _bandwidth = 33e6; - set_lo_freq(dbsrx_freq_range.start()); - - BOOST_FOREACH(const std::string &name, dbsrx_gain_ranges.keys()){ - set_gain(dbsrx_gain_ranges[name].start(), name); - } - - set_bandwidth(33e6); // default bandwidth from datasheet } dbsrx::~dbsrx(void){ @@ -223,7 +250,7 @@ dbsrx::~dbsrx(void){ /*********************************************************************** * Tuning **********************************************************************/ -void dbsrx::set_lo_freq(double target_freq){ +double dbsrx::set_lo_freq(double target_freq){ target_freq = dbsrx_freq_range.clip(target_freq); double actual_freq=0.0, pfd_freq=0.0, ref_clock=0.0; @@ -398,6 +425,8 @@ void dbsrx::set_lo_freq(double target_freq){ if (update_filter_settings) set_bandwidth(_bandwidth); get_locked(); + + return _lo_freq; } /*********************************************************************** @@ -456,7 +485,7 @@ static double gain_to_gc1_rfvga_dac(double &gain){ return dac_volts; } -void dbsrx::set_gain(double gain, const std::string &name){ +double dbsrx::set_gain(double gain, const std::string &name){ assert_has(dbsrx_gain_ranges.keys(), name, "dbsrx gain name"); if (name == "GC2"){ _max2118_write_regs.gc2 = gain_to_gc2_vga_reg(gain); @@ -468,14 +497,19 @@ void dbsrx::set_gain(double gain, const std::string &name){ } else UHD_THROW_INVALID_CODE_PATH(); _gains[name] = gain; + + return gain; } /*********************************************************************** * Bandwidth Handling **********************************************************************/ -void dbsrx::set_bandwidth(double bandwidth){ +double dbsrx::set_bandwidth(double bandwidth){ + //convert complex bandpass to lowpass bandwidth + bandwidth = bandwidth/2.0; + //clip the input - bandwidth = uhd::clip<double>(bandwidth, 4e6, 33e6); + bandwidth = dbsrx_bandwidth_range.clip(bandwidth); double ref_clock = this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX); @@ -492,109 +526,7 @@ void dbsrx::set_bandwidth(double bandwidth){ ) % (_bandwidth/1e6) % int(_max2118_write_regs.m_divider) % int(_max2118_write_regs.f_dac) << std::endl; this->send_reg(0x3, 0x4); -} - -/*********************************************************************** - * RX Get and Set - **********************************************************************/ -void dbsrx::rx_get(const wax::obj &key_, 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_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, "dbsrx gain name"); - val = _gains[key.name]; - return; - - case SUBDEV_PROP_GAIN_RANGE: - assert_has(dbsrx_gain_ranges.keys(), key.name, "dbsrx gain name"); - val = dbsrx_gain_ranges[key.name]; - return; - - case SUBDEV_PROP_GAIN_NAMES: - val = prop_names_t(dbsrx_gain_ranges.keys()); - return; - - case SUBDEV_PROP_FREQ: - val = _lo_freq; - return; - - case SUBDEV_PROP_FREQ_RANGE: - val = dbsrx_freq_range; - return; - - case SUBDEV_PROP_ANTENNA: - val = dbsrx_antennas.at(0); - return; - - case SUBDEV_PROP_ANTENNA_NAMES: - val = dbsrx_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_SENSOR: - UHD_ASSERT_THROW(key.name == "lo_locked"); - val = sensor_value_t("LO", this->get_locked(), "locked", "unlocked"); - return; - - case SUBDEV_PROP_SENSOR_NAMES: - val = prop_names_t(1, "lo_locked"); - return; - - case SUBDEV_PROP_BANDWIDTH: - val = 2*_bandwidth; //_bandwidth is low-pass, we want complex double-sided - return; - - default: UHD_THROW_PROP_GET_ERROR(); - } -} - -void dbsrx::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_FREQ: - this->set_lo_freq(val.as<double>()); - return; - - case SUBDEV_PROP_ANTENNA: - assert_has(dbsrx_antennas, val.as<std::string>(), "DBSRX antenna name"); - return; - case SUBDEV_PROP_GAIN: - this->set_gain(val.as<double>(), key.name); - return; - - case SUBDEV_PROP_ENABLED: - return; //always enabled - - case SUBDEV_PROP_BANDWIDTH: - this->set_bandwidth(val.as<double>()/2.0); //complex double-sided, we want low-pass - return; - - default: UHD_THROW_PROP_SET_ERROR(); - } + //convert lowpass back to complex bandpass bandwidth + return 2.0*_bandwidth; } - diff --git a/host/lib/usrp/dboard/db_dbsrx2.cpp b/host/lib/usrp/dboard/db_dbsrx2.cpp index f19236907..954d7083d 100644 --- a/host/lib/usrp/dboard/db_dbsrx2.cpp +++ b/host/lib/usrp/dboard/db_dbsrx2.cpp @@ -42,9 +42,12 @@ using namespace boost::assign; **********************************************************************/ static const freq_range_t dbsrx2_freq_range(0.8e9, 2.4e9); +//Multiplied by 2.0 for conversion to complex bandpass from lowpass +static const freq_range_t dbsrx2_bandwidth_range(2.0*4.0e6, 2.0*40.0e6); + static const int dbsrx2_ref_divider = 4; // Hitachi HMC426 divider (U7) -static const prop_names_t dbsrx2_antennas = list_of("J3"); +static const std::vector<std::string> dbsrx2_antennas = list_of("J3"); static const uhd::dict<std::string, gain_range_t> dbsrx2_gain_ranges = map_list_of ("GC1", gain_range_t(0, 73, 0.05)) @@ -59,9 +62,6 @@ public: dbsrx2(ctor_args_t args); ~dbsrx2(void); - void rx_get(const wax::obj &key, wax::obj &val); - void rx_set(const wax::obj &key, const wax::obj &val); - private: double _lo_freq; double _bandwidth; @@ -72,9 +72,9 @@ private: return (this->get_iface()->get_special_props().mangle_i2c_addrs)? 0x60 : 0x61; } - void set_lo_freq(double target_freq); - void set_gain(double gain, const std::string &name); - void set_bandwidth(double bandwidth); + double set_lo_freq(double target_freq); + double set_gain(double gain, const std::string &name); + double set_bandwidth(double bandwidth); void send_reg(boost::uint8_t start_reg, boost::uint8_t stop_reg){ start_reg = boost::uint8_t(uhd::clip(int(start_reg), 0x0, 0xB)); @@ -146,10 +146,10 @@ private: } /*! - * Is the LO locked? - * \return true for locked + * Get the lock detect status of the LO. + * \return sensor for locked */ - bool get_locked(void){ + sensor_value_t get_locked(void){ read_reg(0xC, 0xD); //mask and return lock detect @@ -159,9 +159,8 @@ private: "DBSRX2 locked: %d" ) % locked << std::endl; - return locked; + return sensor_value_t("LO", locked, "locked", "unlocked"); } - }; /*********************************************************************** @@ -182,28 +181,53 @@ UHD_STATIC_BLOCK(reg_dbsrx2_dboard){ * Structors **********************************************************************/ dbsrx2::dbsrx2(ctor_args_t args) : rx_dboard_base(args){ - //enable only the clocks we need - 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 - this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x0); // All Inputs - //send initial register settings send_reg(0x0, 0xB); //for (boost::uint8_t addr=0; addr<=12; addr++) this->send_reg(addr, addr); - //set defaults for LO, gains - set_lo_freq(dbsrx2_freq_range.start()); + //////////////////////////////////////////////////////////////////// + // Register properties + //////////////////////////////////////////////////////////////////// + this->get_rx_subtree()->create<std::string>("name") + .set(get_rx_id().to_pp_string()); + this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") + .publish(boost::bind(&dbsrx2::get_locked, this)); BOOST_FOREACH(const std::string &name, dbsrx2_gain_ranges.keys()){ - set_gain(dbsrx2_gain_ranges[name].start(), name); + this->get_rx_subtree()->create<double>("gains/"+name+"/value") + .coerce(boost::bind(&dbsrx2::set_gain, this, _1, name)) + .set(dbsrx2_gain_ranges[name].start()); + this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range") + .set(dbsrx2_gain_ranges[name]); } + this->get_rx_subtree()->create<double>("freq/value") + .coerce(boost::bind(&dbsrx2::set_lo_freq, this, _1)) + .set(dbsrx2_freq_range.start()); + this->get_rx_subtree()->create<meta_range_t>("freq/range") + .set(dbsrx2_freq_range); + this->get_rx_subtree()->create<std::string>("antenna/value") + .set(dbsrx2_antennas.at(0)); + this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options") + .set(dbsrx2_antennas); + this->get_rx_subtree()->create<std::string>("connection") + .set("QI"); + this->get_rx_subtree()->create<bool>("enabled") + .set(true); //always enabled + this->get_rx_subtree()->create<bool>("use_lo_offset") + .set(false); + this->get_rx_subtree()->create<double>("bandwidth/value") + .coerce(boost::bind(&dbsrx2::set_bandwidth, this, _1)) + .set(2.0*40.0e6); //bandwidth in lowpass, convert to complex bandpass + this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") + .set(dbsrx2_bandwidth_range); - set_bandwidth(40e6); // default bandwidth from datasheet - get_locked(); + //enable only the clocks we need + 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 + this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x0); // All Inputs - _max2112_write_regs.bbg = boost::math::iround(dbsrx2_gain_ranges["BBG"].start()); - send_reg(0x9, 0x9); + get_locked(); } dbsrx2::~dbsrx2(void){ @@ -213,8 +237,8 @@ dbsrx2::~dbsrx2(void){ /*********************************************************************** * Tuning **********************************************************************/ -void dbsrx2::set_lo_freq(double target_freq){ - //target_freq = uhd::clip(target_freq, dbsrx2_freq_range.min, dbsrx2_freq_range.max); +double dbsrx2::set_lo_freq(double target_freq){ + //target_freq = dbsrx2_freq_range.clip(target_freq); //variables used in the calculation below int scaler = target_freq > 1125e6 ? 2 : 4; @@ -257,6 +281,7 @@ void dbsrx2::set_lo_freq(double target_freq){ //FIXME: probably unnecessary to call get_locked here //get_locked(); + return _lo_freq; } /*********************************************************************** @@ -309,7 +334,7 @@ static double gain_to_gc1_rfvga_dac(double &gain){ return dac_volts; } -void dbsrx2::set_gain(double gain, const std::string &name){ +double dbsrx2::set_gain(double gain, const std::string &name){ assert_has(dbsrx2_gain_ranges.keys(), name, "dbsrx2 gain name"); if (name == "BBG"){ _max2112_write_regs.bbg = gain_to_bbg_vga_reg(gain); @@ -321,14 +346,19 @@ void dbsrx2::set_gain(double gain, const std::string &name){ } else UHD_THROW_INVALID_CODE_PATH(); _gains[name] = gain; + + return gain; } /*********************************************************************** * Bandwidth Handling **********************************************************************/ -void dbsrx2::set_bandwidth(double bandwidth){ +double dbsrx2::set_bandwidth(double bandwidth){ + //convert complex bandpass to lowpass bandwidth + bandwidth = bandwidth/2.0; + //clip the input - bandwidth = uhd::clip<double>(bandwidth, 4e6, 40e6); + bandwidth = dbsrx2_bandwidth_range.clip(bandwidth); _max2112_write_regs.lp = int((bandwidth/1e6 - 4)/0.29 + 12); _bandwidth = double(4 + (_max2112_write_regs.lp - 12) * 0.29)*1e6; @@ -339,105 +369,7 @@ void dbsrx2::set_bandwidth(double bandwidth){ << std::endl; this->send_reg(0x8, 0x8); -} -/*********************************************************************** - * RX Get and Set - **********************************************************************/ -void dbsrx2::rx_get(const wax::obj &key_, 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_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, "dbsrx2 gain name"); - val = _gains[key.name]; - return; - - case SUBDEV_PROP_GAIN_RANGE: - assert_has(dbsrx2_gain_ranges.keys(), key.name, "dbsrx2 gain name"); - val = dbsrx2_gain_ranges[key.name]; - return; - - case SUBDEV_PROP_GAIN_NAMES: - val = prop_names_t(dbsrx2_gain_ranges.keys()); - return; - - case SUBDEV_PROP_FREQ: - val = _lo_freq; - return; - - case SUBDEV_PROP_FREQ_RANGE: - val = dbsrx2_freq_range; - return; - - case SUBDEV_PROP_ANTENNA: - val = std::string("J3"); - return; - - case SUBDEV_PROP_ANTENNA_NAMES: - val = dbsrx2_antennas; - return; - - case SUBDEV_PROP_CONNECTION: - val = SUBDEV_CONN_COMPLEX_QI; - return; - - case SUBDEV_PROP_ENABLED: - val = true; //always enabled - return; - - case SUBDEV_PROP_USE_LO_OFFSET: - val = false; - return; - - case SUBDEV_PROP_SENSOR: - UHD_ASSERT_THROW(key.name == "lo_locked"); - val = sensor_value_t("LO", this->get_locked(), "locked", "unlocked"); - return; - - case SUBDEV_PROP_SENSOR_NAMES: - val = prop_names_t(1, "lo_locked"); - return; - - case SUBDEV_PROP_BANDWIDTH: - val = _bandwidth; - return; - - default: UHD_THROW_PROP_GET_ERROR(); - } + //convert lowpass back to complex bandpass bandwidth + return 2.0*_bandwidth; } - -void dbsrx2::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_FREQ: - this->set_lo_freq(val.as<double>()); - return; - - case SUBDEV_PROP_GAIN: - this->set_gain(val.as<double>(), key.name); - return; - - case SUBDEV_PROP_ENABLED: - return; //always enabled - - case SUBDEV_PROP_BANDWIDTH: - this->set_bandwidth(val.as<double>()); - 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 14129ef72..3896534cd 100644 --- a/host/lib/usrp/dboard/db_rfx.cpp +++ b/host/lib/usrp/dboard/db_rfx.cpp @@ -45,6 +45,7 @@ #include <uhd/usrp/dboard_base.hpp> #include <uhd/usrp/dboard_manager.hpp> #include <boost/assign/list_of.hpp> +#include <boost/bind.hpp> #include <boost/format.hpp> #include <boost/math/special_functions/round.hpp> @@ -55,11 +56,9 @@ using namespace boost::assign; /*********************************************************************** * The RFX Series constants **********************************************************************/ -static const prop_names_t rfx_tx_antennas = list_of("TX/RX"); +static const std::vector<std::string> rfx_tx_antennas = list_of("TX/RX"); -static const prop_names_t rfx_rx_antennas = list_of("TX/RX")("RX2"); - -static const uhd::dict<std::string, gain_range_t> rfx_tx_gain_ranges; //empty +static const std::vector<std::string> rfx_rx_antennas = list_of("TX/RX")("RX2"); static const uhd::dict<std::string, gain_range_t> rfx_rx_gain_ranges = map_list_of ("PGA0", gain_range_t(0, 70, 0.022)) @@ -81,27 +80,17 @@ public: ); ~rfx_xcvr(void); - void rx_get(const wax::obj &key, wax::obj &val); - void rx_set(const wax::obj &key, const wax::obj &val); - - void tx_get(const wax::obj &key, wax::obj &val); - void tx_set(const wax::obj &key, const wax::obj &val); - private: const freq_range_t _freq_range; const uhd::dict<std::string, gain_range_t> _rx_gain_ranges; const uhd::dict<dboard_iface::unit_t, bool> _div2; - double _rx_lo_freq, _tx_lo_freq; std::string _rx_ant; uhd::dict<std::string, double> _rx_gains; boost::uint16_t _power_up; - void set_rx_lo_freq(double freq); - void set_tx_lo_freq(double freq); void set_rx_ant(const std::string &ant); void set_tx_ant(const std::string &ant); - void set_rx_gain(double gain, const std::string &name); - void set_tx_gain(double gain, const std::string &name); + double set_rx_gain(double gain, const std::string &name); /*! * Set the LO frequency for the particular dboard unit. @@ -114,17 +103,18 @@ private: /*! * Get the lock detect status of the LO. * \param unit which unit rx or tx - * \return true for locked + * \return sensor for locked */ - bool get_locked(dboard_iface::unit_t unit){ - return (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0; + sensor_value_t get_locked(dboard_iface::unit_t unit){ + const bool locked = (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0; + return sensor_value_t("LO", locked, "locked", "unlocked"); } /*! * Read the RSSI from the aux adc - * \return the rssi in dB + * \return the rssi sensor in dBm */ - double get_rssi(void){ + sensor_value_t get_rssi(void){ //RSSI from VAGC vs RF Power, Fig 34, pg 13 double max_power = -3.0; @@ -133,7 +123,8 @@ private: static const double rssi_dyn_range = 60; //calculate the rssi from the voltage double voltage = this->get_iface()->read_aux_adc(dboard_iface::UNIT_RX, dboard_iface::AUX_ADC_B); - return max_power - rssi_dyn_range*(voltage - min_v)/(max_v - min_v); + const double rssi = max_power - rssi_dyn_range*(voltage - min_v)/(max_v - min_v); + return sensor_value_t("RSSI", rssi, "dBm"); } }; @@ -192,6 +183,59 @@ rfx_xcvr::rfx_xcvr( ), _power_up((get_rx_id() == 0x0024 && get_tx_id() == 0x0028) ? POWER_IO : 0) { + //////////////////////////////////////////////////////////////////// + // Register RX properties + //////////////////////////////////////////////////////////////////// + this->get_rx_subtree()->create<std::string>("name").set("RFX RX"); + this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") + .publish(boost::bind(&rfx_xcvr::get_locked, this, dboard_iface::UNIT_RX)); + this->get_rx_subtree()->create<sensor_value_t>("sensors/rssi") + .publish(boost::bind(&rfx_xcvr::get_rssi, this)); + BOOST_FOREACH(const std::string &name, _rx_gain_ranges.keys()){ + this->get_rx_subtree()->create<double>("gains/"+name+"/value") + .coerce(boost::bind(&rfx_xcvr::set_rx_gain, this, _1, name)) + .set(_rx_gain_ranges[name].start()); + this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range") + .set(_rx_gain_ranges[name]); + } + this->get_rx_subtree()->create<double>("freq/value") + .coerce(boost::bind(&rfx_xcvr::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) + .set((_freq_range.start() + _freq_range.stop())/2.0); + this->get_rx_subtree()->create<meta_range_t>("freq/range").set(_freq_range); + this->get_rx_subtree()->create<std::string>("antenna/value") + .subscribe(boost::bind(&rfx_xcvr::set_rx_ant, this, _1)) + .set("RX2"); + this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options") + .set(rfx_rx_antennas); + this->get_rx_subtree()->create<std::string>("connection").set("QI"); + this->get_rx_subtree()->create<bool>("enabled").set(true); //always enabled + this->get_rx_subtree()->create<bool>("use_lo_offset").set(false); + this->get_rx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided + this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") + .set(freq_range_t(2*20.0e6, 2*20.0e6)); + + //////////////////////////////////////////////////////////////////// + // Register TX properties + //////////////////////////////////////////////////////////////////// + this->get_tx_subtree()->create<std::string>("name").set("RFX TX"); + this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") + .publish(boost::bind(&rfx_xcvr::get_locked, this, dboard_iface::UNIT_TX)); + this->get_tx_subtree()->create<int>("gains"); //phony property so this dir exists + this->get_tx_subtree()->create<double>("freq/value") + .coerce(boost::bind(&rfx_xcvr::set_lo_freq, this, dboard_iface::UNIT_TX, _1)) + .set((_freq_range.start() + _freq_range.stop())/2.0); + this->get_tx_subtree()->create<meta_range_t>("freq/range").set(_freq_range); + this->get_tx_subtree()->create<std::string>("antenna/value") + .subscribe(boost::bind(&rfx_xcvr::set_tx_ant, this, _1)).set(rfx_tx_antennas.at(0)); + this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options") + .set(rfx_tx_antennas); + this->get_tx_subtree()->create<std::string>("connection").set("IQ"); + this->get_tx_subtree()->create<bool>("enabled").set(true); //always enabled + this->get_tx_subtree()->create<bool>("use_lo_offset").set(true); + this->get_tx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided + this->get_tx_subtree()->create<meta_range_t>("bandwidth/range") + .set(freq_range_t(2*20.0e6, 2*20.0e6)); + //enable the clocks that we need this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); @@ -213,15 +257,6 @@ rfx_xcvr::rfx_xcvr( this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, _power_up | ANT_XX | MIXER_DIS); this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, _power_up | ANT_XX | MIXER_DIS); this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_RX2| MIXER_ENB); - - //set some default values - set_rx_lo_freq((_freq_range.start() + _freq_range.stop())/2.0); - set_tx_lo_freq((_freq_range.start() + _freq_range.stop())/2.0); - set_rx_ant("RX2"); - - BOOST_FOREACH(const std::string &name, _rx_gain_ranges.keys()){ - set_rx_gain(_rx_gain_ranges[name].start(), name); - } } rfx_xcvr::~rfx_xcvr(void){ @@ -267,20 +302,16 @@ static double rx_pga0_gain_to_dac_volts(double &gain, double range){ return dac_volts; } -void rfx_xcvr::set_tx_gain(double, const std::string &name){ - assert_has(rfx_tx_gain_ranges.keys(), name, "rfx tx gain name"); - UHD_THROW_INVALID_CODE_PATH(); //no gains to set -} - -void rfx_xcvr::set_rx_gain(double gain, const std::string &name){ +double rfx_xcvr::set_rx_gain(double gain, const std::string &name){ assert_has(_rx_gain_ranges.keys(), name, "rfx rx gain name"); if(name == "PGA0"){ double dac_volts = rx_pga0_gain_to_dac_volts(gain, (_rx_gain_ranges["PGA0"].stop() - _rx_gain_ranges["PGA0"].start())); - _rx_gains[name] = gain; //write the new voltage to the aux dac this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_A, dac_volts); + + return gain; } else UHD_THROW_INVALID_CODE_PATH(); } @@ -288,14 +319,6 @@ void rfx_xcvr::set_rx_gain(double gain, const std::string &name){ /*********************************************************************** * Tuning **********************************************************************/ -void rfx_xcvr::set_rx_lo_freq(double freq){ - _rx_lo_freq = set_lo_freq(dboard_iface::UNIT_RX, freq); -} - -void rfx_xcvr::set_tx_lo_freq(double freq){ - _tx_lo_freq = set_lo_freq(dboard_iface::UNIT_TX, freq); -} - double rfx_xcvr::set_lo_freq( dboard_iface::unit_t unit, double target_freq @@ -408,214 +431,3 @@ double rfx_xcvr::set_lo_freq( ) % (actual_freq/1e6) << std::endl; return actual_freq; } - -/*********************************************************************** - * RX Get and Set - **********************************************************************/ -void rfx_xcvr::rx_get(const wax::obj &key_, 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_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(_rx_gains.keys(), key.name, "rfx rx gain name"); - val = _rx_gains[key.name]; - return; - - case SUBDEV_PROP_GAIN_RANGE: - assert_has(_rx_gain_ranges.keys(), key.name, "rfx rx gain name"); - val = _rx_gain_ranges[key.name]; - return; - - case SUBDEV_PROP_GAIN_NAMES: - val = prop_names_t(_rx_gain_ranges.keys()); - return; - - case SUBDEV_PROP_FREQ: - val = _rx_lo_freq; - return; - - case SUBDEV_PROP_FREQ_RANGE: - val = _freq_range; - return; - - case SUBDEV_PROP_ANTENNA: - val = _rx_ant; - return; - - case SUBDEV_PROP_ANTENNA_NAMES: - val = rfx_rx_antennas; - return; - - case SUBDEV_PROP_CONNECTION: - val = SUBDEV_CONN_COMPLEX_QI; - return; - - case SUBDEV_PROP_ENABLED: - val = true; //always enabled - return; - - case SUBDEV_PROP_USE_LO_OFFSET: - val = false; - return; - - case SUBDEV_PROP_SENSOR: - if (key.name == "lo_locked") - val = sensor_value_t("LO", this->get_locked(dboard_iface::UNIT_RX), "locked", "unlocked"); - else if ((key.name == "rssi") and (get_rx_id() != 0x0024)) - val = sensor_value_t("RSSI", this->get_rssi(), "dBm"); - else - UHD_THROW_INVALID_CODE_PATH(); - return; - - case SUBDEV_PROP_SENSOR_NAMES:{ - prop_names_t names = list_of("lo_locked"); - if (get_rx_id() != 0x0024) names.push_back("rssi"); - val = names; - } - return; - - case SUBDEV_PROP_BANDWIDTH: - val = 2*20.0e6; //20MHz low-pass, we want complex double-sided - return; - - default: UHD_THROW_PROP_GET_ERROR(); - } -} - -void rfx_xcvr::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_FREQ: - this->set_rx_lo_freq(val.as<double>()); - return; - - case SUBDEV_PROP_GAIN: - this->set_rx_gain(val.as<double>(), key.name); - return; - - case SUBDEV_PROP_ANTENNA: - this->set_rx_ant(val.as<std::string>()); - return; - - case SUBDEV_PROP_ENABLED: - return; //always enabled - - case SUBDEV_PROP_BANDWIDTH: - UHD_MSG(warning) << "RFX: No tunable bandwidth, fixed filtered to 40MHz"; - return; - - default: UHD_THROW_PROP_SET_ERROR(); - } -} - -/*********************************************************************** - * TX Get and Set - **********************************************************************/ -void rfx_xcvr::tx_get(const wax::obj &key_, 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_NAME: - val = get_tx_id().to_pp_string(); - return; - - case SUBDEV_PROP_OTHERS: - val = prop_names_t(); //empty - return; - - case SUBDEV_PROP_GAIN: - case SUBDEV_PROP_GAIN_RANGE: - assert_has(rfx_tx_gain_ranges.keys(), key.name, "rfx tx gain name"); - //no controllable tx gains, will not get here - return; - - case SUBDEV_PROP_GAIN_NAMES: - val = prop_names_t(rfx_tx_gain_ranges.keys()); - return; - - case SUBDEV_PROP_FREQ: - val = _tx_lo_freq; - return; - - case SUBDEV_PROP_FREQ_RANGE: - val = _freq_range; - return; - - case SUBDEV_PROP_ANTENNA: - val = std::string("TX/RX"); - return; - - case SUBDEV_PROP_ANTENNA_NAMES: - val = rfx_tx_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 = true; - return; - - case SUBDEV_PROP_SENSOR: - UHD_ASSERT_THROW(key.name == "lo_locked"); - val = sensor_value_t("LO", this->get_locked(dboard_iface::UNIT_TX), "locked", "unlocked"); - return; - - case SUBDEV_PROP_SENSOR_NAMES: - val = prop_names_t(1, "lo_locked"); - return; - - case SUBDEV_PROP_BANDWIDTH: - val = 2*20.0e6; //20MHz low-pass, we want complex double-sided - return; - - default: UHD_THROW_PROP_GET_ERROR(); - } -} - -void rfx_xcvr::tx_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_FREQ: - this->set_tx_lo_freq(val.as<double>()); - return; - - case SUBDEV_PROP_GAIN: - this->set_tx_gain(val.as<double>(), key.name); - return; - - case SUBDEV_PROP_ANTENNA: - this->set_tx_ant(val.as<std::string>()); - return; - - case SUBDEV_PROP_ENABLED: - return; //always enabled - - case SUBDEV_PROP_BANDWIDTH: - UHD_MSG(warning) << "RFX: No tunable bandwidth, fixed filtered to 40MHz"; - return; - - default: UHD_THROW_PROP_SET_ERROR(); - } -} diff --git a/host/lib/usrp/dboard/db_sbx.cpp b/host/lib/usrp/dboard/db_sbx.cpp deleted file mode 100644 index 40dbd286d..000000000 --- a/host/lib/usrp/dboard/db_sbx.cpp +++ /dev/null @@ -1,785 +0,0 @@ -// -// Copyright 2011 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/>. -// - -// Common IO Pins -#define LO_LPF_EN (1 << 15) -#define ADF4350_CE (1 << 3) -#define ADF4350_PDBRF (1 << 2) -#define ADF4350_MUXOUT (1 << 1) // INPUT!!! -#define LOCKDET_MASK (1 << 0) // INPUT!!! - -// TX IO Pins -#define TRSW (1 << 14) // 0 = TX, 1 = RX -#define TX_LED_TXRX (1 << 7) // LED for TX Antenna Selection TX/RX -#define TX_LED_LD (1 << 6) // LED for TX Lock Detect -#define DIS_POWER_TX (1 << 5) // on UNIT_TX, 0 powers up TX -#define TX_ENABLE (1 << 4) // on UNIT_TX, 0 disables TX Mixer - -// RX IO Pins -#define LNASW (1 << 14) // 0 = TX/RX, 1 = RX2 -#define RX_LED_RX1RX2 (1 << 7) // LED for RX Antenna Selection RX1/RX2 -#define RX_LED_LD (1 << 6) // LED for RX Lock Detect -#define DIS_POWER_RX (1 << 5) // on UNIT_RX, 0 powers up RX -#define RX_DISABLE (1 << 4) // on UNIT_RX, 1 disables RX Mixer and Baseband - -// RX Attenuator Pins -#define RX_ATTN_SHIFT 8 // lsb of RX Attenuator Control -#define RX_ATTN_MASK (63 << RX_ATTN_SHIFT) // valid bits of RX Attenuator Control - -// TX Attenuator Pins -#define TX_ATTN_SHIFT 8 // lsb of RX Attenuator Control -#define TX_ATTN_MASK (63 << TX_ATTN_SHIFT) // valid bits of RX Attenuator Control - -// Mixer functions -#define TX_MIXER_ENB (ADF4350_PDBRF) -#define TX_MIXER_DIS 0 - -#define RX_MIXER_ENB (ADF4350_PDBRF) -#define RX_MIXER_DIS 0 - -// Pin functions -#define TX_LED_IO (TX_LED_TXRX|TX_LED_LD) // LED gpio lines, pull down for LED -#define TXIO_MASK (LO_LPF_EN|TRSW|ADF4350_CE|ADF4350_PDBRF|TX_ATTN_MASK|DIS_POWER_TX|TX_ENABLE) - -#define RX_LED_IO (RX_LED_RX1RX2|RX_LED_LD) // LED gpio lines, pull down for LED -#define RXIO_MASK (LO_LPF_EN|LNASW|ADF4350_CE|ADF4350_PDBRF|RX_ATTN_MASK|DIS_POWER_RX|RX_DISABLE) - -// Power functions -#define TX_POWER_UP (ADF4350_CE|TX_ENABLE) -#define TX_POWER_DOWN (DIS_POWER_TX) - -#define RX_POWER_UP (ADF4350_CE) -#define RX_POWER_DOWN (DIS_POWER_RX) - -// Antenna constants -#define ANT_TX TRSW //the tx line is transmitting -#define ANT_RX 0 //the tx line is receiving -#define ANT_TXRX 0 //the rx line is on txrx -#define ANT_RX2 LNASW //the rx line in on rx2 -#define ANT_XX LNASW //dont care how the antenna is set - -#include "adf4350_regs.hpp" -#include <uhd/types/dict.hpp> -#include <uhd/types/ranges.hpp> -#include <uhd/types/sensors.hpp> -#include <uhd/utils/assert_has.hpp> -#include <uhd/utils/log.hpp> -#include <uhd/utils/static.hpp> -#include <uhd/utils/algorithm.hpp> -#include <uhd/utils/msg.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/math/special_functions/round.hpp> -#include <boost/thread.hpp> - -using namespace uhd; -using namespace uhd::usrp; -using namespace boost::assign; - -/*********************************************************************** - * The SBX dboard constants - **********************************************************************/ -static const freq_range_t sbx_freq_range(400e6, 4.4e9); - -static const freq_range_t sbx_tx_lo_2dbm = list_of - (range_t(0.35e9, 0.37e9)) -; - -static const freq_range_t sbx_enable_tx_lo_filter = list_of - (range_t(0.4e9, 1.5e9)) -; - -static const freq_range_t sbx_enable_rx_lo_filter = list_of - (range_t(0.4e9, 1.5e9)) -; - -static const prop_names_t sbx_tx_antennas = list_of("TX/RX"); - -static const prop_names_t sbx_rx_antennas = list_of("TX/RX")("RX2"); - -static const uhd::dict<std::string, gain_range_t> sbx_tx_gain_ranges = map_list_of - ("PGA0", gain_range_t(0, 31.5, double(0.5))) -; - -static const uhd::dict<std::string, gain_range_t> sbx_rx_gain_ranges = map_list_of - ("PGA0", gain_range_t(0, 31.5, double(0.5))) -; - -/*********************************************************************** - * The SBX dboard - **********************************************************************/ -class sbx_xcvr : public xcvr_dboard_base{ -public: - sbx_xcvr(ctor_args_t args); - ~sbx_xcvr(void); - - void rx_get(const wax::obj &key, wax::obj &val); - void rx_set(const wax::obj &key, const wax::obj &val); - - void tx_get(const wax::obj &key, wax::obj &val); - void tx_set(const wax::obj &key, const wax::obj &val); - -private: - uhd::dict<std::string, double> _tx_gains, _rx_gains; - double _rx_lo_freq, _tx_lo_freq; - std::string _tx_ant, _rx_ant; - - void set_rx_lo_freq(double freq); - void set_tx_lo_freq(double freq); - void set_rx_ant(const std::string &ant); - void set_tx_ant(const std::string &ant); - void set_rx_gain(double gain, const std::string &name); - void set_tx_gain(double gain, const std::string &name); - - void update_atr(void); - - /*! - * Set the LO frequency for the particular dboard unit. - * \param unit which unit rx or tx - * \param target_freq the desired frequency in Hz - * \return the actual frequency in Hz - */ - double set_lo_freq(dboard_iface::unit_t unit, double target_freq); - - /*! - * Get the lock detect status of the LO. - * \param unit which unit rx or tx - * \return true for locked - */ - bool get_locked(dboard_iface::unit_t unit){ - return (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0; - } - - /*! - * Flash the LEDs - */ - void flash_leds(void) { - //Remove LED gpios from ATR control temporarily and set to outputs - this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, TXIO_MASK); - this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXIO_MASK); - this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|RX_LED_IO)); - this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); - - /* - //flash All LEDs - for (int i = 0; i < 3; i++) { - this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_IO, RX_LED_IO); - this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_IO, TX_LED_IO); - - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - - this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO); - this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO); - - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - } - */ - - this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_LD, TX_LED_IO); - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - - this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_TXRX|TX_LED_LD, TX_LED_IO); - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - - this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_LD, RX_LED_IO); - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - - this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_RX1RX2|RX_LED_LD, RX_LED_IO); - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - - this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_LD, RX_LED_IO); - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - - this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO); - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - - this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_LD, TX_LED_IO); - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - - this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO); - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - - /* - //flash All LEDs - for (int i = 0; i < 3; i++) { - this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO); - this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO); - - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - - this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_IO, RX_LED_IO); - this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_IO, TX_LED_IO); - - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - } - */ - //Put LED gpios back in ATR control and update atr - this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); - this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); - this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); - this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); - } - -}; - -/*********************************************************************** - * Register the SBX dboard (min freq, max freq, rx div2, tx div2) - **********************************************************************/ -static dboard_base::sptr make_sbx(dboard_base::ctor_args_t args){ - return dboard_base::sptr(new sbx_xcvr(args)); -} - -UHD_STATIC_BLOCK(reg_sbx_dboards){ - dboard_manager::register_dboard(0x0054, 0x0055, &make_sbx, "SBX"); -} - -/*********************************************************************** - * Structors - **********************************************************************/ -sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){ - - //enable the clocks that we need - this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); - 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_TX, (TXIO_MASK|TX_LED_IO)); - this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); - this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); - this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); - - //flash LEDs - flash_leds(); - - UHD_LOGV(often) << boost::format( - "SBX GPIO Direction: RX: 0x%08x, TX: 0x%08x" - ) % RXIO_MASK % TXIO_MASK << std::endl; - - //set some default values - set_rx_lo_freq((sbx_freq_range.start() + sbx_freq_range.stop())/2.0); - set_tx_lo_freq((sbx_freq_range.start() + sbx_freq_range.stop())/2.0); - set_rx_ant("RX2"); - - BOOST_FOREACH(const std::string &name, sbx_tx_gain_ranges.keys()){ - set_tx_gain(sbx_tx_gain_ranges[name].start(), name); - } - BOOST_FOREACH(const std::string &name, sbx_rx_gain_ranges.keys()){ - set_rx_gain(sbx_rx_gain_ranges[name].start(), name); - } -} - -sbx_xcvr::~sbx_xcvr(void){ - /* NOP */ -} - -/*********************************************************************** - * Gain Handling - **********************************************************************/ -static int rx_pga0_gain_to_iobits(double &gain){ - //clip the input - gain = sbx_rx_gain_ranges["PGA0"].clip(gain); - - //convert to attenuation and update iobits for atr - double attn = sbx_rx_gain_ranges["PGA0"].stop() - gain; - - //calculate the RX attenuation - int attn_code = int(floor(attn*2)); - int iobits = ((~attn_code) << RX_ATTN_SHIFT) & RX_ATTN_MASK; - - - UHD_LOGV(often) << boost::format( - "SBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x" - ) % attn % attn_code % (iobits & RX_ATTN_MASK) % RX_ATTN_MASK << std::endl; - - //the actual gain setting - gain = sbx_rx_gain_ranges["PGA0"].stop() - double(attn_code)/2; - - return iobits; -} - -static int tx_pga0_gain_to_iobits(double &gain){ - //clip the input - gain = sbx_tx_gain_ranges["PGA0"].clip(gain); - - //convert to attenuation and update iobits for atr - double attn = sbx_tx_gain_ranges["PGA0"].stop() - gain; - - //calculate the TX attenuation - int attn_code = int(floor(attn*2)); - int iobits = ((~attn_code) << TX_ATTN_SHIFT) & TX_ATTN_MASK; - - - UHD_LOGV(often) << boost::format( - "SBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x" - ) % attn % attn_code % (iobits & TX_ATTN_MASK) % TX_ATTN_MASK << std::endl; - - //the actual gain setting - gain = sbx_tx_gain_ranges["PGA0"].stop() - double(attn_code)/2; - - return iobits; -} - -void sbx_xcvr::set_tx_gain(double gain, const std::string &name){ - assert_has(sbx_tx_gain_ranges.keys(), name, "sbx tx gain name"); - if(name == "PGA0"){ - tx_pga0_gain_to_iobits(gain); - _tx_gains[name] = gain; - - //write the new gain to atr regs - update_atr(); - } - else UHD_THROW_INVALID_CODE_PATH(); -} - -void sbx_xcvr::set_rx_gain(double gain, const std::string &name){ - assert_has(sbx_rx_gain_ranges.keys(), name, "sbx rx gain name"); - if(name == "PGA0"){ - rx_pga0_gain_to_iobits(gain); - _rx_gains[name] = gain; - - //write the new gain to atr regs - update_atr(); - } - else UHD_THROW_INVALID_CODE_PATH(); -} - -/*********************************************************************** - * Antenna Handling - **********************************************************************/ -void sbx_xcvr::update_atr(void){ - //calculate atr pins - int rx_pga0_iobits = rx_pga0_gain_to_iobits(_rx_gains["PGA0"]); - int tx_pga0_iobits = tx_pga0_gain_to_iobits(_tx_gains["PGA0"]); - int rx_lo_lpf_en = (_rx_lo_freq == sbx_enable_rx_lo_filter.clip(_rx_lo_freq)) ? LO_LPF_EN : 0; - int tx_lo_lpf_en = (_tx_lo_freq == sbx_enable_tx_lo_filter.clip(_tx_lo_freq)) ? LO_LPF_EN : 0; - int rx_ld_led = get_locked(dboard_iface::UNIT_RX) ? 0 : RX_LED_LD; - int tx_ld_led = get_locked(dboard_iface::UNIT_TX) ? 0 : TX_LED_LD; - int rx_ant_led = _rx_ant == "TX/RX" ? RX_LED_RX1RX2 : 0; - int tx_ant_led = _rx_ant == "TX/RX" ? 0 : TX_LED_TXRX; - - //setup the tx atr (this does not change with antenna) - this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, - tx_pga0_iobits | tx_lo_lpf_en | tx_ld_led | tx_ant_led | TX_POWER_UP | ANT_XX | TX_MIXER_DIS); - this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, - tx_pga0_iobits | tx_lo_lpf_en | tx_ld_led | tx_ant_led | TX_POWER_UP | ANT_TX | TX_MIXER_ENB); - this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, - tx_pga0_iobits | tx_lo_lpf_en | tx_ld_led | tx_ant_led | TX_POWER_UP | ANT_TX | TX_MIXER_ENB); - - //setup the rx atr (this does not change with antenna) - this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, - rx_pga0_iobits | rx_lo_lpf_en | rx_ld_led | rx_ant_led | RX_POWER_UP | ANT_XX | RX_MIXER_DIS); - this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, - rx_pga0_iobits | rx_lo_lpf_en | rx_ld_led | rx_ant_led | RX_POWER_UP | ANT_RX2 | RX_MIXER_DIS); - this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, - rx_pga0_iobits | rx_lo_lpf_en | rx_ld_led | rx_ant_led | RX_POWER_UP | ANT_RX2 | RX_MIXER_ENB); - - //set the atr regs that change with antenna setting - this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY, - tx_pga0_iobits | tx_lo_lpf_en | tx_ld_led | tx_ant_led | TX_POWER_UP | TX_MIXER_DIS | - ((_rx_ant == "TX/RX")? ANT_RX : ANT_TX)); - this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, - rx_pga0_iobits | rx_lo_lpf_en | rx_ld_led | rx_ant_led | RX_POWER_UP | RX_MIXER_ENB | - ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2)); - - UHD_LOGV(often) << boost::format( - "SBX RXONLY ATR REG: 0x%08x" - ) % (rx_pga0_iobits | RX_POWER_UP | RX_MIXER_ENB | ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2)) << std::endl; -} - -void sbx_xcvr::set_rx_ant(const std::string &ant){ - //validate input - assert_has(sbx_rx_antennas, ant, "sbx rx antenna name"); - - //shadow the setting - _rx_ant = ant; - - //write the new antenna setting to atr regs - update_atr(); -} - -void sbx_xcvr::set_tx_ant(const std::string &ant){ - assert_has(sbx_tx_antennas, ant, "sbx tx antenna name"); - //only one antenna option, do nothing -} - -/*********************************************************************** - * Tuning - **********************************************************************/ -void sbx_xcvr::set_rx_lo_freq(double freq){ - _rx_lo_freq = set_lo_freq(dboard_iface::UNIT_RX, freq); -} - -void sbx_xcvr::set_tx_lo_freq(double freq){ - _tx_lo_freq = set_lo_freq(dboard_iface::UNIT_TX, freq); -} - -double sbx_xcvr::set_lo_freq( - dboard_iface::unit_t unit, - double target_freq -){ - UHD_LOGV(often) << boost::format( - "SBX tune: target frequency %f Mhz" - ) % (target_freq/1e6) << std::endl; - - //clip the input - target_freq = sbx_freq_range.clip(target_freq); - - //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) - static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of - (0,23) //adf4350_regs_t::PRESCALER_4_5 - (1,75) //adf4350_regs_t::PRESCALER_8_9 - ; - - //map rf divider select output dividers to enums - static const uhd::dict<int, adf4350_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of - (1, adf4350_regs_t::RF_DIVIDER_SELECT_DIV1) - (2, adf4350_regs_t::RF_DIVIDER_SELECT_DIV2) - (4, adf4350_regs_t::RF_DIVIDER_SELECT_DIV4) - (8, adf4350_regs_t::RF_DIVIDER_SELECT_DIV8) - (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) - ; - - double actual_freq, pfd_freq; - double ref_freq = this->get_iface()->get_clock_rate(unit); - int R=0, BS=0, N=0, FRAC=0, MOD=0; - int RFdiv = 1; - adf4350_regs_t::reference_divide_by_2_t T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; - adf4350_regs_t::reference_doubler_t D = adf4350_regs_t::REFERENCE_DOUBLER_DISABLED; - - //Reference doubler for 50% duty cycle - // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 - if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; - - //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 - double vco_freq = target_freq; - while (vco_freq < 2.2e9) { - vco_freq *= 2; - RFdiv *= 2; - } - - //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) - adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; - - /* - * The goal here is to loop though possible R dividers, - * band select clock dividers, N (int) dividers, and FRAC - * (frac) dividers. - * - * Calculate the N and F dividers for each set of values. - * The loop exists when it meets all of the constraints. - * The resulting loop values are loaded into the registers. - * - * from pg.21 - * - * f_pfd = f_ref*(1+D)/(R*(1+T)) - * f_vco = (N + (FRAC/MOD))*f_pfd - * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD - * f_rf = f_vco/RFdiv) - * f_actual = f_rf/2 - */ - for(R = 1; R <= 1023; R+=1){ - //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) - pfd_freq = ref_freq*(1+D)/(R*(1+T)); - - //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) - if (pfd_freq > 25e6) continue; - - //ignore fractional part of tuning - N = int(std::floor(vco_freq/pfd_freq)); - - //keep N > minimum int divider requirement - if (N < prescaler_to_min_int_div[prescaler]) continue; - - for(BS=1; BS <= 255; BS+=1){ - //keep the band select frequency at or below 100KHz - //constraint on band select clock - if (pfd_freq/BS > 100e3) continue; - goto done_loop; - } - } done_loop: - - //Fractional-N calculation - MOD = 4095; //max fractional accuracy - FRAC = int((vco_freq/pfd_freq - N)*MOD); - - //Reference divide-by-2 for 50% duty cycle - // if R even, move one divide by 2 to to regs.reference_divide_by_2 - if(R % 2 == 0){ - T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; - R /= 2; - } - - //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv); - - UHD_LOGV(often) - << boost::format("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%d" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % get_locked(unit)<< std::endl - << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" - ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; - - //load the register values - adf4350_regs_t regs; - - if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq))) - regs.output_power = adf4350_regs_t::OUTPUT_POWER_2DBM; - else - regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; - - regs.frac_12_bit = FRAC; - regs.int_16_bit = N; - regs.mod_12_bit = MOD; - regs.prescaler = prescaler; - regs.r_counter_10_bit = R; - regs.reference_divide_by_2 = T; - regs.reference_doubler = D; - regs.band_select_clock_div = BS; - UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); - regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; - - //write the registers - //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) - int addr; - - for(addr=5; addr>=0; addr--){ - UHD_LOGV(often) << boost::format( - "SBX SPI Reg (0x%02x): 0x%08x" - ) % addr % regs.get_reg(addr) << std::endl; - this->get_iface()->write_spi( - unit, spi_config_t::EDGE_RISE, - regs.get_reg(addr), 32 - ); - } - - //return the actual frequency - UHD_LOGV(often) << boost::format( - "SBX tune: actual frequency %f Mhz" - ) % (actual_freq/1e6) << std::endl; - return actual_freq; -} - -/*********************************************************************** - * RX Get and Set - **********************************************************************/ -void sbx_xcvr::rx_get(const wax::obj &key_, 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_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(_rx_gains.keys(), key.name, "sbx rx gain name"); - val = _rx_gains[key.name]; - return; - - case SUBDEV_PROP_GAIN_RANGE: - assert_has(sbx_rx_gain_ranges.keys(), key.name, "sbx rx gain name"); - val = sbx_rx_gain_ranges[key.name]; - return; - - case SUBDEV_PROP_GAIN_NAMES: - val = prop_names_t(sbx_rx_gain_ranges.keys()); - return; - - case SUBDEV_PROP_FREQ: - val = _rx_lo_freq; - return; - - case SUBDEV_PROP_FREQ_RANGE: - val = sbx_freq_range; - return; - - case SUBDEV_PROP_ANTENNA: - val = _rx_ant; - return; - - case SUBDEV_PROP_ANTENNA_NAMES: - val = sbx_rx_antennas; - return; - - case SUBDEV_PROP_CONNECTION: - val = SUBDEV_CONN_COMPLEX_IQ; - return; - - case SUBDEV_PROP_USE_LO_OFFSET: - val = false; - return; - - case SUBDEV_PROP_ENABLED: - val = true; //always enabled - return; - - case SUBDEV_PROP_SENSOR: - UHD_ASSERT_THROW(key.name == "lo_locked"); - val = sensor_value_t("LO", this->get_locked(dboard_iface::UNIT_RX), "locked", "unlocked"); - return; - - case SUBDEV_PROP_SENSOR_NAMES: - val = prop_names_t(1, "lo_locked"); - return; - - case SUBDEV_PROP_BANDWIDTH: - val = 2*20.0e6; //20MHz low-pass, we want complex double-sided - return; - - default: UHD_THROW_PROP_GET_ERROR(); - } -} - -void sbx_xcvr::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_FREQ: - this->set_rx_lo_freq(val.as<double>()); - return; - - case SUBDEV_PROP_GAIN: - this->set_rx_gain(val.as<double>(), key.name); - return; - - case SUBDEV_PROP_ANTENNA: - this->set_rx_ant(val.as<std::string>()); - return; - - case SUBDEV_PROP_ENABLED: - return; //always enabled - - case SUBDEV_PROP_BANDWIDTH: - UHD_MSG(warning) << "SBX: No tunable bandwidth, fixed filtered to 40MHz"; - return; - - default: UHD_THROW_PROP_SET_ERROR(); - } -} - -/*********************************************************************** - * TX Get and Set - **********************************************************************/ -void sbx_xcvr::tx_get(const wax::obj &key_, 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_NAME: - val = get_tx_id().to_pp_string(); - return; - - case SUBDEV_PROP_OTHERS: - val = prop_names_t(); //empty - return; - - case SUBDEV_PROP_GAIN: - assert_has(_tx_gains.keys(), key.name, "sbx tx gain name"); - val = _tx_gains[key.name]; - return; - - case SUBDEV_PROP_GAIN_RANGE: - assert_has(sbx_tx_gain_ranges.keys(), key.name, "sbx tx gain name"); - val = sbx_tx_gain_ranges[key.name]; - return; - - case SUBDEV_PROP_GAIN_NAMES: - val = prop_names_t(sbx_tx_gain_ranges.keys()); - return; - - case SUBDEV_PROP_FREQ: - val = _tx_lo_freq; - return; - - case SUBDEV_PROP_FREQ_RANGE: - val = sbx_freq_range; - return; - - case SUBDEV_PROP_ANTENNA: - val = std::string("TX/RX"); - return; - - case SUBDEV_PROP_ANTENNA_NAMES: - val = sbx_tx_antennas; - return; - - case SUBDEV_PROP_CONNECTION: - val = SUBDEV_CONN_COMPLEX_QI; - return; - - case SUBDEV_PROP_USE_LO_OFFSET: - val = false; - return; - - case SUBDEV_PROP_ENABLED: - val = true; //always enabled - return; - - case SUBDEV_PROP_SENSOR: - UHD_ASSERT_THROW(key.name == "lo_locked"); - val = sensor_value_t("LO", this->get_locked(dboard_iface::UNIT_TX), "locked", "unlocked"); - return; - - case SUBDEV_PROP_SENSOR_NAMES: - val = prop_names_t(1, "lo_locked"); - return; - - case SUBDEV_PROP_BANDWIDTH: - val = 2*20.0e6; //20MHz low-pass, we want complex double-sided - return; - - default: UHD_THROW_PROP_GET_ERROR(); - } -} - -void sbx_xcvr::tx_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_FREQ: - this->set_tx_lo_freq(val.as<double>()); - return; - - case SUBDEV_PROP_GAIN: - this->set_tx_gain(val.as<double>(), key.name); - return; - - case SUBDEV_PROP_ANTENNA: - this->set_tx_ant(val.as<std::string>()); - return; - - case SUBDEV_PROP_ENABLED: - return; //always enabled - - case SUBDEV_PROP_BANDWIDTH: - UHD_MSG(warning) << "SBX: No tunable bandwidth, fixed filtered to 40MHz"; - return; - - default: UHD_THROW_PROP_SET_ERROR(); - } -} diff --git a/host/lib/usrp/dboard/db_sbx_common.cpp b/host/lib/usrp/dboard/db_sbx_common.cpp new file mode 100644 index 000000000..27b930f81 --- /dev/null +++ b/host/lib/usrp/dboard/db_sbx_common.cpp @@ -0,0 +1,353 @@ +// +// Copyright 2011 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 "db_sbx_common.hpp" +#include "adf4350_regs.hpp" + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + + +/*********************************************************************** + * Register the SBX dboard (min freq, max freq, rx div2, tx div2) + **********************************************************************/ +static dboard_base::sptr make_sbx(dboard_base::ctor_args_t args){ + return dboard_base::sptr(new sbx_xcvr(args)); +} + +UHD_STATIC_BLOCK(reg_sbx_dboards){ + dboard_manager::register_dboard(0x0054, 0x0055, &make_sbx, "SBX"); + dboard_manager::register_dboard(0x0065, 0x0064, &make_sbx, "SBX v4"); +} + + +/*********************************************************************** + * Gain Handling + **********************************************************************/ +static int rx_pga0_gain_to_iobits(double &gain){ + //clip the input + gain = sbx_rx_gain_ranges["PGA0"].clip(gain); + + //convert to attenuation and update iobits for atr + double attn = sbx_rx_gain_ranges["PGA0"].stop() - gain; + + //calculate the RX attenuation + int attn_code = int(floor(attn*2)); + int iobits = ((~attn_code) << RX_ATTN_SHIFT) & RX_ATTN_MASK; + + UHD_LOGV(often) << boost::format( + "SBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x" + ) % attn % attn_code % (iobits & RX_ATTN_MASK) % RX_ATTN_MASK << std::endl; + + //the actual gain setting + gain = sbx_rx_gain_ranges["PGA0"].stop() - double(attn_code)/2; + + return iobits; +} + +static int tx_pga0_gain_to_iobits(double &gain){ + //clip the input + gain = sbx_tx_gain_ranges["PGA0"].clip(gain); + + //convert to attenuation and update iobits for atr + double attn = sbx_tx_gain_ranges["PGA0"].stop() - gain; + + //calculate the TX attenuation + int attn_code = int(floor(attn*2)); + int iobits = ((~attn_code) << TX_ATTN_SHIFT) & TX_ATTN_MASK; + + UHD_LOGV(often) << boost::format( + "SBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x" + ) % attn % attn_code % (iobits & TX_ATTN_MASK) % TX_ATTN_MASK << std::endl; + + //the actual gain setting + gain = sbx_tx_gain_ranges["PGA0"].stop() - double(attn_code)/2; + + return iobits; +} + +double sbx_xcvr::set_tx_gain(double gain, const std::string &name){ + assert_has(sbx_tx_gain_ranges.keys(), name, "sbx tx gain name"); + if(name == "PGA0"){ + tx_pga0_gain_to_iobits(gain); + _tx_gains[name] = gain; + + //write the new gain to atr regs + update_atr(); + } + else UHD_THROW_INVALID_CODE_PATH(); + return _tx_gains[name]; +} + +double sbx_xcvr::set_rx_gain(double gain, const std::string &name){ + assert_has(sbx_rx_gain_ranges.keys(), name, "sbx rx gain name"); + if(name == "PGA0"){ + rx_pga0_gain_to_iobits(gain); + _rx_gains[name] = gain; + + //write the new gain to atr regs + update_atr(); + } + else UHD_THROW_INVALID_CODE_PATH(); + return _rx_gains[name]; +} + + +/*********************************************************************** + * Structors + **********************************************************************/ +sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){ + switch(get_rx_id().to_uint16()) { + case 0x054: + db_actual = sbx_versionx_sptr(new sbx_version3(this)); + break; + case 0x065: + db_actual = sbx_versionx_sptr(new sbx_version4(this)); + break; + default: + /* We didn't recognize the version of the board... */ + UHD_THROW_INVALID_CODE_PATH(); + } + + //////////////////////////////////////////////////////////////////// + // Register RX properties + //////////////////////////////////////////////////////////////////// + this->get_rx_subtree()->create<std::string>("name").set("SBX RX"); + this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") + .publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_RX)); + BOOST_FOREACH(const std::string &name, sbx_rx_gain_ranges.keys()){ + this->get_rx_subtree()->create<double>("gains/"+name+"/value") + .coerce(boost::bind(&sbx_xcvr::set_rx_gain, this, _1, name)) + .set(sbx_rx_gain_ranges[name].start()); + this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range") + .set(sbx_rx_gain_ranges[name]); + } + this->get_rx_subtree()->create<double>("freq/value") + .coerce(boost::bind(&sbx_xcvr::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) + .set((sbx_freq_range.start() + sbx_freq_range.stop())/2.0); + this->get_rx_subtree()->create<meta_range_t>("freq/range").set(sbx_freq_range); + this->get_rx_subtree()->create<std::string>("antenna/value") + .subscribe(boost::bind(&sbx_xcvr::set_rx_ant, this, _1)) + .set("RX2"); + this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options") + .set(sbx_rx_antennas); + this->get_rx_subtree()->create<std::string>("connection").set("IQ"); + this->get_rx_subtree()->create<bool>("enabled").set(true); //always enabled + this->get_rx_subtree()->create<bool>("use_lo_offset").set(false); + this->get_rx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided + this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") + .set(freq_range_t(2*20.0e6, 2*20.0e6)); + + //////////////////////////////////////////////////////////////////// + // Register TX properties + //////////////////////////////////////////////////////////////////// + this->get_tx_subtree()->create<std::string>("name").set("SBX TX"); + this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") + .publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_TX)); + BOOST_FOREACH(const std::string &name, sbx_tx_gain_ranges.keys()){ + this->get_tx_subtree()->create<double>("gains/"+name+"/value") + .coerce(boost::bind(&sbx_xcvr::set_tx_gain, this, _1, name)) + .set(sbx_tx_gain_ranges[name].start()); + this->get_tx_subtree()->create<meta_range_t>("gains/"+name+"/range") + .set(sbx_tx_gain_ranges[name]); + } + this->get_tx_subtree()->create<double>("freq/value") + .coerce(boost::bind(&sbx_xcvr::set_lo_freq, this, dboard_iface::UNIT_TX, _1)) + .set((sbx_freq_range.start() + sbx_freq_range.stop())/2.0); + this->get_tx_subtree()->create<meta_range_t>("freq/range").set(sbx_freq_range); + this->get_tx_subtree()->create<std::string>("antenna/value") + .subscribe(boost::bind(&sbx_xcvr::set_tx_ant, this, _1)) + .set(sbx_tx_antennas.at(0)); + this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options") + .set(sbx_tx_antennas); + this->get_tx_subtree()->create<std::string>("connection").set("QI"); + this->get_tx_subtree()->create<bool>("enabled").set(true); //always enabled + this->get_tx_subtree()->create<bool>("use_lo_offset").set(false); + this->get_tx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided + this->get_tx_subtree()->create<meta_range_t>("bandwidth/range") + .set(freq_range_t(2*20.0e6, 2*20.0e6)); + + //enable the clocks that we need + this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); + 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_TX, (TXIO_MASK|TX_LED_IO)); + this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); + this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); + this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); + + //flash LEDs + flash_leds(); + + UHD_LOGV(often) << boost::format( + "SBX GPIO Direction: RX: 0x%08x, TX: 0x%08x" + ) % RXIO_MASK % TXIO_MASK << std::endl; +} + +sbx_xcvr::~sbx_xcvr(void){ + /* NOP */ +} + +/*********************************************************************** + * Antenna Handling + **********************************************************************/ +void sbx_xcvr::update_atr(void){ + //calculate atr pins + int rx_pga0_iobits = rx_pga0_gain_to_iobits(_rx_gains["PGA0"]); + int tx_pga0_iobits = tx_pga0_gain_to_iobits(_tx_gains["PGA0"]); + int rx_lo_lpf_en = (_rx_lo_freq == sbx_enable_rx_lo_filter.clip(_rx_lo_freq)) ? LO_LPF_EN : 0; + int tx_lo_lpf_en = (_tx_lo_freq == sbx_enable_tx_lo_filter.clip(_tx_lo_freq)) ? LO_LPF_EN : 0; + int rx_ld_led = get_locked(dboard_iface::UNIT_RX).to_bool() ? 0 : RX_LED_LD; + int tx_ld_led = get_locked(dboard_iface::UNIT_TX).to_bool() ? 0 : TX_LED_LD; + int rx_ant_led = _rx_ant == "TX/RX" ? RX_LED_RX1RX2 : 0; + int tx_ant_led = _rx_ant == "TX/RX" ? 0 : TX_LED_TXRX; + + //setup the tx atr (this does not change with antenna) + this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, + tx_pga0_iobits | tx_lo_lpf_en | tx_ld_led | tx_ant_led | TX_POWER_UP | ANT_XX | TX_MIXER_DIS); + this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, + tx_pga0_iobits | tx_lo_lpf_en | tx_ld_led | tx_ant_led | TX_POWER_UP | ANT_TX | TX_MIXER_ENB); + this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, + tx_pga0_iobits | tx_lo_lpf_en | tx_ld_led | tx_ant_led | TX_POWER_UP | ANT_TX | TX_MIXER_ENB); + + //setup the rx atr (this does not change with antenna) + this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, + rx_pga0_iobits | rx_lo_lpf_en | rx_ld_led | rx_ant_led | RX_POWER_UP | ANT_XX | RX_MIXER_DIS); + this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, + rx_pga0_iobits | rx_lo_lpf_en | rx_ld_led | rx_ant_led | RX_POWER_UP | ANT_RX2 | RX_MIXER_DIS); + this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, + rx_pga0_iobits | rx_lo_lpf_en | rx_ld_led | rx_ant_led | RX_POWER_UP | ANT_RX2 | RX_MIXER_ENB); + + //set the atr regs that change with antenna setting + this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY, + tx_pga0_iobits | tx_lo_lpf_en | tx_ld_led | tx_ant_led | TX_POWER_UP | TX_MIXER_DIS | + ((_rx_ant == "TX/RX")? ANT_RX : ANT_TX)); + this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, + rx_pga0_iobits | rx_lo_lpf_en | rx_ld_led | rx_ant_led | RX_POWER_UP | RX_MIXER_ENB | + ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2)); + + UHD_LOGV(often) << boost::format( + "SBX RXONLY ATR REG: 0x%08x" + ) % (rx_pga0_iobits | RX_POWER_UP | RX_MIXER_ENB | ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2)) << std::endl; +} + +void sbx_xcvr::set_rx_ant(const std::string &ant){ + //validate input + assert_has(sbx_rx_antennas, ant, "sbx rx antenna name"); + + //shadow the setting + _rx_ant = ant; + + //write the new antenna setting to atr regs + update_atr(); +} + +void sbx_xcvr::set_tx_ant(const std::string &ant){ + assert_has(sbx_tx_antennas, ant, "sbx tx antenna name"); + //only one antenna option, do nothing +} + +/*********************************************************************** + * Tuning + **********************************************************************/ +void sbx_xcvr::set_rx_lo_freq(double freq){ + _rx_lo_freq = db_actual->set_lo_freq(dboard_iface::UNIT_RX, freq); +} + +void sbx_xcvr::set_tx_lo_freq(double freq){ + _tx_lo_freq = db_actual->set_lo_freq(dboard_iface::UNIT_TX, freq); +} + +double sbx_xcvr::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { + return db_actual->set_lo_freq(unit, target_freq); +} + +sensor_value_t sbx_xcvr::get_locked(dboard_iface::unit_t unit) { + const bool locked = (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0; + return sensor_value_t("LO", locked, "locked", "unlocked"); +} + + +void sbx_xcvr::flash_leds(void) { + //Remove LED gpios from ATR control temporarily and set to outputs + this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, TXIO_MASK); + this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXIO_MASK); + this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|RX_LED_IO)); + this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); + + /* + //flash All LEDs + for (int i = 0; i < 3; i++) { + this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_IO, RX_LED_IO); + this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_IO, TX_LED_IO); + + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + + this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO); + this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO); + + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + } + */ + + this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_LD, TX_LED_IO); + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + + this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_TXRX|TX_LED_LD, TX_LED_IO); + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + + this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_LD, RX_LED_IO); + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + + this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_RX1RX2|RX_LED_LD, RX_LED_IO); + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + + this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_LD, RX_LED_IO); + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + + this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO); + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + + this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_LD, TX_LED_IO); + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + + this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO); + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + + /* + //flash All LEDs + for (int i = 0; i < 3; i++) { + this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO); + this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO); + + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + + this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_IO, RX_LED_IO); + this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_IO, TX_LED_IO); + + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + } + */ + //Put LED gpios back in ATR control and update atr + this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); + this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); + this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); + this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); +} + diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp new file mode 100644 index 000000000..c90cce456 --- /dev/null +++ b/host/lib/usrp/dboard/db_sbx_common.hpp @@ -0,0 +1,227 @@ +// +// Copyright 2011 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/>. +// + + + +// Common IO Pins +#define LO_LPF_EN (1 << 15) +#define SYNTH_CE (1 << 3) +#define SYNTH_PDBRF (1 << 2) +#define SYNTH_MUXOUT (1 << 1) // INPUT!!! +#define LOCKDET_MASK (1 << 0) // INPUT!!! + +// TX IO Pins +#define TRSW (1 << 14) // 0 = TX, 1 = RX +#define TX_LED_TXRX (1 << 7) // LED for TX Antenna Selection TX/RX +#define TX_LED_LD (1 << 6) // LED for TX Lock Detect +#define DIS_POWER_TX (1 << 5) // on UNIT_TX, 0 powers up TX +#define TX_ENABLE (1 << 4) // on UNIT_TX, 0 disables TX Mixer + +// RX IO Pins +#define LNASW (1 << 14) // 0 = TX/RX, 1 = RX2 +#define RX_LED_RX1RX2 (1 << 7) // LED for RX Antenna Selection RX1/RX2 +#define RX_LED_LD (1 << 6) // LED for RX Lock Detect +#define DIS_POWER_RX (1 << 5) // on UNIT_RX, 0 powers up RX +#define RX_DISABLE (1 << 4) // on UNIT_RX, 1 disables RX Mixer and Baseband + +// RX Attenuator Pins +#define RX_ATTN_SHIFT 8 // lsb of RX Attenuator Control +#define RX_ATTN_MASK (63 << RX_ATTN_SHIFT) // valid bits of RX Attenuator Control + +// TX Attenuator Pins +#define TX_ATTN_SHIFT 8 // lsb of RX Attenuator Control +#define TX_ATTN_MASK (63 << TX_ATTN_SHIFT) // valid bits of RX Attenuator Control + +// Mixer functions +#define TX_MIXER_ENB (SYNTH_PDBRF) +#define TX_MIXER_DIS 0 + +#define RX_MIXER_ENB (SYNTH_PDBRF) +#define RX_MIXER_DIS 0 + +// Pin functions +#define TX_LED_IO (TX_LED_TXRX|TX_LED_LD) // LED gpio lines, pull down for LED +#define TXIO_MASK (LO_LPF_EN|TRSW|SYNTH_CE|SYNTH_PDBRF|TX_ATTN_MASK|DIS_POWER_TX|TX_ENABLE) + +#define RX_LED_IO (RX_LED_RX1RX2|RX_LED_LD) // LED gpio lines, pull down for LED +#define RXIO_MASK (LO_LPF_EN|LNASW|SYNTH_CE|SYNTH_PDBRF|RX_ATTN_MASK|DIS_POWER_RX|RX_DISABLE) + +// Power functions +#define TX_POWER_UP (SYNTH_CE|TX_ENABLE) +#define TX_POWER_DOWN (DIS_POWER_TX) + +#define RX_POWER_UP (SYNTH_CE) +#define RX_POWER_DOWN (DIS_POWER_RX) + +// Antenna constants +#define ANT_TX TRSW //the tx line is transmitting +#define ANT_RX 0 //the tx line is receiving +#define ANT_TXRX 0 //the rx line is on txrx +#define ANT_RX2 LNASW //the rx line in on rx2 +#define ANT_XX LNASW //dont care how the antenna is set + + +#include <uhd/types/dict.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/sensors.hpp> +#include <uhd/utils/assert_has.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/static.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/msg.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/math/special_functions/round.hpp> +#include <boost/thread.hpp> + + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + + +/*********************************************************************** + * The SBX dboard constants + **********************************************************************/ +static const freq_range_t sbx_freq_range(400e6, 4.4e9); + +static const freq_range_t sbx_tx_lo_2dbm = list_of + (range_t(0.35e9, 0.37e9)) +; + +static const freq_range_t sbx_enable_tx_lo_filter = list_of + (range_t(0.4e9, 1.5e9)) +; + +static const freq_range_t sbx_enable_rx_lo_filter = list_of + (range_t(0.4e9, 1.5e9)) +; + +static const std::vector<std::string> sbx_tx_antennas = list_of("TX/RX"); + +static const std::vector<std::string> sbx_rx_antennas = list_of("TX/RX")("RX2"); + +static const uhd::dict<std::string, gain_range_t> sbx_tx_gain_ranges = map_list_of + ("PGA0", gain_range_t(0, 31.5, double(0.5))) +; + +static const uhd::dict<std::string, gain_range_t> sbx_rx_gain_ranges = map_list_of + ("PGA0", gain_range_t(0, 31.5, double(0.5))) +; + +/*********************************************************************** + * The SBX dboard + **********************************************************************/ +class sbx_xcvr : public xcvr_dboard_base{ +public: + sbx_xcvr(ctor_args_t args); + ~sbx_xcvr(void); + +protected: + + uhd::dict<std::string, double> _tx_gains, _rx_gains; + double _rx_lo_freq, _tx_lo_freq; + std::string _tx_ant, _rx_ant; + + void set_rx_lo_freq(double freq); + void set_tx_lo_freq(double freq); + void set_rx_ant(const std::string &ant); + void set_tx_ant(const std::string &ant); + double set_rx_gain(double gain, const std::string &name); + double set_tx_gain(double gain, const std::string &name); + + void update_atr(void); + + /*! + * Set the LO frequency for the particular dboard unit. + * \param unit which unit rx or tx + * \param target_freq the desired frequency in Hz + * \return the actual frequency in Hz + */ + virtual double set_lo_freq(dboard_iface::unit_t unit, double target_freq); + + /*! + * Get the lock detect status of the LO. + * \param unit which unit rx or tx + * \return true for locked + */ + sensor_value_t get_locked(dboard_iface::unit_t unit); + + /*! + * Flash the LEDs + */ + void flash_leds(void); + + /*! + * Version-agnostic ABC that wraps version-specific implementations of the + * WBX base daughterboard. + * + * This class is an abstract base class, and thus is impossible to + * instantiate. + */ + class sbx_versionx { + public: + sbx_versionx() {} + ~sbx_versionx(void) {} + + virtual double set_lo_freq(dboard_iface::unit_t unit, double target_freq) = 0; + }; + + /*! + * Version 3 of the SBX Daughterboard + */ + class sbx_version3 : public sbx_versionx { + public: + sbx_version3(sbx_xcvr *_self_sbx_xcvr); + ~sbx_version3(void); + + double set_lo_freq(dboard_iface::unit_t unit, double target_freq); + + /*! This is the registered instance of the wrapper class, sbx_base. */ + sbx_xcvr *self_base; + }; + + /*! + * Version 4 of the SBX Daughterboard + * + * The only difference in the fourth revision is the ADF4351 vs the ADF4350. + */ + class sbx_version4 : public sbx_versionx { + public: + sbx_version4(sbx_xcvr *_self_sbx_xcvr); + ~sbx_version4(void); + + double set_lo_freq(dboard_iface::unit_t unit, double target_freq); + + /*! This is the registered instance of the wrapper class, sbx_base. */ + sbx_xcvr *self_base; + }; + + /*! + * Handle to the version-specific implementation of the SBX. + * + * Since many of this class's functions are dependent on the version of the + * SBX board, this class will instantiate an object of the appropriate + * sbx_version* subclass, and invoke any relevant functions through that + * object. This pointer is set to the proper object at construction time. + */ + typedef boost::shared_ptr<sbx_versionx> sbx_versionx_sptr; + sbx_versionx_sptr db_actual; +}; + diff --git a/host/lib/usrp/dboard/db_sbx_version3.cpp b/host/lib/usrp/dboard/db_sbx_version3.cpp new file mode 100644 index 000000000..6e20d5882 --- /dev/null +++ b/host/lib/usrp/dboard/db_sbx_version3.cpp @@ -0,0 +1,186 @@ +// +// Copyright 2011 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 "adf4350_regs.hpp" +#include "db_sbx_common.hpp" + + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + +/*********************************************************************** + * Structors + **********************************************************************/ +sbx_xcvr::sbx_version3::sbx_version3(sbx_xcvr *_self_sbx_xcvr) { + //register the handle to our base SBX class + self_base = _self_sbx_xcvr; +} + +sbx_xcvr::sbx_version3::~sbx_version3(void){ + /* NOP */ +} + + +/*********************************************************************** + * Tuning + **********************************************************************/ +double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { + UHD_LOGV(often) << boost::format( + "SBX tune: target frequency %f Mhz" + ) % (target_freq/1e6) << std::endl; + + //clip the input + target_freq = sbx_freq_range.clip(target_freq); + + //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) + static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of + (0,23) //adf4350_regs_t::PRESCALER_4_5 + (1,75) //adf4350_regs_t::PRESCALER_8_9 + ; + + //map rf divider select output dividers to enums + static const uhd::dict<int, adf4350_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of + (1, adf4350_regs_t::RF_DIVIDER_SELECT_DIV1) + (2, adf4350_regs_t::RF_DIVIDER_SELECT_DIV2) + (4, adf4350_regs_t::RF_DIVIDER_SELECT_DIV4) + (8, adf4350_regs_t::RF_DIVIDER_SELECT_DIV8) + (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) + ; + + double actual_freq, pfd_freq; + double ref_freq = self_base->get_iface()->get_clock_rate(unit); + int R=0, BS=0, N=0, FRAC=0, MOD=0; + int RFdiv = 1; + adf4350_regs_t::reference_divide_by_2_t T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; + adf4350_regs_t::reference_doubler_t D = adf4350_regs_t::REFERENCE_DOUBLER_DISABLED; + + //Reference doubler for 50% duty cycle + // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 + if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; + + //increase RF divider until acceptable VCO frequency + //start with target_freq*2 because mixer has divide by 2 + double vco_freq = target_freq; + while (vco_freq < 2.2e9) { + vco_freq *= 2; + RFdiv *= 2; + } + + //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) + adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; + + /* + * The goal here is to loop though possible R dividers, + * band select clock dividers, N (int) dividers, and FRAC + * (frac) dividers. + * + * Calculate the N and F dividers for each set of values. + * The loop exists when it meets all of the constraints. + * The resulting loop values are loaded into the registers. + * + * from pg.21 + * + * f_pfd = f_ref*(1+D)/(R*(1+T)) + * f_vco = (N + (FRAC/MOD))*f_pfd + * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD + * f_rf = f_vco/RFdiv) + * f_actual = f_rf/2 + */ + for(R = 1; R <= 1023; R+=1){ + //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) + pfd_freq = ref_freq*(1+D)/(R*(1+T)); + + //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) + if (pfd_freq > 25e6) continue; + + //ignore fractional part of tuning + N = int(std::floor(vco_freq/pfd_freq)); + + //keep N > minimum int divider requirement + if (N < prescaler_to_min_int_div[prescaler]) continue; + + for(BS=1; BS <= 255; BS+=1){ + //keep the band select frequency at or below 100KHz + //constraint on band select clock + if (pfd_freq/BS > 100e3) continue; + goto done_loop; + } + } done_loop: + + //Fractional-N calculation + MOD = 4095; //max fractional accuracy + FRAC = int((vco_freq/pfd_freq - N)*MOD); + + //Reference divide-by-2 for 50% duty cycle + // if R even, move one divide by 2 to to regs.reference_divide_by_2 + if(R % 2 == 0){ + T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; + R /= 2; + } + + //actual frequency calculation + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv); + + UHD_LOGV(often) + << boost::format("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl + << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" + ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; + + //load the register values + adf4350_regs_t regs; + + if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq))) + regs.output_power = adf4350_regs_t::OUTPUT_POWER_2DBM; + else + regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; + + regs.frac_12_bit = FRAC; + regs.int_16_bit = N; + regs.mod_12_bit = MOD; + regs.prescaler = prescaler; + regs.r_counter_10_bit = R; + regs.reference_divide_by_2 = T; + regs.reference_doubler = D; + regs.band_select_clock_div = BS; + UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); + regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; + + //write the registers + //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) + int addr; + + for(addr=5; addr>=0; addr--){ + UHD_LOGV(often) << boost::format( + "SBX SPI Reg (0x%02x): 0x%08x" + ) % addr % regs.get_reg(addr) << std::endl; + self_base->get_iface()->write_spi( + unit, spi_config_t::EDGE_RISE, + regs.get_reg(addr), 32 + ); + } + + //return the actual frequency + UHD_LOGV(often) << boost::format( + "SBX tune: actual frequency %f Mhz" + ) % (actual_freq/1e6) << std::endl; + return actual_freq; +} + diff --git a/host/lib/usrp/dboard/db_sbx_version4.cpp b/host/lib/usrp/dboard/db_sbx_version4.cpp new file mode 100644 index 000000000..d22e83b51 --- /dev/null +++ b/host/lib/usrp/dboard/db_sbx_version4.cpp @@ -0,0 +1,189 @@ +// +// Copyright 2011 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 "adf4351_regs.hpp" +#include "db_sbx_common.hpp" + + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + +/*********************************************************************** + * Structors + **********************************************************************/ +sbx_xcvr::sbx_version4::sbx_version4(sbx_xcvr *_self_sbx_xcvr) { + //register the handle to our base SBX class + self_base = _self_sbx_xcvr; +} + + +sbx_xcvr::sbx_version4::~sbx_version4(void){ + /* NOP */ +} + + +/*********************************************************************** + * Tuning + **********************************************************************/ +double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { + UHD_LOGV(often) << boost::format( + "SBX tune: target frequency %f Mhz" + ) % (target_freq/1e6) << std::endl; + + //clip the input + target_freq = sbx_freq_range.clip(target_freq); + + //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) + static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of + (0,23) //adf4351_regs_t::PRESCALER_4_5 + (1,75) //adf4351_regs_t::PRESCALER_8_9 + ; + + //map rf divider select output dividers to enums + static const uhd::dict<int, adf4351_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of + (1, adf4351_regs_t::RF_DIVIDER_SELECT_DIV1) + (2, adf4351_regs_t::RF_DIVIDER_SELECT_DIV2) + (4, adf4351_regs_t::RF_DIVIDER_SELECT_DIV4) + (8, adf4351_regs_t::RF_DIVIDER_SELECT_DIV8) + (16, adf4351_regs_t::RF_DIVIDER_SELECT_DIV16) + (32, adf4351_regs_t::RF_DIVIDER_SELECT_DIV16) + (64, adf4351_regs_t::RF_DIVIDER_SELECT_DIV16) + ; + + double actual_freq, pfd_freq; + double ref_freq = self_base->get_iface()->get_clock_rate(unit); + int R=0, BS=0, N=0, FRAC=0, MOD=0; + int RFdiv = 1; + adf4351_regs_t::reference_divide_by_2_t T = adf4351_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; + adf4351_regs_t::reference_doubler_t D = adf4351_regs_t::REFERENCE_DOUBLER_DISABLED; + + //Reference doubler for 50% duty cycle + // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 + if(ref_freq <= 12.5e6) D = adf4351_regs_t::REFERENCE_DOUBLER_ENABLED; + + //increase RF divider until acceptable VCO frequency + //start with target_freq*2 because mixer has divide by 2 + double vco_freq = target_freq; + while (vco_freq < 2.2e9) { + vco_freq *= 2; + RFdiv *= 2; + } + + //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) + adf4351_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; + + /* + * The goal here is to loop though possible R dividers, + * band select clock dividers, N (int) dividers, and FRAC + * (frac) dividers. + * + * Calculate the N and F dividers for each set of values. + * The loop exists when it meets all of the constraints. + * The resulting loop values are loaded into the registers. + * + * from pg.21 + * + * f_pfd = f_ref*(1+D)/(R*(1+T)) + * f_vco = (N + (FRAC/MOD))*f_pfd + * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD + * f_rf = f_vco/RFdiv) + * f_actual = f_rf/2 + */ + for(R = 1; R <= 1023; R+=1){ + //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) + pfd_freq = ref_freq*(1+D)/(R*(1+T)); + + //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) + if (pfd_freq > 25e6) continue; + + //ignore fractional part of tuning + N = int(std::floor(vco_freq/pfd_freq)); + + //keep N > minimum int divider requirement + if (N < prescaler_to_min_int_div[prescaler]) continue; + + for(BS=1; BS <= 255; BS+=1){ + //keep the band select frequency at or below 100KHz + //constraint on band select clock + if (pfd_freq/BS > 100e3) continue; + goto done_loop; + } + } done_loop: + + //Fractional-N calculation + MOD = 4095; //max fractional accuracy + FRAC = int((vco_freq/pfd_freq - N)*MOD); + + //Reference divide-by-2 for 50% duty cycle + // if R even, move one divide by 2 to to regs.reference_divide_by_2 + if(R % 2 == 0){ + T = adf4351_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; + R /= 2; + } + + //actual frequency calculation + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv); + + UHD_LOGV(often) + << boost::format("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl + << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" + ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; + + //load the register values + adf4351_regs_t regs; + + if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq))) + regs.output_power = adf4351_regs_t::OUTPUT_POWER_2DBM; + else + regs.output_power = adf4351_regs_t::OUTPUT_POWER_5DBM; + + regs.frac_12_bit = FRAC; + regs.int_16_bit = N; + regs.mod_12_bit = MOD; + regs.prescaler = prescaler; + regs.r_counter_10_bit = R; + regs.reference_divide_by_2 = T; + regs.reference_doubler = D; + regs.band_select_clock_div = BS; + UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); + regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; + + //write the registers + //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) + int addr; + + for(addr=5; addr>=0; addr--){ + UHD_LOGV(often) << boost::format( + "SBX SPI Reg (0x%02x): 0x%08x" + ) % addr % regs.get_reg(addr) << std::endl; + self_base->get_iface()->write_spi( + unit, spi_config_t::EDGE_RISE, + regs.get_reg(addr), 32 + ); + } + + //return the actual frequency + UHD_LOGV(often) << boost::format( + "SBX tune: actual frequency %f Mhz" + ) % (actual_freq/1e6) << std::endl; + return actual_freq; +} + diff --git a/host/lib/usrp/dboard/db_tvrx.cpp b/host/lib/usrp/dboard/db_tvrx.cpp index 907d798dd..dfa617e77 100644 --- a/host/lib/usrp/dboard/db_tvrx.cpp +++ b/host/lib/usrp/dboard/db_tvrx.cpp @@ -57,7 +57,7 @@ using namespace boost::assign; **********************************************************************/ static const freq_range_t tvrx_freq_range(50e6, 860e6); -static const prop_names_t tvrx_antennas = list_of("RX"); +static const std::vector<std::string> 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)) @@ -136,9 +136,6 @@ 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, double> _gains; double _lo_freq; @@ -147,8 +144,8 @@ private: return (this->get_iface()->get_special_props().mangle_i2c_addrs)? 0x61 : 0x60; //ok really? we could rename that call }; - void set_gain(double gain, const std::string &name); - void set_freq(double freq); + double set_gain(double gain, const std::string &name); + double set_freq(double freq); void update_regs(void){ byte_vector_t regs_vector(4); @@ -185,6 +182,39 @@ UHD_STATIC_BLOCK(reg_tvrx_dboard){ * Structors **********************************************************************/ tvrx::tvrx(ctor_args_t args) : rx_dboard_base(args){ + //////////////////////////////////////////////////////////////////// + // Register properties + //////////////////////////////////////////////////////////////////// + this->get_rx_subtree()->create<std::string>("name") + .set(get_rx_id().to_pp_string()); + BOOST_FOREACH(const std::string &name, get_tvrx_gain_ranges().keys()){ + this->get_rx_subtree()->create<double>("gains/"+name+"/value") + .coerce(boost::bind(&tvrx::set_gain, this, _1, name)) + .set(get_tvrx_gain_ranges()[name].start()); + this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range") + .set(get_tvrx_gain_ranges()[name]); + } + this->get_rx_subtree()->create<double>("freq/value") + .coerce(boost::bind(&tvrx::set_freq, this, _1)) + .set(tvrx_freq_range.start()); + this->get_rx_subtree()->create<meta_range_t>("freq/range") + .set(tvrx_freq_range); + this->get_rx_subtree()->create<std::string>("antenna/value") + .set(tvrx_antennas.at(0)); + this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options") + .set(tvrx_antennas); + this->get_rx_subtree()->create<std::string>("connection") + .set("I"); + this->get_rx_subtree()->create<bool>("enabled") + .set(true); //always enabled + this->get_rx_subtree()->create<bool>("use_lo_offset") + .set(false); + this->get_rx_subtree()->create<double>("bandwidth/value") + .set(6.0e6); + this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") + .set(freq_range_t(6.0e6, 6.0e6)); + + //enable only the clocks we need this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); //set the gpio directions and atr controls (identically) @@ -317,7 +347,7 @@ static double if_gain_to_voltage(double gain){ return dac_volts; } -void tvrx::set_gain(double gain, const std::string &name){ +double tvrx::set_gain(double 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)); @@ -327,6 +357,8 @@ void tvrx::set_gain(double gain, const std::string &name){ } else UHD_THROW_INVALID_CODE_PATH(); _gains[name] = gain; + + return gain; } /*! @@ -334,7 +366,7 @@ void tvrx::set_gain(double gain, const std::string &name){ * \param freq the requested frequency */ -void tvrx::set_freq(double freq) { +double tvrx::set_freq(double freq) { freq = tvrx_freq_range.clip(freq); std::string prev_band = get_band(_lo_freq - tvrx_if_freq); std::string new_band = get_band(freq); @@ -367,131 +399,6 @@ void tvrx::set_freq(double freq) { UHD_LOGV(often) << 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 = freq - fs; - } 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); - UHD_LOGV(often) - << "Getting TVRX freq..." << std::endl - << "\tCodec rate: " << codec_rate << std::endl - << "\tLO freq: " << _lo_freq << std::endl - << "\tIF freq: " << tvrx_if_freq << std::endl - << "\tAlias freq: " << get_alias(tvrx_if_freq, codec_rate) << std::endl - << "\tCalculated freq: " << val.as<double>() << std::endl; - 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_REAL_I; - return; - - case SUBDEV_PROP_ENABLED: - val = true; //always enabled - return; - - case SUBDEV_PROP_USE_LO_OFFSET: - val = false; - return; - - case SUBDEV_PROP_BANDWIDTH: - val = 6.0e6; - return; - - case SUBDEV_PROP_SENSOR_NAMES: - val = std::vector<std::string>(); //empty - 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<double>(), 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_MSG(warning) << "TVRX: No tunable bandwidth, fixed filtered to 6MHz"; - return; - - default: UHD_THROW_PROP_SET_ERROR(); - } + return _lo_freq; } - diff --git a/host/lib/usrp/dboard/db_tvrx2.cpp b/host/lib/usrp/dboard/db_tvrx2.cpp index 23f203b8c..628221527 100644 --- a/host/lib/usrp/dboard/db_tvrx2.cpp +++ b/host/lib/usrp/dboard/db_tvrx2.cpp @@ -704,14 +704,22 @@ static const std::vector<tvrx2_tda18272_freq_map_t> tvrx2_tda18272_freq_map = li static const freq_range_t tvrx2_freq_range(42e6, 870e6); +static const freq_range_t tvrx2_bandwidth_range = list_of + (range_t(1.7e6)) + (range_t(6.0e6)) + (range_t(7.0e6)) + (range_t(8.0e6)) + (range_t(10.0e6)) +; + static const uhd::dict<std::string, std::string> tvrx2_sd_name_to_antennas = map_list_of ("RX1", "J100") ("RX2", "J140") ; -static const uhd::dict<std::string, subdev_conn_t> tvrx2_sd_name_to_conn = map_list_of - ("RX1", SUBDEV_CONN_REAL_Q) - ("RX2", SUBDEV_CONN_REAL_I) +static const uhd::dict<std::string, std::string> tvrx2_sd_name_to_conn = map_list_of + ("RX1", "Q") + ("RX2", "I") ; static const uhd::dict<std::string, boost::uint8_t> tvrx2_sd_name_to_i2c_addr = map_list_of @@ -745,9 +753,6 @@ public: tvrx2(ctor_args_t args); ~tvrx2(void); - void rx_get(const wax::obj &key, wax::obj &val); - void rx_set(const wax::obj &key, const wax::obj &val); - private: double _freq_scalar; double _lo_freq; @@ -760,12 +765,11 @@ private: bool _enabled; - void set_enabled(void); - void set_disabled(void); + bool set_enabled(bool); - void set_lo_freq(double target_freq); - void set_gain(double gain, const std::string &name); - void set_bandwidth(double bandwidth); + double set_lo_freq(double target_freq); + double set_gain(double gain, const std::string &name); + double set_bandwidth(double bandwidth); void set_scaled_rf_freq(double rf_freq); double get_scaled_rf_freq(void); @@ -825,10 +829,10 @@ private: } /*! - * Is the LO locked? - * \return true for locked + * Get the lock detect status of the LO. + * \return sensor for locked */ - bool get_locked(void){ + sensor_value_t get_locked(void){ read_reg(0x5, 0x5); //return lock detect @@ -838,14 +842,15 @@ private: "TVRX2 (%s): locked %d" ) % (get_subdev_name()) % locked << std::endl; - return locked; + return sensor_value_t("LO", locked, "locked", "unlocked"); } /*! * Read the RSSI from the registers - * \return the rssi in dB(m?) FIXME + * Read the RSSI from the aux adc + * \return the rssi sensor in dB(m?) FIXME */ - double get_rssi(void){ + sensor_value_t get_rssi(void){ //Launch RSSI calculation with MSM statemachine _tda18272hnm_regs.set_reg(0x19, 0x80); //set MSM_byte_1 for rssi calculation _tda18272hnm_regs.set_reg(0x1A, 0x01); //set MSM_byte_2 for launching rssi calculation @@ -859,14 +864,16 @@ private: //calculate the rssi from the voltage double rssi_dBuV = 40.0 + double(((110.0 - 40.0)/128.0) * _tda18272hnm_regs.get_reg(0x7)); - return rssi_dBuV - 107.0; //convert to dBm in 50ohm environment ( -108.8 if 75ohm ) FIXME + double rssi = rssi_dBuV - 107.0; //convert to dBm in 50ohm environment ( -108.8 if 75ohm ) FIXME + + return sensor_value_t("RSSI", rssi, "dBm"); } /*! * Read the Temperature from the registers * \return the temp in degC */ - double get_temp(void){ + sensor_value_t get_temp(void){ //Enable Temperature reading _tda18272hnm_regs.tm_on = tda18272hnm_regs_t::TM_ON_SENSOR_ON; send_reg(0x4, 0x4); @@ -882,7 +889,7 @@ private: _tda18272hnm_regs.tm_on = tda18272hnm_regs_t::TM_ON_SENSOR_OFF; send_reg(0x4, 0x4); - return (double(_tda18272hnm_regs.tm_d)); + return sensor_value_t("TEMP", double(_tda18272hnm_regs.tm_d), "degC"); } }; @@ -930,24 +937,64 @@ tvrx2::tvrx2(ctor_args_t args) : rx_dboard_base(args){ ( 7, tvrx2_tda18272_rfcal_coeffs_t(10) ) ; - //set the gpio directions and atr controls (identically) - this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0); // All unused in atr - this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, OUTPUT_MASK); // Set outputs + //set defaults for LO, gains, and filter bandwidth + _bandwidth = 10e6; + + _if_freq = 12.5e6; - double ref_clock=0.0; + _enabled = false; - //configure ref_clock + //send initial register settings + //this->read_reg(0x0, 0x43); + //this->send_reg(0x0, 0x43); - /* - std::vector<double> clock_rates = this->get_iface()->get_clock_rates(dboard_iface::UNIT_RX); - BOOST_FOREACH(ref_clock, uhd::sorted(clock_rates)){ - if (ref_clock < 16.0e6) continue; - if (ref_clock >= 16.0e6) break; + //send magic xtal_cal_dac setting + send_reg(0x65, 0x65); + + //////////////////////////////////////////////////////////////////// + // Register properties + //////////////////////////////////////////////////////////////////// + this->get_rx_subtree()->create<std::string>("name") + .set(get_rx_id().to_pp_string()); + this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") + .publish(boost::bind(&tvrx2::get_locked, this)); + this->get_rx_subtree()->create<sensor_value_t>("sensors/rssi") + .publish(boost::bind(&tvrx2::get_rssi, this)); + this->get_rx_subtree()->create<sensor_value_t>("sensors/temperature") + .publish(boost::bind(&tvrx2::get_temp, this)); + BOOST_FOREACH(const std::string &name, tvrx2_gain_ranges.keys()){ + this->get_rx_subtree()->create<double>("gains/"+name+"/value") + .coerce(boost::bind(&tvrx2::set_gain, this, _1, name)); + this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range") + .set(tvrx2_gain_ranges[name]); } - this->get_iface()->set_clock_rate(dboard_iface::UNIT_RX, ref_clock); - */ + this->get_rx_subtree()->create<double>("freq/value") + .coerce(boost::bind(&tvrx2::set_lo_freq, this, _1)); + this->get_rx_subtree()->create<meta_range_t>("freq/range") + .set(tvrx2_freq_range); + this->get_rx_subtree()->create<std::string>("antenna/value") + .set(tvrx2_sd_name_to_antennas[get_subdev_name()]); + this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options") + .set(list_of(tvrx2_sd_name_to_antennas[get_subdev_name()])); + this->get_rx_subtree()->create<std::string>("connection") + .set(tvrx2_sd_name_to_conn[get_subdev_name()]); + this->get_rx_subtree()->create<bool>("enabled") + .coerce(boost::bind(&tvrx2::set_enabled, this, _1)) + .set(_enabled); + this->get_rx_subtree()->create<bool>("use_lo_offset") + .set(false); + this->get_rx_subtree()->create<double>("bandwidth/value") + .coerce(boost::bind(&tvrx2::set_bandwidth, this, _1)) + .set(_bandwidth); + this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") + .set(tvrx2_bandwidth_range); - ref_clock = this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX); + //set the gpio directions and atr controls (identically) + this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0); // All unused in atr + this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, OUTPUT_MASK); // Set outputs + + //configure ref_clock + double ref_clock = this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX); if (ref_clock == 64.0e6) { this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, REFCLOCK_DIV4); @@ -978,22 +1025,6 @@ tvrx2::tvrx2(ctor_args_t args) : rx_dboard_base(args){ "TVRX2 (%s): Refclock %f Hz, scalar = %f" ) % (get_subdev_name()) % (this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX)) % _freq_scalar << std::endl; - //set defaults for LO, gains, and filter bandwidth - _bandwidth = 10e6; - - _if_freq = 12.5e6; - - _lo_freq = tvrx2_freq_range.start(); - - _enabled = false; - - //send initial register settings - //this->read_reg(0x0, 0x43); - //this->send_reg(0x0, 0x43); - - //send magic xtal_cal_dac setting - send_reg(0x65, 0x65); - _tda18272hnm_regs.irq_polarity = tda18272hnm_regs_t::IRQ_POLARITY_RAISED_VCC; _tda18272hnm_regs.irq_clear = tda18272hnm_regs_t::IRQ_CLEAR_TRUE; send_reg(0x37, 0x37); @@ -1013,39 +1044,47 @@ tvrx2::tvrx2(ctor_args_t args) : rx_dboard_base(args){ transition_0(); } -void tvrx2::set_enabled(void){ - //setup tuner parameters - transition_1(); +bool tvrx2::set_enabled(bool enable){ + if (enable == _enabled) return _enabled; - transition_2(int(tvrx2_freq_range.start())); + if (enable and not _enabled){ + //setup tuner parameters + transition_1(); - test_rf_filter_robustness(); + transition_2(int(tvrx2_freq_range.start())); - BOOST_FOREACH(const std::string &name, tvrx2_gain_ranges.keys()){ - set_gain(tvrx2_gain_ranges[name].start(), name); - } + test_rf_filter_robustness(); + + BOOST_FOREACH(const std::string &name, tvrx2_gain_ranges.keys()){ + this->get_rx_subtree()->access<double>("gains/"+name+"/value") + .set(tvrx2_gain_ranges[name].start()); + } + + this->get_rx_subtree()->access<double>("bandwidth/value") + .set(_bandwidth); // default bandwidth from datasheet + + //transition_2 equivalent + this->get_rx_subtree()->access<double>("freq/value") + .set(tvrx2_freq_range.start()); - set_bandwidth(_bandwidth); // default bandwidth from datasheet + //enter standby mode + transition_3(); + _enabled = true; - //transition_2 equivalent - set_lo_freq(tvrx2_freq_range.start()); + } else { + //enter standby mode + transition_3(); + _enabled = false; + } - //enter standby mode - transition_3(); - _enabled = true; + return _enabled; } tvrx2::~tvrx2(void){ UHD_LOGV(often) << boost::format( "TVRX2 (%s): Called Destructor" ) % (get_subdev_name()) << std::endl; - UHD_SAFE_CALL(if (_enabled) set_disabled();) -} - -void tvrx2::set_disabled(void){ - //enter standby mode - transition_3(); - _enabled = false; + UHD_SAFE_CALL(if (_enabled) set_enabled(false);) } @@ -1682,7 +1721,7 @@ void tvrx2::wait_irq(void){ /*********************************************************************** * Tuning **********************************************************************/ -void tvrx2::set_lo_freq(double target_freq){ +double tvrx2::set_lo_freq(double target_freq){ //target_freq = std::clip(target_freq, tvrx2_freq_range.min, tvrx2_freq_range.max); read_reg(0x6, 0x6); @@ -1711,7 +1750,9 @@ void tvrx2::set_lo_freq(double target_freq){ UHD_LOGV(often) << boost::format( "\nTVRX2 (%s): RSSI = %f dBm\n" - ) % (get_subdev_name()) % (get_rssi()) << std::endl; + ) % (get_subdev_name()) % (get_rssi().to_real()) << std::endl; + + return _lo_freq; } /*********************************************************************** @@ -1741,7 +1782,7 @@ static double gain_to_if_gain_dac(double &gain){ return dac_volts; } -void tvrx2::set_gain(double gain, const std::string &name){ +double tvrx2::set_gain(double gain, const std::string &name){ assert_has(tvrx2_gain_ranges.keys(), name, "tvrx2 gain name"); if (name == "IF"){ @@ -1752,6 +1793,8 @@ void tvrx2::set_gain(double gain, const std::string &name){ //shadow gain setting _gains[name] = gain; + + return gain; } /*********************************************************************** @@ -1780,7 +1823,10 @@ static tda18272hnm_regs_t::lp_fc_t bandwidth_to_lp_fc_reg(double &bandwidth){ UHD_THROW_INVALID_CODE_PATH(); } -void tvrx2::set_bandwidth(double bandwidth){ +double tvrx2::set_bandwidth(double bandwidth){ + //clip the input + bandwidth = tvrx2_bandwidth_range.clip(bandwidth); + //compute low pass cutoff frequency setting _tda18272hnm_regs.lp_fc = bandwidth_to_lp_fc_reg(bandwidth); @@ -1793,119 +1839,6 @@ void tvrx2::set_bandwidth(double bandwidth){ UHD_LOGV(often) << boost::format( "TVRX2 (%s) Bandwidth (lp_fc): %f Hz, reg: %d" ) % (get_subdev_name()) % _bandwidth % (int(_tda18272hnm_regs.lp_fc)) << std::endl; -} - -/*********************************************************************** - * RX Get and Set - **********************************************************************/ -void tvrx2::rx_get(const wax::obj &key_, 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_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, "tvrx2 gain name"); - val = _gains[key.name]; - return; - - case SUBDEV_PROP_GAIN_RANGE: - assert_has(tvrx2_gain_ranges.keys(), key.name, "tvrx2 gain name"); - val = tvrx2_gain_ranges[key.name]; - return; - - case SUBDEV_PROP_GAIN_NAMES: - val = prop_names_t(tvrx2_gain_ranges.keys()); - return; - - case SUBDEV_PROP_FREQ: - val = _lo_freq; - return; - - case SUBDEV_PROP_FREQ_RANGE: - val = tvrx2_freq_range; - return; - - case SUBDEV_PROP_ANTENNA: - val = tvrx2_sd_name_to_antennas[get_subdev_name()]; - return; - - case SUBDEV_PROP_ANTENNA_NAMES: - val = prop_names_t(1, tvrx2_sd_name_to_antennas[get_subdev_name()]); - return; - - case SUBDEV_PROP_CONNECTION: - val = tvrx2_sd_name_to_conn[get_subdev_name()]; - return; - - case SUBDEV_PROP_ENABLED: - val = _enabled; - return; - - case SUBDEV_PROP_USE_LO_OFFSET: - val = false; - return; - - case SUBDEV_PROP_SENSOR: - if (key.name == "lo_locked") - val = sensor_value_t("LO", this->get_locked(), "locked", "unlocked"); - else if (key.name == "rssi") - val = sensor_value_t("RSSI", this->get_rssi(), "dBm"); - else if (key.name == "temperature") - val = sensor_value_t("TEMP", this->get_temp(), "degC"); - else - UHD_THROW_INVALID_CODE_PATH(); - return; - - case SUBDEV_PROP_SENSOR_NAMES:{ - prop_names_t names = list_of("lo_locked")("rssi")("temperature"); - val = names; - } - return; - case SUBDEV_PROP_BANDWIDTH: - val = _bandwidth; - return; - - default: UHD_THROW_PROP_GET_ERROR(); - } + return _bandwidth; } - -void tvrx2::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_FREQ: - this->set_lo_freq(val.as<double>()); - return; - - case SUBDEV_PROP_GAIN: - this->set_gain( val.as<double>(), key.name); - return; - - case SUBDEV_PROP_ANTENNA: - return; - - case SUBDEV_PROP_ENABLED: - if ((val.as<bool>())) this->set_enabled(); - else if (not (val.as<bool>())) this->set_disabled(); - - return; - - case SUBDEV_PROP_BANDWIDTH: - this->set_bandwidth(val.as<double>()); - 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 3a11f1e5b..2ed50cd91 100644 --- a/host/lib/usrp/dboard/db_unknown.cpp +++ b/host/lib/usrp/dboard/db_unknown.cpp @@ -64,17 +64,11 @@ static void warn_if_old_rfx(const dboard_id_t &dboard_id, const std::string &xx) class unknown_rx : public rx_dboard_base{ public: unknown_rx(ctor_args_t args); - - void rx_get(const wax::obj &key, wax::obj &val); - void rx_set(const wax::obj &key, const wax::obj &val); }; class unknown_tx : public tx_dboard_base{ public: unknown_tx(ctor_args_t args); - - void tx_get(const wax::obj &key, wax::obj &val); - void tx_set(const wax::obj &key, const wax::obj &val); }; /*********************************************************************** @@ -98,99 +92,35 @@ UHD_STATIC_BLOCK(reg_unknown_dboards){ **********************************************************************/ unknown_rx::unknown_rx(ctor_args_t args) : rx_dboard_base(args){ warn_if_old_rfx(this->get_rx_id(), "RX"); -} - -void unknown_rx::rx_get(const wax::obj &key_, 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_NAME: - val = "Unknown - " + get_rx_id().to_pp_string(); - return; - - case SUBDEV_PROP_OTHERS: - val = prop_names_t(); //empty - return; - - case SUBDEV_PROP_GAIN: - val = double(0); - return; - - case SUBDEV_PROP_GAIN_RANGE: - val = gain_range_t(0, 0, 0); - return; - - case SUBDEV_PROP_GAIN_NAMES: - val = prop_names_t(); //empty - return; - - case SUBDEV_PROP_FREQ: - val = double(0); - return; - - case SUBDEV_PROP_FREQ_RANGE: - val = freq_range_t(0.0, 0.0); - return; - - case SUBDEV_PROP_ANTENNA: - val = std::string(""); - return; - - case SUBDEV_PROP_ANTENNA_NAMES: - val = prop_names_t(1, ""); //vector of 1 empty string - return; - - case SUBDEV_PROP_SENSOR_NAMES: - val = std::vector<std::string>(); //empty - 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_BANDWIDTH: - val = 0.0; - return; - - default: UHD_THROW_PROP_GET_ERROR(); - } -} - -void unknown_rx::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: - UHD_ASSERT_THROW(val.as<double>() == double(0)); - return; - - case SUBDEV_PROP_ANTENNA: - if (val.as<std::string>().empty()) return; - throw uhd::value_error("Unknown Daughterboard: No selectable antenna"); - - 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_MSG(warning) << "Unknown Daughterboard: No tunable bandwidth, fixed filtered to 0.0MHz"; - return; - default: UHD_THROW_PROP_SET_ERROR(); - } + //////////////////////////////////////////////////////////////////// + // Register properties + //////////////////////////////////////////////////////////////////// + this->get_rx_subtree()->create<std::string>("name").set( + std::string(str(boost::format("%s - %s") + % get_rx_id().to_pp_string() + % get_subdev_name() + ))); + this->get_rx_subtree()->create<int>("gains"); //phony property so this dir exists + this->get_rx_subtree()->create<double>("freq/value") + .set(double(0.0)); + this->get_rx_subtree()->create<meta_range_t>("freq/range") + .set(freq_range_t(double(0.0), double(0.0))); + this->get_rx_subtree()->create<std::string>("antenna/value") + .set(""); + this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options") + .set(list_of("")); + this->get_rx_subtree()->create<int>("sensors"); //phony property so this dir exists + this->get_rx_subtree()->create<std::string>("connection") + .set("IQ"); + this->get_rx_subtree()->create<bool>("enabled") + .set(true); //always enabled + this->get_rx_subtree()->create<bool>("use_lo_offset") + .set(false); + this->get_rx_subtree()->create<double>("bandwidth/value") + .set(double(0.0)); + this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") + .set(freq_range_t(0.0, 0.0)); } /*********************************************************************** @@ -198,97 +128,33 @@ void unknown_rx::rx_set(const wax::obj &key_, const wax::obj &val){ **********************************************************************/ unknown_tx::unknown_tx(ctor_args_t args) : tx_dboard_base(args){ warn_if_old_rfx(this->get_tx_id(), "TX"); -} - -void unknown_tx::tx_get(const wax::obj &key_, 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_NAME: - val = "Unknown - " + get_tx_id().to_pp_string(); - return; - - case SUBDEV_PROP_OTHERS: - val = prop_names_t(); //empty - return; - - case SUBDEV_PROP_GAIN: - val = double(0); - return; - - case SUBDEV_PROP_GAIN_RANGE: - val = gain_range_t(0, 0, 0); - return; - - case SUBDEV_PROP_GAIN_NAMES: - val = prop_names_t(); //empty - return; - - case SUBDEV_PROP_FREQ: - val = double(0); - return; - - case SUBDEV_PROP_FREQ_RANGE: - val = freq_range_t(0.0, 0.0); - return; - - case SUBDEV_PROP_ANTENNA: - val = std::string(""); - return; - - case SUBDEV_PROP_ANTENNA_NAMES: - val = prop_names_t(1, ""); //vector of 1 empty string - return; - - case SUBDEV_PROP_SENSOR_NAMES: - val = std::vector<std::string>(); //empty - 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_BANDWIDTH: - val = 0.0; - return; - - default: UHD_THROW_PROP_GET_ERROR(); - } -} - -void unknown_tx::tx_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: - UHD_ASSERT_THROW(val.as<double>() == double(0)); - return; - - case SUBDEV_PROP_ANTENNA: - if (val.as<std::string>().empty()) return; - throw uhd::value_error("Unknown Daughterboard: No selectable antenna"); - - 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_MSG(warning) << "Unknown Daughterboard: No tunable bandwidth, fixed filtered to 0.0MHz"; - return; - - default: UHD_THROW_PROP_SET_ERROR(); - } + //////////////////////////////////////////////////////////////////// + // Register properties + //////////////////////////////////////////////////////////////////// + this->get_tx_subtree()->create<std::string>("name").set( + std::string(str(boost::format("%s - %s") + % get_tx_id().to_pp_string() + % get_subdev_name() + ))); + this->get_tx_subtree()->create<int>("gains"); //phony property so this dir exists + this->get_tx_subtree()->create<double>("freq/value") + .set(double(0.0)); + this->get_tx_subtree()->create<meta_range_t>("freq/range") + .set(freq_range_t(double(0.0), double(0.0))); + this->get_tx_subtree()->create<std::string>("antenna/value") + .set(""); + this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options") + .set(list_of("")); + this->get_tx_subtree()->create<int>("sensors"); //phony property so this dir exists + this->get_tx_subtree()->create<std::string>("connection") + .set("IQ"); + this->get_tx_subtree()->create<bool>("enabled") + .set(true); //always enabled + this->get_tx_subtree()->create<bool>("use_lo_offset") + .set(false); + this->get_tx_subtree()->create<double>("bandwidth/value") + .set(double(0.0)); + this->get_tx_subtree()->create<meta_range_t>("bandwidth/range") + .set(freq_range_t(0.0, 0.0)); } diff --git a/host/lib/usrp/dboard/db_wbx_common.cpp b/host/lib/usrp/dboard/db_wbx_common.cpp index c7f8ba55d..daf6dbc98 100644 --- a/host/lib/usrp/dboard/db_wbx_common.cpp +++ b/host/lib/usrp/dboard/db_wbx_common.cpp @@ -15,151 +15,22 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -// Common IO Pins -#define ADF4350_CE (1 << 3) -#define ADF4350_PDBRF (1 << 2) -#define ADF4350_MUXOUT (1 << 1) // INPUT!!! -#define LOCKDET_MASK (1 << 0) // INPUT!!! - -// TX IO Pins -#define TX_PUP_5V (1 << 7) // enables 5.0V power supply -#define TX_PUP_3V (1 << 6) // enables 3.3V supply -#define TXMOD_EN (1 << 4) // on UNIT_TX, 1 enables TX Modulator - -// RX IO Pins -#define RX_PUP_5V (1 << 7) // enables 5.0V power supply -#define RX_PUP_3V (1 << 6) // enables 3.3V supply -#define RXBB_PDB (1 << 4) // on UNIT_RX, 1 powers up RX baseband - -// RX Attenuator Pins -#define RX_ATTN_SHIFT 8 // lsb of RX Attenuator Control -#define RX_ATTN_MASK (63 << RX_ATTN_SHIFT) // valid bits of RX Attenuator Control - -// TX Attenuator Pins (v3 only) -#define TX_ATTN_16 (1 << 14) -#define TX_ATTN_8 (1 << 5) -#define TX_ATTN_4 (1 << 4) -#define TX_ATTN_2 (1 << 3) -#define TX_ATTN_1 (1 << 1) -#define TX_ATTN_MASK (TX_ATTN_16|TX_ATTN_8|TX_ATTN_4|TX_ATTN_2|TX_ATTN_1) // valid bits of TX Attenuator Control - -// Mixer functions -#define TX_MIXER_ENB (TXMOD_EN|ADF4350_PDBRF) // for v3, TXMOD_EN tied to ADF4350_PDBRF rather than separate -#define TX_MIXER_DIS 0 - -#define RX_MIXER_ENB (RXBB_PDB|ADF4350_PDBRF) -#define RX_MIXER_DIS 0 - -// Power functions -#define TX_POWER_UP (TX_PUP_5V|TX_PUP_3V) // high enables power supply -#define TX_POWER_DOWN 0 - -#define RX_POWER_UP (RX_PUP_5V|RX_PUP_3V|ADF4350_CE) // high enables power supply -#define RX_POWER_DOWN 0 - #include "db_wbx_common.hpp" #include "adf4350_regs.hpp" -#include <uhd/utils/log.hpp> #include <uhd/types/dict.hpp> #include <uhd/types/ranges.hpp> #include <uhd/types/sensors.hpp> #include <uhd/utils/assert_has.hpp> #include <uhd/utils/algorithm.hpp> #include <uhd/utils/msg.hpp> -#include <uhd/usrp/dboard_base.hpp> -#include <boost/assign/list_of.hpp> -#include <boost/format.hpp> -#include <boost/math/special_functions/round.hpp> using namespace uhd; using namespace uhd::usrp; using namespace boost::assign; -/*********************************************************************** - * The WBX Common dboard constants - **********************************************************************/ -static const uhd::dict<std::string, gain_range_t> wbx_tx_gain_ranges = map_list_of - ("PGA0", gain_range_t(0, 25, 0.05)) -; - -static const uhd::dict<std::string, gain_range_t> wbx_v3_tx_gain_ranges = map_list_of - ("PGA0", gain_range_t(0, 31, 1.0)) -; - -static const uhd::dict<std::string, gain_range_t> wbx_rx_gain_ranges = map_list_of - ("PGA0", gain_range_t(0, 31.5, 0.5)) -; - -/*********************************************************************** - * WBX Common Implementation - **********************************************************************/ -wbx_base::wbx_base(ctor_args_t args) : xcvr_dboard_base(args){ - - //enable the clocks that we need - this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); - this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); - - //v3 has different io bits for attenuator control - int v3_iobits = is_v3() ? TX_ATTN_MASK : ADF4350_CE; - int v3_tx_mod = is_v3() ? ADF4350_PDBRF : TXMOD_EN|ADF4350_PDBRF; - - //set the gpio directions and atr controls - this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, v3_tx_mod); - this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXBB_PDB|ADF4350_PDBRF); - this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, TX_PUP_5V|TX_PUP_3V|v3_tx_mod|v3_iobits); - this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RX_PUP_5V|RX_PUP_3V|ADF4350_CE|RXBB_PDB|ADF4350_PDBRF|RX_ATTN_MASK); - - //setup ATR for the mixer enables (always enabled to prevent phase slip between bursts) - this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, v3_tx_mod, TX_MIXER_DIS | v3_tx_mod); - this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY, v3_tx_mod, TX_MIXER_DIS | v3_tx_mod); - this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, v3_tx_mod, TX_MIXER_DIS | v3_tx_mod); - this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, v3_tx_mod, TX_MIXER_DIS | v3_tx_mod); - - this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); - this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); - this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); - this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); - - //set some default values - if (is_v3()) { - BOOST_FOREACH(const std::string &name, wbx_v3_tx_gain_ranges.keys()){ - set_tx_gain(wbx_v3_tx_gain_ranges[name].start(), name); - } - } - else { - BOOST_FOREACH(const std::string &name, wbx_tx_gain_ranges.keys()){ - set_tx_gain(wbx_tx_gain_ranges[name].start(), name); - } - } - - BOOST_FOREACH(const std::string &name, wbx_rx_gain_ranges.keys()){ - set_rx_gain(wbx_rx_gain_ranges[name].start(), name); - } - set_rx_enabled(false); - set_tx_enabled(false); -} - -wbx_base::~wbx_base(void){ - /* NOP */ -} - -/*********************************************************************** - * Enables - **********************************************************************/ -void wbx_base::set_rx_enabled(bool enb){ - this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, - (enb)? RX_POWER_UP : RX_POWER_DOWN, RX_POWER_UP | RX_POWER_DOWN - ); -} - -void wbx_base::set_tx_enabled(bool enb){ - this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, - (enb)? TX_POWER_UP | ADF4350_CE : TX_POWER_DOWN, TX_POWER_UP | TX_POWER_DOWN | (is_v3() ? 0 : ADF4350_CE) - ); -} /*********************************************************************** - * Gain Handling + * Gain-related functions **********************************************************************/ static int rx_pga0_gain_to_iobits(double &gain){ //clip the input @@ -182,82 +53,84 @@ static int rx_pga0_gain_to_iobits(double &gain){ return iobits; } -//v3 TX gains -static int tx_pga0_gain_to_iobits(double &gain){ - //clip the input - gain = wbx_v3_tx_gain_ranges["PGA0"].clip(gain); - - //convert to attenuation - double attn = wbx_v3_tx_gain_ranges["PGA0"].stop() - gain; - //calculate the attenuation - int attn_code = boost::math::iround(attn); - int iobits = ( - (attn_code & 16 ? 0 : TX_ATTN_16) | - (attn_code & 8 ? 0 : TX_ATTN_8) | - (attn_code & 4 ? 0 : TX_ATTN_4) | - (attn_code & 2 ? 0 : TX_ATTN_2) | - (attn_code & 1 ? 0 : TX_ATTN_1) - ) & TX_ATTN_MASK; +/*********************************************************************** + * WBX Common Implementation + **********************************************************************/ +wbx_base::wbx_base(ctor_args_t args) : xcvr_dboard_base(args){ - UHD_LOGV(often) << boost::format( - "WBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x" - ) % attn % attn_code % (iobits & TX_ATTN_MASK) % TX_ATTN_MASK << std::endl; + //enable the clocks that we need + this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); + this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); - //the actual gain setting - gain = wbx_v3_tx_gain_ranges["PGA0"].stop() - double(attn_code); + //////////////////////////////////////////////////////////////////// + // Register RX properties + //////////////////////////////////////////////////////////////////// + this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") + .publish(boost::bind(&wbx_base::get_locked, this, dboard_iface::UNIT_RX)); + BOOST_FOREACH(const std::string &name, wbx_rx_gain_ranges.keys()){ + this->get_rx_subtree()->create<double>("gains/"+name+"/value") + .coerce(boost::bind(&wbx_base::set_rx_gain, this, _1, name)) + .set(wbx_rx_gain_ranges[name].start()); + this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range") + .set(wbx_rx_gain_ranges[name]); + } + this->get_rx_subtree()->create<std::string>("connection").set("IQ"); + this->get_rx_subtree()->create<bool>("enabled") + .subscribe(boost::bind(&wbx_base::set_rx_enabled, this, _1)) + .set(true); //start enabled + this->get_rx_subtree()->create<bool>("use_lo_offset").set(false); + this->get_rx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided + this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") + .set(freq_range_t(2*20.0e6, 2*20.0e6)); + + //////////////////////////////////////////////////////////////////// + // Register TX properties + //////////////////////////////////////////////////////////////////// + this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") + .publish(boost::bind(&wbx_base::get_locked, this, dboard_iface::UNIT_TX)); + this->get_tx_subtree()->create<std::string>("connection").set("IQ"); + this->get_tx_subtree()->create<bool>("use_lo_offset").set(false); + this->get_tx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided + this->get_tx_subtree()->create<meta_range_t>("bandwidth/range") + .set(freq_range_t(2*20.0e6, 2*20.0e6)); + + // instantiate subclass foo + switch(get_rx_id().to_uint16()) { + case 0x053: + db_actual = wbx_versionx_sptr(new wbx_version2(this)); + return; + case 0x057: + db_actual = wbx_versionx_sptr(new wbx_version3(this)); + return; + case 0x063: + db_actual = wbx_versionx_sptr(new wbx_version4(this)); + return; + default: + /* We didn't recognize the version of the board... */ + UHD_THROW_INVALID_CODE_PATH(); + } - return iobits; } -//Pre v3 TX gains -static double tx_pga0_gain_to_dac_volts(double &gain){ - //clip the input - gain = wbx_tx_gain_ranges["PGA0"].clip(gain); - - //voltage level constants - static const double max_volts = 0.5, min_volts = 1.4; - static const double slope = (max_volts-min_volts)/wbx_tx_gain_ranges["PGA0"].stop(); - - //calculate the voltage for the aux dac - double dac_volts = gain*slope + min_volts; - UHD_LOGV(often) << boost::format( - "WBX TX Gain: %f dB, dac_volts: %f V" - ) % gain % dac_volts << std::endl; - - //the actual gain setting - gain = (dac_volts - min_volts)/slope; - - return dac_volts; +wbx_base::~wbx_base(void){ + /* NOP */ } -void wbx_base::set_tx_gain(double gain, const std::string &name){ - if (is_v3()) { - assert_has(wbx_v3_tx_gain_ranges.keys(), name, "wbx tx gain name"); - if(name == "PGA0"){ - boost::uint16_t io_bits = tx_pga0_gain_to_iobits(gain); - _tx_gains[name] = gain; - - //write the new gain to tx gpio outputs - this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, io_bits, TX_ATTN_MASK); - } - else UHD_THROW_INVALID_CODE_PATH(); - } - else { - assert_has(wbx_tx_gain_ranges.keys(), name, "wbx tx gain name"); - if(name == "PGA0"){ - double dac_volts = tx_pga0_gain_to_dac_volts(gain); - _tx_gains[name] = gain; - - //write the new voltage to the aux dac - this->get_iface()->write_aux_dac(dboard_iface::UNIT_TX, dboard_iface::AUX_DAC_A, dac_volts); - } - else UHD_THROW_INVALID_CODE_PATH(); - } +/*********************************************************************** + * Enables + **********************************************************************/ +void wbx_base::set_rx_enabled(bool enb){ + this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, + (enb)? RX_POWER_UP : RX_POWER_DOWN, RX_POWER_UP | RX_POWER_DOWN + ); } -void wbx_base::set_rx_gain(double gain, const std::string &name){ +/*********************************************************************** + * Gain Handling + **********************************************************************/ +double wbx_base::set_rx_gain(double gain, const std::string &name){ assert_has(wbx_rx_gain_ranges.keys(), name, "wbx rx gain name"); if(name == "PGA0"){ boost::uint16_t io_bits = rx_pga0_gain_to_iobits(gain); @@ -267,390 +140,17 @@ void wbx_base::set_rx_gain(double gain, const std::string &name){ this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, io_bits, RX_ATTN_MASK); } else UHD_THROW_INVALID_CODE_PATH(); + return _rx_gains[name]; //returned shadowed } /*********************************************************************** * Tuning **********************************************************************/ -double wbx_base::set_lo_freq( - dboard_iface::unit_t unit, - double target_freq -){ - UHD_LOGV(often) << boost::format( - "WBX tune: target frequency %f Mhz" - ) % (target_freq/1e6) << std::endl; - - //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) - static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of - (0,23) //adf4350_regs_t::PRESCALER_4_5 - (1,75) //adf4350_regs_t::PRESCALER_8_9 - ; - - //map rf divider select output dividers to enums - static const uhd::dict<int, adf4350_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of - (1, adf4350_regs_t::RF_DIVIDER_SELECT_DIV1) - (2, adf4350_regs_t::RF_DIVIDER_SELECT_DIV2) - (4, adf4350_regs_t::RF_DIVIDER_SELECT_DIV4) - (8, adf4350_regs_t::RF_DIVIDER_SELECT_DIV8) - (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) - ; - - double actual_freq, pfd_freq; - double ref_freq = this->get_iface()->get_clock_rate(unit); - int R=0, BS=0, N=0, FRAC=0, MOD=0; - int RFdiv = 1; - adf4350_regs_t::reference_divide_by_2_t T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; - adf4350_regs_t::reference_doubler_t D = adf4350_regs_t::REFERENCE_DOUBLER_DISABLED; - - //Reference doubler for 50% duty cycle - // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 - if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; - - //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 - double vco_freq = target_freq*2; - while (vco_freq < 2.2e9) { - vco_freq *= 2; - RFdiv *= 2; - } - - //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) - adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; - - /* - * The goal here is to loop though possible R dividers, - * band select clock dividers, N (int) dividers, and FRAC - * (frac) dividers. - * - * Calculate the N and F dividers for each set of values. - * The loop exists when it meets all of the constraints. - * The resulting loop values are loaded into the registers. - * - * from pg.21 - * - * f_pfd = f_ref*(1+D)/(R*(1+T)) - * f_vco = (N + (FRAC/MOD))*f_pfd - * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD - * f_rf = f_vco/RFdiv) - * f_actual = f_rf/2 - */ - for(R = 1; R <= 1023; R+=1){ - //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) - pfd_freq = ref_freq*(1+D)/(R*(1+T)); - - //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) - if (pfd_freq > 25e6) continue; - - //ignore fractional part of tuning - N = int(std::floor(vco_freq/pfd_freq)); - - //keep N > minimum int divider requirement - if (N < prescaler_to_min_int_div[prescaler]) continue; - - for(BS=1; BS <= 255; BS+=1){ - //keep the band select frequency at or below 100KHz - //constraint on band select clock - if (pfd_freq/BS > 100e3) continue; - goto done_loop; - } - } done_loop: - - //Fractional-N calculation - MOD = 4095; //max fractional accuracy - FRAC = int((vco_freq/pfd_freq - N)*MOD); - - //Reference divide-by-2 for 50% duty cycle - // if R even, move one divide by 2 to to regs.reference_divide_by_2 - if(R % 2 == 0){ - T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; - R /= 2; - } - - //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2); - - - UHD_LOGV(often) - << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - - << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%d" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % get_locked(unit)<< std::endl - << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" - ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; - - //load the register values - adf4350_regs_t regs; - - regs.frac_12_bit = FRAC; - regs.int_16_bit = N; - regs.mod_12_bit = MOD; - regs.prescaler = prescaler; - regs.r_counter_10_bit = R; - regs.reference_divide_by_2 = T; - regs.reference_doubler = D; - regs.band_select_clock_div = BS; - UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); - regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; - - if (unit == dboard_iface::UNIT_RX) { - freq_range_t rx_lo_5dbm = list_of - (range_t(0.05e9, 1.4e9)) - ; - - freq_range_t rx_lo_2dbm = list_of - (range_t(1.4e9, 2.2e9)) - ; - - if (actual_freq == rx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; - - if (actual_freq == rx_lo_2dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_2DBM; - - } else if (unit == dboard_iface::UNIT_TX) { - freq_range_t tx_lo_5dbm = list_of - (range_t(0.05e9, 1.7e9)) - (range_t(1.9e9, 2.2e9)) - ; - - freq_range_t tx_lo_m1dbm = list_of - (range_t(1.7e9, 1.9e9)) - ; - - if (actual_freq == tx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; - - if (actual_freq == tx_lo_m1dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_M1DBM; - - } - - //write the registers - //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) - int addr; - - for(addr=5; addr>=0; addr--){ - UHD_LOGV(often) << boost::format( - "WBX SPI Reg (0x%02x): 0x%08x" - ) % addr % regs.get_reg(addr) << std::endl; - this->get_iface()->write_spi( - unit, spi_config_t::EDGE_RISE, - regs.get_reg(addr), 32 - ); - } - - //return the actual frequency - UHD_LOGV(often) << boost::format( - "WBX tune: actual frequency %f Mhz" - ) % (actual_freq/1e6) << std::endl; - return actual_freq; +double wbx_base::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { + return db_actual->set_lo_freq(unit, target_freq); } -bool wbx_base::get_locked(dboard_iface::unit_t unit){ - return (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0; -} - -bool wbx_base::is_v3(void){ - return get_rx_id().to_uint16() == 0x057; -} - -/*********************************************************************** - * RX Get and Set - **********************************************************************/ -void wbx_base::rx_get(const wax::obj &key_, 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_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(_rx_gains.keys(), key.name, "wbx rx gain name"); - val = _rx_gains[key.name]; - return; - - case SUBDEV_PROP_GAIN_RANGE: - assert_has(wbx_rx_gain_ranges.keys(), key.name, "wbx rx gain name"); - val = wbx_rx_gain_ranges[key.name]; - return; - - case SUBDEV_PROP_GAIN_NAMES: - val = prop_names_t(wbx_rx_gain_ranges.keys()); - return; - - case SUBDEV_PROP_FREQ: - val = 0.0; - return; - - case SUBDEV_PROP_FREQ_RANGE: - val = freq_range_t(0.0, 0.0, 0.0);; - return; - - case SUBDEV_PROP_ANTENNA: - val = std::string(""); - return; - - case SUBDEV_PROP_ANTENNA_NAMES: - val = prop_names_t(1, ""); - return; - - case SUBDEV_PROP_CONNECTION: - val = SUBDEV_CONN_COMPLEX_IQ; - return; - - case SUBDEV_PROP_ENABLED: - val = _rx_enabled; - return; - - case SUBDEV_PROP_USE_LO_OFFSET: - val = false; - return; - - case SUBDEV_PROP_SENSOR: - UHD_ASSERT_THROW(key.name == "lo_locked"); - val = sensor_value_t("LO", this->get_locked(dboard_iface::UNIT_RX), "locked", "unlocked"); - return; - - case SUBDEV_PROP_SENSOR_NAMES: - val = prop_names_t(1, "lo_locked"); - return; - - case SUBDEV_PROP_BANDWIDTH: - val = 2*20.0e6; //20MHz low-pass, we want complex double-sided - return; - - default: UHD_THROW_PROP_GET_ERROR(); - } -} - -void wbx_base::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_rx_gain(val.as<double>(), key.name); - return; - - case SUBDEV_PROP_ENABLED: - _rx_enabled = val.as<bool>(); - this->set_rx_enabled(_rx_enabled); - return; - - case SUBDEV_PROP_BANDWIDTH: - UHD_MSG(warning) << "WBX: No tunable bandwidth, fixed filtered to 40MHz"; - return; - - default: UHD_THROW_PROP_SET_ERROR(); - } -} - -/*********************************************************************** - * TX Get and Set - **********************************************************************/ -void wbx_base::tx_get(const wax::obj &key_, 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_NAME: - val = get_tx_id().to_pp_string(); - return; - - case SUBDEV_PROP_OTHERS: - val = prop_names_t(); //empty - return; - - case SUBDEV_PROP_GAIN: - assert_has(_tx_gains.keys(), key.name, "wbx tx gain name"); - val = _tx_gains[key.name]; - return; - - case SUBDEV_PROP_GAIN_RANGE: - if (is_v3()) { - assert_has(wbx_v3_tx_gain_ranges.keys(), key.name, "wbx tx gain name"); - val = wbx_v3_tx_gain_ranges[key.name]; - } - else { - assert_has(wbx_tx_gain_ranges.keys(), key.name, "wbx tx gain name"); - val = wbx_tx_gain_ranges[key.name]; - } - return; - - case SUBDEV_PROP_GAIN_NAMES: - if (is_v3()) - val = prop_names_t(wbx_v3_tx_gain_ranges.keys()); - else - val = prop_names_t(wbx_tx_gain_ranges.keys()); - return; - - case SUBDEV_PROP_FREQ: - val = 0.0; - return; - - case SUBDEV_PROP_FREQ_RANGE: - val = freq_range_t(0.0, 0.0, 0.0); - return; - - case SUBDEV_PROP_ANTENNA: - val = std::string(""); - return; - - case SUBDEV_PROP_ANTENNA_NAMES: - val = prop_names_t(1, ""); - return; - - case SUBDEV_PROP_CONNECTION: - val = SUBDEV_CONN_COMPLEX_IQ; - return; - - case SUBDEV_PROP_ENABLED: - val = _tx_enabled; - return; - - case SUBDEV_PROP_USE_LO_OFFSET: - val = false; - return; - - case SUBDEV_PROP_SENSOR: - UHD_ASSERT_THROW(key.name == "lo_locked"); - val = sensor_value_t("LO", this->get_locked(dboard_iface::UNIT_TX), "locked", "unlocked"); - return; - - case SUBDEV_PROP_SENSOR_NAMES: - val = prop_names_t(1, "lo_locked"); - return; - - case SUBDEV_PROP_BANDWIDTH: - val = 2*20.0e6; //20MHz low-pass, we want complex double-sided - return; - - default: UHD_THROW_PROP_GET_ERROR(); - } -} - -void wbx_base::tx_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_tx_gain(val.as<double>(), key.name); - return; - - case SUBDEV_PROP_ENABLED: - _tx_enabled = val.as<bool>(); - this->set_tx_enabled(_tx_enabled); - return; - - case SUBDEV_PROP_BANDWIDTH: - UHD_MSG(warning) << "WBX: No tunable bandwidth, fixed filtered to 40MHz"; - return; - - default: UHD_THROW_PROP_SET_ERROR(); - } +sensor_value_t wbx_base::get_locked(dboard_iface::unit_t unit){ + const bool locked = (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0; + return sensor_value_t("LO", locked, "locked", "unlocked"); } diff --git a/host/lib/usrp/dboard/db_wbx_common.hpp b/host/lib/usrp/dboard/db_wbx_common.hpp index 5d33ddce9..e7eb3f31a 100644 --- a/host/lib/usrp/dboard/db_wbx_common.hpp +++ b/host/lib/usrp/dboard/db_wbx_common.hpp @@ -18,14 +18,75 @@ #ifndef INCLUDED_LIBUHD_USRP_DBOARD_DB_WBX_COMMON_HPP #define INCLUDED_LIBUHD_USRP_DBOARD_DB_WBX_COMMON_HPP -#include "adf4350_regs.hpp" +// Common IO Pins +#define ADF4350_CE (1 << 3) +#define ADF4350_PDBRF (1 << 2) +#define ADF4350_MUXOUT (1 << 1) // INPUT!!! +#define ADF4351_CE (1 << 3) +#define ADF4351_PDBRF (1 << 2) +#define ADF4351_MUXOUT (1 << 1) // INPUT!!! + +#define LOCKDET_MASK (1 << 0) // INPUT!!! + +// TX IO Pins +#define TX_PUP_5V (1 << 7) // enables 5.0V power supply +#define TX_PUP_3V (1 << 6) // enables 3.3V supply +#define TXMOD_EN (1 << 4) // on UNIT_TX, 1 enables TX Modulator + +// RX IO Pins +#define RX_PUP_5V (1 << 7) // enables 5.0V power supply +#define RX_PUP_3V (1 << 6) // enables 3.3V supply +#define RXBB_PDB (1 << 4) // on UNIT_RX, 1 powers up RX baseband + +// RX Attenuator Pins +#define RX_ATTN_SHIFT 8 // lsb of RX Attenuator Control +#define RX_ATTN_MASK (63 << RX_ATTN_SHIFT) // valid bits of RX Attenuator Control + +// TX Attenuator Pins (v3 only) +#define TX_ATTN_16 (1 << 14) +#define TX_ATTN_8 (1 << 5) +#define TX_ATTN_4 (1 << 4) +#define TX_ATTN_2 (1 << 3) +#define TX_ATTN_1 (1 << 1) +#define TX_ATTN_MASK (TX_ATTN_16|TX_ATTN_8|TX_ATTN_4|TX_ATTN_2|TX_ATTN_1) // valid bits of TX Attenuator Control + +// Mixer functions +#define TX_MIXER_ENB (TXMOD_EN|ADF4350_PDBRF) // for v3, TXMOD_EN tied to ADF4350_PDBRF rather than separate +#define TX_MIXER_DIS 0 + +#define RX_MIXER_ENB (RXBB_PDB|ADF4350_PDBRF) +#define RX_MIXER_DIS 0 + +// Power functions +#define TX_POWER_UP (TX_PUP_5V|TX_PUP_3V) // high enables power supply +#define TX_POWER_DOWN 0 + +#define RX_POWER_UP (RX_PUP_5V|RX_PUP_3V|ADF4350_CE) // high enables power supply +#define RX_POWER_DOWN 0 + + #include <uhd/types/dict.hpp> #include <uhd/types/ranges.hpp> -#include <uhd/utils/props.hpp> +#include <uhd/types/sensors.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/static.hpp> #include <uhd/usrp/dboard_base.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/format.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/math/special_functions/round.hpp> +#include <boost/bind.hpp> namespace uhd{ namespace usrp{ + +/*********************************************************************** + * The WBX Common dboard constants + **********************************************************************/ +static const uhd::dict<std::string, gain_range_t> wbx_rx_gain_ranges = boost::assign::map_list_of + ("PGA0", gain_range_t(0, 31.5, 0.5)); + + /*********************************************************************** * The WBX dboard base class **********************************************************************/ @@ -35,17 +96,9 @@ public: virtual ~wbx_base(void); protected: - virtual void set_rx_gain(double gain, const std::string &name); - virtual void set_tx_gain(double gain, const std::string &name); + virtual double set_rx_gain(double gain, const std::string &name); virtual void set_rx_enabled(bool enb); - virtual void set_tx_enabled(bool enb); - - void rx_get(const wax::obj &key, wax::obj &val); - void rx_set(const wax::obj &key, const wax::obj &val); - - void tx_get(const wax::obj &key, wax::obj &val); - void tx_set(const wax::obj &key, const wax::obj &val); /*! * Set the LO frequency for the particular dboard unit. @@ -57,22 +110,103 @@ protected: /*! * Get the lock detect status of the LO. + * + * This operation is identical for all versions of the WBX board. * \param unit which unit rx or tx * \return true for locked */ - virtual bool get_locked(dboard_iface::unit_t unit); + virtual sensor_value_t get_locked(dboard_iface::unit_t unit); /*! - * Detect if this a v3 WBX - * \return true for locked + * Version-agnostic ABC that wraps version-specific implementations of the + * WBX base daughterboard. + * + * This class is an abstract base class, and thus is impossible to + * instantiate. + */ + class wbx_versionx { + public: + wbx_versionx() {} + ~wbx_versionx(void) {} + + virtual double set_tx_gain(double gain, const std::string &name) = 0; + virtual void set_tx_enabled(bool enb) = 0; + virtual double set_lo_freq(dboard_iface::unit_t unit, double target_freq) = 0; + + /*! This is the registered instance of the wrapper class, wbx_base. */ + wbx_base *self_base; + + property_tree::sptr get_rx_subtree(void){ + return self_base->get_rx_subtree(); + } + + property_tree::sptr get_tx_subtree(void){ + return self_base->get_tx_subtree(); + } + }; + + + /*! + * Version 2 of the WBX Daughterboard + * + * Basically the original release of the DB. + */ + class wbx_version2 : public wbx_versionx { + public: + wbx_version2(wbx_base *_self_wbx_base); + ~wbx_version2(void); + + double set_tx_gain(double gain, const std::string &name); + void set_tx_enabled(bool enb); + double set_lo_freq(dboard_iface::unit_t unit, double target_freq); + }; + + /*! + * Version 3 of the WBX Daughterboard + * + * Fixed a problem with the AGC from Version 2. + */ + class wbx_version3 : public wbx_versionx { + public: + wbx_version3(wbx_base *_self_wbx_base); + ~wbx_version3(void); + + double set_tx_gain(double gain, const std::string &name); + void set_tx_enabled(bool enb); + double set_lo_freq(dboard_iface::unit_t unit, double target_freq); + }; + + /*! + * Version 4 of the WBX Daughterboard + * + * Upgrades the Frequnecy Synthensizer from ADF4350 to ADF4351. */ - virtual bool is_v3(void); + class wbx_version4 : public wbx_versionx { + public: + wbx_version4(wbx_base *_self_wbx_base); + ~wbx_version4(void); + + double set_tx_gain(double gain, const std::string &name); + void set_tx_enabled(bool enb); + double set_lo_freq(dboard_iface::unit_t unit, double target_freq); + }; + + /*! + * Handle to the version-specific implementation of the WBX. + * + * Since many of this class's functions are dependent on the version of the + * WBX board, this class will instantiate an object of the appropriate + * wbx_version_* subclass, and invoke any relevant functions through that + * object. This pointer is set to the proper object at construction time. + */ + typedef boost::shared_ptr<wbx_versionx> wbx_versionx_sptr; + wbx_versionx_sptr db_actual; -private: uhd::dict<std::string, double> _tx_gains, _rx_gains; bool _rx_enabled, _tx_enabled; }; + }} //namespace uhd::usrp #endif /* INCLUDED_LIBUHD_USRP_DBOARD_DB_WBX_COMMON_HPP */ diff --git a/host/lib/usrp/dboard/db_wbx_simple.cpp b/host/lib/usrp/dboard/db_wbx_simple.cpp index 990bacbc8..f46ea70d1 100644 --- a/host/lib/usrp/dboard/db_wbx_simple.cpp +++ b/host/lib/usrp/dboard/db_wbx_simple.cpp @@ -32,14 +32,13 @@ using namespace uhd; using namespace uhd::usrp; using namespace boost::assign; + /*********************************************************************** * The WBX Simple dboard constants **********************************************************************/ -static const freq_range_t wbx_freq_range(68.75e6, 2.2e9); - -static const prop_names_t wbx_tx_antennas = list_of("TX/RX"); +static const std::vector<std::string> wbx_tx_antennas = list_of("TX/RX"); -static const prop_names_t wbx_rx_antennas = list_of("TX/RX")("RX2"); +static const std::vector<std::string> wbx_rx_antennas = list_of("TX/RX")("RX2"); /*********************************************************************** * The WBX simple implementation @@ -49,17 +48,7 @@ public: wbx_simple(ctor_args_t args); ~wbx_simple(void); - void rx_get(const wax::obj &key, wax::obj &val); - void rx_set(const wax::obj &key, const wax::obj &val); - - void tx_get(const wax::obj &key, wax::obj &val); - void tx_set(const wax::obj &key, const wax::obj &val); - private: - void set_rx_lo_freq(double freq); - void set_tx_lo_freq(double freq); - double _rx_lo_freq, _tx_lo_freq; - void set_rx_ant(const std::string &ant); void set_tx_ant(const std::string &ant); std::string _rx_ant; @@ -72,11 +61,16 @@ static dboard_base::sptr make_wbx_simple(dboard_base::ctor_args_t args){ return dboard_base::sptr(new wbx_simple(args)); } +/*********************************************************************** + * ID Numbers for WBX daughterboard combinations. + **********************************************************************/ UHD_STATIC_BLOCK(reg_wbx_simple_dboards){ dboard_manager::register_dboard(0x0053, 0x0052, &make_wbx_simple, "WBX"); dboard_manager::register_dboard(0x0053, 0x004f, &make_wbx_simple, "WBX + Simple GDB"); dboard_manager::register_dboard(0x0057, 0x0056, &make_wbx_simple, "WBX v3"); dboard_manager::register_dboard(0x0057, 0x004f, &make_wbx_simple, "WBX v3 + Simple GDB"); + dboard_manager::register_dboard(0x0063, 0x0062, &make_wbx_simple, "WBX v4"); + dboard_manager::register_dboard(0x0063, 0x004f, &make_wbx_simple, "WBX v4 + Simple GDB"); } /*********************************************************************** @@ -84,6 +78,28 @@ UHD_STATIC_BLOCK(reg_wbx_simple_dboards){ **********************************************************************/ wbx_simple::wbx_simple(ctor_args_t args) : wbx_base(args){ + //////////////////////////////////////////////////////////////////// + // Register RX properties + //////////////////////////////////////////////////////////////////// + this->get_rx_subtree()->access<std::string>("name").set( + this->get_rx_subtree()->access<std::string>("name").get() + " + Simple GDB"); + this->get_rx_subtree()->create<std::string>("antenna/value") + .subscribe(boost::bind(&wbx_simple::set_rx_ant, this, _1)) + .set("RX2"); + this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options") + .set(wbx_rx_antennas); + + //////////////////////////////////////////////////////////////////// + // Register TX properties + //////////////////////////////////////////////////////////////////// + this->get_tx_subtree()->access<std::string>("name").set( + this->get_tx_subtree()->access<std::string>("name").get() + " + Simple GDB"); + this->get_tx_subtree()->create<std::string>("antenna/value") + .subscribe(boost::bind(&wbx_simple::set_tx_ant, this, _1)) + .set(wbx_tx_antennas.at(0)); + this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options") + .set(wbx_tx_antennas); + //set the gpio directions and atr controls (antenna switches all under ATR) this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, ANTSW_IO, ANTSW_IO); this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, ANTSW_IO, ANTSW_IO); @@ -99,11 +115,6 @@ wbx_simple::wbx_simple(ctor_args_t args) : wbx_base(args){ this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, ANT_TXRX, ANTSW_IO); this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, ANT_RX2, ANTSW_IO); this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, ANT_RX2, ANTSW_IO); - - //set some default values - set_rx_lo_freq((wbx_freq_range.start() + wbx_freq_range.stop())/2.0); - set_tx_lo_freq((wbx_freq_range.start() + wbx_freq_range.stop())/2.0); - set_rx_ant("RX2"); } wbx_simple::~wbx_simple(void){ @@ -128,128 +139,3 @@ void wbx_simple::set_tx_ant(const std::string &ant){ assert_has(wbx_tx_antennas, ant, "wbx tx antenna name"); //only one antenna option, do nothing } - -/*********************************************************************** - * Tuning - **********************************************************************/ -void wbx_simple::set_rx_lo_freq(double freq){ - _rx_lo_freq = set_lo_freq(dboard_iface::UNIT_RX, wbx_freq_range.clip(freq)); -} - -void wbx_simple::set_tx_lo_freq(double freq){ - _tx_lo_freq = set_lo_freq(dboard_iface::UNIT_TX, wbx_freq_range.clip(freq)); -} - -/*********************************************************************** - * RX Get and Set - **********************************************************************/ -void wbx_simple::rx_get(const wax::obj &key_, 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_NAME: - if (is_v3()) - val = std::string("WBX v3 RX + Simple GDB"); - else - val = std::string("WBX RX + Simple GDB"); - return; - - case SUBDEV_PROP_FREQ: - val = _rx_lo_freq; - return; - - case SUBDEV_PROP_FREQ_RANGE: - val = wbx_freq_range; - return; - - case SUBDEV_PROP_ANTENNA: - val = _rx_ant; - return; - - case SUBDEV_PROP_ANTENNA_NAMES: - val = wbx_rx_antennas; - return; - - default: - //call into the base class for other properties - wbx_base::rx_get(key_, val); - } -} - -void wbx_simple::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_FREQ: - this->set_rx_lo_freq(val.as<double>()); - return; - - case SUBDEV_PROP_ANTENNA: - this->set_rx_ant(val.as<std::string>()); - return; - - default: - //call into the base class for other properties - wbx_base::rx_set(key_, val); - } -} - -/*********************************************************************** - * TX Get and Set - **********************************************************************/ -void wbx_simple::tx_get(const wax::obj &key_, 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_NAME: - if (is_v3()) - val = std::string("WBX v3 TX + Simple GDB"); - else - val = std::string("WBX TX + Simple GDB"); - return; - - case SUBDEV_PROP_FREQ: - val = _tx_lo_freq; - return; - - case SUBDEV_PROP_FREQ_RANGE: - val = wbx_freq_range; - return; - - case SUBDEV_PROP_ANTENNA: - val = std::string("TX/RX"); - return; - - case SUBDEV_PROP_ANTENNA_NAMES: - val = wbx_tx_antennas; - return; - - default: - //call into the base class for other properties - wbx_base::tx_get(key_, val); - } -} - -void wbx_simple::tx_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_FREQ: - this->set_tx_lo_freq(val.as<double>()); - return; - - case SUBDEV_PROP_ANTENNA: - this->set_tx_ant(val.as<std::string>()); - return; - - default: - //call into the base class for other properties - wbx_base::tx_set(key_, val); - } -} diff --git a/host/lib/usrp/dboard/db_wbx_version2.cpp b/host/lib/usrp/dboard/db_wbx_version2.cpp new file mode 100644 index 000000000..e25cb2e4c --- /dev/null +++ b/host/lib/usrp/dboard/db_wbx_version2.cpp @@ -0,0 +1,326 @@ +// +// Copyright 2011 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 "db_wbx_common.hpp" +#include "adf4350_regs.hpp" +#include <uhd/utils/log.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/sensors.hpp> +#include <uhd/utils/assert_has.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/usrp/dboard_base.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/format.hpp> +#include <boost/math/special_functions/round.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + + +/*********************************************************************** + * WBX Version 2 Constants + **********************************************************************/ +static const uhd::dict<std::string, gain_range_t> wbx_v2_tx_gain_ranges = map_list_of + ("PGA0", gain_range_t(0, 25, 0.05)) +; + +static const freq_range_t wbx_v2_freq_range(68.75e6, 2.2e9); + +/*********************************************************************** + * Gain-related functions + **********************************************************************/ +static double tx_pga0_gain_to_dac_volts(double &gain){ + //clip the input + gain = wbx_v2_tx_gain_ranges["PGA0"].clip(gain); + + //voltage level constants + static const double max_volts = 0.5, min_volts = 1.4; + static const double slope = (max_volts-min_volts)/wbx_v2_tx_gain_ranges["PGA0"].stop(); + + //calculate the voltage for the aux dac + double dac_volts = gain*slope + min_volts; + + UHD_LOGV(often) << boost::format( + "WBX TX Gain: %f dB, dac_volts: %f V" + ) % gain % dac_volts << std::endl; + + //the actual gain setting + gain = (dac_volts - min_volts)/slope; + + return dac_volts; +} + + +/*********************************************************************** + * WBX Version 2 Implementation + **********************************************************************/ +wbx_base::wbx_version2::wbx_version2(wbx_base *_self_wbx_base) { + //register our handle on the primary wbx_base instance + self_base = _self_wbx_base; + + //////////////////////////////////////////////////////////////////// + // Register RX properties + //////////////////////////////////////////////////////////////////// + this->get_rx_subtree()->create<std::string>("name").set("WBX RX v2"); + this->get_rx_subtree()->create<double>("freq/value") + .coerce(boost::bind(&wbx_base::wbx_version2::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) + .set((wbx_v2_freq_range.start() + wbx_v2_freq_range.stop())/2.0); + this->get_rx_subtree()->create<meta_range_t>("freq/range").set(wbx_v2_freq_range); + + //////////////////////////////////////////////////////////////////// + // Register TX properties + //////////////////////////////////////////////////////////////////// + this->get_tx_subtree()->create<std::string>("name").set("WBX TX v2"); + BOOST_FOREACH(const std::string &name, wbx_v2_tx_gain_ranges.keys()){ + self_base->get_tx_subtree()->create<double>("gains/"+name+"/value") + .coerce(boost::bind(&wbx_base::wbx_version2::set_tx_gain, this, _1, name)) + .set(wbx_v2_tx_gain_ranges[name].start()); + self_base->get_tx_subtree()->create<meta_range_t>("gains/"+name+"/range") + .set(wbx_v2_tx_gain_ranges[name]); + } + this->get_tx_subtree()->create<double>("freq/value") + .coerce(boost::bind(&wbx_base::wbx_version2::set_lo_freq, this, dboard_iface::UNIT_TX, _1)) + .set((wbx_v2_freq_range.start() + wbx_v2_freq_range.stop())/2.0); + this->get_tx_subtree()->create<meta_range_t>("freq/range").set(wbx_v2_freq_range); + this->get_tx_subtree()->create<bool>("enabled") + .subscribe(boost::bind(&wbx_base::wbx_version2::set_tx_enabled, this, _1)) + .set(true); //start enabled + + //set attenuator control bits + int v2_iobits = ADF4350_CE; + int v2_tx_mod = TXMOD_EN|ADF4350_PDBRF; + + //set the gpio directions and atr controls + self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, v2_tx_mod); + self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXBB_PDB|ADF4350_PDBRF); + self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, TX_PUP_5V|TX_PUP_3V|v2_tx_mod|v2_iobits); + self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RX_PUP_5V|RX_PUP_3V|ADF4350_CE|RXBB_PDB|ADF4350_PDBRF|RX_ATTN_MASK); + + //setup ATR for the mixer enables (always enabled to prevent phase slip between bursts) + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY, v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); + + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +} + +wbx_base::wbx_version2::~wbx_version2(void){ + /* NOP */ +} + +/*********************************************************************** + * Enables + **********************************************************************/ +void wbx_base::wbx_version2::set_tx_enabled(bool enb){ + self_base->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, + (enb)? TX_POWER_UP | ADF4350_CE : TX_POWER_DOWN, TX_POWER_UP | TX_POWER_DOWN | ADF4350_CE); +} + + +/*********************************************************************** + * Gain Handling + **********************************************************************/ +double wbx_base::wbx_version2::set_tx_gain(double gain, const std::string &name){ + assert_has(wbx_v2_tx_gain_ranges.keys(), name, "wbx tx gain name"); + if(name == "PGA0"){ + double dac_volts = tx_pga0_gain_to_dac_volts(gain); + self_base->_tx_gains[name] = gain; + + //write the new voltage to the aux dac + self_base->get_iface()->write_aux_dac(dboard_iface::UNIT_TX, dboard_iface::AUX_DAC_A, dac_volts); + } + else UHD_THROW_INVALID_CODE_PATH(); + return self_base->_tx_gains[name]; //shadowed +} + + +/*********************************************************************** + * Tuning + **********************************************************************/ +double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { + UHD_LOGV(often) << boost::format( + "WBX tune: target frequency %f Mhz" + ) % (target_freq/1e6) << std::endl; + + //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) + static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of + (0,23) //adf4350_regs_t::PRESCALER_4_5 + (1,75) //adf4350_regs_t::PRESCALER_8_9 + ; + + //map rf divider select output dividers to enums + static const uhd::dict<int, adf4350_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of + (1, adf4350_regs_t::RF_DIVIDER_SELECT_DIV1) + (2, adf4350_regs_t::RF_DIVIDER_SELECT_DIV2) + (4, adf4350_regs_t::RF_DIVIDER_SELECT_DIV4) + (8, adf4350_regs_t::RF_DIVIDER_SELECT_DIV8) + (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) + ; + + double actual_freq, pfd_freq; + double ref_freq = self_base->get_iface()->get_clock_rate(unit); + int R=0, BS=0, N=0, FRAC=0, MOD=0; + int RFdiv = 1; + adf4350_regs_t::reference_divide_by_2_t T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; + adf4350_regs_t::reference_doubler_t D = adf4350_regs_t::REFERENCE_DOUBLER_DISABLED; + + //Reference doubler for 50% duty cycle + // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 + if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; + + //increase RF divider until acceptable VCO frequency + //start with target_freq*2 because mixer has divide by 2 + double vco_freq = target_freq*2; + while (vco_freq < 2.2e9) { + vco_freq *= 2; + RFdiv *= 2; + } + + //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) + adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; + + /* + * The goal here is to loop though possible R dividers, + * band select clock dividers, N (int) dividers, and FRAC + * (frac) dividers. + * + * Calculate the N and F dividers for each set of values. + * The loop exists when it meets all of the constraints. + * The resulting loop values are loaded into the registers. + * + * from pg.21 + * + * f_pfd = f_ref*(1+D)/(R*(1+T)) + * f_vco = (N + (FRAC/MOD))*f_pfd + * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD + * f_rf = f_vco/RFdiv) + * f_actual = f_rf/2 + */ + for(R = 1; R <= 1023; R+=1){ + //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) + pfd_freq = ref_freq*(1+D)/(R*(1+T)); + + //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) + if (pfd_freq > 25e6) continue; + + //ignore fractional part of tuning + N = int(std::floor(vco_freq/pfd_freq)); + + //keep N > minimum int divider requirement + if (N < prescaler_to_min_int_div[prescaler]) continue; + + for(BS=1; BS <= 255; BS+=1){ + //keep the band select frequency at or below 100KHz + //constraint on band select clock + if (pfd_freq/BS > 100e3) continue; + goto done_loop; + } + } done_loop: + + //Fractional-N calculation + MOD = 4095; //max fractional accuracy + FRAC = int((vco_freq/pfd_freq - N)*MOD); + + //Reference divide-by-2 for 50% duty cycle + // if R even, move one divide by 2 to to regs.reference_divide_by_2 + if(R % 2 == 0){ + T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; + R /= 2; + } + + //actual frequency calculation + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2); + + + UHD_LOGV(often) + << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl + + << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" + ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; + + //load the register values + adf4350_regs_t regs; + + regs.frac_12_bit = FRAC; + regs.int_16_bit = N; + regs.mod_12_bit = MOD; + regs.prescaler = prescaler; + regs.r_counter_10_bit = R; + regs.reference_divide_by_2 = T; + regs.reference_doubler = D; + regs.band_select_clock_div = BS; + UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); + regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; + + if (unit == dboard_iface::UNIT_RX) { + freq_range_t rx_lo_5dbm = list_of + (range_t(0.05e9, 1.4e9)) + ; + + freq_range_t rx_lo_2dbm = list_of + (range_t(1.4e9, 2.2e9)) + ; + + if (actual_freq == rx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; + + if (actual_freq == rx_lo_2dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_2DBM; + + } else if (unit == dboard_iface::UNIT_TX) { + freq_range_t tx_lo_5dbm = list_of + (range_t(0.05e9, 1.7e9)) + (range_t(1.9e9, 2.2e9)) + ; + + freq_range_t tx_lo_m1dbm = list_of + (range_t(1.7e9, 1.9e9)) + ; + + if (actual_freq == tx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; + + if (actual_freq == tx_lo_m1dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_M1DBM; + + } + + //write the registers + //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) + int addr; + + for(addr=5; addr>=0; addr--){ + UHD_LOGV(often) << boost::format( + "WBX SPI Reg (0x%02x): 0x%08x" + ) % addr % regs.get_reg(addr) << std::endl; + self_base->get_iface()->write_spi( + unit, spi_config_t::EDGE_RISE, + regs.get_reg(addr), 32 + ); + } + + //return the actual frequency + UHD_LOGV(often) << boost::format( + "WBX tune: actual frequency %f Mhz" + ) % (actual_freq/1e6) << std::endl; + return actual_freq; +} diff --git a/host/lib/usrp/dboard/db_wbx_version3.cpp b/host/lib/usrp/dboard/db_wbx_version3.cpp new file mode 100644 index 000000000..70981ce94 --- /dev/null +++ b/host/lib/usrp/dboard/db_wbx_version3.cpp @@ -0,0 +1,333 @@ +// +// Copyright 2011 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 "db_wbx_common.hpp" +#include "adf4350_regs.hpp" +#include <uhd/utils/log.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/sensors.hpp> +#include <uhd/utils/assert_has.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/usrp/dboard_base.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/format.hpp> +#include <boost/math/special_functions/round.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + + +/*********************************************************************** + * WBX Version 3 Constants + **********************************************************************/ +static const uhd::dict<std::string, gain_range_t> wbx_v3_tx_gain_ranges = map_list_of + ("PGA0", gain_range_t(0, 31, 1.0)) +; + +static const freq_range_t wbx_v3_freq_range(68.75e6, 2.2e9); + +/*********************************************************************** + * Gain-related functions + **********************************************************************/ +static int tx_pga0_gain_to_iobits(double &gain){ + //clip the input + gain = wbx_v3_tx_gain_ranges["PGA0"].clip(gain); + + //convert to attenuation + double attn = wbx_v3_tx_gain_ranges["PGA0"].stop() - gain; + + //calculate the attenuation + int attn_code = boost::math::iround(attn); + int iobits = ( + (attn_code & 16 ? 0 : TX_ATTN_16) | + (attn_code & 8 ? 0 : TX_ATTN_8) | + (attn_code & 4 ? 0 : TX_ATTN_4) | + (attn_code & 2 ? 0 : TX_ATTN_2) | + (attn_code & 1 ? 0 : TX_ATTN_1) + ) & TX_ATTN_MASK; + + UHD_LOGV(often) << boost::format( + "WBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x" + ) % attn % attn_code % (iobits & TX_ATTN_MASK) % TX_ATTN_MASK << std::endl; + + //the actual gain setting + gain = wbx_v3_tx_gain_ranges["PGA0"].stop() - double(attn_code); + + return iobits; +} + + +/*********************************************************************** + * WBX Common Implementation + **********************************************************************/ +wbx_base::wbx_version3::wbx_version3(wbx_base *_self_wbx_base) { + //register our handle on the primary wbx_base instance + self_base = _self_wbx_base; + + //////////////////////////////////////////////////////////////////// + // Register RX properties + //////////////////////////////////////////////////////////////////// + this->get_rx_subtree()->create<std::string>("name").set("WBX RX v3"); + this->get_rx_subtree()->create<double>("freq/value") + .coerce(boost::bind(&wbx_base::wbx_version3::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) + .set((wbx_v3_freq_range.start() + wbx_v3_freq_range.stop())/2.0); + this->get_rx_subtree()->create<meta_range_t>("freq/range").set(wbx_v3_freq_range); + + //////////////////////////////////////////////////////////////////// + // Register TX properties + //////////////////////////////////////////////////////////////////// + this->get_tx_subtree()->create<std::string>("name").set("WBX TX v3"); + BOOST_FOREACH(const std::string &name, wbx_v3_tx_gain_ranges.keys()){ + self_base->get_tx_subtree()->create<double>("gains/"+name+"/value") + .coerce(boost::bind(&wbx_base::wbx_version3::set_tx_gain, this, _1, name)) + .set(wbx_v3_tx_gain_ranges[name].start()); + self_base->get_tx_subtree()->create<meta_range_t>("gains/"+name+"/range") + .set(wbx_v3_tx_gain_ranges[name]); + } + this->get_tx_subtree()->create<double>("freq/value") + .coerce(boost::bind(&wbx_base::wbx_version3::set_lo_freq, this, dboard_iface::UNIT_TX, _1)) + .set((wbx_v3_freq_range.start() + wbx_v3_freq_range.stop())/2.0); + this->get_tx_subtree()->create<meta_range_t>("freq/range").set(wbx_v3_freq_range); + this->get_tx_subtree()->create<bool>("enabled") + .subscribe(boost::bind(&wbx_base::wbx_version3::set_tx_enabled, this, _1)) + .set(true); //start enabled + + //set attenuator control bits + int v3_iobits = TX_ATTN_MASK; + int v3_tx_mod = ADF4350_PDBRF; + + //set the gpio directions and atr controls + self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, v3_tx_mod); + self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXBB_PDB|ADF4350_PDBRF); + self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, TX_PUP_5V|TX_PUP_3V|v3_tx_mod|v3_iobits); + self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RX_PUP_5V|RX_PUP_3V|ADF4350_CE|RXBB_PDB|ADF4350_PDBRF|RX_ATTN_MASK); + + //setup ATR for the mixer enables (always enabled to prevent phase slip between bursts) + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, v3_tx_mod, TX_MIXER_DIS | v3_tx_mod); + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY, v3_tx_mod, TX_MIXER_DIS | v3_tx_mod); + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, v3_tx_mod, TX_MIXER_DIS | v3_tx_mod); + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, v3_tx_mod, TX_MIXER_DIS | v3_tx_mod); + + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +} + +wbx_base::wbx_version3::~wbx_version3(void){ + /* NOP */ +} + + +/*********************************************************************** + * Enables + **********************************************************************/ +void wbx_base::wbx_version3::set_tx_enabled(bool enb){ + self_base->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, + (enb)? TX_POWER_UP | ADF4350_CE : TX_POWER_DOWN, TX_POWER_UP | TX_POWER_DOWN | 0); +} + + +/*********************************************************************** + * Gain Handling + **********************************************************************/ +double wbx_base::wbx_version3::set_tx_gain(double gain, const std::string &name){ + assert_has(wbx_v3_tx_gain_ranges.keys(), name, "wbx tx gain name"); + if(name == "PGA0"){ + boost::uint16_t io_bits = tx_pga0_gain_to_iobits(gain); + self_base->_tx_gains[name] = gain; + + //write the new gain to tx gpio outputs + self_base->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, io_bits, TX_ATTN_MASK); + } + else UHD_THROW_INVALID_CODE_PATH(); + return self_base->_tx_gains[name]; //shadow +} + + +/*********************************************************************** + * Tuning + **********************************************************************/ +double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { + UHD_LOGV(often) << boost::format( + "WBX tune: target frequency %f Mhz" + ) % (target_freq/1e6) << std::endl; + + //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) + static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of + (0,23) //adf4350_regs_t::PRESCALER_4_5 + (1,75) //adf4350_regs_t::PRESCALER_8_9 + ; + + //map rf divider select output dividers to enums + static const uhd::dict<int, adf4350_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of + (1, adf4350_regs_t::RF_DIVIDER_SELECT_DIV1) + (2, adf4350_regs_t::RF_DIVIDER_SELECT_DIV2) + (4, adf4350_regs_t::RF_DIVIDER_SELECT_DIV4) + (8, adf4350_regs_t::RF_DIVIDER_SELECT_DIV8) + (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) + ; + + double actual_freq, pfd_freq; + double ref_freq = self_base->get_iface()->get_clock_rate(unit); + int R=0, BS=0, N=0, FRAC=0, MOD=0; + int RFdiv = 1; + adf4350_regs_t::reference_divide_by_2_t T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; + adf4350_regs_t::reference_doubler_t D = adf4350_regs_t::REFERENCE_DOUBLER_DISABLED; + + //Reference doubler for 50% duty cycle + // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 + if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; + + //increase RF divider until acceptable VCO frequency + //start with target_freq*2 because mixer has divide by 2 + double vco_freq = target_freq*2; + while (vco_freq < 2.2e9) { + vco_freq *= 2; + RFdiv *= 2; + } + + //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) + adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; + + /* + * The goal here is to loop though possible R dividers, + * band select clock dividers, N (int) dividers, and FRAC + * (frac) dividers. + * + * Calculate the N and F dividers for each set of values. + * The loop exists when it meets all of the constraints. + * The resulting loop values are loaded into the registers. + * + * from pg.21 + * + * f_pfd = f_ref*(1+D)/(R*(1+T)) + * f_vco = (N + (FRAC/MOD))*f_pfd + * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD + * f_rf = f_vco/RFdiv) + * f_actual = f_rf/2 + */ + for(R = 1; R <= 1023; R+=1){ + //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) + pfd_freq = ref_freq*(1+D)/(R*(1+T)); + + //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) + if (pfd_freq > 25e6) continue; + + //ignore fractional part of tuning + N = int(std::floor(vco_freq/pfd_freq)); + + //keep N > minimum int divider requirement + if (N < prescaler_to_min_int_div[prescaler]) continue; + + for(BS=1; BS <= 255; BS+=1){ + //keep the band select frequency at or below 100KHz + //constraint on band select clock + if (pfd_freq/BS > 100e3) continue; + goto done_loop; + } + } done_loop: + + //Fractional-N calculation + MOD = 4095; //max fractional accuracy + FRAC = int((vco_freq/pfd_freq - N)*MOD); + + //Reference divide-by-2 for 50% duty cycle + // if R even, move one divide by 2 to to regs.reference_divide_by_2 + if(R % 2 == 0){ + T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; + R /= 2; + } + + //actual frequency calculation + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2); + + + UHD_LOGV(often) + << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl + + << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" + ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; + + //load the register values + adf4350_regs_t regs; + + regs.frac_12_bit = FRAC; + regs.int_16_bit = N; + regs.mod_12_bit = MOD; + regs.prescaler = prescaler; + regs.r_counter_10_bit = R; + regs.reference_divide_by_2 = T; + regs.reference_doubler = D; + regs.band_select_clock_div = BS; + UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); + regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; + + if (unit == dboard_iface::UNIT_RX) { + freq_range_t rx_lo_5dbm = list_of + (range_t(0.05e9, 1.4e9)) + ; + + freq_range_t rx_lo_2dbm = list_of + (range_t(1.4e9, 2.2e9)) + ; + + if (actual_freq == rx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; + + if (actual_freq == rx_lo_2dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_2DBM; + + } else if (unit == dboard_iface::UNIT_TX) { + freq_range_t tx_lo_5dbm = list_of + (range_t(0.05e9, 1.7e9)) + (range_t(1.9e9, 2.2e9)) + ; + + freq_range_t tx_lo_m1dbm = list_of + (range_t(1.7e9, 1.9e9)) + ; + + if (actual_freq == tx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; + + if (actual_freq == tx_lo_m1dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_M1DBM; + + } + + //write the registers + //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) + int addr; + + for(addr=5; addr>=0; addr--){ + UHD_LOGV(often) << boost::format( + "WBX SPI Reg (0x%02x): 0x%08x" + ) % addr % regs.get_reg(addr) << std::endl; + self_base->get_iface()->write_spi( + unit, spi_config_t::EDGE_RISE, + regs.get_reg(addr), 32 + ); + } + + //return the actual frequency + UHD_LOGV(often) << boost::format( + "WBX tune: actual frequency %f Mhz" + ) % (actual_freq/1e6) << std::endl; + return actual_freq; +} diff --git a/host/lib/usrp/dboard/db_wbx_version4.cpp b/host/lib/usrp/dboard/db_wbx_version4.cpp new file mode 100644 index 000000000..faaf9e3fd --- /dev/null +++ b/host/lib/usrp/dboard/db_wbx_version4.cpp @@ -0,0 +1,336 @@ +// +// Copyright 2011 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 "db_wbx_common.hpp" +#include "adf4351_regs.hpp" +#include <uhd/utils/log.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/sensors.hpp> +#include <uhd/utils/assert_has.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/usrp/dboard_base.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/format.hpp> +#include <boost/math/special_functions/round.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + + +/*********************************************************************** + * WBX Version 3 Constants + **********************************************************************/ +static const uhd::dict<std::string, gain_range_t> wbx_v4_tx_gain_ranges = map_list_of + ("PGA0", gain_range_t(0, 31, 1.0)) +; + +static const freq_range_t wbx_v4_freq_range(50.0e6, 2.2e9); + + +/*********************************************************************** + * Gain-related functions + **********************************************************************/ +static int tx_pga0_gain_to_iobits(double &gain){ + //clip the input + gain = wbx_v4_tx_gain_ranges["PGA0"].clip(gain); + + //convert to attenuation + double attn = wbx_v4_tx_gain_ranges["PGA0"].stop() - gain; + + //calculate the attenuation + int attn_code = boost::math::iround(attn); + int iobits = ( + (attn_code & 16 ? 0 : TX_ATTN_16) | + (attn_code & 8 ? 0 : TX_ATTN_8) | + (attn_code & 4 ? 0 : TX_ATTN_4) | + (attn_code & 2 ? 0 : TX_ATTN_2) | + (attn_code & 1 ? 0 : TX_ATTN_1) + ) & TX_ATTN_MASK; + + UHD_LOGV(often) << boost::format( + "WBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x" + ) % attn % attn_code % (iobits & TX_ATTN_MASK) % TX_ATTN_MASK << std::endl; + + //the actual gain setting + gain = wbx_v4_tx_gain_ranges["PGA0"].stop() - double(attn_code); + + return iobits; +} + + +/*********************************************************************** + * WBX Common Implementation + **********************************************************************/ +wbx_base::wbx_version4::wbx_version4(wbx_base *_self_wbx_base) { + //register our handle on the primary wbx_base instance + self_base = _self_wbx_base; + + //////////////////////////////////////////////////////////////////// + // Register RX properties + //////////////////////////////////////////////////////////////////// + this->get_rx_subtree()->create<std::string>("name").set("WBX RX v4"); + this->get_rx_subtree()->create<double>("freq/value") + .coerce(boost::bind(&wbx_base::wbx_version4::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) + .set((wbx_v4_freq_range.start() + wbx_v4_freq_range.stop())/2.0); + this->get_rx_subtree()->create<meta_range_t>("freq/range").set(wbx_v4_freq_range); + + //////////////////////////////////////////////////////////////////// + // Register TX properties + //////////////////////////////////////////////////////////////////// + this->get_tx_subtree()->create<std::string>("name").set("WBX TX v4"); + BOOST_FOREACH(const std::string &name, wbx_v4_tx_gain_ranges.keys()){ + self_base->get_tx_subtree()->create<double>("gains/"+name+"/value") + .coerce(boost::bind(&wbx_base::wbx_version4::set_tx_gain, this, _1, name)) + .set(wbx_v4_tx_gain_ranges[name].start()); + self_base->get_tx_subtree()->create<meta_range_t>("gains/"+name+"/range") + .set(wbx_v4_tx_gain_ranges[name]); + } + this->get_tx_subtree()->create<double>("freq/value") + .coerce(boost::bind(&wbx_base::wbx_version4::set_lo_freq, this, dboard_iface::UNIT_TX, _1)) + .set((wbx_v4_freq_range.start() + wbx_v4_freq_range.stop())/2.0); + this->get_tx_subtree()->create<meta_range_t>("freq/range").set(wbx_v4_freq_range); + this->get_tx_subtree()->create<bool>("enabled") + .subscribe(boost::bind(&wbx_base::wbx_version4::set_tx_enabled, this, _1)) + .set(true); //start enabled + + //set attenuator control bits + int v4_iobits = TX_ATTN_MASK; + int v4_tx_mod = ADF4351_PDBRF; + + //set the gpio directions and atr controls + self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, v4_tx_mod); + self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXBB_PDB|ADF4351_PDBRF); + self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, TX_PUP_5V|TX_PUP_3V|v4_tx_mod|v4_iobits); + self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RX_PUP_5V|RX_PUP_3V|ADF4351_CE|RXBB_PDB|ADF4351_PDBRF|RX_ATTN_MASK); + + //setup ATR for the mixer enables (always enabled to prevent phase slip between bursts) + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, v4_tx_mod, TX_MIXER_DIS | v4_tx_mod); + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY, v4_tx_mod, TX_MIXER_DIS | v4_tx_mod); + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, v4_tx_mod, TX_MIXER_DIS | v4_tx_mod); + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, v4_tx_mod, TX_MIXER_DIS | v4_tx_mod); + + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); + self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +} + +wbx_base::wbx_version4::~wbx_version4(void){ + /* NOP */ +} + + +/*********************************************************************** + * Enables + **********************************************************************/ +void wbx_base::wbx_version4::set_tx_enabled(bool enb) { + self_base->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, + (enb)? TX_POWER_UP | ADF4351_CE : TX_POWER_DOWN, TX_POWER_UP | TX_POWER_DOWN | 0); +} + + +/*********************************************************************** + * Gain Handling + **********************************************************************/ +double wbx_base::wbx_version4::set_tx_gain(double gain, const std::string &name) { + assert_has(wbx_v4_tx_gain_ranges.keys(), name, "wbx tx gain name"); + if(name == "PGA0"){ + boost::uint16_t io_bits = tx_pga0_gain_to_iobits(gain); + self_base->_tx_gains[name] = gain; + + //write the new gain to tx gpio outputs + self_base->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, io_bits, TX_ATTN_MASK); + } + else UHD_THROW_INVALID_CODE_PATH(); + return self_base->_tx_gains[name]; +} + + +/*********************************************************************** + * Tuning + **********************************************************************/ +double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { + UHD_LOGV(often) << boost::format( + "WBX tune: target frequency %f Mhz" + ) % (target_freq/1e6) << std::endl; + + //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) + static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of + (0,23) //adf4351_regs_t::PRESCALER_4_5 + (1,75) //adf4351_regs_t::PRESCALER_8_9 + ; + + //map rf divider select output dividers to enums + static const uhd::dict<int, adf4351_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of + (1, adf4351_regs_t::RF_DIVIDER_SELECT_DIV1) + (2, adf4351_regs_t::RF_DIVIDER_SELECT_DIV2) + (4, adf4351_regs_t::RF_DIVIDER_SELECT_DIV4) + (8, adf4351_regs_t::RF_DIVIDER_SELECT_DIV8) + (16, adf4351_regs_t::RF_DIVIDER_SELECT_DIV16) + (32, adf4351_regs_t::RF_DIVIDER_SELECT_DIV32) + (64, adf4351_regs_t::RF_DIVIDER_SELECT_DIV64) + ; + + double actual_freq, pfd_freq; + double ref_freq = self_base->get_iface()->get_clock_rate(unit); + int R=0, BS=0, N=0, FRAC=0, MOD=0; + int RFdiv = 1; + adf4351_regs_t::reference_divide_by_2_t T = adf4351_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; + adf4351_regs_t::reference_doubler_t D = adf4351_regs_t::REFERENCE_DOUBLER_DISABLED; + + //Reference doubler for 50% duty cycle + // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 + if(ref_freq <= 12.5e6) D = adf4351_regs_t::REFERENCE_DOUBLER_ENABLED; + + //increase RF divider until acceptable VCO frequency + //start with target_freq*2 because mixer has divide by 2 + double vco_freq = target_freq*2; + while (vco_freq < 2.2e9) { + vco_freq *= 2; + RFdiv *= 2; + } + + //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) + adf4351_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; + + /* + * The goal here is to loop though possible R dividers, + * band select clock dividers, N (int) dividers, and FRAC + * (frac) dividers. + * + * Calculate the N and F dividers for each set of values. + * The loop exits when it meets all of the constraints. + * The resulting loop values are loaded into the registers. + * + * from pg.21 + * + * f_pfd = f_ref*(1+D)/(R*(1+T)) + * f_vco = (N + (FRAC/MOD))*f_pfd + * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD + * f_rf = f_vco/RFdiv) + * f_actual = f_rf/2 + */ + for(R = 1; R <= 1023; R+=1){ + //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) + pfd_freq = ref_freq*(1+D)/(R*(1+T)); + + //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) + if (pfd_freq > 25e6) continue; + + //ignore fractional part of tuning + N = int(std::floor(vco_freq/pfd_freq)); + + //keep N > minimum int divider requirement + if (N < prescaler_to_min_int_div[prescaler]) continue; + + for(BS=1; BS <= 255; BS+=1){ + //keep the band select frequency at or below 100KHz + //constraint on band select clock + if (pfd_freq/BS > 100e3) continue; + goto done_loop; + } + } done_loop: + + //Fractional-N calculation + MOD = 4095; //max fractional accuracy + FRAC = int((vco_freq/pfd_freq - N)*MOD); + + //Reference divide-by-2 for 50% duty cycle + // if R even, move one divide by 2 to to regs.reference_divide_by_2 + if(R % 2 == 0){ + T = adf4351_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; + R /= 2; + } + + //actual frequency calculation + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2); + + + UHD_LOGV(often) + << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl + + << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" + ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; + + //load the register values + adf4351_regs_t regs; + + regs.frac_12_bit = FRAC; + regs.int_16_bit = N; + regs.mod_12_bit = MOD; + regs.prescaler = prescaler; + regs.r_counter_10_bit = R; + regs.reference_divide_by_2 = T; + regs.reference_doubler = D; + regs.band_select_clock_div = BS; + UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); + regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; + + if (unit == dboard_iface::UNIT_RX) { + freq_range_t rx_lo_5dbm = list_of + (range_t(0.05e9, 1.4e9)) + ; + + freq_range_t rx_lo_2dbm = list_of + (range_t(1.4e9, 2.2e9)) + ; + + if (actual_freq == rx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4351_regs_t::OUTPUT_POWER_5DBM; + + if (actual_freq == rx_lo_2dbm.clip(actual_freq)) regs.output_power = adf4351_regs_t::OUTPUT_POWER_2DBM; + + } else if (unit == dboard_iface::UNIT_TX) { + freq_range_t tx_lo_5dbm = list_of + (range_t(0.05e9, 1.7e9)) + (range_t(1.9e9, 2.2e9)) + ; + + freq_range_t tx_lo_m1dbm = list_of + (range_t(1.7e9, 1.9e9)) + ; + + if (actual_freq == tx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4351_regs_t::OUTPUT_POWER_5DBM; + + if (actual_freq == tx_lo_m1dbm.clip(actual_freq)) regs.output_power = adf4351_regs_t::OUTPUT_POWER_M1DBM; + + } + + //write the registers + //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) + int addr; + + for(addr=5; addr>=0; addr--){ + UHD_LOGV(often) << boost::format( + "WBX SPI Reg (0x%02x): 0x%08x" + ) % addr % regs.get_reg(addr) << std::endl; + self_base->get_iface()->write_spi( + unit, spi_config_t::EDGE_RISE, + regs.get_reg(addr), 32 + ); + } + + //return the actual frequency + UHD_LOGV(often) << boost::format( + "WBX tune: actual frequency %f Mhz" + ) % (actual_freq/1e6) << std::endl; + return actual_freq; +} diff --git a/host/lib/usrp/dboard/db_xcvr2450.cpp b/host/lib/usrp/dboard/db_xcvr2450.cpp index bfd4421b8..cf1637335 100644 --- a/host/lib/usrp/dboard/db_xcvr2450.cpp +++ b/host/lib/usrp/dboard/db_xcvr2450.cpp @@ -76,7 +76,22 @@ static const freq_range_t xcvr_freq_range = list_of (range_t(4.9e9, 6.0e9)) ; -static const prop_names_t xcvr_antennas = list_of("J1")("J2"); +//Multiplied by 2.0 for conversion to complex bandpass from lowpass +static const freq_range_t xcvr_tx_bandwidth_range = list_of + (range_t(2.0*12e6)) + (range_t(2.0*18e6)) + (range_t(2.0*24e6)) +; + +//Multiplied by 2.0 for conversion to complex bandpass from lowpass +static const freq_range_t xcvr_rx_bandwidth_range = list_of + (range_t(2.0*0.9*7.5e6, 2.0*1.1*7.5e6)) + (range_t(2.0*0.9*9.5e6, 2.0*1.1*9.5e6)) + (range_t(2.0*0.9*14e6, 2.0*1.1*14e6)) + (range_t(2.0*0.9*18e6, 2.0*1.1*18e6)) +; + +static const std::vector<std::string> xcvr_antennas = list_of("J1")("J2"); static const uhd::dict<std::string, gain_range_t> xcvr_tx_gain_ranges = map_list_of ("VGA", gain_range_t(0, 30, 0.5)) @@ -99,12 +114,6 @@ public: xcvr2450(ctor_args_t args); ~xcvr2450(void); - void rx_get(const wax::obj &key, wax::obj &val); - void rx_set(const wax::obj &key, const wax::obj &val); - - void tx_get(const wax::obj &key, wax::obj &val); - void tx_set(const wax::obj &key, const wax::obj &val); - private: double _lo_freq; double _rx_bandwidth, _tx_bandwidth; @@ -113,14 +122,14 @@ private: int _ad9515div; max2829_regs_t _max2829_regs; - void set_lo_freq(double target_freq); - void set_lo_freq_core(double target_freq); + double set_lo_freq(double target_freq); + double set_lo_freq_core(double target_freq); void set_tx_ant(const std::string &ant); void set_rx_ant(const std::string &ant); - void set_tx_gain(double gain, const std::string &name); - void set_rx_gain(double gain, const std::string &name); - void set_rx_bandwidth(double bandwidth); - void set_tx_bandwidth(double bandwidth); + double set_tx_gain(double gain, const std::string &name); + double set_rx_gain(double gain, const std::string &name); + double set_rx_bandwidth(double bandwidth); + double set_tx_bandwidth(double bandwidth); void update_atr(void); void spi_reset(void); @@ -139,18 +148,19 @@ private: static bool is_highband(double freq){return freq > 3e9;} /*! - * Is the LO locked? - * \return true for locked + * Get the lock detect status of the LO. + * \return sensor for locked */ - bool get_locked(void){ - return (this->get_iface()->read_gpio(dboard_iface::UNIT_RX) & LOCKDET_RXIO) != 0; + sensor_value_t get_locked(void){ + const bool locked = (this->get_iface()->read_gpio(dboard_iface::UNIT_RX) & LOCKDET_RXIO) != 0; + return sensor_value_t("LO", locked, "locked", "unlocked"); } /*! * Read the RSSI from the aux adc - * \return the rssi in dB + * \return the rssi sensor in dBm */ - double get_rssi(void){ + sensor_value_t get_rssi(void){ //*FIXME* RSSI depends on LNA Gain Setting (datasheet pg 16 top middle chart) double max_power = 0.0; switch(_max2829_regs.rx_lna_gain){ @@ -165,7 +175,8 @@ private: static const double rssi_dyn_range = 60; //calculate the rssi from the voltage double voltage = this->get_iface()->read_aux_adc(dboard_iface::UNIT_RX, dboard_iface::AUX_ADC_B); - return max_power - rssi_dyn_range*(voltage - min_v)/(max_v - min_v); + double rssi = max_power - rssi_dyn_range*(voltage - min_v)/(max_v - min_v); + return sensor_value_t("RSSI", rssi, "dBm"); } }; @@ -185,15 +196,6 @@ UHD_STATIC_BLOCK(reg_xcvr2450_dboard){ * Structors **********************************************************************/ xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){ - //enable only the clocks we need - this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); - - //set the gpio directions and atr controls (identically) - this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, TXIO_MASK); - this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXIO_MASK); - this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, TXIO_MASK); - this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RXIO_MASK); - spi_reset(); //prepare the spi _rx_bandwidth = 9.5e6; @@ -222,16 +224,88 @@ xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){ this->send_reg(reg); } - //set defaults for LO, gains, antennas - set_lo_freq(2.45e9); - set_rx_ant(xcvr_antennas.at(0)); - set_tx_ant(xcvr_antennas.at(1)); - BOOST_FOREACH(const std::string &name, xcvr_tx_gain_ranges.keys()){ - set_tx_gain(xcvr_tx_gain_ranges[name].start(), name); - } + //////////////////////////////////////////////////////////////////// + // Register RX properties + //////////////////////////////////////////////////////////////////// + this->get_rx_subtree()->create<std::string>("name") + .set(get_rx_id().to_pp_string()); + this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") + .publish(boost::bind(&xcvr2450::get_locked, this)); + this->get_rx_subtree()->create<sensor_value_t>("sensors/rssi") + .publish(boost::bind(&xcvr2450::get_rssi, this)); BOOST_FOREACH(const std::string &name, xcvr_rx_gain_ranges.keys()){ - set_rx_gain(xcvr_rx_gain_ranges[name].start(), name); + this->get_rx_subtree()->create<double>("gains/"+name+"/value") + .coerce(boost::bind(&xcvr2450::set_rx_gain, this, _1, name)) + .set(xcvr_rx_gain_ranges[name].start()); + this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range") + .set(xcvr_rx_gain_ranges[name]); } + this->get_rx_subtree()->create<double>("freq/value") + .coerce(boost::bind(&xcvr2450::set_lo_freq, this, _1)) + .set(double(2.45e9)); + this->get_rx_subtree()->create<meta_range_t>("freq/range") + .set(xcvr_freq_range); + this->get_rx_subtree()->create<std::string>("antenna/value") + .subscribe(boost::bind(&xcvr2450::set_rx_ant, this, _1)) + .set(xcvr_antennas.at(0)); + this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options") + .set(xcvr_antennas); + this->get_rx_subtree()->create<std::string>("connection") + .set("IQ"); + this->get_rx_subtree()->create<bool>("enabled") + .set(true); //always enabled + this->get_rx_subtree()->create<bool>("use_lo_offset") + .set(false); + this->get_rx_subtree()->create<double>("bandwidth/value") + .coerce(boost::bind(&xcvr2450::set_rx_bandwidth, this, _1)) //complex bandpass bandwidth + .set(2.0*_rx_bandwidth); //_rx_bandwidth in lowpass, convert to complex bandpass + this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") + .set(xcvr_rx_bandwidth_range); + + //////////////////////////////////////////////////////////////////// + // Register TX properties + //////////////////////////////////////////////////////////////////// + this->get_tx_subtree()->create<std::string>("name") + .set(get_tx_id().to_pp_string()); + this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") + .publish(boost::bind(&xcvr2450::get_locked, this)); + BOOST_FOREACH(const std::string &name, xcvr_tx_gain_ranges.keys()){ + this->get_tx_subtree()->create<double>("gains/"+name+"/value") + .coerce(boost::bind(&xcvr2450::set_tx_gain, this, _1, name)) + .set(xcvr_tx_gain_ranges[name].start()); + this->get_tx_subtree()->create<meta_range_t>("gains/"+name+"/range") + .set(xcvr_tx_gain_ranges[name]); + } + this->get_tx_subtree()->create<double>("freq/value") + .coerce(boost::bind(&xcvr2450::set_lo_freq, this, _1)) + .set(double(2.45e9)); + this->get_tx_subtree()->create<meta_range_t>("freq/range") + .set(xcvr_freq_range); + this->get_tx_subtree()->create<std::string>("antenna/value") + .subscribe(boost::bind(&xcvr2450::set_tx_ant, this, _1)) + .set(xcvr_antennas.at(1)); + this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options") + .set(xcvr_antennas); + this->get_tx_subtree()->create<std::string>("connection") + .set("IQ"); + this->get_tx_subtree()->create<bool>("enabled") + .set(true); //always enabled + this->get_tx_subtree()->create<bool>("use_lo_offset") + .set(true); + this->get_tx_subtree()->create<double>("bandwidth/value") + .coerce(boost::bind(&xcvr2450::set_tx_bandwidth, this, _1)) //complex bandpass bandwidth + .set(2.0*_tx_bandwidth); //_tx_bandwidth in lowpass, convert to complex bandpass + this->get_tx_subtree()->create<meta_range_t>("bandwidth/range") + .set(xcvr_tx_bandwidth_range); + + //enable only the clocks we need + this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); + + //set the gpio directions and atr controls (identically) + this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, TXIO_MASK); + this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXIO_MASK); + this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, TXIO_MASK); + this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RXIO_MASK); } xcvr2450::~xcvr2450(void){ @@ -249,6 +323,9 @@ void xcvr2450::spi_reset(void){ boost::this_thread::sleep(boost::posix_time::milliseconds(10)); } +/*********************************************************************** + * Update ATR regs which change with Antenna or Freq + **********************************************************************/ void xcvr2450::update_atr(void){ //calculate tx atr pins int band_sel = (xcvr2450::is_highband(_lo_freq))? HB_PA_TXIO : LB_PA_TXIO; @@ -274,17 +351,19 @@ void xcvr2450::update_atr(void){ /*********************************************************************** * Tuning **********************************************************************/ -void xcvr2450::set_lo_freq(double target_freq){ +double xcvr2450::set_lo_freq(double target_freq){ //tune the LO and sleep a bit for lock //if not locked, try some carrier offsets + double actual = 0.0; for (double offset = 0.0; offset <= 3e6; offset+=1e6){ - this->set_lo_freq_core(target_freq + offset); + actual = this->set_lo_freq_core(target_freq + offset); boost::this_thread::sleep(boost::posix_time::milliseconds(50)); - if (this->get_locked()) return; + if (this->get_locked().to_bool()) break; } + return actual; } -void xcvr2450::set_lo_freq_core(double target_freq){ +double xcvr2450::set_lo_freq_core(double target_freq){ //clip the input to the range target_freq = xcvr_freq_range.clip(target_freq); @@ -348,6 +427,8 @@ void xcvr2450::set_lo_freq_core(double target_freq){ this->send_reg(0x5); _max2829_regs.vco_bandswitch = max2829_regs_t::VCO_BANDSWITCH_AUTOMATIC;; this->send_reg(0x5); + + return _lo_freq; } /*********************************************************************** @@ -441,7 +522,7 @@ static int gain_to_rx_lna_reg(double &gain){ return reg; } -void xcvr2450::set_tx_gain(double gain, const std::string &name){ +double xcvr2450::set_tx_gain(double gain, const std::string &name){ assert_has(xcvr_tx_gain_ranges.keys(), name, "xcvr tx gain name"); if (name == "VGA"){ _max2829_regs.tx_vga_gain = gain_to_tx_vga_reg(gain); @@ -453,9 +534,11 @@ void xcvr2450::set_tx_gain(double gain, const std::string &name){ } else UHD_THROW_INVALID_CODE_PATH(); _tx_gains[name] = gain; + + return gain; } -void xcvr2450::set_rx_gain(double gain, const std::string &name){ +double xcvr2450::set_rx_gain(double gain, const std::string &name){ assert_has(xcvr_rx_gain_ranges.keys(), name, "xcvr rx gain name"); if (name == "VGA"){ _max2829_regs.rx_vga_gain = gain_to_rx_vga_reg(gain); @@ -467,6 +550,8 @@ void xcvr2450::set_rx_gain(double gain, const std::string &name){ } else UHD_THROW_INVALID_CODE_PATH(); _rx_gains[name] = gain; + + return gain; } @@ -541,9 +626,12 @@ static max2829_regs_t::rx_lpf_coarse_adj_t bandwidth_to_rx_lpf_coarse_reg(double UHD_THROW_INVALID_CODE_PATH(); } -void xcvr2450::set_rx_bandwidth(double bandwidth){ +double xcvr2450::set_rx_bandwidth(double bandwidth){ double requested_bandwidth = bandwidth; + //convert complex bandpass to lowpass bandwidth + bandwidth = bandwidth/2.0; + //compute coarse low pass cutoff frequency setting _max2829_regs.rx_lpf_coarse_adj = bandwidth_to_rx_lpf_coarse_reg(bandwidth); @@ -559,9 +647,14 @@ void xcvr2450::set_rx_bandwidth(double bandwidth){ UHD_LOGV(often) << 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; + + return 2.0*_rx_bandwidth; } -void xcvr2450::set_tx_bandwidth(double bandwidth){ +double xcvr2450::set_tx_bandwidth(double bandwidth){ + //convert complex bandpass to lowpass bandwidth + bandwidth = bandwidth/2.0; + //compute coarse low pass cutoff frequency setting _max2829_regs.tx_lpf_coarse_adj = bandwidth_to_tx_lpf_coarse_reg(bandwidth); @@ -574,219 +667,7 @@ void xcvr2450::set_tx_bandwidth(double bandwidth){ UHD_LOGV(often) << 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 - **********************************************************************/ -void xcvr2450::rx_get(const wax::obj &key_, 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_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(_rx_gains.keys(), key.name, "xcvr rx gain name"); - val = _rx_gains[key.name]; - return; - - case SUBDEV_PROP_GAIN_RANGE: - assert_has(xcvr_rx_gain_ranges.keys(), key.name, "xcvr rx gain name"); - val = xcvr_rx_gain_ranges[key.name]; - return; - - case SUBDEV_PROP_GAIN_NAMES: - val = prop_names_t(xcvr_rx_gain_ranges.keys()); - return; - - case SUBDEV_PROP_FREQ: - val = _lo_freq; - return; - - case SUBDEV_PROP_FREQ_RANGE: - val = xcvr_freq_range; - return; - - case SUBDEV_PROP_ANTENNA: - val = _rx_ant; - return; - - case SUBDEV_PROP_ANTENNA_NAMES: - val = xcvr_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_SENSOR: - if (key.name == "lo_locked") - val = sensor_value_t("LO", this->get_locked(), "locked", "unlocked"); - else if (key.name == "rssi") - val = sensor_value_t("RSSI", this->get_rssi(), "dBm"); - else - UHD_THROW_INVALID_CODE_PATH(); - return; - - case SUBDEV_PROP_SENSOR_NAMES:{ - prop_names_t names = list_of("lo_locked")("rssi"); - val = names; - } - 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(); - } -} - -void xcvr2450::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_FREQ: - this->set_lo_freq(val.as<double>()); - return; - - case SUBDEV_PROP_GAIN: - this->set_rx_gain(val.as<double>(), key.name); - return; - - case SUBDEV_PROP_ANTENNA: - 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(); - } -} - -/*********************************************************************** - * TX Get and Set - **********************************************************************/ -void xcvr2450::tx_get(const wax::obj &key_, 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_NAME: - val = get_tx_id().to_pp_string(); - return; - - case SUBDEV_PROP_OTHERS: - val = prop_names_t(); //empty - return; - - case SUBDEV_PROP_GAIN: - assert_has(_tx_gains.keys(), key.name, "xcvr tx gain name"); - val = _tx_gains[key.name]; - return; - - case SUBDEV_PROP_GAIN_RANGE: - assert_has(xcvr_tx_gain_ranges.keys(), key.name, "xcvr tx gain name"); - val = xcvr_tx_gain_ranges[key.name]; - return; - - case SUBDEV_PROP_GAIN_NAMES: - val = prop_names_t(xcvr_tx_gain_ranges.keys()); - return; - - case SUBDEV_PROP_FREQ: - val = _lo_freq; - return; - - case SUBDEV_PROP_FREQ_RANGE: - val = xcvr_freq_range; - return; - - case SUBDEV_PROP_ANTENNA: - val = _tx_ant; - return; - - case SUBDEV_PROP_ANTENNA_NAMES: - val = xcvr_antennas; - return; - - case SUBDEV_PROP_CONNECTION: - val = SUBDEV_CONN_COMPLEX_QI; - return; - - case SUBDEV_PROP_ENABLED: - val = true; //always enabled - return; - - case SUBDEV_PROP_USE_LO_OFFSET: - val = false; - return; - - case SUBDEV_PROP_SENSOR: - UHD_ASSERT_THROW(key.name == "lo_locked"); - val = sensor_value_t("LO", this->get_locked(), "locked", "unlocked"); - return; - - case SUBDEV_PROP_SENSOR_NAMES: - val = prop_names_t(1, "lo_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(); - } -} - -void xcvr2450::tx_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_FREQ: - set_lo_freq(val.as<double>()); - return; - - case SUBDEV_PROP_GAIN: - this->set_tx_gain(val.as<double>(), 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(); - } + //convert lowpass back to complex bandpass bandwidth + return 2.0*_tx_bandwidth; } diff --git a/host/lib/usrp/dboard_base.cpp b/host/lib/usrp/dboard_base.cpp index e14c9d144..fe14c02b9 100644 --- a/host/lib/usrp/dboard_base.cpp +++ b/host/lib/usrp/dboard_base.cpp @@ -20,6 +20,7 @@ #include <boost/format.hpp> #include <stdexcept> +using namespace uhd; using namespace uhd::usrp; /*********************************************************************** @@ -34,10 +35,6 @@ dboard_base::dboard_base(ctor_args_t args){ _impl->args = *static_cast<dboard_ctor_args_t *>(args); } -dboard_base::~dboard_base(void){ - /* NOP */ -} - std::string dboard_base::get_subdev_name(void){ return _impl->args.sd_name; } @@ -54,6 +51,14 @@ dboard_id_t dboard_base::get_tx_id(void){ return _impl->args.tx_id; } +property_tree::sptr dboard_base::get_rx_subtree(void){ + return _impl->args.rx_subtree; +} + +property_tree::sptr dboard_base::get_tx_subtree(void){ + return _impl->args.tx_subtree; +} + /*********************************************************************** * xcvr dboard dboard_base class **********************************************************************/ @@ -70,10 +75,6 @@ xcvr_dboard_base::xcvr_dboard_base(ctor_args_t args) : dboard_base(args){ } } -xcvr_dboard_base::~xcvr_dboard_base(void){ - /* NOP */ -} - /*********************************************************************** * rx dboard dboard_base class **********************************************************************/ @@ -86,18 +87,6 @@ rx_dboard_base::rx_dboard_base(ctor_args_t args) : dboard_base(args){ } } -rx_dboard_base::~rx_dboard_base(void){ - /* NOP */ -} - -void rx_dboard_base::tx_get(const wax::obj &, wax::obj &){ - throw uhd::runtime_error("cannot call tx_get on a rx dboard"); -} - -void rx_dboard_base::tx_set(const wax::obj &, const wax::obj &){ - throw uhd::runtime_error("cannot call tx_set on a rx dboard"); -} - /*********************************************************************** * tx dboard dboard_base class **********************************************************************/ @@ -109,15 +98,3 @@ tx_dboard_base::tx_dboard_base(ctor_args_t args) : dboard_base(args){ ) % get_rx_id().to_pp_string() % dboard_id_t::none().to_pp_string())); } } - -tx_dboard_base::~tx_dboard_base(void){ - /* NOP */ -} - -void tx_dboard_base::rx_get(const wax::obj &, wax::obj &){ - throw uhd::runtime_error("cannot call rx_get on a tx dboard"); -} - -void tx_dboard_base::rx_set(const wax::obj &, const wax::obj &){ - throw uhd::runtime_error("cannot call rx_set on a tx dboard"); -} diff --git a/host/lib/usrp/dboard_ctor_args.hpp b/host/lib/usrp/dboard_ctor_args.hpp index 708f2ea08..99c071ff8 100644 --- a/host/lib/usrp/dboard_ctor_args.hpp +++ b/host/lib/usrp/dboard_ctor_args.hpp @@ -18,6 +18,7 @@ #ifndef INCLUDED_LIBUHD_USRP_DBOARD_CTOR_ARGS_HPP #define INCLUDED_LIBUHD_USRP_DBOARD_CTOR_ARGS_HPP +#include <uhd/property_tree.hpp> #include <uhd/usrp/dboard_id.hpp> #include <uhd/usrp/dboard_base.hpp> #include <uhd/usrp/dboard_iface.hpp> @@ -29,6 +30,7 @@ namespace uhd{ namespace usrp{ std::string sd_name; dboard_iface::sptr db_iface; dboard_id_t rx_id, tx_id; + property_tree::sptr rx_subtree, tx_subtree; }; }} //namespace diff --git a/host/lib/usrp/dboard_manager.cpp b/host/lib/usrp/dboard_manager.cpp index 0326c28ce..816fba0c4 100644 --- a/host/lib/usrp/dboard_manager.cpp +++ b/host/lib/usrp/dboard_manager.cpp @@ -79,7 +79,7 @@ bool operator==(const dboard_key_t &lhs, const dboard_key_t &rhs){ * storage and registering for dboards **********************************************************************/ //dboard registry tuple: dboard constructor, canonical name, subdev names -typedef boost::tuple<dboard_manager::dboard_ctor_t, std::string, prop_names_t> args_t; +typedef boost::tuple<dboard_manager::dboard_ctor_t, std::string, std::vector<std::string> > args_t; //map a dboard id to a dboard constructor typedef uhd::dict<dboard_key_t, args_t> id_to_args_map_t; @@ -89,7 +89,7 @@ static void register_dboard_key( const dboard_key_t &dboard_key, dboard_manager::dboard_ctor_t dboard_ctor, const std::string &name, - const prop_names_t &subdev_names + const std::vector<std::string> &subdev_names ){ UHD_LOGV(always) << "registering: " << name << std::endl; if (get_id_to_args_map().has_key(dboard_key)){ @@ -110,7 +110,7 @@ void dboard_manager::register_dboard( const dboard_id_t &dboard_id, dboard_ctor_t dboard_ctor, const std::string &name, - const prop_names_t &subdev_names + const std::vector<std::string> &subdev_names ){ register_dboard_key(dboard_key_t(dboard_id), dboard_ctor, name, subdev_names); } @@ -120,7 +120,7 @@ void dboard_manager::register_dboard( const dboard_id_t &tx_dboard_id, dboard_ctor_t dboard_ctor, const std::string &name, - const prop_names_t &subdev_names + const std::vector<std::string> &subdev_names ){ register_dboard_key(dboard_key_t(rx_dboard_id, tx_dboard_id), dboard_ctor, name, subdev_names); } @@ -144,46 +144,6 @@ std::string dboard_id_t::to_pp_string(void) const{ } /*********************************************************************** - * internal helper classes - **********************************************************************/ -/*! - * A special wax proxy object that forwards calls to a subdev. - * A sptr to an instance will be used in the properties structure. - */ -class subdev_proxy : boost::noncopyable, public wax::obj{ -public: - typedef boost::shared_ptr<subdev_proxy> sptr; - enum type_t{RX_TYPE, TX_TYPE}; - - //structors - subdev_proxy(dboard_base::sptr subdev, type_t type): - _subdev(subdev), _type(type) - { - /* NOP */ - } - -private: - dboard_base::sptr _subdev; - type_t _type; - - //forward the get calls to the rx or tx - void get(const wax::obj &key, wax::obj &val){ - switch(_type){ - case RX_TYPE: return _subdev->rx_get(key, val); - case TX_TYPE: return _subdev->tx_get(key, val); - } - } - - //forward the set calls to the rx or tx - void set(const wax::obj &key, const wax::obj &val){ - switch(_type){ - case RX_TYPE: return _subdev->rx_set(key, val); - case TX_TYPE: return _subdev->tx_set(key, val); - } - } -}; - -/*********************************************************************** * dboard manager implementation class **********************************************************************/ class dboard_manager_impl : public dboard_manager{ @@ -192,23 +152,18 @@ public: dboard_manager_impl( dboard_id_t rx_dboard_id, dboard_id_t tx_dboard_id, - dboard_iface::sptr iface + dboard_iface::sptr iface, + property_tree::sptr subtree ); ~dboard_manager_impl(void); - //dboard_iface - prop_names_t get_rx_subdev_names(void); - prop_names_t get_tx_subdev_names(void); - wax::obj get_rx_subdev(const std::string &subdev_name); - wax::obj get_tx_subdev(const std::string &subdev_name); - private: - void init(dboard_id_t, dboard_id_t); + void init(dboard_id_t, dboard_id_t, property_tree::sptr); //list of rx and tx dboards in this dboard_manager //each dboard here is actually a subdevice proxy //the subdevice proxy is internal to the cpp file - uhd::dict<std::string, subdev_proxy::sptr> _rx_dboards; - uhd::dict<std::string, subdev_proxy::sptr> _tx_dboards; + uhd::dict<std::string, dboard_base::sptr> _rx_dboards; + uhd::dict<std::string, dboard_base::sptr> _tx_dboards; dboard_iface::sptr _iface; void set_nice_dboard_if(void); }; @@ -219,10 +174,16 @@ private: dboard_manager::sptr dboard_manager::make( dboard_id_t rx_dboard_id, dboard_id_t tx_dboard_id, - dboard_iface::sptr iface + dboard_id_t gdboard_id, + dboard_iface::sptr iface, + property_tree::sptr subtree ){ return dboard_manager::sptr( - new dboard_manager_impl(rx_dboard_id, tx_dboard_id, iface) + new dboard_manager_impl( + rx_dboard_id, + (gdboard_id == dboard_id_t::none())? tx_dboard_id : gdboard_id, + iface, subtree + ) ); } @@ -232,21 +193,22 @@ dboard_manager::sptr dboard_manager::make( dboard_manager_impl::dboard_manager_impl( dboard_id_t rx_dboard_id, dboard_id_t tx_dboard_id, - dboard_iface::sptr iface + dboard_iface::sptr iface, + property_tree::sptr subtree ): _iface(iface) { try{ - this->init(rx_dboard_id, tx_dboard_id); + this->init(rx_dboard_id, tx_dboard_id, subtree); } catch(const std::exception &e){ UHD_MSG(error) << "The daughterboard manager encountered a recoverable error in init" << std::endl << e.what(); - this->init(dboard_id_t::none(), dboard_id_t::none()); + this->init(dboard_id_t::none(), dboard_id_t::none(), subtree); } } void dboard_manager_impl::init( - dboard_id_t rx_dboard_id, dboard_id_t tx_dboard_id + dboard_id_t rx_dboard_id, dboard_id_t tx_dboard_id, property_tree::sptr subtree ){ //find the dboard key matches for the dboard ids dboard_key_t rx_dboard_key, tx_dboard_key, xcvr_dboard_key; @@ -283,7 +245,7 @@ void dboard_manager_impl::init( if (xcvr_dboard_key.is_xcvr()){ //extract data for the xcvr dboard key - dboard_ctor_t dboard_ctor; std::string name; prop_names_t subdevs; + dboard_ctor_t dboard_ctor; std::string name; std::vector<std::string> subdevs; boost::tie(dboard_ctor, name, subdevs) = get_id_to_args_map()[xcvr_dboard_key]; //create the xcvr object for each subdevice @@ -291,15 +253,11 @@ void dboard_manager_impl::init( db_ctor_args.sd_name = subdev; db_ctor_args.rx_id = rx_dboard_id; db_ctor_args.tx_id = tx_dboard_id; + db_ctor_args.rx_subtree = subtree->subtree("rx_frontends/" + subdev); + db_ctor_args.tx_subtree = subtree->subtree("tx_frontends/" + subdev); dboard_base::sptr xcvr_dboard = dboard_ctor(&db_ctor_args); - //create a rx proxy for this xcvr board - _rx_dboards[subdev] = subdev_proxy::sptr( - new subdev_proxy(xcvr_dboard, subdev_proxy::RX_TYPE) - ); - //create a tx proxy for this xcvr board - _tx_dboards[subdev] = subdev_proxy::sptr( - new subdev_proxy(xcvr_dboard, subdev_proxy::TX_TYPE) - ); + _rx_dboards[subdev] = xcvr_dboard; + _tx_dboards[subdev] = xcvr_dboard; } } @@ -312,7 +270,7 @@ void dboard_manager_impl::init( } //extract data for the rx dboard key - dboard_ctor_t rx_dboard_ctor; std::string rx_name; prop_names_t rx_subdevs; + dboard_ctor_t rx_dboard_ctor; std::string rx_name; std::vector<std::string> rx_subdevs; boost::tie(rx_dboard_ctor, rx_name, rx_subdevs) = get_id_to_args_map()[rx_dboard_key]; //make the rx subdevs @@ -320,11 +278,9 @@ void dboard_manager_impl::init( db_ctor_args.sd_name = subdev; db_ctor_args.rx_id = rx_dboard_id; db_ctor_args.tx_id = dboard_id_t::none(); - dboard_base::sptr rx_dboard = rx_dboard_ctor(&db_ctor_args); - //create a rx proxy for this rx board - _rx_dboards[subdev] = subdev_proxy::sptr( - new subdev_proxy(rx_dboard, subdev_proxy::RX_TYPE) - ); + db_ctor_args.rx_subtree = subtree->subtree("rx_frontends/" + subdev); + db_ctor_args.tx_subtree = property_tree::sptr(); //null + _rx_dboards[subdev] = rx_dboard_ctor(&db_ctor_args); } //force the tx key to the unknown board for bad combinations @@ -333,7 +289,7 @@ void dboard_manager_impl::init( } //extract data for the tx dboard key - dboard_ctor_t tx_dboard_ctor; std::string tx_name; prop_names_t tx_subdevs; + dboard_ctor_t tx_dboard_ctor; std::string tx_name; std::vector<std::string> tx_subdevs; boost::tie(tx_dboard_ctor, tx_name, tx_subdevs) = get_id_to_args_map()[tx_dboard_key]; //make the tx subdevs @@ -341,11 +297,9 @@ void dboard_manager_impl::init( db_ctor_args.sd_name = subdev; db_ctor_args.rx_id = dboard_id_t::none(); db_ctor_args.tx_id = tx_dboard_id; - dboard_base::sptr tx_dboard = tx_dboard_ctor(&db_ctor_args); - //create a tx proxy for this tx board - _tx_dboards[subdev] = subdev_proxy::sptr( - new subdev_proxy(tx_dboard, subdev_proxy::TX_TYPE) - ); + db_ctor_args.rx_subtree = property_tree::sptr(); //null + db_ctor_args.tx_subtree = subtree->subtree("tx_frontends/" + subdev); + _tx_dboards[subdev] = tx_dboard_ctor(&db_ctor_args); } } } @@ -354,30 +308,6 @@ dboard_manager_impl::~dboard_manager_impl(void){UHD_SAFE_CALL( set_nice_dboard_if(); )} -prop_names_t dboard_manager_impl::get_rx_subdev_names(void){ - return _rx_dboards.keys(); -} - -prop_names_t dboard_manager_impl::get_tx_subdev_names(void){ - return _tx_dboards.keys(); -} - -wax::obj dboard_manager_impl::get_rx_subdev(const std::string &subdev_name){ - if (not _rx_dboards.has_key(subdev_name)) throw uhd::key_error( - str(boost::format("Unknown rx subdev name %s") % subdev_name) - ); - //get a link to the rx subdev proxy - return _rx_dboards[subdev_name]->get_link(); -} - -wax::obj dboard_manager_impl::get_tx_subdev(const std::string &subdev_name){ - if (not _tx_dboards.has_key(subdev_name)) throw uhd::key_error( - str(boost::format("Unknown tx subdev name %s") % subdev_name) - ); - //get a link to the tx subdev proxy - return _tx_dboards[subdev_name]->get_link(); -} - void dboard_manager_impl::set_nice_dboard_if(void){ //make a list of possible unit types std::vector<dboard_iface::unit_t> units = boost::assign::list_of @@ -392,137 +322,4 @@ 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; - } -} - -/*********************************************************************** - * Populate a properties tree from a subdev waxy object - **********************************************************************/ -#include <uhd/types/ranges.hpp> -#include <uhd/types/sensors.hpp> - -static sensor_value_t get_sensor(wax::obj subdev, const std::string &name){ - return subdev[named_prop_t(SUBDEV_PROP_SENSOR, name)].as<sensor_value_t>(); -} - -static void set_gain(wax::obj subdev, const std::string &name, const double gain){ - subdev[named_prop_t(SUBDEV_PROP_GAIN, name)] = gain; -} - -static double get_gain(wax::obj subdev, const std::string &name){ - return subdev[named_prop_t(SUBDEV_PROP_GAIN, name)].as<double>(); -} - -static meta_range_t get_gain_range(wax::obj subdev, const std::string &name){ - return subdev[named_prop_t(SUBDEV_PROP_GAIN_RANGE, name)].as<meta_range_t>(); -} - -static void set_freq(wax::obj subdev, const double freq){ - subdev[SUBDEV_PROP_FREQ] = freq; -} - -static double get_freq(wax::obj subdev){ - return subdev[SUBDEV_PROP_FREQ].as<double>(); -} - -static meta_range_t get_freq_range(wax::obj subdev){ - return subdev[SUBDEV_PROP_FREQ_RANGE].as<meta_range_t>(); -} - -static void set_ant(wax::obj subdev, const std::string &ant){ - subdev[SUBDEV_PROP_ANTENNA] = ant; -} - -static std::string get_ant(wax::obj subdev){ - return subdev[SUBDEV_PROP_ANTENNA].as<std::string>(); -} - -static std::vector<std::string> get_ants(wax::obj subdev){ - return subdev[SUBDEV_PROP_ANTENNA_NAMES].as<std::vector<std::string> >(); -} - -static std::string get_conn(wax::obj subdev){ - switch(subdev[SUBDEV_PROP_CONNECTION].as<subdev_conn_t>()){ - case SUBDEV_CONN_COMPLEX_IQ: return "IQ"; - case SUBDEV_CONN_COMPLEX_QI: return "QI"; - case SUBDEV_CONN_REAL_I: return "I"; - case SUBDEV_CONN_REAL_Q: return "Q"; - } - UHD_THROW_INVALID_CODE_PATH(); -} - -static bool get_use_lo_off(wax::obj subdev){ - return subdev[SUBDEV_PROP_USE_LO_OFFSET].as<bool>(); -} - -static bool get_set_enb(wax::obj subdev, const bool enb){ - subdev[SUBDEV_PROP_ENABLED] = enb; - return subdev[SUBDEV_PROP_ENABLED].as<bool>(); -} - -static void set_bw(wax::obj subdev, const double freq){ - subdev[SUBDEV_PROP_BANDWIDTH] = freq; -} - -static double get_bw(wax::obj subdev){ - return subdev[SUBDEV_PROP_BANDWIDTH].as<double>(); -} - -void dboard_manager::populate_prop_tree_from_subdev( - property_tree::sptr subtree, wax::obj subdev -){ - subtree->create<std::string>("name").set(subdev[SUBDEV_PROP_NAME].as<std::string>()); - - const prop_names_t sensor_names = subdev[SUBDEV_PROP_SENSOR_NAMES].as<prop_names_t>(); - subtree->create<int>("sensors"); //phony property so this dir exists - BOOST_FOREACH(const std::string &name, sensor_names){ - subtree->create<sensor_value_t>("sensors/" + name) - .publish(boost::bind(&get_sensor, subdev, name)); - } - - const prop_names_t gain_names = subdev[SUBDEV_PROP_GAIN_NAMES].as<prop_names_t>(); - subtree->create<int>("gains"); //phony property so this dir exists - BOOST_FOREACH(const std::string &name, gain_names){ - subtree->create<double>("gains/" + name + "/value") - .publish(boost::bind(&get_gain, subdev, name)) - .subscribe(boost::bind(&set_gain, subdev, name, _1)); - subtree->create<meta_range_t>("gains/" + name + "/range") - .publish(boost::bind(&get_gain_range, subdev, name)); - } - - subtree->create<double>("freq/value") - .publish(boost::bind(&get_freq, subdev)) - .subscribe(boost::bind(&set_freq, subdev, _1)); - - subtree->create<meta_range_t>("freq/range") - .publish(boost::bind(&get_freq_range, subdev)); - - subtree->create<std::string>("antenna/value") - .publish(boost::bind(&get_ant, subdev)) - .subscribe(boost::bind(&set_ant, subdev, _1)); - - subtree->create<std::vector<std::string> >("antenna/options") - .publish(boost::bind(&get_ants, subdev)); - - subtree->create<std::string>("connection") - .publish(boost::bind(&get_conn, subdev)); - - subtree->create<bool>("enabled") - .coerce(boost::bind(&get_set_enb, subdev, _1)); - - subtree->create<bool>("use_lo_offset") - .publish(boost::bind(&get_use_lo_off, subdev)); - - subtree->create<double>("bandwidth/value") - .publish(boost::bind(&get_bw, subdev)) - .subscribe(boost::bind(&set_bw, subdev, _1)); } diff --git a/host/lib/usrp/e100/dboard_iface.cpp b/host/lib/usrp/e100/dboard_iface.cpp index d45577bd9..6afc7bc48 100644 --- a/host/lib/usrp/e100/dboard_iface.cpp +++ b/host/lib/usrp/e100/dboard_iface.cpp @@ -15,7 +15,7 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include "wb_iface.hpp" +#include "gpio_core_200.hpp" #include <uhd/types/serial.hpp> #include "e100_regs.hpp" #include "clock_ctrl.hpp" @@ -45,13 +45,11 @@ public: _spi_iface = spi_iface; _clock = clock; _codec = codec; + _gpio = gpio_core_200::make(_wb_iface, E100_REG_SR_ADDR(UE_SR_GPIO), E100_REG_RB_GPIO); //init the clock rate shadows this->set_clock_rate(UNIT_RX, _clock->get_fpga_clock_rate()); this->set_clock_rate(UNIT_TX, _clock->get_fpga_clock_rate()); - - _wb_iface->poke16(E100_REG_GPIO_RX_DBG, 0); - _wb_iface->poke16(E100_REG_GPIO_TX_DBG, 0); } ~e100_dboard_iface(void){ @@ -104,6 +102,7 @@ private: spi_iface::sptr _spi_iface; e100_clock_ctrl::sptr _clock; e100_codec_ctrl::sptr _codec; + gpio_core_200::sptr _gpio; }; /*********************************************************************** @@ -160,77 +159,27 @@ double e100_dboard_iface::get_codec_rate(unit_t){ * GPIO **********************************************************************/ void e100_dboard_iface::_set_pin_ctrl(unit_t unit, boost::uint16_t value){ - UHD_ASSERT_THROW(GPIO_SEL_ATR == 1); //make this assumption - switch(unit){ - case UNIT_RX: _wb_iface->poke16(E100_REG_GPIO_RX_SEL, value); return; - case UNIT_TX: _wb_iface->poke16(E100_REG_GPIO_TX_SEL, value); return; - } + return _gpio->set_pin_ctrl(unit, value); } void e100_dboard_iface::_set_gpio_ddr(unit_t unit, boost::uint16_t value){ - switch(unit){ - case UNIT_RX: _wb_iface->poke16(E100_REG_GPIO_RX_DDR, value); return; - case UNIT_TX: _wb_iface->poke16(E100_REG_GPIO_TX_DDR, value); return; - } + return _gpio->set_gpio_ddr(unit, value); } void e100_dboard_iface::_set_gpio_out(unit_t unit, boost::uint16_t value){ - switch(unit){ - case UNIT_RX: _wb_iface->poke16(E100_REG_GPIO_RX_IO, value); return; - case UNIT_TX: _wb_iface->poke16(E100_REG_GPIO_TX_IO, value); return; - } + return _gpio->set_gpio_out(unit, value); } boost::uint16_t e100_dboard_iface::read_gpio(unit_t unit){ - switch(unit){ - case UNIT_RX: return _wb_iface->peek16(E100_REG_GPIO_RX_IO); - case UNIT_TX: return _wb_iface->peek16(E100_REG_GPIO_TX_IO); - default: UHD_THROW_INVALID_CODE_PATH(); - } + return _gpio->read_gpio(unit); } void e100_dboard_iface::_set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value){ - //define mapping of unit to atr regs to register address - static const uhd::dict< - unit_t, uhd::dict<atr_reg_t, boost::uint32_t> - > unit_to_atr_to_addr = map_list_of - (UNIT_RX, map_list_of - (ATR_REG_IDLE, E100_REG_ATR_IDLE_RXSIDE) - (ATR_REG_TX_ONLY, E100_REG_ATR_INTX_RXSIDE) - (ATR_REG_RX_ONLY, E100_REG_ATR_INRX_RXSIDE) - (ATR_REG_FULL_DUPLEX, E100_REG_ATR_FULL_RXSIDE) - ) - (UNIT_TX, map_list_of - (ATR_REG_IDLE, E100_REG_ATR_IDLE_TXSIDE) - (ATR_REG_TX_ONLY, E100_REG_ATR_INTX_TXSIDE) - (ATR_REG_RX_ONLY, E100_REG_ATR_INRX_TXSIDE) - (ATR_REG_FULL_DUPLEX, E100_REG_ATR_FULL_TXSIDE) - ) - ; - _wb_iface->poke16(unit_to_atr_to_addr[unit][atr], value); + return _gpio->set_atr_reg(unit, atr, value); } -void e100_dboard_iface::set_gpio_debug(unit_t unit, int which){ - //set this unit to all outputs - this->set_gpio_ddr(unit, 0xffff); - - //calculate the debug selections - boost::uint32_t dbg_sels = 0x0; - int sel = (which == 0)? GPIO_SEL_DEBUG_0 : GPIO_SEL_DEBUG_1; - for(size_t i = 0; i < 16; i++) dbg_sels |= sel << i; - - //set the debug on and which debug selection - switch(unit){ - case UNIT_RX: - _wb_iface->poke16(E100_REG_GPIO_RX_DBG, 0xffff); - _wb_iface->poke16(E100_REG_GPIO_RX_SEL, dbg_sels); - return; - - case UNIT_TX: - _wb_iface->poke16(E100_REG_GPIO_TX_DBG, 0xffff); - _wb_iface->poke16(E100_REG_GPIO_TX_SEL, dbg_sels); - return; - } +void e100_dboard_iface::set_gpio_debug(unit_t, int){ + throw uhd::not_implemented_error("no set_gpio_debug implemented"); } /*********************************************************************** diff --git a/host/lib/usrp/e100/e100_impl.cpp b/host/lib/usrp/e100/e100_impl.cpp index 564a05a7e..c0a8f46f3 100644 --- a/host/lib/usrp/e100/e100_impl.cpp +++ b/host/lib/usrp/e100/e100_impl.cpp @@ -106,6 +106,7 @@ UHD_STATIC_BLOCK(register_e100_device){ * Structors **********************************************************************/ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ + _tree = property_tree::make(); //setup the main interface into fpga const std::string node = device_addr["node"]; @@ -167,15 +168,7 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ ); //check that the compatibility is correct - const boost::uint16_t fpga_compat_num = _fpga_ctrl->peek16(E100_REG_MISC_COMPAT); - if (fpga_compat_num != E100_FPGA_COMPAT_NUM){ - throw uhd::runtime_error(str(boost::format( - "\nPlease update the FPGA image for your device.\n" - "See the application notes for USRP E-Series for instructions.\n" - "Expected FPGA compatibility number 0x%x, but got 0x%x:\n" - "The FPGA build is not compatible with the host code build." - ) % E100_FPGA_COMPAT_NUM % fpga_compat_num)); - } + this->check_fpga_compat(); //////////////////////////////////////////////////////////////////// // Create controller objects @@ -187,7 +180,6 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // Initialize the properties tree //////////////////////////////////////////////////////////////////// - _tree = property_tree::make(); _tree->create<std::string>("/name").set("E-Series Device"); const fs_path mb_path = "/mboards/0"; _tree->create<std::string>(mb_path / "name").set(str(boost::format("%s (euewanee)") % model)); @@ -250,12 +242,31 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// _rx_fe = rx_frontend_core_200::make(_fpga_ctrl, E100_REG_SR_ADDR(UE_SR_RX_FRONT)); _tx_fe = tx_frontend_core_200::make(_fpga_ctrl, E100_REG_SR_ADDR(UE_SR_TX_FRONT)); - //TODO lots of properties to expose here for frontends + _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec") .subscribe(boost::bind(&e100_impl::update_rx_subdev_spec, this, _1)); _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec") .subscribe(boost::bind(&e100_impl::update_tx_subdev_spec, this, _1)); + const fs_path rx_fe_path = mb_path / "rx_frontends" / "A"; + const fs_path tx_fe_path = mb_path / "tx_frontends" / "A"; + + _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value") + .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, _rx_fe, _1)) + .set(std::complex<double>(0.0, 0.0)); + _tree->create<bool>(rx_fe_path / "dc_offset" / "enable") + .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, _rx_fe, _1)) + .set(true); + _tree->create<std::complex<double> >(rx_fe_path / "iq_balance" / "value") + .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, _rx_fe, _1)) + .set(std::complex<double>(0.0, 0.0)); + _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value") + .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, _tx_fe, _1)) + .set(std::complex<double>(0.0, 0.0)); + _tree->create<std::complex<double> >(tx_fe_path / "iq_balance" / "value") + .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, _tx_fe, _1)) + .set(std::complex<double>(0.0, 0.0)); + //////////////////////////////////////////////////////////////////// // create rx dsp control objects //////////////////////////////////////////////////////////////////// @@ -270,9 +281,12 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&rx_dsp_core_200::set_tick_rate, _rx_dsps[dspno], _1)); fs_path rx_dsp_path = mb_path / str(boost::format("rx_dsps/%u") % dspno); + _tree->create<meta_range_t>(rx_dsp_path / "rate/range") + .publish(boost::bind(&rx_dsp_core_200::get_host_rates, _rx_dsps[dspno])); _tree->create<double>(rx_dsp_path / "rate/value") + .set(1e6) //some default .coerce(boost::bind(&rx_dsp_core_200::set_host_rate, _rx_dsps[dspno], _1)) - .subscribe(boost::bind(&e100_impl::update_rx_samp_rate, this, _1)); + .subscribe(boost::bind(&e100_impl::update_rx_samp_rate, this, dspno, _1)); _tree->create<double>(rx_dsp_path / "freq/value") .coerce(boost::bind(&rx_dsp_core_200::set_freq, _rx_dsps[dspno], _1)); _tree->create<meta_range_t>(rx_dsp_path / "freq/range") @@ -290,9 +304,12 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ _tx_dsp->set_link_rate(E100_TX_LINK_RATE_BPS); _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&tx_dsp_core_200::set_tick_rate, _tx_dsp, _1)); + _tree->create<meta_range_t>(mb_path / "tx_dsps/0/rate/range") + .publish(boost::bind(&tx_dsp_core_200::get_host_rates, _tx_dsp)); _tree->create<double>(mb_path / "tx_dsps/0/rate/value") + .set(1e6) //some default .coerce(boost::bind(&tx_dsp_core_200::set_host_rate, _tx_dsp, _1)) - .subscribe(boost::bind(&e100_impl::update_tx_samp_rate, this, _1)); + .subscribe(boost::bind(&e100_impl::update_tx_samp_rate, this, 0, _1)); _tree->create<double>(mb_path / "tx_dsps/0/freq/value") .coerce(boost::bind(&tx_dsp_core_200::set_freq, _tx_dsp, _1)); _tree->create<meta_range_t>(mb_path / "tx_dsps/0/freq/range") @@ -353,22 +370,9 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ _dboard_iface = make_e100_dboard_iface(_fpga_ctrl, _fpga_i2c_ctrl, _fpga_spi_ctrl, _clock_ctrl, _codec_ctrl); _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_dboard_iface); _dboard_manager = dboard_manager::make( - rx_db_eeprom.id, - ((gdb_eeprom.id == dboard_id_t::none())? tx_db_eeprom : gdb_eeprom).id, - _dboard_iface + rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id, + _dboard_iface, _tree->subtree(mb_path / "dboards/A") ); - BOOST_FOREACH(const std::string &name, _dboard_manager->get_rx_subdev_names()){ - dboard_manager::populate_prop_tree_from_subdev( - _tree->subtree(mb_path / "dboards/A/rx_frontends" / name), - _dboard_manager->get_rx_subdev(name) - ); - } - BOOST_FOREACH(const std::string &name, _dboard_manager->get_tx_subdev_names()){ - dboard_manager::populate_prop_tree_from_subdev( - _tree->subtree(mb_path / "dboards/A/tx_frontends" / name), - _dboard_manager->get_tx_subdev(name) - ); - } //initialize io handling this->io_init(); @@ -376,19 +380,13 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // do some post-init tasks //////////////////////////////////////////////////////////////////// - _tree->access<double>(mb_path / "tick_rate").update() //update and then subscribe the clock callback - .subscribe(boost::bind(&e100_clock_ctrl::set_fpga_clock_rate, _clock_ctrl, _1)); + this->update_rates(); - //and now that the tick rate is set, init the host rates to something - BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){ - _tree->access<double>(mb_path / "rx_dsps" / name / "rate" / "value").set(1e6); - } - BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "tx_dsps")){ - _tree->access<double>(mb_path / "tx_dsps" / name / "rate" / "value").set(1e6); - } + _tree->access<double>(mb_path / "tick_rate") //now subscribe the clock rate setter + .subscribe(boost::bind(&e100_clock_ctrl::set_fpga_clock_rate, _clock_ctrl, _1)); - _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(subdev_spec_t("A:"+_dboard_manager->get_rx_subdev_names()[0])); - _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(subdev_spec_t("A:"+_dboard_manager->get_tx_subdev_names()[0])); + _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(mb_path / "dboards/A/rx_frontends").at(0))); + _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(mb_path / "dboards/A/tx_frontends").at(0))); _tree->access<std::string>(mb_path / "clock_source/value").set("internal"); _tree->access<std::string>(mb_path / "time_source/value").set("none"); @@ -436,3 +434,19 @@ sensor_value_t e100_impl::get_ref_locked(void){ const bool lock = _clock_ctrl->get_locked(); return sensor_value_t("Ref", lock, "locked", "unlocked"); } + +void e100_impl::check_fpga_compat(void){ + const boost::uint32_t fpga_compat_num = _fpga_ctrl->peek32(E100_REG_RB_COMPAT); + boost::uint16_t fpga_major = fpga_compat_num >> 16, fpga_minor = fpga_compat_num & 0xffff; + if (fpga_major == 0){ //old version scheme + fpga_major = fpga_minor; + fpga_minor = 0; + } + if (fpga_major != E100_FPGA_COMPAT_NUM){ + throw uhd::runtime_error(str(boost::format( + "Expected FPGA compatibility number %d, but got %d:\n" + "The FPGA build is not compatible with the host code build." + ) % int(E100_FPGA_COMPAT_NUM) % fpga_major)); + } + _tree->create<std::string>("/mboards/0/fpga_version").set(str(boost::format("%u.%u") % fpga_major % fpga_minor)); +} diff --git a/host/lib/usrp/e100/e100_impl.hpp b/host/lib/usrp/e100/e100_impl.hpp index 4b2ec5ee0..f3e481b93 100644 --- a/host/lib/usrp/e100/e100_impl.hpp +++ b/host/lib/usrp/e100/e100_impl.hpp @@ -33,11 +33,10 @@ #include <uhd/usrp/mboard_eeprom.hpp> #include <uhd/usrp/gps_ctrl.hpp> #include <uhd/types/sensors.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> #include <uhd/transport/zero_copy.hpp> +#include <boost/weak_ptr.hpp> #ifndef INCLUDED_E100_IMPL_HPP #define INCLUDED_E100_IMPL_HPP @@ -49,7 +48,7 @@ static const double E100_RX_LINK_RATE_BPS = 166e6/3/2*2; static const double E100_TX_LINK_RATE_BPS = 166e6/3/1*2; static const std::string E100_I2C_DEV_NODE = "/dev/i2c-3"; static const std::string E100_UART_DEV_NODE = "/dev/ttyO0"; -static const boost::uint16_t E100_FPGA_COMPAT_NUM = 0x06; +static const boost::uint16_t E100_FPGA_COMPAT_NUM = 0x08; static const boost::uint32_t E100_RX_SID_BASE = 2; static const boost::uint32_t E100_TX_ASYNC_SID = 1; static const double E100_DEFAULT_CLOCK_RATE = 64e6; @@ -78,11 +77,9 @@ public: ~e100_impl(void); //the io interface - size_t send(const send_buffs_type &, size_t, const uhd::tx_metadata_t &, const uhd::io_type_t &, send_mode_t, double); - size_t recv(const recv_buffs_type &, size_t, uhd::rx_metadata_t &, const uhd::io_type_t &, recv_mode_t, double); + uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &args); + uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args); bool recv_async_msg(uhd::async_metadata_t &, double); - size_t get_max_send_samps_per_packet(void) const; - size_t get_max_recv_samps_per_packet(void) const; private: uhd::property_tree::sptr _tree; @@ -110,7 +107,6 @@ private: uhd::usrp::dboard_iface::sptr _dboard_iface; //handle io stuff - uhd::otw_type_t _rx_otw_type, _tx_otw_type; UHD_PIMPL_DECL(io_impl) _io_impl; void io_init(void); @@ -119,16 +115,21 @@ private: return _tree; } + std::vector<boost::weak_ptr<uhd::rx_streamer> > _rx_streamers; + std::vector<boost::weak_ptr<uhd::tx_streamer> > _tx_streamers; + double update_rx_codec_gain(const double); //sets A and B at once void set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &); void set_db_eeprom(const std::string &, const uhd::usrp::dboard_eeprom_t &); void update_tick_rate(const double rate); - void update_rx_samp_rate(const double rate); - void update_tx_samp_rate(const double rate); + void update_rx_samp_rate(const size_t, const double rate); + void update_tx_samp_rate(const size_t, const double rate); + void update_rates(void); void update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &); void update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &); void update_clock_source(const std::string &); uhd::sensor_value_t get_ref_locked(void); + void check_fpga_compat(void); }; diff --git a/host/lib/usrp/e100/e100_regs.hpp b/host/lib/usrp/e100/e100_regs.hpp index 28ef707dc..f24f5895b 100644 --- a/host/lib/usrp/e100/e100_regs.hpp +++ b/host/lib/usrp/e100/e100_regs.hpp @@ -31,7 +31,6 @@ #define E100_REG_MISC_RX_LEN E100_REG_MISC_BASE + 10 #define E100_REG_MISC_TX_LEN E100_REG_MISC_BASE + 12 #define E100_REG_MISC_XFER_RATE E100_REG_MISC_BASE + 14 -#define E100_REG_MISC_COMPAT E100_REG_MISC_BASE + 16 ///////////////////////////////////////////////////// // Slave 1 -- UART @@ -67,43 +66,6 @@ #define E100_REG_ERR_BUFF E100_REG_SLAVE(5) -//////////////////////////////////////////////// -// Slave 4 -- GPIO - -#define E100_REG_GPIO_BASE E100_REG_SLAVE(4) - -#define E100_REG_GPIO_RX_IO E100_REG_GPIO_BASE + 0 -#define E100_REG_GPIO_TX_IO E100_REG_GPIO_BASE + 2 -#define E100_REG_GPIO_RX_DDR E100_REG_GPIO_BASE + 4 -#define E100_REG_GPIO_TX_DDR E100_REG_GPIO_BASE + 6 -#define E100_REG_GPIO_RX_SEL E100_REG_GPIO_BASE + 8 -#define E100_REG_GPIO_TX_SEL E100_REG_GPIO_BASE + 10 -#define E100_REG_GPIO_RX_DBG E100_REG_GPIO_BASE + 12 -#define E100_REG_GPIO_TX_DBG E100_REG_GPIO_BASE + 14 - -//possible bit values for sel when dbg is 0: -#define GPIO_SEL_SW 0 // if pin is an output, set by software in the io reg -#define GPIO_SEL_ATR 1 // if pin is an output, set by ATR logic - -//possible bit values for sel when dbg is 1: -#define GPIO_SEL_DEBUG_0 0 // if pin is an output, debug lines from FPGA fabric -#define GPIO_SEL_DEBUG_1 1 // if pin is an output, debug lines from FPGA fabric - -/////////////////////////////////////////////////// -// Slave 6 -- ATR Controller -// 16 regs - -#define E100_REG_ATR_BASE E100_REG_SLAVE(6) - -#define E100_REG_ATR_IDLE_RXSIDE E100_REG_ATR_BASE + 0 -#define E100_REG_ATR_IDLE_TXSIDE E100_REG_ATR_BASE + 2 -#define E100_REG_ATR_INTX_RXSIDE E100_REG_ATR_BASE + 4 -#define E100_REG_ATR_INTX_TXSIDE E100_REG_ATR_BASE + 6 -#define E100_REG_ATR_INRX_RXSIDE E100_REG_ATR_BASE + 8 -#define E100_REG_ATR_INRX_TXSIDE E100_REG_ATR_BASE + 10 -#define E100_REG_ATR_FULL_RXSIDE E100_REG_ATR_BASE + 12 -#define E100_REG_ATR_FULL_TXSIDE E100_REG_ATR_BASE + 14 - /////////////////////////////////////////////////// // Slave 7 -- Readback Mux 32 @@ -115,6 +77,8 @@ #define E100_REG_RB_TIME_PPS_TICKS E100_REG_RB_MUX_32_BASE + 12 #define E100_REG_RB_MISC_TEST32 E100_REG_RB_MUX_32_BASE + 16 #define E100_REG_RB_ERR_STATUS E100_REG_RB_MUX_32_BASE + 20 +#define E100_REG_RB_COMPAT E100_REG_RB_MUX_32_BASE + 24 +#define E100_REG_RB_GPIO E100_REG_RB_MUX_32_BASE + 28 //////////////////////////////////////////////////// // Slave 8 -- Settings Bus @@ -141,6 +105,8 @@ #define UE_SR_CLEAR_TX_FIFO 62 // 1 reg #define UE_SR_GLOBAL_RESET 63 // 1 reg +#define UE_SR_GPIO 128 + #define E100_REG_SR_ADDR(n) (E100_REG_SLAVE(8) + (4*(n))) #define E100_REG_SR_MISC_TEST32 E100_REG_SR_ADDR(UE_SR_REG_TEST32) diff --git a/host/lib/usrp/e100/io_impl.cpp b/host/lib/usrp/e100/io_impl.cpp index 0b81c1a86..3b0828b45 100644 --- a/host/lib/usrp/e100/io_impl.cpp +++ b/host/lib/usrp/e100/io_impl.cpp @@ -35,6 +35,7 @@ #include <fcntl.h> //open, close #include <sstream> #include <fstream> +#include <boost/make_shared.hpp> using namespace uhd; using namespace uhd::usrp; @@ -60,10 +61,6 @@ struct e100_impl::io_impl{ //which is after the states and booty which may hold managed buffers. recv_packet_demuxer::sptr demuxer; - //state management for the vrt packet handler code - sph::recv_packet_handler recv_handler; - sph::send_packet_handler send_handler; - //a pirate's life is the life for me! void recv_pirate_loop( spi_iface::sptr //keep a sptr to iface which shares gpio147 @@ -159,16 +156,6 @@ void e100_impl::io_impl::handle_irq(void){ **********************************************************************/ void e100_impl::io_init(void){ - //setup rx otw type - _rx_otw_type.width = 16; - _rx_otw_type.shift = 0; - _rx_otw_type.byteorder = uhd::otw_type_t::BO_LITTLE_ENDIAN; - - //setup tx otw type - _tx_otw_type.width = 16; - _tx_otw_type.shift = 0; - _tx_otw_type.byteorder = uhd::otw_type_t::BO_LITTLE_ENDIAN; - //create new io impl _io_impl = UHD_PIMPL_MAKE(io_impl, ()); _io_impl->demuxer = recv_packet_demuxer::make(_data_transport, _rx_dsps.size(), E100_RX_SID_BASE); @@ -178,6 +165,10 @@ void e100_impl::io_init(void){ _fpga_ctrl->poke32(E100_REG_CLEAR_RX, 0); _fpga_ctrl->poke32(E100_REG_CLEAR_TX, 0); + //allocate streamer weak ptrs containers + _rx_streamers.resize(_rx_dsps.size()); + _tx_streamers.resize(1/*known to be 1 dsp*/); + //prepare the async msg buffer for incoming messages _fpga_ctrl->poke32(E100_REG_SR_ERR_CTRL, 1 << 0); //clear while ((_fpga_ctrl->peek32(E100_REG_RB_ERR_STATUS) & (1 << 2)) == 0){} //wait for idle @@ -187,37 +178,58 @@ void e100_impl::io_init(void){ _io_impl->pirate_task = task::make(boost::bind( &e100_impl::io_impl::recv_pirate_loop, _io_impl.get(), _aux_spi_iface )); - - //init some handler stuff - _io_impl->recv_handler.set_vrt_unpacker(&vrt::if_hdr_unpack_le); - _io_impl->recv_handler.set_converter(_rx_otw_type); - _io_impl->send_handler.set_vrt_packer(&vrt::if_hdr_pack_le); - _io_impl->send_handler.set_converter(_tx_otw_type); - _io_impl->send_handler.set_max_samples_per_packet(get_max_send_samps_per_packet()); } void e100_impl::update_tick_rate(const double rate){ _io_impl->tick_rate = rate; - boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); - _io_impl->recv_handler.set_tick_rate(rate); - boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); - _io_impl->send_handler.set_tick_rate(rate); + + //update the tick rate on all existing streamers -> thread safe + for (size_t i = 0; i < _rx_streamers.size(); i++){ + boost::shared_ptr<sph::recv_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_rx_streamers[i].lock()); + if (my_streamer.get() == NULL) continue; + my_streamer->set_tick_rate(rate); + } + for (size_t i = 0; i < _tx_streamers.size(); i++){ + boost::shared_ptr<sph::send_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::send_packet_streamer>(_tx_streamers[i].lock()); + if (my_streamer.get() == NULL) continue; + my_streamer->set_tick_rate(rate); + } } -void e100_impl::update_rx_samp_rate(const double rate){ - boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); - _io_impl->recv_handler.set_samp_rate(rate); - const double adj = _rx_dsps.front()->get_scaling_adjustment(); - _io_impl->recv_handler.set_scale_factor(adj/32767.); +void e100_impl::update_rx_samp_rate(const size_t dspno, const double rate){ + boost::shared_ptr<sph::recv_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_rx_streamers[dspno].lock()); + if (my_streamer.get() == NULL) return; + + my_streamer->set_samp_rate(rate); + const double adj = _rx_dsps[dspno]->get_scaling_adjustment(); + my_streamer->set_scale_factor(adj); +} + +void e100_impl::update_tx_samp_rate(const size_t dspno, const double rate){ + boost::shared_ptr<sph::send_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::send_packet_streamer>(_tx_streamers[dspno].lock()); + if (my_streamer.get() == NULL) return; + + my_streamer->set_samp_rate(rate); } -void e100_impl::update_tx_samp_rate(const double rate){ - boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); - _io_impl->send_handler.set_samp_rate(rate); +void e100_impl::update_rates(void){ + const fs_path mb_path = "/mboards/0"; + _tree->access<double>(mb_path / "tick_rate").update(); + + //and now that the tick rate is set, init the host rates to something + BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){ + _tree->access<double>(mb_path / "rx_dsps" / name / "rate" / "value").update(); + } + BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "tx_dsps")){ + _tree->access<double>(mb_path / "tx_dsps" / name / "rate" / "value").update(); + } } void e100_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ - boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); fs_path root = "/mboards/0/dboards"; //sanity checking @@ -231,22 +243,9 @@ void e100_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ _rx_dsps[i]->set_mux(conn, fe_swapped); } _rx_fe->set_mux(fe_swapped); - - //resize for the new occupancy - _io_impl->recv_handler.resize(spec.size()); - - //bind new callbacks for the handler - for (size_t i = 0; i < _io_impl->recv_handler.size(); i++){ - _rx_dsps[i]->set_nsamps_per_packet(get_max_recv_samps_per_packet()); //seems to be a good place to set this - _io_impl->recv_handler.set_xport_chan_get_buff(i, boost::bind( - &recv_packet_demuxer::get_recv_buff, _io_impl->demuxer, i, _1 - )); - _io_impl->recv_handler.set_overflow_handler(i, boost::bind(&rx_dsp_core_200::handle_overflow, _rx_dsps[i])); - } } void e100_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ - boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); fs_path root = "/mboards/0/dboards"; //sanity checking @@ -255,73 +254,122 @@ void e100_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ //set the mux for this spec const std::string conn = _tree->access<std::string>(root / spec[0].db_name / "tx_frontends" / spec[0].sd_name / "connection").get(); _tx_fe->set_mux(conn); +} - //resize for the new occupancy - _io_impl->send_handler.resize(spec.size()); - - //bind new callbacks for the handler - for (size_t i = 0; i < _io_impl->send_handler.size(); i++){ - _io_impl->send_handler.set_xport_chan_get_buff(i, boost::bind( - &zero_copy_if::get_send_buff, _data_transport, _1 - )); - } +/*********************************************************************** + * Async Recv + **********************************************************************/ +bool e100_impl::recv_async_msg( + async_metadata_t &async_metadata, double timeout +){ + boost::this_thread::disable_interruption di; //disable because the wait can throw + return _io_impl->async_msg_fifo.pop_with_timed_wait(async_metadata, timeout); } /*********************************************************************** - * Data Send + * Receive streamer **********************************************************************/ -size_t e100_impl::get_max_send_samps_per_packet(void) const{ +rx_streamer::sptr e100_impl::get_rx_stream(const uhd::stream_args_t &args_){ + stream_args_t args = args_; + + //setup defaults for unspecified values + args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; + args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + const unsigned sc8_scalar = unsigned(args.args.cast<double>("scalar", 0x400)); + + //calculate packet size 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 = _data_transport->get_send_frame_size() - hdr_size; - return bpp/_tx_otw_type.get_sample_size(); -} + const size_t bpp = _data_transport->get_recv_frame_size() - hdr_size; + const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format); + + //make the new streamer given the samples per packet + boost::shared_ptr<sph::recv_packet_streamer> my_streamer = boost::make_shared<sph::recv_packet_streamer>(spp); + + //init some streamer stuff + my_streamer->resize(args.channels.size()); + my_streamer->set_vrt_unpacker(&vrt::if_hdr_unpack_le); + + //set the converter + uhd::convert::id_type id; + id.input_format = args.otw_format + "_item32_le"; + id.num_inputs = 1; + id.output_format = args.cpu_format; + id.num_outputs = 1; + my_streamer->set_converter(id); + + //bind callbacks for the handler + for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){ + const size_t dsp = args.channels[chan_i]; + _rx_dsps[dsp]->set_nsamps_per_packet(spp); //seems to be a good place to set this + _rx_dsps[dsp]->set_format(args.otw_format, sc8_scalar); + my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( + &recv_packet_demuxer::get_recv_buff, _io_impl->demuxer, dsp, _1 + )); + my_streamer->set_overflow_handler(chan_i, boost::bind( + &rx_dsp_core_200::handle_overflow, _rx_dsps[dsp] + )); + _rx_streamers[dsp] = my_streamer; //store weak pointer + } -size_t e100_impl::send( - const send_buffs_type &buffs, size_t nsamps_per_buff, - const tx_metadata_t &metadata, const io_type_t &io_type, - send_mode_t send_mode, double timeout -){ - return _io_impl->send_handler.send( - buffs, nsamps_per_buff, - metadata, io_type, - send_mode, timeout - ); + //sets all tick and samp rates on this streamer + this->update_rates(); + + return my_streamer; } /*********************************************************************** - * Data Recv + * Transmit streamer **********************************************************************/ -size_t e100_impl::get_max_recv_samps_per_packet(void) const{ +tx_streamer::sptr e100_impl::get_tx_stream(const uhd::stream_args_t &args_){ + stream_args_t args = args_; + + //setup defaults for unspecified values + args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; + args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + + if (args.otw_format != "sc16"){ + throw uhd::value_error("USRP TX cannot handle requested wire format: " + args.otw_format); + } + + //calculate packet size 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 = _data_transport->get_recv_frame_size() - hdr_size; - return bpp/_rx_otw_type.get_sample_size(); -} + static const size_t bpp = _data_transport->get_send_frame_size() - hdr_size; + const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format); + + //make the new streamer given the samples per packet + boost::shared_ptr<sph::send_packet_streamer> my_streamer = boost::make_shared<sph::send_packet_streamer>(spp); + + //init some streamer stuff + my_streamer->resize(args.channels.size()); + my_streamer->set_vrt_packer(&vrt::if_hdr_pack_le); + + //set the converter + uhd::convert::id_type id; + id.input_format = args.cpu_format; + id.num_inputs = 1; + id.output_format = args.otw_format + "_item32_le"; + id.num_outputs = 1; + my_streamer->set_converter(id); + + //bind callbacks for the handler + for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){ + const size_t dsp = args.channels[chan_i]; + UHD_ASSERT_THROW(dsp == 0); //always 0 + my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( + &zero_copy_if::get_send_buff, _data_transport, _1 + )); + _tx_streamers[dsp] = my_streamer; //store weak pointer + } -size_t e100_impl::recv( - const recv_buffs_type &buffs, size_t nsamps_per_buff, - rx_metadata_t &metadata, const io_type_t &io_type, - recv_mode_t recv_mode, double timeout -){ - return _io_impl->recv_handler.recv( - buffs, nsamps_per_buff, - metadata, io_type, - recv_mode, timeout - ); -} + //sets all tick and samp rates on this streamer + this->update_rates(); -/*********************************************************************** - * Async Recv - **********************************************************************/ -bool e100_impl::recv_async_msg( - async_metadata_t &async_metadata, double timeout -){ - boost::this_thread::disable_interruption di; //disable because the wait can throw - return _io_impl->async_msg_fifo.pop_with_timed_wait(async_metadata, timeout); + return my_streamer; } diff --git a/host/lib/usrp/gps_ctrl.cpp b/host/lib/usrp/gps_ctrl.cpp index c645d2948..45fa1f39e 100644 --- a/host/lib/usrp/gps_ctrl.cpp +++ b/host/lib/usrp/gps_ctrl.cpp @@ -17,7 +17,6 @@ #include <uhd/usrp/gps_ctrl.hpp> #include <uhd/utils/msg.hpp> -#include <uhd/utils/props.hpp> #include <uhd/exception.hpp> #include <uhd/types/sensors.hpp> #include <boost/algorithm/string.hpp> @@ -121,7 +120,7 @@ public: return sensor_value_t("GPS lock status", locked(), "locked", "unlocked"); } else { - UHD_THROW_PROP_GET_ERROR(); + throw uhd::value_error("gps ctrl get_sensor unknown key: " + key); } } diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index 73699dc81..1110f5ebd 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -17,7 +17,6 @@ #include <uhd/property_tree.hpp> #include <uhd/usrp/multi_usrp.hpp> -#include <uhd/usrp/mboard_iface.hpp> #include <uhd/utils/msg.hpp> #include <uhd/exception.hpp> #include <uhd/utils/msg.hpp> @@ -348,6 +347,14 @@ public: return true; } + void set_command_time(const time_spec_t &, size_t){ + throw uhd::not_implemented_error("Not implemented yet, but we have a very good idea of how to do it."); + } + + void clear_command_time(size_t){ + throw uhd::not_implemented_error("Not implemented yet, but we have a very good idea of how to do it."); + } + void issue_stream_cmd(const stream_cmd_t &stream_cmd, size_t chan){ if (chan != ALL_CHANS){ _tree->access<stream_cmd_t>(rx_dsp_root(chan) / "stream_cmd").set(stream_cmd); @@ -359,34 +366,64 @@ public: } void set_clock_config(const clock_config_t &clock_config, size_t mboard){ + //set the reference source... + std::string clock_source; + switch(clock_config.ref_source){ + case clock_config_t::REF_INT: clock_source = "internal"; break; + case clock_config_t::PPS_SMA: clock_source = "external"; break; + case clock_config_t::PPS_MIMO: clock_source = "mimo"; break; + default: clock_source = "unknown"; + } + this->set_clock_source(clock_source, mboard); + + //set the time source + std::string time_source; + switch(clock_config.pps_source){ + case clock_config_t::PPS_INT: time_source = "internal"; break; + case clock_config_t::PPS_SMA: time_source = "external"; break; + case clock_config_t::PPS_MIMO: time_source = "mimo"; break; + default: time_source = "unknown"; + } + if (time_source == "external" and clock_config.pps_polarity == clock_config_t::PPS_NEG) time_source = "_external_"; + this->set_time_source(time_source, mboard); + } + + void set_time_source(const std::string &source, const size_t mboard){ if (mboard != ALL_MBOARDS){ - //set the reference source... - std::string clock_source; - switch(clock_config.ref_source){ - case clock_config_t::REF_INT: clock_source = "internal"; break; - case clock_config_t::PPS_SMA: clock_source = "external"; break; - case clock_config_t::PPS_MIMO: clock_source = "mimo"; break; - default: clock_source = "unknown"; - } - _tree->access<std::string>(mb_root(mboard) / "clock_source" / "value").set(clock_source); - - //set the time source - std::string time_source; - switch(clock_config.pps_source){ - case clock_config_t::PPS_INT: time_source = "internal"; break; - case clock_config_t::PPS_SMA: time_source = "external"; break; - case clock_config_t::PPS_MIMO: time_source = "mimo"; break; - default: time_source = "unknown"; - } - if (clock_source == "external" and clock_config.pps_polarity == clock_config_t::PPS_NEG) time_source = "_external_"; - _tree->access<std::string>(mb_root(mboard) / "time_source" / "value").set(time_source); + _tree->access<std::string>(mb_root(mboard) / "time_source" / "value").set(source); return; } for (size_t m = 0; m < get_num_mboards(); m++){ - set_clock_config(clock_config, m); + return this->set_time_source(source, m); } } + std::string get_time_source(const size_t mboard){ + return _tree->access<std::string>(mb_root(mboard) / "time_source" / "value").get(); + } + + std::vector<std::string> get_time_sources(const size_t mboard){ + return _tree->access<std::vector<std::string> >(mb_root(mboard) / "time_source" / "options").get(); + } + + void set_clock_source(const std::string &source, const size_t mboard){ + if (mboard != ALL_MBOARDS){ + _tree->access<std::string>(mb_root(mboard) / "clock_source" / "value").set(source); + return; + } + for (size_t m = 0; m < get_num_mboards(); m++){ + return this->set_clock_source(source, m); + } + } + + std::string get_clock_source(const size_t mboard){ + return _tree->access<std::string>(mb_root(mboard) / "clock_source" / "value").get(); + } + + std::vector<std::string> get_clock_sources(const size_t mboard){ + return _tree->access<std::vector<std::string> >(mb_root(mboard) / "clock_source" / "options").get(); + } + size_t get_num_mboards(void){ return _tree->list("/mboards").size(); } @@ -399,10 +436,6 @@ public: return _tree->list(mb_root(mboard) / "sensors"); } - mboard_iface::sptr get_mboard_iface(size_t){ - return mboard_iface::sptr(); //not implemented - } - /******************************************************************* * RX methods ******************************************************************/ @@ -447,6 +480,10 @@ public: return _tree->access<double>(rx_dsp_root(chan) / "rate" / "value").get(); } + meta_range_t get_rx_rates(size_t chan){ + return _tree->access<meta_range_t>(rx_dsp_root(chan) / "rate" / "range").get(); + } + tune_result_t set_rx_freq(const tune_request_t &tune_request, size_t chan){ tune_result_t r = tune_xx_subdev_and_dsp(RX_SIGN, _tree->subtree(rx_dsp_root(chan)), _tree->subtree(rx_rf_fe_root(chan)), tune_request); do_tune_freq_warning_message(tune_request.target_freq, get_rx_freq(chan), "RX"); @@ -501,6 +538,10 @@ public: return _tree->access<double>(rx_rf_fe_root(chan) / "bandwidth" / "value").get(); } + meta_range_t get_rx_bandwidth_range(size_t chan){ + return _tree->access<meta_range_t>(rx_rf_fe_root(chan) / "bandwidth" / "range").get(); + } + dboard_iface::sptr get_rx_dboard_iface(size_t chan){ return _tree->access<dboard_iface::sptr>(rx_rf_fe_root(chan).branch_path().branch_path() / "iface").get(); } @@ -513,6 +554,36 @@ public: return _tree->list(rx_rf_fe_root(chan) / "sensors"); } + void set_rx_dc_offset(const bool enb, size_t chan){ + if (chan != ALL_CHANS){ + _tree->access<bool>(rx_fe_root(chan) / "dc_offset" / "enable").set(enb); + return; + } + for (size_t c = 0; c < get_rx_num_channels(); c++){ + this->set_rx_dc_offset(enb, c); + } + } + + void set_rx_dc_offset(const std::complex<double> &offset, size_t chan){ + if (chan != ALL_CHANS){ + _tree->access<std::complex<double> >(rx_fe_root(chan) / "dc_offset" / "value").set(offset); + return; + } + for (size_t c = 0; c < get_rx_num_channels(); c++){ + this->set_rx_dc_offset(offset, c); + } + } + + void set_rx_iq_balance(const std::complex<double> &offset, size_t chan){ + if (chan != ALL_CHANS){ + _tree->access<std::complex<double> >(rx_fe_root(chan) / "iq_balance" / "value").set(offset); + return; + } + for (size_t c = 0; c < get_rx_num_channels(); c++){ + this->set_rx_iq_balance(offset, c); + } + } + /******************************************************************* * TX methods ******************************************************************/ @@ -557,6 +628,10 @@ public: return _tree->access<double>(tx_dsp_root(chan) / "rate" / "value").get(); } + meta_range_t get_tx_rates(size_t chan){ + return _tree->access<meta_range_t>(tx_dsp_root(chan) / "rate" / "range").get(); + } + tune_result_t set_tx_freq(const tune_request_t &tune_request, size_t chan){ tune_result_t r = tune_xx_subdev_and_dsp(TX_SIGN, _tree->subtree(tx_dsp_root(chan)), _tree->subtree(tx_rf_fe_root(chan)), tune_request); do_tune_freq_warning_message(tune_request.target_freq, get_tx_freq(chan), "TX"); @@ -611,6 +686,10 @@ public: return _tree->access<double>(tx_rf_fe_root(chan) / "bandwidth" / "value").get(); } + meta_range_t get_tx_bandwidth_range(size_t chan){ + return _tree->access<meta_range_t>(tx_rf_fe_root(chan) / "bandwidth" / "range").get(); + } + dboard_iface::sptr get_tx_dboard_iface(size_t chan){ return _tree->access<dboard_iface::sptr>(tx_rf_fe_root(chan).branch_path().branch_path() / "iface").get(); } @@ -623,6 +702,26 @@ public: return _tree->list(tx_rf_fe_root(chan) / "sensors"); } + void set_tx_dc_offset(const std::complex<double> &offset, size_t chan){ + if (chan != ALL_CHANS){ + _tree->access<std::complex<double> >(tx_fe_root(chan) / "dc_offset" / "value").set(offset); + return; + } + for (size_t c = 0; c < get_tx_num_channels(); c++){ + this->set_tx_dc_offset(offset, c); + } + } + + void set_tx_iq_balance(const std::complex<double> &offset, size_t chan){ + if (chan != ALL_CHANS){ + _tree->access<std::complex<double> >(tx_fe_root(chan) / "iq_balance" / "value").set(offset); + return; + } + for (size_t c = 0; c < get_tx_num_channels(); c++){ + this->set_tx_iq_balance(offset, c); + } + } + private: device::sptr _dev; property_tree::sptr _tree; @@ -671,6 +770,18 @@ private: return mb_root(mcp.mboard) / "tx_dsps" / name; } + fs_path rx_fe_root(const size_t chan){ + mboard_chan_pair mcp = rx_chan_to_mcp(chan); + const subdev_spec_pair_t spec = get_rx_subdev_spec(mcp.mboard).at(mcp.chan); + return mb_root(mcp.mboard) / "rx_frontends" / spec.db_name; + } + + fs_path tx_fe_root(const size_t chan){ + mboard_chan_pair mcp = tx_chan_to_mcp(chan); + const subdev_spec_pair_t spec = get_tx_subdev_spec(mcp.mboard).at(mcp.chan); + return mb_root(mcp.mboard) / "tx_frontends" / spec.db_name; + } + fs_path rx_rf_fe_root(const size_t chan){ mboard_chan_pair mcp = rx_chan_to_mcp(chan); const subdev_spec_pair_t spec = get_rx_subdev_spec(mcp.mboard).at(mcp.chan); diff --git a/host/lib/usrp/usrp1/dboard_iface.cpp b/host/lib/usrp/usrp1/dboard_iface.cpp index 449ec64fe..34bbe1893 100644 --- a/host/lib/usrp/usrp1/dboard_iface.cpp +++ b/host/lib/usrp/usrp1/dboard_iface.cpp @@ -39,7 +39,7 @@ public: usrp1_dboard_iface(usrp1_iface::sptr iface, usrp1_codec_ctrl::sptr codec, usrp1_impl::dboard_slot_t dboard_slot, - const double master_clock_rate, + const double &master_clock_rate, const dboard_id_t &rx_dboard_id ): _dboard_slot(dboard_slot), @@ -49,10 +49,8 @@ public: _iface = iface; _codec = codec; - //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()); - + _dbsrx_classic_div = 1; + //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 @@ -103,9 +101,9 @@ public: private: usrp1_iface::sptr _iface; usrp1_codec_ctrl::sptr _codec; - uhd::dict<unit_t, double> _clock_rates; + unsigned _dbsrx_classic_div; const usrp1_impl::dboard_slot_t _dboard_slot; - const double _master_clock_rate; + const double &_master_clock_rate; const dboard_id_t _rx_dboard_id; }; @@ -115,7 +113,7 @@ private: dboard_iface::sptr usrp1_impl::make_dboard_iface(usrp1_iface::sptr iface, usrp1_codec_ctrl::sptr codec, usrp1_impl::dboard_slot_t dboard_slot, - const double master_clock_rate, + const double &master_clock_rate, const dboard_id_t &rx_dboard_id ){ return dboard_iface::sptr(new usrp1_dboard_iface( @@ -137,17 +135,16 @@ static const dboard_id_t dbsrx_classic_id(0x0002); void usrp1_dboard_iface::set_clock_rate(unit_t unit, double rate) { assert_has(this->get_clock_rates(unit), rate, "dboard clock rate"); - _clock_rates[unit] = rate; if (unit == UNIT_RX && _rx_dboard_id == dbsrx_classic_id){ - size_t divider = size_t(_master_clock_rate/rate); + _dbsrx_classic_div = size_t(_master_clock_rate/rate); switch(_dboard_slot){ case usrp1_impl::DBOARD_SLOT_A: - _iface->poke32(FR_RX_A_REFCLK, (divider & 0x7f) | 0x80); + _iface->poke32(FR_RX_A_REFCLK, (_dbsrx_classic_div & 0x7f) | 0x80); break; case usrp1_impl::DBOARD_SLOT_B: - _iface->poke32(FR_RX_B_REFCLK, (divider & 0x7f) | 0x80); + _iface->poke32(FR_RX_B_REFCLK, (_dbsrx_classic_div & 0x7f) | 0x80); break; } } @@ -168,7 +165,10 @@ std::vector<double> usrp1_dboard_iface::get_clock_rates(unit_t unit) double usrp1_dboard_iface::get_clock_rate(unit_t unit) { - return _clock_rates[unit]; + if (unit == UNIT_RX && _rx_dboard_id == dbsrx_classic_id){ + return _master_clock_rate/_dbsrx_classic_div; + } + return _master_clock_rate; } void usrp1_dboard_iface::set_clock_enabled(unit_t, bool) diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp index de325ea5d..937706fdd 100644 --- a/host/lib/usrp/usrp1/io_impl.cpp +++ b/host/lib/usrp/usrp1/io_impl.cpp @@ -22,6 +22,7 @@ #include "../../transport/super_send_packet_handler.hpp" #include "usrp1_calc_mux.hpp" #include "fpga_regs_standard.h" +#include "fpga_regs_common.h" #include "usrp_commands.h" #include "usrp1_impl.hpp" #include <uhd/utils/msg.hpp> @@ -33,6 +34,7 @@ #include <boost/thread/thread.hpp> #include <boost/bind.hpp> #include <boost/format.hpp> +#include <boost/make_shared.hpp> using namespace uhd; using namespace uhd::usrp; @@ -109,6 +111,7 @@ static void usrp1_bs_vrt_unpacker( ){ if_packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA; if_packet_info.num_payload_words32 = if_packet_info.num_packet_words32; + if_packet_info.num_payload_bytes = if_packet_info.num_packet_words32*sizeof(boost::uint32_t); if_packet_info.num_header_words32 = 0; if_packet_info.packet_count = 0; if_packet_info.sob = false; @@ -138,10 +141,6 @@ struct usrp1_impl::io_impl{ zero_copy_if::sptr data_transport; - //state management for the vrt packet handler code - sph::recv_packet_handler recv_handler; - sph::send_packet_handler send_handler; - //wrapper around the actual send buffer interface //all of this to ensure only aligned lengths are committed //NOTE: you must commit before getting a new buffer @@ -219,13 +218,6 @@ void usrp1_impl::io_impl::flush_send_buff(void){ * Initialize internals within this file **********************************************************************/ void usrp1_impl::io_init(void){ - _rx_otw_type.width = 16; - _rx_otw_type.shift = 0; - _rx_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; - - _tx_otw_type.width = 16; - _tx_otw_type.shift = 0; - _tx_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; _io_impl = UHD_PIMPL_MAKE(io_impl, (_data_transport)); @@ -234,18 +226,6 @@ void usrp1_impl::io_init(void){ &usrp1_impl::vandal_conquest_loop, this )); - //init some handler stuff - _io_impl->recv_handler.set_tick_rate(_master_clock_rate); - _io_impl->recv_handler.set_vrt_unpacker(&usrp1_bs_vrt_unpacker); - _io_impl->recv_handler.set_xport_chan_get_buff(0, boost::bind( - &uhd::transport::zero_copy_if::get_recv_buff, _io_impl->data_transport, _1 - )); - _io_impl->send_handler.set_tick_rate(_master_clock_rate); - _io_impl->send_handler.set_vrt_packer(&usrp1_bs_vrt_packer); - _io_impl->send_handler.set_xport_chan_get_buff(0, boost::bind( - &usrp1_impl::io_impl::get_send_buff, _io_impl.get(), _1 - )); - //init as disabled, then call the real function (uses restore) this->enable_rx(false); this->enable_tx(false); @@ -325,17 +305,109 @@ void usrp1_impl::vandal_conquest_loop(void){ } /*********************************************************************** + * RX streamer wrapper that talks to soft time control + **********************************************************************/ +class usrp1_recv_packet_streamer : public sph::recv_packet_handler, public rx_streamer{ +public: + usrp1_recv_packet_streamer(const size_t max_num_samps, soft_time_ctrl::sptr stc){ + _max_num_samps = max_num_samps; + _stc = stc; + } + + size_t get_num_channels(void) const{ + return this->size(); + } + + size_t get_max_num_samps(void) const{ + return _max_num_samps; + } + + size_t recv( + const rx_streamer::buffs_type &buffs, + const size_t nsamps_per_buff, + uhd::rx_metadata_t &metadata, + const double timeout, + const bool one_packet + ){ + //interleave a "soft" inline message into the receive stream: + if (_stc->get_inline_queue().pop_with_haste(metadata)) return 0; + + size_t num_samps_recvd = sph::recv_packet_handler::recv( + buffs, nsamps_per_buff, metadata, timeout, one_packet + ); + + return _stc->recv_post(metadata, num_samps_recvd); + } + +private: + size_t _max_num_samps; + soft_time_ctrl::sptr _stc; +}; + +/*********************************************************************** + * TX streamer wrapper that talks to soft time control + **********************************************************************/ +class usrp1_send_packet_streamer : public sph::send_packet_handler, public tx_streamer{ +public: + usrp1_send_packet_streamer(const size_t max_num_samps, soft_time_ctrl::sptr stc, boost::function<void(bool)> tx_enb_fcn){ + _max_num_samps = max_num_samps; + this->set_max_samples_per_packet(_max_num_samps); + _stc = stc; + _tx_enb_fcn = tx_enb_fcn; + } + + size_t get_num_channels(void) const{ + return this->size(); + } + + size_t get_max_num_samps(void) const{ + return _max_num_samps; + } + + size_t send( + const tx_streamer::buffs_type &buffs, + const size_t nsamps_per_buff, + const uhd::tx_metadata_t &metadata, + const double timeout_ + ){ + double timeout = timeout_; //rw copy + _stc->send_pre(metadata, timeout); + + _tx_enb_fcn(true); //always enable (it will do the right thing) + size_t num_samps_sent = sph::send_packet_handler::send( + buffs, nsamps_per_buff, metadata, timeout + ); + + //handle eob flag (commit the buffer, //disable the DACs) + //check num samps sent to avoid flush on incomplete/timeout + if (metadata.end_of_burst and num_samps_sent == nsamps_per_buff){ + async_metadata_t metadata; + metadata.channel = 0; + metadata.has_time_spec = true; + metadata.time_spec = _stc->get_time(); + metadata.event_code = async_metadata_t::EVENT_CODE_BURST_ACK; + _stc->get_async_queue().push_with_pop_on_full(metadata); + _tx_enb_fcn(false); + } + + return num_samps_sent; + } + +private: + size_t _max_num_samps; + soft_time_ctrl::sptr _stc; + boost::function<void(bool)> _tx_enb_fcn; +}; + +/*********************************************************************** * Properties callback methods below **********************************************************************/ void usrp1_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ - boost::mutex::scoped_lock lock = _io_impl->recv_handler.get_scoped_lock(); //sanity checking validate_subdev_spec(_tree, spec, "rx"); _rx_subdev_spec = spec; //shadow - //_io_impl->recv_handler.resize(spec.size()); //always 1 - _io_impl->recv_handler.set_converter(_rx_otw_type, spec.size()); //set the mux and set the number of rx channels std::vector<mapping_pair_t> mapping; @@ -351,14 +423,11 @@ void usrp1_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ } void usrp1_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ - boost::mutex::scoped_lock lock = _io_impl->send_handler.get_scoped_lock(); //sanity checking validate_subdev_spec(_tree, spec, "tx"); _tx_subdev_spec = spec; //shadow - //_io_impl->send_handler.resize(spec.size()); //always 1 - _io_impl->send_handler.set_converter(_tx_otw_type, spec.size()); //set the mux and set the number of tx channels std::vector<mapping_pair_t> mapping; @@ -371,41 +440,94 @@ void usrp1_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ bool s = this->disable_tx(); _iface->poke32(FR_TX_MUX, calc_tx_mux(mapping)); this->restore_tx(s); +} - //if the spec changes size, so does the max samples per packet... - _io_impl->send_handler.set_max_samples_per_packet(get_max_send_samps_per_packet()); +void usrp1_impl::update_tick_rate(const double rate){ + //updating this variable should: + //update dboard iface -> it has a reference + //update dsp freq bounds -> publisher + _master_clock_rate = rate; } -double usrp1_impl::update_rx_samp_rate(const double samp_rate){ - boost::mutex::scoped_lock lock = _io_impl->recv_handler.get_scoped_lock(); +uhd::meta_range_t usrp1_impl::get_rx_dsp_host_rates(void){ + meta_range_t range; + const size_t div = this->has_rx_halfband()? 2 : 1; + for (int rate = 256; rate >= 4; rate -= div){ + range.push_back(range_t(_master_clock_rate/rate)); + } + return range; +} - const size_t rate = uhd::clip<size_t>( - boost::math::iround(_master_clock_rate / samp_rate), size_t(std::ceil(_master_clock_rate / 8e6)), 256 - ); +uhd::meta_range_t usrp1_impl::get_tx_dsp_host_rates(void){ + meta_range_t range; + const size_t div = this->has_tx_halfband()? 2 : 1; + for (int rate = 256; rate >= 8; rate -= div){ + range.push_back(range_t(_master_clock_rate/rate)); + } + return range; +} - bool s = this->disable_rx(); - _iface->poke32(FR_DECIM_RATE, rate/2 - 1); - this->restore_rx(s); +double usrp1_impl::update_rx_samp_rate(size_t dspno, const double samp_rate){ + + const size_t div = this->has_rx_halfband()? 2 : 1; + const size_t rate = boost::math::iround(_master_clock_rate/this->get_rx_dsp_host_rates().clip(samp_rate, true)); + + if (rate < 8 and this->has_rx_halfband()) UHD_MSG(warning) << + "USRP1 cannot achieve decimations below 8 when the half-band filter is present.\n" + "The usrp1_fpga_4rx.rbf file is a special FPGA image without RX half-band filters.\n" + "To load this image, set the device address key/value pair: fpga=usrp1_fpga_4rx.rbf\n" + << std::endl; + + if (dspno == 0){ //only care if dsp0 is set since its homogeneous + bool s = this->disable_rx(); + _iface->poke32(FR_RX_SAMPLE_RATE_DIV, div - 1); + _iface->poke32(FR_DECIM_RATE, rate/div - 1); + this->restore_rx(s); + + //update the streamer if created + boost::shared_ptr<usrp1_recv_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<usrp1_recv_packet_streamer>(_rx_streamer.lock()); + if (my_streamer.get() != NULL){ + my_streamer->set_samp_rate(_master_clock_rate / rate); + } + } - _io_impl->recv_handler.set_samp_rate(_master_clock_rate / rate); return _master_clock_rate / rate; } -double usrp1_impl::update_tx_samp_rate(const double samp_rate){ - boost::mutex::scoped_lock lock = _io_impl->send_handler.get_scoped_lock(); +double usrp1_impl::update_tx_samp_rate(size_t dspno, const double samp_rate){ - const size_t rate = uhd::clip<size_t>( - boost::math::iround(_master_clock_rate / samp_rate), size_t(std::ceil(_master_clock_rate / 8e6)), 256 - ); + const size_t div = this->has_tx_halfband()? 2 : 1; + const size_t rate = boost::math::iround(_master_clock_rate/this->get_tx_dsp_host_rates().clip(samp_rate, true)); - bool s = this->disable_tx(); - _iface->poke32(FR_INTERP_RATE, rate/2 - 1); - this->restore_tx(s); + if (dspno == 0){ //only care if dsp0 is set since its homogeneous + bool s = this->disable_tx(); + _iface->poke32(FR_TX_SAMPLE_RATE_DIV, div - 1); + _iface->poke32(FR_INTERP_RATE, rate/div - 1); + this->restore_tx(s); + + //update the streamer if created + boost::shared_ptr<usrp1_send_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<usrp1_send_packet_streamer>(_tx_streamer.lock()); + if (my_streamer.get() != NULL){ + my_streamer->set_samp_rate(_master_clock_rate / rate); + } + } - _io_impl->send_handler.set_samp_rate(_master_clock_rate / rate); return _master_clock_rate / rate; } +void usrp1_impl::update_rates(void){ + const fs_path mb_path = "/mboards/0"; + this->update_tick_rate(_master_clock_rate); + BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){ + _tree->access<double>(mb_path / "rx_dsps" / name / "rate" / "value").update(); + } + BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "tx_dsps")){ + _tree->access<double>(mb_path / "tx_dsps" / name / "rate" / "value").update(); + } +} + double usrp1_impl::update_rx_dsp_freq(const size_t dspno, const double freq_){ //correct for outside of rate (wrap around) @@ -443,67 +565,120 @@ bool usrp1_impl::recv_async_msg( } /*********************************************************************** - * Data send + helper functions + * Receive streamer **********************************************************************/ -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() - ; -} +rx_streamer::sptr usrp1_impl::get_rx_stream(const uhd::stream_args_t &args_){ + stream_args_t args = args_; + + //setup defaults for unspecified values + args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; + args.channels.clear(); //NOTE: we have no choice about the channel mapping + for (size_t ch = 0; ch < _rx_subdev_spec.size(); ch++){ + args.channels.push_back(ch); + } -size_t usrp1_impl::send( - const send_buffs_type &buffs, size_t nsamps_per_buff, - const tx_metadata_t &metadata, const io_type_t &io_type, - send_mode_t send_mode, double timeout -){ - if (_soft_time_ctrl->send_pre(metadata, timeout)) return 0; + if (args.otw_format == "sc16"){ + _iface->poke32(FR_RX_FORMAT, 0 + | (0 << bmFR_RX_FORMAT_SHIFT_SHIFT) + | (16 << bmFR_RX_FORMAT_WIDTH_SHIFT) + | bmFR_RX_FORMAT_WANT_Q + ); + } + else if (args.otw_format == "sc8"){ + _iface->poke32(FR_RX_FORMAT, 0 + | (8 << bmFR_RX_FORMAT_SHIFT_SHIFT) + | (8 << bmFR_RX_FORMAT_WIDTH_SHIFT) + | bmFR_RX_FORMAT_WANT_Q + ); + } + else{ + throw uhd::value_error("USRP1 RX cannot handle requested wire format: " + args.otw_format); + } - this->tx_stream_on_off(true); //always enable (it will do the right thing) - size_t num_samps_sent = _io_impl->send_handler.send( - buffs, nsamps_per_buff, - metadata, io_type, - send_mode, timeout - ); + //calculate packet size + const size_t bpp = _data_transport->get_recv_frame_size()/args.channels.size(); + const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format); - //handle eob flag (commit the buffer, /*disable the DACs*/) - //check num samps sent to avoid flush on incomplete/timeout - if (metadata.end_of_burst and num_samps_sent == nsamps_per_buff){ - async_metadata_t metadata; - metadata.channel = 0; - metadata.has_time_spec = true; - metadata.time_spec = _soft_time_ctrl->get_time(); - metadata.event_code = async_metadata_t::EVENT_CODE_BURST_ACK; - _soft_time_ctrl->get_async_queue().push_with_pop_on_full(metadata); - this->tx_stream_on_off(false); - } + //make the new streamer given the samples per packet + boost::shared_ptr<usrp1_recv_packet_streamer> my_streamer = + boost::make_shared<usrp1_recv_packet_streamer>(spp, _soft_time_ctrl); + + //special scale factor change for sc8 + if (args.otw_format == "sc8") + my_streamer->set_scale_factor(1.0/127); + + //init some streamer stuff + my_streamer->set_tick_rate(_master_clock_rate); + my_streamer->set_vrt_unpacker(&usrp1_bs_vrt_unpacker); + my_streamer->set_xport_chan_get_buff(0, boost::bind( + &uhd::transport::zero_copy_if::get_recv_buff, _io_impl->data_transport, _1 + )); + + //set the converter + uhd::convert::id_type id; + id.input_format = args.otw_format + "_item16_usrp1"; + id.num_inputs = 1; + id.output_format = args.cpu_format; + id.num_outputs = args.channels.size(); + my_streamer->set_converter(id); - return num_samps_sent; + //save as weak ptr for update access + _rx_streamer = my_streamer; + + //sets all tick and samp rates on this streamer + this->update_rates(); + + return my_streamer; } /*********************************************************************** - * Data recv + helper functions + * Transmit streamer **********************************************************************/ -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() - ; -} +tx_streamer::sptr usrp1_impl::get_tx_stream(const uhd::stream_args_t &args_){ + stream_args_t args = args_; + + //setup defaults for unspecified values + args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; + args.channels.clear(); //NOTE: we have no choice about the channel mapping + for (size_t ch = 0; ch < _tx_subdev_spec.size(); ch++){ + args.channels.push_back(ch); + } -size_t usrp1_impl::recv( - const recv_buffs_type &buffs, size_t nsamps_per_buff, - rx_metadata_t &metadata, const io_type_t &io_type, - recv_mode_t recv_mode, double timeout -){ - //interleave a "soft" inline message into the receive stream: - if (_soft_time_ctrl->get_inline_queue().pop_with_haste(metadata)) return 0; + if (args.otw_format != "sc16"){ + throw uhd::value_error("USRP1 TX cannot handle requested wire format: " + args.otw_format); + } - size_t num_samps_recvd = _io_impl->recv_handler.recv( - buffs, nsamps_per_buff, - metadata, io_type, - recv_mode, timeout - ); + _iface->poke32(FR_TX_FORMAT, bmFR_TX_FORMAT_16_IQ); + + //calculate packet size + const size_t bpp = _data_transport->get_send_frame_size()/args.channels.size(); + const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format); + + //make the new streamer given the samples per packet + boost::function<void(bool)> tx_fcn = boost::bind(&usrp1_impl::tx_stream_on_off, this, _1); + boost::shared_ptr<usrp1_send_packet_streamer> my_streamer = + boost::make_shared<usrp1_send_packet_streamer>(spp, _soft_time_ctrl, tx_fcn); + + //init some streamer stuff + my_streamer->set_tick_rate(_master_clock_rate); + my_streamer->set_vrt_packer(&usrp1_bs_vrt_packer); + my_streamer->set_xport_chan_get_buff(0, boost::bind( + &usrp1_impl::io_impl::get_send_buff, _io_impl.get(), _1 + )); + + //set the converter + uhd::convert::id_type id; + id.input_format = args.cpu_format; + id.num_inputs = args.channels.size(); + id.output_format = args.otw_format + "_item16_usrp1"; + id.num_outputs = 1; + my_streamer->set_converter(id); + + //save as weak ptr for update access + _tx_streamer = my_streamer; + + //sets all tick and samp rates on this streamer + this->update_rates(); - return _soft_time_ctrl->recv_post(metadata, num_samps_recvd); + return my_streamer; } diff --git a/host/lib/usrp/usrp1/soft_time_ctrl.cpp b/host/lib/usrp/usrp1/soft_time_ctrl.cpp index 78481c3ff..b8af8af06 100644 --- a/host/lib/usrp/usrp1/soft_time_ctrl.cpp +++ b/host/lib/usrp/usrp1/soft_time_ctrl.cpp @@ -131,8 +131,8 @@ public: /******************************************************************* * Transmit control ******************************************************************/ - bool send_pre(const tx_metadata_t &md, double &timeout){ - if (not md.has_time_spec) return false; + void send_pre(const tx_metadata_t &md, double &timeout){ + if (not md.has_time_spec) return; boost::mutex::scoped_lock lock(_update_mutex); @@ -146,12 +146,11 @@ public: metadata.time_spec = this->time_now(); metadata.event_code = async_metadata_t::EVENT_CODE_TIME_ERROR; _async_msg_queue.push_with_pop_on_full(metadata); - return true; + return; } timeout -= (time_at - time_now()).get_real_secs(); sleep_until_time(lock, time_at); - return false; } /******************************************************************* diff --git a/host/lib/usrp/usrp1/soft_time_ctrl.hpp b/host/lib/usrp/usrp1/soft_time_ctrl.hpp index e91aaf6a2..b92b51252 100644 --- a/host/lib/usrp/usrp1/soft_time_ctrl.hpp +++ b/host/lib/usrp/usrp1/soft_time_ctrl.hpp @@ -57,7 +57,7 @@ public: virtual size_t recv_post(rx_metadata_t &md, const size_t nsamps) = 0; //! Call before the internal send function - virtual bool send_pre(const tx_metadata_t &md, double &timeout) = 0; + virtual void send_pre(const tx_metadata_t &md, double &timeout) = 0; //! Issue a stream command to receive virtual void issue_stream_cmd(const stream_cmd_t &cmd) = 0; diff --git a/host/lib/usrp/usrp1/usrp1_calc_mux.hpp b/host/lib/usrp/usrp1/usrp1_calc_mux.hpp index 31c190db0..d86a7a809 100644 --- a/host/lib/usrp/usrp1/usrp1_calc_mux.hpp +++ b/host/lib/usrp/usrp1/usrp1_calc_mux.hpp @@ -37,7 +37,7 @@ typedef std::pair<std::string, std::string> mapping_pair_t; * to account for the reversal in the type conversion routines. **********************************************************************/ static int calc_rx_mux_pair(int adc_for_i, int adc_for_q){ - return (adc_for_i << 2) | (adc_for_q << 0); //shift reversal here + return (adc_for_i << 0) | (adc_for_q << 2); } /*! @@ -98,7 +98,7 @@ static boost::uint32_t calc_rx_mux(const std::vector<mapping_pair_t> &mapping){ * to account for the reversal in the type conversion routines. **********************************************************************/ static int calc_tx_mux_pair(int chn_for_i, int chn_for_q){ - return (chn_for_i << 4) | (chn_for_q << 0); //shift reversal here + return (chn_for_i << 0) | (chn_for_q << 4); } /*! diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index fe4541d38..4be5a3a2b 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -33,6 +33,7 @@ #include <boost/filesystem.hpp> #include <boost/thread/thread.hpp> #include <boost/lexical_cast.hpp> +#include <boost/math/special_functions/round.hpp> #include <cstdio> using namespace uhd; @@ -187,21 +188,6 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ // Normal mode with no loopback or Rx counting _iface->poke32(FR_MODE, 0x00000000); _iface->poke32(FR_DEBUG_EN, 0x00000000); - _iface->poke32(FR_RX_SAMPLE_RATE_DIV, 0x00000001); //divide by 2 - _iface->poke32(FR_TX_SAMPLE_RATE_DIV, 0x00000001); //divide by 2 - _iface->poke32(FR_DC_OFFSET_CL_EN, 0x0000000f); - - // Reset offset correction registers - _iface->poke32(FR_ADC_OFFSET_0, 0x00000000); - _iface->poke32(FR_ADC_OFFSET_1, 0x00000000); - _iface->poke32(FR_ADC_OFFSET_2, 0x00000000); - _iface->poke32(FR_ADC_OFFSET_3, 0x00000000); - - // Set default for RX format to 16-bit I&Q and no half-band filter bypass - _iface->poke32(FR_RX_FORMAT, 0x00000300); - - // Set default for TX format to 16-bit I&Q - _iface->poke32(FR_TX_FORMAT, 0x00000000); UHD_LOG << "USRP1 Capabilities" << std::endl @@ -233,14 +219,25 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ // create clock control objects //////////////////////////////////////////////////////////////////// _master_clock_rate = 64e6; - try{ - if (not mb_eeprom["mcr"].empty()) + if (device_addr.has_key("mcr")){ + try{ + _master_clock_rate = boost::lexical_cast<double>(device_addr["mcr"]); + } + catch(const std::exception &e){ + UHD_MSG(error) << "Error parsing FPGA clock rate from device address: " << e.what() << std::endl; + } + } + else if (not mb_eeprom["mcr"].empty()){ + try{ _master_clock_rate = boost::lexical_cast<double>(mb_eeprom["mcr"]); - }catch(const std::exception &e){ - UHD_MSG(error) << "Error parsing FPGA clock rate from EEPROM: " << e.what() << std::endl; + } + catch(const std::exception &e){ + UHD_MSG(error) << "Error parsing FPGA clock rate from EEPROM: " << e.what() << std::endl; + } } UHD_MSG(status) << boost::format("Using FPGA clock rate of %fMHz...") % (_master_clock_rate/1e6) << std::endl; - _tree->create<double>(mb_path / "tick_rate").set(_master_clock_rate); + _tree->create<double>(mb_path / "tick_rate") + .subscribe(boost::bind(&usrp1_impl::update_tick_rate, this, _1)); //////////////////////////////////////////////////////////////////// // create codec control objects @@ -274,18 +271,31 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec") .subscribe(boost::bind(&usrp1_impl::update_tx_subdev_spec, this, _1)); + BOOST_FOREACH(const std::string &db, _dbc.keys()){ + const fs_path rx_fe_path = mb_path / "rx_frontends" / db; + _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value") + .coerce(boost::bind(&usrp1_impl::set_rx_dc_offset, this, db, _1)) + .set(std::complex<double>(0.0, 0.0)); + _tree->create<bool>(rx_fe_path / "dc_offset" / "enable") + .subscribe(boost::bind(&usrp1_impl::set_enb_rx_dc_offset, this, db, _1)) + .set(true); + } + //////////////////////////////////////////////////////////////////// // create rx dsp control objects //////////////////////////////////////////////////////////////////// _tree->create<int>(mb_path / "rx_dsps"); //dummy in case we have none for (size_t dspno = 0; dspno < get_num_ddcs(); dspno++){ fs_path rx_dsp_path = mb_path / str(boost::format("rx_dsps/%u") % dspno); + _tree->create<meta_range_t>(rx_dsp_path / "rate/range") + .publish(boost::bind(&usrp1_impl::get_rx_dsp_host_rates, this)); _tree->create<double>(rx_dsp_path / "rate/value") - .coerce(boost::bind(&usrp1_impl::update_rx_samp_rate, this, _1)); + .set(1e6) //some default rate + .coerce(boost::bind(&usrp1_impl::update_rx_samp_rate, this, dspno, _1)); _tree->create<double>(rx_dsp_path / "freq/value") .coerce(boost::bind(&usrp1_impl::update_rx_dsp_freq, this, dspno, _1)); _tree->create<meta_range_t>(rx_dsp_path / "freq/range") - .set(meta_range_t(-_master_clock_rate/2, +_master_clock_rate/2)); + .publish(boost::bind(&usrp1_impl::get_rx_dsp_freq_range, this)); _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd"); if (dspno == 0){ //only subscribe the callback for dspno 0 since it will stream all dsps @@ -300,12 +310,15 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ _tree->create<int>(mb_path / "tx_dsps"); //dummy in case we have none for (size_t dspno = 0; dspno < get_num_ducs(); dspno++){ fs_path tx_dsp_path = mb_path / str(boost::format("tx_dsps/%u") % dspno); + _tree->create<meta_range_t>(tx_dsp_path / "rate/range") + .publish(boost::bind(&usrp1_impl::get_tx_dsp_host_rates, this)); _tree->create<double>(tx_dsp_path / "rate/value") - .coerce(boost::bind(&usrp1_impl::update_tx_samp_rate, this, _1)); + .set(1e6) //some default rate + .coerce(boost::bind(&usrp1_impl::update_tx_samp_rate, this, dspno, _1)); _tree->create<double>(tx_dsp_path / "freq/value") .coerce(boost::bind(&usrp1_impl::update_tx_dsp_freq, this, dspno, _1)); - _tree->create<meta_range_t>(tx_dsp_path / "freq/range") //magic scalar comes from codec control: - .set(meta_range_t(-_master_clock_rate*0.6875, +_master_clock_rate*0.6875)); + _tree->create<meta_range_t>(tx_dsp_path / "freq/range") + .publish(boost::bind(&usrp1_impl::get_tx_dsp_freq_range, this)); } //////////////////////////////////////////////////////////////////// @@ -350,29 +363,16 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ ); _tree->create<dboard_iface::sptr>(mb_path / "dboards" / db/ "iface").set(_dbc[db].dboard_iface); _dbc[db].dboard_manager = dboard_manager::make( - rx_db_eeprom.id, - ((gdb_eeprom.id == dboard_id_t::none())? tx_db_eeprom : gdb_eeprom).id, - _dbc[db].dboard_iface + rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id, + _dbc[db].dboard_iface, _tree->subtree(mb_path / "dboards" / db) ); - BOOST_FOREACH(const std::string &name, _dbc[db].dboard_manager->get_rx_subdev_names()){ - dboard_manager::populate_prop_tree_from_subdev( - _tree->subtree(mb_path / "dboards" / db/ "rx_frontends" / name), - _dbc[db].dboard_manager->get_rx_subdev(name) - ); - } - BOOST_FOREACH(const std::string &name, _dbc[db].dboard_manager->get_tx_subdev_names()){ - dboard_manager::populate_prop_tree_from_subdev( - _tree->subtree(mb_path / "dboards" / db/ "tx_frontends" / name), - _dbc[db].dboard_manager->get_tx_subdev(name) - ); - } //init the subdev specs if we have a dboard (wont leave this loop empty) if (rx_db_eeprom.id != dboard_id_t::none() or _rx_subdev_spec.empty()){ - _rx_subdev_spec = subdev_spec_t(db + ":" + _dbc[db].dboard_manager->get_rx_subdev_names()[0]); + _rx_subdev_spec = subdev_spec_t(db + ":" + _tree->list(mb_path / "dboards" / db / "rx_frontends").at(0)); } if (tx_db_eeprom.id != dboard_id_t::none() or _tx_subdev_spec.empty()){ - _tx_subdev_spec = subdev_spec_t(db + ":" + _dbc[db].dboard_manager->get_tx_subdev_names()[0]); + _tx_subdev_spec = subdev_spec_t(db + ":" + _tree->list(mb_path / "dboards" / db / "tx_frontends").at(0)); } } @@ -382,14 +382,7 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // do some post-init tasks //////////////////////////////////////////////////////////////////// - //and now that the tick rate is set, init the host rates to something - BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){ - _tree->access<double>(mb_path / "rx_dsps" / name / "rate" / "value").set(1e6); - } - BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "tx_dsps")){ - _tree->access<double>(mb_path / "tx_dsps" / name / "rate" / "value").set(1e6); - } - + this->update_rates(); if (_tree->list(mb_path / "rx_dsps").size() > 0) _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(_rx_subdev_spec); if (_tree->list(mb_path / "tx_dsps").size() > 0) @@ -456,3 +449,36 @@ double usrp1_impl::update_rx_codec_gain(const std::string &db, const double gain _dbc[db].codec->set_rx_pga_gain(gain, 'B'); return _dbc[db].codec->get_rx_pga_gain('A'); } + +uhd::meta_range_t usrp1_impl::get_rx_dsp_freq_range(void){ + return meta_range_t(-_master_clock_rate/2, +_master_clock_rate/2); +} + +uhd::meta_range_t usrp1_impl::get_tx_dsp_freq_range(void){ + //magic scalar comes from codec control: + return meta_range_t(-_master_clock_rate*0.6875, +_master_clock_rate*0.6875); +} + +void usrp1_impl::set_enb_rx_dc_offset(const std::string &db, const bool enb){ + const size_t shift = (db == "A")? 0 : 2; + _rx_dc_offset_shadow &= ~(0x3 << shift); //clear bits + _rx_dc_offset_shadow &= ((enb)? 0x3 : 0x0) << shift; + _iface->poke32(FR_DC_OFFSET_CL_EN, _rx_dc_offset_shadow & 0xf); +} + +std::complex<double> usrp1_impl::set_rx_dc_offset(const std::string &db, const std::complex<double> &offset){ + const boost::int32_t i_off = boost::math::iround(offset.real() * (1ul << 31)); + const boost::int32_t q_off = boost::math::iround(offset.imag() * (1ul << 31)); + + if (db == "A"){ + _iface->poke32(FR_ADC_OFFSET_0, i_off); + _iface->poke32(FR_ADC_OFFSET_1, q_off); + } + + if (db == "B"){ + _iface->poke32(FR_ADC_OFFSET_2, i_off); + _iface->poke32(FR_ADC_OFFSET_3, q_off); + } + + return std::complex<double>(double(i_off) * (1ul << 31), double(q_off) * (1ul << 31)); +} diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp index 68ce31a54..99bb01c76 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.hpp +++ b/host/lib/usrp/usrp1/usrp1_impl.hpp @@ -31,6 +31,7 @@ #include <uhd/usrp/dboard_eeprom.hpp> #include <uhd/usrp/dboard_manager.hpp> #include <uhd/transport/usb_zero_copy.hpp> +#include <complex> #ifndef INCLUDED_USRP1_IMPL_HPP #define INCLUDED_USRP1_IMPL_HPP @@ -55,21 +56,8 @@ public: ~usrp1_impl(void); //the io interface - size_t send(const send_buffs_type &, - size_t, - const uhd::tx_metadata_t &, - const uhd::io_type_t &, - send_mode_t, double); - - size_t recv(const recv_buffs_type &, - size_t, uhd::rx_metadata_t &, - const uhd::io_type_t &, - recv_mode_t, double); - - size_t get_max_send_samps_per_packet(void) const; - - size_t get_max_recv_samps_per_packet(void) const; - + uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &args); + uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args); bool recv_async_msg(uhd::async_metadata_t &, double); private: @@ -94,21 +82,34 @@ private: double _master_clock_rate; //clock rate shadow + //weak pointers to streamers for update purposes + boost::weak_ptr<uhd::rx_streamer> _rx_streamer; + boost::weak_ptr<uhd::tx_streamer> _tx_streamer; + void set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &); void set_db_eeprom(const std::string &, const std::string &, const uhd::usrp::dboard_eeprom_t &); double update_rx_codec_gain(const std::string &, const double); //sets A and B at once void update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &); void update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &); - double update_rx_samp_rate(const double); - double update_tx_samp_rate(const double); + double update_rx_samp_rate(size_t dspno, const double); + double update_tx_samp_rate(size_t dspno, const double); + void update_rates(void); double update_rx_dsp_freq(const size_t, const double); double update_tx_dsp_freq(const size_t, const double); + void update_tick_rate(const double rate); + uhd::meta_range_t get_rx_dsp_freq_range(void); + uhd::meta_range_t get_tx_dsp_freq_range(void); + uhd::meta_range_t get_rx_dsp_host_rates(void); + uhd::meta_range_t get_tx_dsp_host_rates(void); + size_t _rx_dc_offset_shadow; + void set_enb_rx_dc_offset(const std::string &db, const bool); + std::complex<double> set_rx_dc_offset(const std::string &db, const std::complex<double> &); static uhd::usrp::dboard_iface::sptr make_dboard_iface( usrp1_iface::sptr, usrp1_codec_ctrl::sptr, dboard_slot_t, - const double, + const double &, const uhd::usrp::dboard_id_t & ); @@ -119,8 +120,7 @@ private: void tx_stream_on_off(bool); void handle_overrun(size_t); - //otw types - uhd::otw_type_t _rx_otw_type, _tx_otw_type; + //channel mapping shadows uhd::usrp::subdev_spec_t _rx_subdev_spec, _tx_subdev_spec; //capabilities diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp index c31fc52b7..bc510c8a1 100644 --- a/host/lib/usrp/usrp2/dboard_iface.cpp +++ b/host/lib/usrp/usrp2/dboard_iface.cpp @@ -107,7 +107,7 @@ usrp2_dboard_iface::usrp2_dboard_iface( ){ _iface = iface; _clock_ctrl = clock_ctrl; - _gpio = gpio_core_200::make(_iface, GPIO_BASE); + _gpio = gpio_core_200::make(_iface, U2_REG_SR_ADDR(SR_GPIO), U2_REG_GPIO_RB); //reset the aux dacs _dac_regs[UNIT_RX] = ad5623_regs_t(); diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index 7ad06f33f..62ba2d792 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -30,7 +30,7 @@ extern "C" { #endif //fpga and firmware compatibility numbers -#define USRP2_FPGA_COMPAT_NUM 7 +#define USRP2_FPGA_COMPAT_NUM 8 #define USRP2_FW_COMPAT_NUM 11 #define USRP2_FW_VER_MINOR 0 diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index 70331e536..8018875ec 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -31,6 +31,7 @@ #include <boost/format.hpp> #include <boost/bind.hpp> #include <boost/thread/mutex.hpp> +#include <boost/make_shared.hpp> #include <iostream> using namespace uhd; @@ -158,10 +159,6 @@ struct usrp2_impl::io_impl{ std::vector<zero_copy_if::sptr> tx_xports; std::vector<flow_control_monitor::sptr> fc_mons; - //state management for the vrt packet handler code - sph::recv_packet_handler recv_handler; - sph::send_packet_handler send_handler; - //methods and variables for the pirate crew void recv_pirate_loop(zero_copy_if::sptr, size_t); std::list<task::sptr> pirate_tasks; @@ -237,17 +234,6 @@ void usrp2_impl::io_impl::recv_pirate_loop( * Helper Functions **********************************************************************/ void usrp2_impl::io_init(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; - //create new io impl _io_impl = UHD_PIMPL_MAKE(io_impl, ()); @@ -260,6 +246,12 @@ void usrp2_impl::io_init(void){ ))); } + //allocate streamer weak ptrs containers + BOOST_FOREACH(const std::string &mb, _mbc.keys()){ + _mbc[mb].rx_streamers.resize(_mbc[mb].rx_dsps.size()); + _mbc[mb].tx_streamers.resize(1/*known to be 1 dsp*/); + } + //create a new pirate thread for each zc if (yarr!!) size_t index = 0; BOOST_FOREACH(const std::string &mb, _mbc.keys()){ @@ -269,58 +261,62 @@ void usrp2_impl::io_init(void){ _mbc[mb].tx_dsp_xport, index++ ))); } - - //init some handler stuff - _io_impl->recv_handler.set_vrt_unpacker(&vrt::if_hdr_unpack_be); - _io_impl->recv_handler.set_converter(_rx_otw_type); - _io_impl->send_handler.set_vrt_packer(&vrt::if_hdr_pack_be, vrt_send_header_offset_words32); - _io_impl->send_handler.set_converter(_tx_otw_type); - _io_impl->send_handler.set_max_samples_per_packet(get_max_send_samps_per_packet()); - - //set the packet threshold to be an entire socket buffer's worth - const size_t packets_per_sock_buff = size_t(50e6/_mbc[_mbc.keys().front()].rx_dsp_xports[0]->get_recv_frame_size()); - _io_impl->recv_handler.set_alignment_failure_threshold(packets_per_sock_buff); } void usrp2_impl::update_tick_rate(const double rate){ - _io_impl->tick_rate = rate; - boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); - _io_impl->recv_handler.set_tick_rate(rate); - boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); - _io_impl->send_handler.set_tick_rate(rate); + _io_impl->tick_rate = rate; //shadow for async msg + + //update the tick rate on all existing streamers -> thread safe + BOOST_FOREACH(const std::string &mb, _mbc.keys()){ + for (size_t i = 0; i < _mbc[mb].rx_streamers.size(); i++){ + boost::shared_ptr<sph::recv_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_mbc[mb].rx_streamers[i].lock()); + if (my_streamer.get() == NULL) continue; + my_streamer->set_tick_rate(rate); + } + for (size_t i = 0; i < _mbc[mb].tx_streamers.size(); i++){ + boost::shared_ptr<sph::send_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::send_packet_streamer>(_mbc[mb].tx_streamers[i].lock()); + if (my_streamer.get() == NULL) continue; + my_streamer->set_tick_rate(rate); + } + } } -void usrp2_impl::update_rx_samp_rate(const double rate){ - boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); - _io_impl->recv_handler.set_samp_rate(rate); - const double adj = _mbc[_mbc.keys().front()].rx_dsps.front()->get_scaling_adjustment(); - _io_impl->recv_handler.set_scale_factor(adj/32767.); +void usrp2_impl::update_rx_samp_rate(const std::string &mb, const size_t dsp, const double rate){ + boost::shared_ptr<sph::recv_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_mbc[mb].rx_streamers[dsp].lock()); + if (my_streamer.get() == NULL) return; + + my_streamer->set_samp_rate(rate); + const double adj = _mbc[mb].rx_dsps[dsp]->get_scaling_adjustment(); + my_streamer->set_scale_factor(adj); } -void usrp2_impl::update_tx_samp_rate(const double rate){ - boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); - _io_impl->send_handler.set_samp_rate(rate); +void usrp2_impl::update_tx_samp_rate(const std::string &mb, const size_t dsp, const double rate){ + boost::shared_ptr<sph::send_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::send_packet_streamer>(_mbc[mb].tx_streamers[dsp].lock()); + if (my_streamer.get() == NULL) return; + + my_streamer->set_samp_rate(rate); } -static subdev_spec_t replace_zero_in_spec(const std::string &type, const subdev_spec_t &spec){ - subdev_spec_t new_spec; - BOOST_FOREACH(const subdev_spec_pair_t &pair, spec){ - if (pair.db_name == "0"){ - UHD_MSG(warning) - << boost::format("In the %s subdevice specification: %s") % type % spec.to_string() << std::endl - << "Accepting dboard slot name \"0\" for backward compatibility." << std::endl - << "The official name of the dboard slot on USRP2/N-Series is \"A\"." << std::endl - ; - new_spec.push_back(subdev_spec_pair_t("A", pair.sd_name)); +void usrp2_impl::update_rates(void){ + BOOST_FOREACH(const std::string &mb, _mbc.keys()){ + fs_path root = "/mboards/" + mb; + _tree->access<double>(root / "tick_rate").update(); + + //and now that the tick rate is set, init the host rates to something + BOOST_FOREACH(const std::string &name, _tree->list(root / "rx_dsps")){ + _tree->access<double>(root / "rx_dsps" / name / "rate" / "value").update(); + } + BOOST_FOREACH(const std::string &name, _tree->list(root / "tx_dsps")){ + _tree->access<double>(root / "tx_dsps" / name / "rate" / "value").update(); } - else new_spec.push_back(pair); } - return new_spec; } -subdev_spec_t usrp2_impl::update_rx_subdev_spec(const std::string &which_mb, const subdev_spec_t &spec_){ - const subdev_spec_t spec = replace_zero_in_spec("RX", spec_); - boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); +void usrp2_impl::update_rx_subdev_spec(const std::string &which_mb, const subdev_spec_t &spec){ fs_path root = "/mboards/" + which_mb + "/dboards"; //sanity checking @@ -339,24 +335,9 @@ subdev_spec_t usrp2_impl::update_rx_subdev_spec(const std::string &which_mb, con _mbc[which_mb].rx_chan_occ = spec.size(); size_t nchan = 0; BOOST_FOREACH(const std::string &mb, _mbc.keys()) nchan += _mbc[mb].rx_chan_occ; - _io_impl->recv_handler.resize(nchan); - - //bind new callbacks for the handler - size_t chan = 0; - BOOST_FOREACH(const std::string &mb, _mbc.keys()){ - for (size_t dsp = 0; dsp < _mbc[mb].rx_chan_occ; dsp++){ - _mbc[mb].rx_dsps[dsp]->set_nsamps_per_packet(get_max_recv_samps_per_packet()); //seems to be a good place to set this - _io_impl->recv_handler.set_xport_chan_get_buff(chan++, boost::bind( - &zero_copy_if::get_recv_buff, _mbc[mb].rx_dsp_xports[dsp], _1 - )); - } - } - return spec; } -subdev_spec_t usrp2_impl::update_tx_subdev_spec(const std::string &which_mb, const subdev_spec_t &spec_){ - const subdev_spec_t spec = replace_zero_in_spec("TX", spec_); - boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); +void usrp2_impl::update_tx_subdev_spec(const std::string &which_mb, const subdev_spec_t &spec){ fs_path root = "/mboards/" + which_mb + "/dboards"; //sanity checking @@ -370,18 +351,6 @@ subdev_spec_t usrp2_impl::update_tx_subdev_spec(const std::string &which_mb, con _mbc[which_mb].tx_chan_occ = spec.size(); size_t nchan = 0; BOOST_FOREACH(const std::string &mb, _mbc.keys()) nchan += _mbc[mb].tx_chan_occ; - _io_impl->send_handler.resize(nchan); - - //bind new callbacks for the handler - size_t chan = 0, i = 0; - BOOST_FOREACH(const std::string &mb, _mbc.keys()){ - for (size_t dsp = 0; dsp < _mbc[mb].tx_chan_occ; dsp++){ - _io_impl->send_handler.set_xport_chan_get_buff(chan++, boost::bind( - &usrp2_impl::io_impl::get_send_buff, _io_impl.get(), i++, _1 - )); - } - } - return spec; } /*********************************************************************** @@ -395,51 +364,128 @@ bool usrp2_impl::recv_async_msg( } /*********************************************************************** - * Send Data + * Receive streamer **********************************************************************/ -size_t usrp2_impl::get_max_send_samps_per_packet(void) const{ +rx_streamer::sptr usrp2_impl::get_rx_stream(const uhd::stream_args_t &args_){ + stream_args_t args = args_; + + //setup defaults for unspecified values + args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; + args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + const unsigned sc8_scalar = unsigned(args.args.cast<double>("scalar", 0x400)); + + //calculate packet size static const size_t hdr_size = 0 + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) - + vrt_send_header_offset_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 = _mbc[_mbc.keys().front()].tx_dsp_xport->get_send_frame_size() - hdr_size; - return bpp/_tx_otw_type.get_sample_size(); -} + const size_t bpp = _mbc[_mbc.keys().front()].rx_dsp_xports[0]->get_recv_frame_size() - hdr_size; + const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format); + + //make the new streamer given the samples per packet + boost::shared_ptr<sph::recv_packet_streamer> my_streamer = boost::make_shared<sph::recv_packet_streamer>(spp); + + //init some streamer stuff + my_streamer->resize(args.channels.size()); + my_streamer->set_vrt_unpacker(&vrt::if_hdr_unpack_be); + + //set the converter + uhd::convert::id_type id; + id.input_format = args.otw_format + "_item32_be"; + id.num_inputs = 1; + id.output_format = args.cpu_format; + id.num_outputs = 1; + my_streamer->set_converter(id); + + //bind callbacks for the handler + for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){ + const size_t chan = args.channels[chan_i]; + size_t num_chan_so_far = 0; + BOOST_FOREACH(const std::string &mb, _mbc.keys()){ + num_chan_so_far += _mbc[mb].rx_chan_occ; + if (chan < num_chan_so_far){ + const size_t dsp = chan + _mbc[mb].rx_chan_occ - num_chan_so_far; + _mbc[mb].rx_dsps[dsp]->set_nsamps_per_packet(spp); //seems to be a good place to set this + _mbc[mb].rx_dsps[dsp]->set_format(args.otw_format, sc8_scalar); + my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( + &zero_copy_if::get_recv_buff, _mbc[mb].rx_dsp_xports[dsp], _1 + )); + _mbc[mb].rx_streamers[dsp] = my_streamer; //store weak pointer + break; + } + } + } -size_t usrp2_impl::send( - const send_buffs_type &buffs, size_t nsamps_per_buff, - const tx_metadata_t &metadata, const io_type_t &io_type, - send_mode_t send_mode, double timeout -){ - return _io_impl->send_handler.send( - buffs, nsamps_per_buff, - metadata, io_type, - send_mode, timeout - ); + //set the packet threshold to be an entire socket buffer's worth + const size_t packets_per_sock_buff = size_t(50e6/_mbc[_mbc.keys().front()].rx_dsp_xports[0]->get_recv_frame_size()); + my_streamer->set_alignment_failure_threshold(packets_per_sock_buff); + + //sets all tick and samp rates on this streamer + this->update_rates(); + + return my_streamer; } /*********************************************************************** - * Receive Data + * Transmit streamer **********************************************************************/ -size_t usrp2_impl::get_max_recv_samps_per_packet(void) const{ +tx_streamer::sptr usrp2_impl::get_tx_stream(const uhd::stream_args_t &args_){ + stream_args_t args = args_; + + //setup defaults for unspecified values + args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; + args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + + if (args.otw_format != "sc16"){ + throw uhd::value_error("USRP TX cannot handle requested wire format: " + args.otw_format); + } + + //calculate packet size 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 + + vrt_send_header_offset_words32*sizeof(boost::uint32_t) - sizeof(vrt::if_packet_info_t().cid) //no class id ever used ; - const size_t bpp = _mbc[_mbc.keys().front()].rx_dsp_xports[0]->get_recv_frame_size() - hdr_size; - return bpp/_rx_otw_type.get_sample_size(); -} + const size_t bpp = _mbc[_mbc.keys().front()].tx_dsp_xport->get_send_frame_size() - hdr_size; + const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format); + + //make the new streamer given the samples per packet + boost::shared_ptr<sph::send_packet_streamer> my_streamer = boost::make_shared<sph::send_packet_streamer>(spp); + + //init some streamer stuff + my_streamer->resize(args.channels.size()); + my_streamer->set_vrt_packer(&vrt::if_hdr_pack_be, vrt_send_header_offset_words32); + + //set the converter + uhd::convert::id_type id; + id.input_format = args.cpu_format; + id.num_inputs = 1; + id.output_format = args.otw_format + "_item32_be"; + id.num_outputs = 1; + my_streamer->set_converter(id); + + //bind callbacks for the handler + for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){ + const size_t chan = args.channels[chan_i]; + size_t num_chan_so_far = 0; + size_t abs = 0; + BOOST_FOREACH(const std::string &mb, _mbc.keys()){ + num_chan_so_far += _mbc[mb].tx_chan_occ; + if (chan < num_chan_so_far){ + const size_t dsp = chan + _mbc[mb].tx_chan_occ - num_chan_so_far; + my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( + &usrp2_impl::io_impl::get_send_buff, _io_impl.get(), abs, _1 + )); + _mbc[mb].tx_streamers[dsp] = my_streamer; //store weak pointer + break; + } + abs += 1; //assume 1 tx dsp + } + } -size_t usrp2_impl::recv( - const recv_buffs_type &buffs, size_t nsamps_per_buff, - rx_metadata_t &metadata, const io_type_t &io_type, - recv_mode_t recv_mode, double timeout -){ - return _io_impl->recv_handler.recv( - buffs, nsamps_per_buff, - metadata, io_type, - recv_mode, timeout - ); + //sets all tick and samp rates on this streamer + this->update_rates(); + + return my_streamer; } diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 2b541bcf0..bb3bfc7e4 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -456,11 +456,30 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ _mbc[mb].tx_fe = tx_frontend_core_200::make( _mbc[mb].iface, U2_REG_SR_ADDR(SR_TX_FRONT) ); - //TODO lots of properties to expose here for frontends + _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec") - .coerce(boost::bind(&usrp2_impl::update_rx_subdev_spec, this, mb, _1)); + .subscribe(boost::bind(&usrp2_impl::update_rx_subdev_spec, this, mb, _1)); _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec") - .coerce(boost::bind(&usrp2_impl::update_tx_subdev_spec, this, mb, _1)); + .subscribe(boost::bind(&usrp2_impl::update_tx_subdev_spec, this, mb, _1)); + + const fs_path rx_fe_path = mb_path / "rx_frontends" / "A"; + const fs_path tx_fe_path = mb_path / "tx_frontends" / "A"; + + _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value") + .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, _mbc[mb].rx_fe, _1)) + .set(std::complex<double>(0.0, 0.0)); + _tree->create<bool>(rx_fe_path / "dc_offset" / "enable") + .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, _mbc[mb].rx_fe, _1)) + .set(true); + _tree->create<std::complex<double> >(rx_fe_path / "iq_balance" / "value") + .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, _mbc[mb].rx_fe, _1)) + .set(std::complex<double>(0.0, 0.0)); + _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value") + .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, _mbc[mb].tx_fe, _1)) + .set(std::complex<double>(0.0, 0.0)); + _tree->create<std::complex<double> >(tx_fe_path / "iq_balance" / "value") + .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, _mbc[mb].tx_fe, _1)) + .set(std::complex<double>(0.0, 0.0)); //////////////////////////////////////////////////////////////// // create rx dsp control objects @@ -480,9 +499,12 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ _mbc[mb].rx_dsp_xports[dspno]->get_recv_buff(0.01).get(); //recv with timeout for lingering _mbc[mb].rx_dsp_xports[dspno]->get_recv_buff(0.01).get(); //recv with timeout for expected fs_path rx_dsp_path = mb_path / str(boost::format("rx_dsps/%u") % dspno); + _tree->create<meta_range_t>(rx_dsp_path / "rate/range") + .publish(boost::bind(&rx_dsp_core_200::get_host_rates, _mbc[mb].rx_dsps[dspno])); _tree->create<double>(rx_dsp_path / "rate/value") + .set(1e6) //some default .coerce(boost::bind(&rx_dsp_core_200::set_host_rate, _mbc[mb].rx_dsps[dspno], _1)) - .subscribe(boost::bind(&usrp2_impl::update_rx_samp_rate, this, _1)); + .subscribe(boost::bind(&usrp2_impl::update_rx_samp_rate, this, mb, dspno, _1)); _tree->create<double>(rx_dsp_path / "freq/value") .coerce(boost::bind(&rx_dsp_core_200::set_freq, _mbc[mb].rx_dsps[dspno], _1)); _tree->create<meta_range_t>(rx_dsp_path / "freq/range") @@ -500,9 +522,12 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ _mbc[mb].tx_dsp->set_link_rate(USRP2_LINK_RATE_BPS); _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&tx_dsp_core_200::set_tick_rate, _mbc[mb].tx_dsp, _1)); + _tree->create<meta_range_t>(mb_path / "tx_dsps/0/rate/range") + .publish(boost::bind(&tx_dsp_core_200::get_host_rates, _mbc[mb].tx_dsp)); _tree->create<double>(mb_path / "tx_dsps/0/rate/value") + .set(1e6) //some default .coerce(boost::bind(&tx_dsp_core_200::set_host_rate, _mbc[mb].tx_dsp, _1)) - .subscribe(boost::bind(&usrp2_impl::update_tx_samp_rate, this, _1)); + .subscribe(boost::bind(&usrp2_impl::update_tx_samp_rate, this, mb, 0, _1)); _tree->create<double>(mb_path / "tx_dsps/0/freq/value") .coerce(boost::bind(&usrp2_impl::set_tx_dsp_freq, this, mb, _1)); _tree->create<meta_range_t>(mb_path / "tx_dsps/0/freq/range") @@ -572,42 +597,21 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ _mbc[mb].dboard_iface = make_usrp2_dboard_iface(_mbc[mb].iface, _mbc[mb].clock); _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_mbc[mb].dboard_iface); _mbc[mb].dboard_manager = dboard_manager::make( - rx_db_eeprom.id, - ((gdb_eeprom.id == dboard_id_t::none())? tx_db_eeprom : gdb_eeprom).id, - _mbc[mb].dboard_iface + rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id, + _mbc[mb].dboard_iface, _tree->subtree(mb_path / "dboards/A") ); - BOOST_FOREACH(const std::string &name, _mbc[mb].dboard_manager->get_rx_subdev_names()){ - dboard_manager::populate_prop_tree_from_subdev( - _tree->subtree(mb_path / "dboards/A/rx_frontends" / name), - _mbc[mb].dboard_manager->get_rx_subdev(name) - ); - } - BOOST_FOREACH(const std::string &name, _mbc[mb].dboard_manager->get_tx_subdev_names()){ - dboard_manager::populate_prop_tree_from_subdev( - _tree->subtree(mb_path / "dboards/A/tx_frontends" / name), - _mbc[mb].dboard_manager->get_tx_subdev(name) - ); - } } //initialize io handling this->io_init(); //do some post-init tasks + this->update_rates(); BOOST_FOREACH(const std::string &mb, _mbc.keys()){ fs_path root = "/mboards/" + mb; - _tree->access<double>(root / "tick_rate").update(); - - //and now that the tick rate is set, init the host rates to something - BOOST_FOREACH(const std::string &name, _tree->list(root / "rx_dsps")){ - _tree->access<double>(root / "rx_dsps" / name / "rate" / "value").set(1e6); - } - BOOST_FOREACH(const std::string &name, _tree->list(root / "tx_dsps")){ - _tree->access<double>(root / "tx_dsps" / name / "rate" / "value").set(1e6); - } - _tree->access<subdev_spec_t>(root / "rx_subdev_spec").set(subdev_spec_t("A:"+_mbc[mb].dboard_manager->get_rx_subdev_names()[0])); - _tree->access<subdev_spec_t>(root / "tx_subdev_spec").set(subdev_spec_t("A:"+_mbc[mb].dboard_manager->get_tx_subdev_names()[0])); + _tree->access<subdev_spec_t>(root / "rx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(root / "dboards/A/rx_frontends").at(0))); + _tree->access<subdev_spec_t>(root / "tx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(root / "dboards/A/tx_frontends").at(0))); _tree->access<std::string>(root / "clock_source/value").set("internal"); _tree->access<std::string>(root / "time_source/value").set("none"); diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 6f133f411..31a390af7 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -31,7 +31,6 @@ #include <uhd/device.hpp> #include <uhd/utils/pimpl.hpp> #include <uhd/types/dict.hpp> -#include <uhd/types/otw_type.hpp> #include <uhd/types/stream_cmd.hpp> #include <uhd/types/clock_config.hpp> #include <uhd/usrp/dboard_eeprom.hpp> @@ -73,18 +72,8 @@ public: ~usrp2_impl(void); //the io interface - size_t send( - const send_buffs_type &, size_t, - const uhd::tx_metadata_t &, const uhd::io_type_t &, - uhd::device::send_mode_t, double - ); - size_t recv( - const recv_buffs_type &, size_t, - uhd::rx_metadata_t &, const uhd::io_type_t &, - uhd::device::recv_mode_t, double - ); - size_t get_max_send_samps_per_packet(void) const; - size_t get_max_recv_samps_per_packet(void) const; + uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &args); + uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args); bool recv_async_msg(uhd::async_metadata_t &, double); private: @@ -97,6 +86,8 @@ private: rx_frontend_core_200::sptr rx_fe; tx_frontend_core_200::sptr tx_fe; std::vector<rx_dsp_core_200::sptr> rx_dsps; + std::vector<boost::weak_ptr<uhd::rx_streamer> > rx_streamers; + std::vector<boost::weak_ptr<uhd::tx_streamer> > tx_streamers; tx_dsp_core_200::sptr tx_dsp; time64_core_200::sptr time64; std::vector<uhd::transport::zero_copy_if::sptr> rx_dsp_xports; @@ -120,15 +111,15 @@ private: } //io impl methods and members - uhd::otw_type_t _rx_otw_type, _tx_otw_type; UHD_PIMPL_DECL(io_impl) _io_impl; void io_init(void); void update_tick_rate(const double rate); - void update_rx_samp_rate(const double rate); - void update_tx_samp_rate(const double rate); + void update_rx_samp_rate(const std::string &, const size_t, const double rate); + void update_tx_samp_rate(const std::string &, const size_t, const double rate); + void update_rates(void); //update spec methods are coercers until we only accept db_name == A - uhd::usrp::subdev_spec_t update_rx_subdev_spec(const std::string &, const uhd::usrp::subdev_spec_t &); - uhd::usrp::subdev_spec_t update_tx_subdev_spec(const std::string &, const uhd::usrp::subdev_spec_t &); + void update_rx_subdev_spec(const std::string &, const uhd::usrp::subdev_spec_t &); + void update_tx_subdev_spec(const std::string &, const uhd::usrp::subdev_spec_t &); double set_tx_dsp_freq(const std::string &, const double); uhd::meta_range_t get_tx_dsp_freq_range(const std::string &); void update_clock_source(const std::string &, const std::string &); diff --git a/host/lib/usrp/usrp2/usrp2_regs.hpp b/host/lib/usrp/usrp2/usrp2_regs.hpp index 8839997f1..179a930c6 100644 --- a/host/lib/usrp/usrp2/usrp2_regs.hpp +++ b/host/lib/usrp/usrp2/usrp2_regs.hpp @@ -50,6 +50,7 @@ #define SR_TX_CTRL 144 // 6 #define SR_TX_DSP 160 // 5 +#define SR_GPIO 184 #define SR_UDP_SM 192 // 64 #define U2_REG_SR_ADDR(sr) (SETTING_REGS_BASE + (4 * (sr))) @@ -95,6 +96,7 @@ // Readback regs //////////////////////////////////////////////// #define U2_REG_STATUS READBACK_BASE + 4*8 +#define U2_REG_GPIO_RB READBACK_BASE + 4*9 #define U2_REG_TIME64_SECS_RB_IMM READBACK_BASE + 4*10 #define U2_REG_TIME64_TICKS_RB_IMM READBACK_BASE + 4*11 #define U2_REG_COMPAT_NUM_RB READBACK_BASE + 4*12 diff --git a/host/lib/utils/CMakeLists.txt b/host/lib/utils/CMakeLists.txt index fd3249099..19dde9a56 100644 --- a/host/lib/utils/CMakeLists.txt +++ b/host/lib/utils/CMakeLists.txt @@ -134,7 +134,6 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/log.cpp ${CMAKE_CURRENT_SOURCE_DIR}/msg.cpp ${CMAKE_CURRENT_SOURCE_DIR}/paths.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/props.cpp ${CMAKE_CURRENT_SOURCE_DIR}/static.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tasks.cpp ${CMAKE_CURRENT_SOURCE_DIR}/thread_priority.cpp diff --git a/host/lib/utils/props.cpp b/host/lib/utils/props.cpp deleted file mode 100644 index fc9f8e63f..000000000 --- a/host/lib/utils/props.cpp +++ /dev/null @@ -1,40 +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/utils/props.hpp> - -using namespace uhd; - -named_prop_t::named_prop_t( - const wax::obj &key, - const std::string &name -): - key(key), - name(name) -{ - /* NOP */ -} - -named_prop_t named_prop_t::extract( - const wax::obj &key, - const std::string &name -){ - if (key.type() == typeid(named_prop_t)){ - return key.as<named_prop_t>(); - } - return named_prop_t(key, name); -} diff --git a/host/lib/wax.cpp b/host/lib/wax.cpp deleted file mode 100644 index 5f658acd8..000000000 --- a/host/lib/wax.cpp +++ /dev/null @@ -1,151 +0,0 @@ -// -// Copyright 2010-2011 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/wax.hpp> -#include <uhd/exception.hpp> -#include <boost/format.hpp> -#include <stdexcept> - -/*! - * The link args for internal use within this cpp file: - * - * It contains a link (in this case a pointer) to a wax object. - * Only the methods in this file may create or parse link args. - * The get_link method is the creator of a link args object. - * The [] operator will resolve the link and make the [] call. - * - * TODO: register the link args with the wax obj that it links to. - * That way, if the obj destructs, the link can be invalidated. - * The operator() will throw, rather than dereferencing bad memory. - */ -class link_args_t{ -public: - link_args_t(const wax::obj *obj_ptr) : _obj_ptr(obj_ptr){ - /* NOP */ - } - wax::obj & operator()(void) const{ - //recursively resolve link args to get at original pointer - if (_obj_ptr->type() == typeid(link_args_t)){ - return _obj_ptr->as<link_args_t>()(); - } - return *const_cast<wax::obj *>(_obj_ptr); - } -private: - const wax::obj *_obj_ptr; -}; - -/*! - * The proxy args for internal use within this cpp file: - * - * It contains a link and a key for setting/getting a property. - * Only the methods in this file may create or parse proxy args. - * Class methods have special handling for the case when the - * wax obj contains an instance of the proxy args. - */ -class proxy_args_t{ -public: - proxy_args_t(const wax::obj *obj_ptr, const wax::obj &key) : _key(key){ - _obj_link = obj_ptr->get_link(); - } - wax::obj & operator()(void) const{ - return _obj_link.as<link_args_t>()(); - } - const wax::obj & key(void) const{ - return _key; - } -private: - wax::obj _obj_link; - const wax::obj _key; -}; - -/*********************************************************************** - * Structors - **********************************************************************/ -wax::obj::obj(void){ - /* NOP */ -} - -wax::obj::obj(const obj &o){ - _contents = o._contents; -} - -wax::obj::~obj(void){ - /* NOP */ -} - -/*********************************************************************** - * Special Operators - **********************************************************************/ -wax::obj wax::obj::operator[](const obj &key){ - if (_contents.type() == typeid(proxy_args_t)){ - obj val = resolve(); - //check if its a special link and call - if (val.type() == typeid(link_args_t)){ - return val.as<link_args_t>()()[key]; - } - //unknown obj - throw uhd::type_error("cannot use [] on non wax::obj link"); - } - else{ - return proxy_args_t(this, key); - } -} - -wax::obj & wax::obj::operator=(const obj &val){ - if (_contents.type() == typeid(proxy_args_t)){ - proxy_args_t proxy_args = boost::any_cast<proxy_args_t>(_contents); - proxy_args().set(proxy_args.key(), val); - } - else{ - _contents = val._contents; - } - return *this; -} - -/*********************************************************************** - * Public Methods - **********************************************************************/ -wax::obj wax::obj::get_link(void) const{ - return link_args_t(this); -} - -const std::type_info & wax::obj::type(void) const{ - return resolve().type(); -} - -/*********************************************************************** - * Private Methods - **********************************************************************/ -boost::any wax::obj::resolve(void) const{ - if (_contents.type() == typeid(proxy_args_t)){ - obj val; - proxy_args_t proxy_args = boost::any_cast<proxy_args_t>(_contents); - proxy_args().get(proxy_args.key(), val); - return val.resolve(); - } - else{ - return _contents; - } -} - -void wax::obj::get(const obj &, obj &){ - throw uhd::type_error("Cannot call get on wax obj base class"); -} - -void wax::obj::set(const obj &, const obj &){ - throw uhd::type_error("Cannot call set on wax obj base class"); -} diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index c97116233..28cc1c5da 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -34,7 +34,6 @@ SET(test_sources subdev_spec_test.cpp time_spec_test.cpp vrt_test.cpp - wax_test.cpp ) #turn each test cpp file into an executable with an int main() function diff --git a/host/tests/convert_test.cpp b/host/tests/convert_test.cpp index d828ed64a..b63ff6752 100644 --- a/host/tests/convert_test.cpp +++ b/host/tests/convert_test.cpp @@ -41,8 +41,8 @@ typedef std::complex<double> fc64_t; **********************************************************************/ template <typename Range> static void loopback( size_t nsamps, - const io_type_t &io_type, - const otw_type_t &otw_type, + convert::id_type &in_id, + convert::id_type &out_id, const Range &input, Range &output ){ @@ -53,23 +53,17 @@ template <typename Range> static void loopback( std::vector<void *> output0(1, &interm[0]), output1(1, &output[0]); //convert to intermediate type - convert::get_converter_cpu_to_otw( - io_type, otw_type, input0.size(), output0.size() - )(input0, output0, nsamps, 32767.); + convert::get_converter(in_id)(input0, output0, nsamps, 32767.); //convert back to host type - convert::get_converter_otw_to_cpu( - io_type, otw_type, input1.size(), output1.size() - )(input1, output1, nsamps, 1/32767.); + convert::get_converter(out_id)(input1, output1, nsamps, 1/32767.); } /*********************************************************************** * Test short conversion **********************************************************************/ static void test_convert_types_sc16( - size_t nsamps, - const io_type_t &io_type, - const otw_type_t &otw_type + size_t nsamps, convert::id_type &id ){ //fill the input samples std::vector<sc16_t> input(nsamps), output(nsamps); @@ -79,31 +73,37 @@ static void test_convert_types_sc16( ); //run the loopback and test - loopback(nsamps, io_type, otw_type, input, output); + convert::id_type in_id = id; + convert::id_type out_id = id; + std::swap(out_id.input_format, out_id.output_format); + std::swap(out_id.num_inputs, out_id.num_outputs); + loopback(nsamps, in_id, out_id, input, output); BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); } BOOST_AUTO_TEST_CASE(test_convert_types_be_sc16){ - io_type_t io_type(io_type_t::COMPLEX_INT16); - otw_type_t otw_type; - otw_type.byteorder = otw_type_t::BO_BIG_ENDIAN; - otw_type.width = 16; + convert::id_type id; + id.input_format = "sc16"; + id.num_inputs = 1; + id.output_format = "sc16_item32_be"; + id.num_outputs = 1; //try various lengths to test edge cases for (size_t nsamps = 1; nsamps < 16; nsamps++){ - test_convert_types_sc16(nsamps, io_type, otw_type); + test_convert_types_sc16(nsamps, id); } } BOOST_AUTO_TEST_CASE(test_convert_types_le_sc16){ - io_type_t io_type(io_type_t::COMPLEX_INT16); - otw_type_t otw_type; - otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; - otw_type.width = 16; + convert::id_type id; + id.input_format = "sc16"; + id.num_inputs = 1; + id.output_format = "sc16_item32_le"; + id.num_outputs = 1; //try various lengths to test edge cases for (size_t nsamps = 1; nsamps < 16; nsamps++){ - test_convert_types_sc16(nsamps, io_type, otw_type); + test_convert_types_sc16(nsamps, id); } } @@ -112,9 +112,7 @@ BOOST_AUTO_TEST_CASE(test_convert_types_le_sc16){ **********************************************************************/ template <typename data_type> static void test_convert_types_for_floats( - size_t nsamps, - const io_type_t &io_type, - const otw_type_t &otw_type + size_t nsamps, convert::id_type &id ){ typedef typename data_type::value_type value_type; @@ -126,7 +124,11 @@ static void test_convert_types_for_floats( ); //run the loopback and test - loopback(nsamps, io_type, otw_type, input, output); + convert::id_type in_id = id; + convert::id_type out_id = id; + std::swap(out_id.input_format, out_id.output_format); + std::swap(out_id.num_inputs, out_id.num_outputs); + loopback(nsamps, in_id, out_id, input, output); for (size_t i = 0; i < nsamps; i++){ MY_CHECK_CLOSE(input[i].real(), output[i].real(), value_type(0.01)); MY_CHECK_CLOSE(input[i].imag(), output[i].imag(), value_type(0.01)); @@ -134,50 +136,54 @@ static void test_convert_types_for_floats( } BOOST_AUTO_TEST_CASE(test_convert_types_be_fc32){ - io_type_t io_type(io_type_t::COMPLEX_FLOAT32); - otw_type_t otw_type; - otw_type.byteorder = otw_type_t::BO_BIG_ENDIAN; - otw_type.width = 16; + convert::id_type id; + id.input_format = "fc32"; + id.num_inputs = 1; + id.output_format = "sc16_item32_be"; + id.num_outputs = 1; //try various lengths to test edge cases for (size_t nsamps = 1; nsamps < 16; nsamps++){ - test_convert_types_for_floats<fc32_t>(nsamps, io_type, otw_type); + test_convert_types_for_floats<fc32_t>(nsamps, id); } } BOOST_AUTO_TEST_CASE(test_convert_types_le_fc32){ - io_type_t io_type(io_type_t::COMPLEX_FLOAT32); - otw_type_t otw_type; - otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; - otw_type.width = 16; + convert::id_type id; + id.input_format = "fc32"; + id.num_inputs = 1; + id.output_format = "sc16_item32_le"; + id.num_outputs = 1; //try various lengths to test edge cases for (size_t nsamps = 1; nsamps < 16; nsamps++){ - test_convert_types_for_floats<fc32_t>(nsamps, io_type, otw_type); + test_convert_types_for_floats<fc32_t>(nsamps, id); } } BOOST_AUTO_TEST_CASE(test_convert_types_be_fc64){ - io_type_t io_type(io_type_t::COMPLEX_FLOAT64); - otw_type_t otw_type; - otw_type.byteorder = otw_type_t::BO_BIG_ENDIAN; - otw_type.width = 16; + convert::id_type id; + id.input_format = "fc64"; + id.num_inputs = 1; + id.output_format = "sc16_item32_be"; + id.num_outputs = 1; //try various lengths to test edge cases for (size_t nsamps = 1; nsamps < 16; nsamps++){ - test_convert_types_for_floats<fc64_t>(nsamps, io_type, otw_type); + test_convert_types_for_floats<fc64_t>(nsamps, id); } } BOOST_AUTO_TEST_CASE(test_convert_types_le_fc64){ - io_type_t io_type(io_type_t::COMPLEX_FLOAT64); - otw_type_t otw_type; - otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; - otw_type.width = 16; + convert::id_type id; + id.input_format = "fc64"; + id.num_inputs = 1; + id.output_format = "sc16_item32_le"; + id.num_outputs = 1; //try various lengths to test edge cases for (size_t nsamps = 1; nsamps < 16; nsamps++){ - test_convert_types_for_floats<fc64_t>(nsamps, io_type, otw_type); + test_convert_types_for_floats<fc64_t>(nsamps, id); } } @@ -185,12 +191,17 @@ BOOST_AUTO_TEST_CASE(test_convert_types_le_fc64){ * Test float to short conversion loopback **********************************************************************/ BOOST_AUTO_TEST_CASE(test_convert_types_fc32_to_sc16){ - io_type_t io_type_in(io_type_t::COMPLEX_FLOAT32); - io_type_t io_type_out(io_type_t::COMPLEX_INT16); - - otw_type_t otw_type; - otw_type.byteorder = otw_type_t::BO_NATIVE; - otw_type.width = 16; + convert::id_type in_id; + in_id.input_format = "fc32"; + in_id.num_inputs = 1; + in_id.output_format = "sc16_item32_le"; + in_id.num_outputs = 1; + + convert::id_type out_id; + out_id.input_format = "sc16_item32_le"; + out_id.num_inputs = 1; + out_id.output_format = "sc16"; + out_id.num_outputs = 1; const size_t nsamps = 13; std::vector<fc32_t> input(nsamps); @@ -205,14 +216,10 @@ BOOST_AUTO_TEST_CASE(test_convert_types_fc32_to_sc16){ std::vector<void *> output0(1, &interm[0]), output1(1, &output[0]); //convert float to intermediate - convert::get_converter_cpu_to_otw( - io_type_in, otw_type, input0.size(), output0.size() - )(input0, output0, nsamps, 32767.); + convert::get_converter(in_id)(input0, output0, nsamps, 32767.); //convert intermediate to short - convert::get_converter_otw_to_cpu( - io_type_out, otw_type, input1.size(), output1.size() - )(input1, output1, nsamps, 1/32767.); + convert::get_converter(out_id)(input1, output1, nsamps, 1/32767.); //test that the inputs and outputs match for (size_t i = 0; i < nsamps; i++){ @@ -225,12 +232,17 @@ BOOST_AUTO_TEST_CASE(test_convert_types_fc32_to_sc16){ * Test short to float conversion loopback **********************************************************************/ BOOST_AUTO_TEST_CASE(test_convert_types_sc16_to_fc32){ - io_type_t io_type_in(io_type_t::COMPLEX_INT16); - io_type_t io_type_out(io_type_t::COMPLEX_FLOAT32); - - otw_type_t otw_type; - otw_type.byteorder = otw_type_t::BO_NATIVE; - otw_type.width = 16; + convert::id_type in_id; + in_id.input_format = "sc16"; + in_id.num_inputs = 1; + in_id.output_format = "sc16_item32_le"; + in_id.num_outputs = 1; + + convert::id_type out_id; + out_id.input_format = "sc16_item32_le"; + out_id.num_inputs = 1; + out_id.output_format = "fc32"; + out_id.num_outputs = 1; const size_t nsamps = 13; std::vector<sc16_t> input(nsamps); @@ -245,14 +257,10 @@ BOOST_AUTO_TEST_CASE(test_convert_types_sc16_to_fc32){ std::vector<void *> output0(1, &interm[0]), output1(1, &output[0]); //convert short to intermediate - convert::get_converter_cpu_to_otw( - io_type_in, otw_type, input0.size(), output0.size() - )(input0, output0, nsamps, 32767.); + convert::get_converter(in_id)(input0, output0, nsamps, 32767.); //convert intermediate to float - convert::get_converter_otw_to_cpu( - io_type_out, otw_type, input1.size(), output1.size() - )(input1, output1, nsamps, 1/32767.); + convert::get_converter(out_id)(input1, output1, nsamps, 1/32767.); //test that the inputs and outputs match for (size_t i = 0; i < nsamps; i++){ diff --git a/host/tests/ranges_test.cpp b/host/tests/ranges_test.cpp index 5f6de4645..85bb4c3c4 100644 --- a/host/tests/ranges_test.cpp +++ b/host/tests/ranges_test.cpp @@ -55,3 +55,16 @@ BOOST_AUTO_TEST_CASE(test_ranges_clip){ BOOST_CHECK_CLOSE(mr.clip(50.9, false), 50.9, tolerance); BOOST_CHECK_CLOSE(mr.clip(50.9, true), 51.0, tolerance); } + +BOOST_AUTO_TEST_CASE(test_ranges_clip2){ + meta_range_t mr; + mr.push_back(range_t(1.)); + mr.push_back(range_t(2.)); + mr.push_back(range_t(3.)); + + BOOST_CHECK_CLOSE(mr.clip(2., true), 2., tolerance); + BOOST_CHECK_CLOSE(mr.clip(0., true), 1., tolerance); + BOOST_CHECK_CLOSE(mr.clip(1.2, true), 1., tolerance); + BOOST_CHECK_CLOSE(mr.clip(3.1, true), 3., tolerance); + BOOST_CHECK_CLOSE(mr.clip(4., true), 3., tolerance); +} diff --git a/host/tests/sph_recv_test.cpp b/host/tests/sph_recv_test.cpp index 1387e3b66..85d06aa0d 100644 --- a/host/tests/sph_recv_test.cpp +++ b/host/tests/sph_recv_test.cpp @@ -67,8 +67,8 @@ private: **********************************************************************/ class dummy_recv_xport_class{ public: - dummy_recv_xport_class(const uhd::otw_type_t &otw_type){ - _otw_type = otw_type; + dummy_recv_xport_class(const std::string &end){ + _end = end; } void push_back_packet( @@ -77,10 +77,10 @@ public: ){ const size_t max_pkt_len = (ifpi.num_payload_words32 + uhd::transport::vrt::max_if_hdr_words32 + 1/*tlr*/)*sizeof(boost::uint32_t); _mems.push_back(boost::shared_array<char>(new char[max_pkt_len])); - if (_otw_type.byteorder == uhd::otw_type_t::BO_BIG_ENDIAN){ + if (_end == "big"){ uhd::transport::vrt::if_hdr_pack_be(reinterpret_cast<boost::uint32_t *>(_mems.back().get()), ifpi); } - if (_otw_type.byteorder == uhd::otw_type_t::BO_LITTLE_ENDIAN){ + if (_end == "little"){ uhd::transport::vrt::if_hdr_pack_le(reinterpret_cast<boost::uint32_t *>(_mems.back().get()), ifpi); } (reinterpret_cast<boost::uint32_t *>(_mems.back().get()) + ifpi.num_header_words32)[0] = optional_msg_word | uhd::byteswap(optional_msg_word); @@ -100,18 +100,19 @@ private: std::list<boost::shared_array<char> > _mems; std::list<size_t> _lens; std::list<dummy_mrb> _mrbs; //list means no-realloc - uhd::otw_type_t _otw_type; + std::string _end; }; //////////////////////////////////////////////////////////////////////// BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_normal){ //////////////////////////////////////////////////////////////////////// - uhd::otw_type_t otw_type; - otw_type.width = 16; - otw_type.shift = 0; - otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; + uhd::convert::id_type id; + id.input_format = "sc16_item32_be"; + id.num_inputs = 1; + id.output_format = "fc32"; + id.num_outputs = 1; - dummy_recv_xport_class dummy_recv_xport(otw_type); + dummy_recv_xport_class dummy_recv_xport("big"); uhd::transport::vrt::if_packet_info_t ifpi; ifpi.packet_type = uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_DATA; ifpi.num_payload_words32 = 0; @@ -144,7 +145,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_normal){ handler.set_tick_rate(TICK_RATE); handler.set_samp_rate(SAMP_RATE); handler.set_xport_chan_get_buff(0, boost::bind(&dummy_recv_xport_class::get_recv_buff, &dummy_recv_xport, _1)); - handler.set_converter(otw_type); + handler.set_converter(id); //check the received packets size_t num_accum_samps = 0; @@ -153,9 +154,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_normal){ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){ std::cout << "data check " << i << std::endl; size_t num_samps_ret = handler.recv( - &buff.front(), buff.size(), metadata, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_ONE_PACKET, 1.0 + &buff.front(), buff.size(), metadata, 1.0, true ); BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_NONE); BOOST_CHECK(not metadata.more_fragments); @@ -169,9 +168,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_normal){ for (size_t i = 0; i < 3; i++){ std::cout << "timeout check " << i << std::endl; handler.recv( - &buff.front(), buff.size(), metadata, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_ONE_PACKET, 1.0 + &buff.front(), buff.size(), metadata, 1.0, true ); BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_TIMEOUT); } @@ -180,12 +177,13 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_normal){ //////////////////////////////////////////////////////////////////////// BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_sequence_error){ //////////////////////////////////////////////////////////////////////// - uhd::otw_type_t otw_type; - otw_type.width = 16; - otw_type.shift = 0; - otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; + uhd::convert::id_type id; + id.input_format = "sc16_item32_be"; + id.num_inputs = 1; + id.output_format = "fc32"; + id.num_outputs = 1; - dummy_recv_xport_class dummy_recv_xport(otw_type); + dummy_recv_xport_class dummy_recv_xport("big"); uhd::transport::vrt::if_packet_info_t ifpi; ifpi.packet_type = uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_DATA; ifpi.num_payload_words32 = 0; @@ -220,7 +218,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_sequence_error){ handler.set_tick_rate(TICK_RATE); handler.set_samp_rate(SAMP_RATE); handler.set_xport_chan_get_buff(0, boost::bind(&dummy_recv_xport_class::get_recv_buff, &dummy_recv_xport, _1)); - handler.set_converter(otw_type); + handler.set_converter(id); //check the received packets size_t num_accum_samps = 0; @@ -229,9 +227,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_sequence_error){ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){ std::cout << "data check " << i << std::endl; size_t num_samps_ret = handler.recv( - &buff.front(), buff.size(), metadata, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_ONE_PACKET, 1.0 + &buff.front(), buff.size(), metadata, 1.0, true ); if (i == NUM_PKTS_TO_TEST/2){ //must get the soft overflow here @@ -253,9 +249,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_sequence_error){ for (size_t i = 0; i < 3; i++){ std::cout << "timeout check " << i << std::endl; handler.recv( - &buff.front(), buff.size(), metadata, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_ONE_PACKET, 1.0 + &buff.front(), buff.size(), metadata, 1.0, true ); BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_TIMEOUT); } @@ -264,12 +258,13 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_sequence_error){ //////////////////////////////////////////////////////////////////////// BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_inline_message){ //////////////////////////////////////////////////////////////////////// - uhd::otw_type_t otw_type; - otw_type.width = 16; - otw_type.shift = 0; - otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; + uhd::convert::id_type id; + id.input_format = "sc16_item32_be"; + id.num_inputs = 1; + id.output_format = "fc32"; + id.num_outputs = 1; - dummy_recv_xport_class dummy_recv_xport(otw_type); + dummy_recv_xport_class dummy_recv_xport("big"); uhd::transport::vrt::if_packet_info_t ifpi; ifpi.packet_type = uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_DATA; ifpi.num_payload_words32 = 0; @@ -310,7 +305,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_inline_message){ handler.set_tick_rate(TICK_RATE); handler.set_samp_rate(SAMP_RATE); handler.set_xport_chan_get_buff(0, boost::bind(&dummy_recv_xport_class::get_recv_buff, &dummy_recv_xport, _1)); - handler.set_converter(otw_type); + handler.set_converter(id); //create an overflow handler overflow_handler_type overflow_handler; @@ -323,9 +318,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_inline_message){ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){ std::cout << "data check " << i << std::endl; size_t num_samps_ret = handler.recv( - &buff.front(), buff.size(), metadata, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_ONE_PACKET, 1.0 + &buff.front(), buff.size(), metadata, 1.0, true ); BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_NONE); BOOST_CHECK(not metadata.more_fragments); @@ -335,9 +328,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_inline_message){ num_accum_samps += num_samps_ret; if (i == NUM_PKTS_TO_TEST/2){ handler.recv( - &buff.front(), buff.size(), metadata, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_ONE_PACKET, 1.0 + &buff.front(), buff.size(), metadata, 1.0, true ); std::cout << "metadata.error_code " << metadata.error_code << std::endl; BOOST_REQUIRE(metadata.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW); @@ -350,9 +341,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_inline_message){ for (size_t i = 0; i < 3; i++){ std::cout << "timeout check " << i << std::endl; handler.recv( - &buff.front(), buff.size(), metadata, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_ONE_PACKET, 1.0 + &buff.front(), buff.size(), metadata, 1.0, true ); BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_TIMEOUT); } @@ -361,10 +350,11 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_inline_message){ //////////////////////////////////////////////////////////////////////// BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_normal){ //////////////////////////////////////////////////////////////////////// - uhd::otw_type_t otw_type; - otw_type.width = 16; - otw_type.shift = 0; - otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; + uhd::convert::id_type id; + id.input_format = "sc16_item32_be"; + id.num_inputs = 1; + id.output_format = "fc32"; + id.num_outputs = 1; uhd::transport::vrt::if_packet_info_t ifpi; ifpi.packet_type = uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_DATA; @@ -386,7 +376,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_normal){ static const size_t NUM_SAMPS_PER_BUFF = 20; static const size_t NCHANNELS = 4; - std::vector<dummy_recv_xport_class> dummy_recv_xports(NCHANNELS, dummy_recv_xport_class(otw_type)); + std::vector<dummy_recv_xport_class> dummy_recv_xports(NCHANNELS, dummy_recv_xport_class("big")); //generate a bunch of packets for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){ @@ -406,7 +396,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_normal){ for (size_t ch = 0; ch < NCHANNELS; ch++){ handler.set_xport_chan_get_buff(ch, boost::bind(&dummy_recv_xport_class::get_recv_buff, &dummy_recv_xports[ch], _1)); } - handler.set_converter(otw_type); + handler.set_converter(id); //check the received packets size_t num_accum_samps = 0; @@ -419,9 +409,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_normal){ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){ std::cout << "data check " << i << std::endl; size_t num_samps_ret = handler.recv( - buffs, NUM_SAMPS_PER_BUFF, metadata, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_ONE_PACKET, 1.0 + buffs, NUM_SAMPS_PER_BUFF, metadata, 1.0, true ); BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_NONE); BOOST_CHECK(not metadata.more_fragments); @@ -435,9 +423,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_normal){ for (size_t i = 0; i < 3; i++){ std::cout << "timeout check " << i << std::endl; handler.recv( - buffs, NUM_SAMPS_PER_BUFF, metadata, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_ONE_PACKET, 1.0 + buffs, NUM_SAMPS_PER_BUFF, metadata, 1.0, true ); BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_TIMEOUT); } @@ -447,10 +433,11 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_normal){ //////////////////////////////////////////////////////////////////////// BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_sequence_error){ //////////////////////////////////////////////////////////////////////// - uhd::otw_type_t otw_type; - otw_type.width = 16; - otw_type.shift = 0; - otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; + uhd::convert::id_type id; + id.input_format = "sc16_item32_be"; + id.num_inputs = 1; + id.output_format = "fc32"; + id.num_outputs = 1; uhd::transport::vrt::if_packet_info_t ifpi; ifpi.packet_type = uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_DATA; @@ -472,7 +459,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_sequence_error){ static const size_t NUM_SAMPS_PER_BUFF = 20; static const size_t NCHANNELS = 4; - std::vector<dummy_recv_xport_class> dummy_recv_xports(NCHANNELS, dummy_recv_xport_class(otw_type)); + std::vector<dummy_recv_xport_class> dummy_recv_xports(NCHANNELS, dummy_recv_xport_class("big")); //generate a bunch of packets for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){ @@ -495,7 +482,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_sequence_error){ for (size_t ch = 0; ch < NCHANNELS; ch++){ handler.set_xport_chan_get_buff(ch, boost::bind(&dummy_recv_xport_class::get_recv_buff, &dummy_recv_xports[ch], _1)); } - handler.set_converter(otw_type); + handler.set_converter(id); //check the received packets size_t num_accum_samps = 0; @@ -508,9 +495,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_sequence_error){ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){ std::cout << "data check " << i << std::endl; size_t num_samps_ret = handler.recv( - buffs, NUM_SAMPS_PER_BUFF, metadata, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_ONE_PACKET, 1.0 + buffs, NUM_SAMPS_PER_BUFF, metadata, 1.0, true ); if (i == NUM_PKTS_TO_TEST/2){ //must get the soft overflow here @@ -532,9 +517,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_sequence_error){ for (size_t i = 0; i < 3; i++){ std::cout << "timeout check " << i << std::endl; handler.recv( - buffs, NUM_SAMPS_PER_BUFF, metadata, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_ONE_PACKET, 1.0 + buffs, NUM_SAMPS_PER_BUFF, metadata, 1.0, true ); BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_TIMEOUT); } @@ -543,10 +526,11 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_sequence_error){ //////////////////////////////////////////////////////////////////////// BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_time_error){ //////////////////////////////////////////////////////////////////////// - uhd::otw_type_t otw_type; - otw_type.width = 16; - otw_type.shift = 0; - otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; + uhd::convert::id_type id; + id.input_format = "sc16_item32_be"; + id.num_inputs = 1; + id.output_format = "fc32"; + id.num_outputs = 1; uhd::transport::vrt::if_packet_info_t ifpi; ifpi.packet_type = uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_DATA; @@ -568,7 +552,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_time_error){ static const size_t NUM_SAMPS_PER_BUFF = 20; static const size_t NCHANNELS = 4; - std::vector<dummy_recv_xport_class> dummy_recv_xports(NCHANNELS, dummy_recv_xport_class(otw_type)); + std::vector<dummy_recv_xport_class> dummy_recv_xports(NCHANNELS, dummy_recv_xport_class("big")); //generate a bunch of packets for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){ @@ -591,7 +575,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_time_error){ for (size_t ch = 0; ch < NCHANNELS; ch++){ handler.set_xport_chan_get_buff(ch, boost::bind(&dummy_recv_xport_class::get_recv_buff, &dummy_recv_xports[ch], _1)); } - handler.set_converter(otw_type); + handler.set_converter(id); //check the received packets size_t num_accum_samps = 0; @@ -604,9 +588,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_time_error){ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){ std::cout << "data check " << i << std::endl; size_t num_samps_ret = handler.recv( - buffs, NUM_SAMPS_PER_BUFF, metadata, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_ONE_PACKET, 1.0 + buffs, NUM_SAMPS_PER_BUFF, metadata, 1.0, true ); BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_NONE); BOOST_CHECK(not metadata.more_fragments); @@ -623,9 +605,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_time_error){ for (size_t i = 0; i < 3; i++){ std::cout << "timeout check " << i << std::endl; handler.recv( - buffs, NUM_SAMPS_PER_BUFF, metadata, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_ONE_PACKET, 1.0 + buffs, NUM_SAMPS_PER_BUFF, metadata, 1.0, true ); BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_TIMEOUT); } @@ -634,10 +614,11 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_time_error){ //////////////////////////////////////////////////////////////////////// BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_fragment){ //////////////////////////////////////////////////////////////////////// - uhd::otw_type_t otw_type; - otw_type.width = 16; - otw_type.shift = 0; - otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; + uhd::convert::id_type id; + id.input_format = "sc16_item32_be"; + id.num_inputs = 1; + id.output_format = "fc32"; + id.num_outputs = 1; uhd::transport::vrt::if_packet_info_t ifpi; ifpi.packet_type = uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_DATA; @@ -659,7 +640,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_fragment){ static const size_t NUM_SAMPS_PER_BUFF = 10; static const size_t NCHANNELS = 4; - std::vector<dummy_recv_xport_class> dummy_recv_xports(NCHANNELS, dummy_recv_xport_class(otw_type)); + std::vector<dummy_recv_xport_class> dummy_recv_xports(NCHANNELS, dummy_recv_xport_class("big")); //generate a bunch of packets for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){ @@ -679,7 +660,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_fragment){ for (size_t ch = 0; ch < NCHANNELS; ch++){ handler.set_xport_chan_get_buff(ch, boost::bind(&dummy_recv_xport_class::get_recv_buff, &dummy_recv_xports[ch], _1)); } - handler.set_converter(otw_type); + handler.set_converter(id); //check the received packets size_t num_accum_samps = 0; @@ -692,9 +673,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_fragment){ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){ std::cout << "data check " << i << std::endl; size_t num_samps_ret = handler.recv( - buffs, NUM_SAMPS_PER_BUFF, metadata, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_ONE_PACKET, 1.0 + buffs, NUM_SAMPS_PER_BUFF, metadata, 1.0, true ); BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_NONE); BOOST_CHECK(metadata.has_time_spec); @@ -705,9 +684,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_fragment){ if (not metadata.more_fragments) continue; num_samps_ret = handler.recv( - buffs, NUM_SAMPS_PER_BUFF, metadata, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_ONE_PACKET, 1.0 + buffs, NUM_SAMPS_PER_BUFF, metadata, 1.0, true ); BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_NONE); BOOST_CHECK(not metadata.more_fragments); @@ -722,9 +699,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_fragment){ for (size_t i = 0; i < 3; i++){ std::cout << "timeout check " << i << std::endl; handler.recv( - buffs, NUM_SAMPS_PER_BUFF, metadata, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::RECV_MODE_ONE_PACKET, 1.0 + buffs, NUM_SAMPS_PER_BUFF, metadata, 1.0, true ); BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_TIMEOUT); } diff --git a/host/tests/sph_send_test.cpp b/host/tests/sph_send_test.cpp index ed2f54371..25a3f97ee 100644 --- a/host/tests/sph_send_test.cpp +++ b/host/tests/sph_send_test.cpp @@ -55,18 +55,18 @@ private: **********************************************************************/ class dummy_send_xport_class{ public: - dummy_send_xport_class(const uhd::otw_type_t &otw_type){ - _otw_type = otw_type; + dummy_send_xport_class(const std::string &end){ + _end = end; } void pop_front_packet( uhd::transport::vrt::if_packet_info_t &ifpi ){ ifpi.num_packet_words32 = _lens.front()/sizeof(boost::uint32_t); - if (_otw_type.byteorder == uhd::otw_type_t::BO_BIG_ENDIAN){ + if (_end == "big"){ uhd::transport::vrt::if_hdr_unpack_be(reinterpret_cast<boost::uint32_t *>(_mems.front().get()), ifpi); } - if (_otw_type.byteorder == uhd::otw_type_t::BO_LITTLE_ENDIAN){ + if (_end == "little"){ uhd::transport::vrt::if_hdr_unpack_le(reinterpret_cast<boost::uint32_t *>(_mems.front().get()), ifpi); } _mems.pop_front(); @@ -85,18 +85,19 @@ private: std::list<boost::shared_array<char> > _mems; std::list<size_t> _lens; std::list<dummy_msb> _msbs; //list means no-realloc - uhd::otw_type_t _otw_type; + std::string _end; }; //////////////////////////////////////////////////////////////////////// BOOST_AUTO_TEST_CASE(test_sph_send_one_channel_one_packet_mode){ //////////////////////////////////////////////////////////////////////// - uhd::otw_type_t otw_type; - otw_type.width = 16; - otw_type.shift = 0; - otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; + uhd::convert::id_type id; + id.input_format = "fc32"; + id.num_inputs = 1; + id.output_format = "sc16_item32_be"; + id.num_outputs = 1; - dummy_send_xport_class dummy_send_xport(otw_type); + dummy_send_xport_class dummy_send_xport("big"); static const double TICK_RATE = 100e6; static const double SAMP_RATE = 10e6; @@ -108,7 +109,7 @@ BOOST_AUTO_TEST_CASE(test_sph_send_one_channel_one_packet_mode){ handler.set_tick_rate(TICK_RATE); handler.set_samp_rate(SAMP_RATE); handler.set_xport_chan_get_buff(0, boost::bind(&dummy_send_xport_class::get_send_buff, &dummy_send_xport, _1)); - handler.set_converter(otw_type); + handler.set_converter(id); handler.set_max_samples_per_packet(20); //allocate metadata and buffer @@ -122,9 +123,7 @@ BOOST_AUTO_TEST_CASE(test_sph_send_one_channel_one_packet_mode){ metadata.start_of_burst = (i == 0); metadata.end_of_burst = (i == NUM_PKTS_TO_TEST-1); const size_t num_sent = handler.send( - &buff.front(), 10 + i%10, metadata, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::SEND_MODE_ONE_PACKET, 1.0 + &buff.front(), 10 + i%10, metadata, 1.0 ); BOOST_CHECK_EQUAL(num_sent, 10 + i%10); metadata.time_spec += uhd::time_spec_t(0, num_sent, SAMP_RATE); @@ -150,12 +149,13 @@ BOOST_AUTO_TEST_CASE(test_sph_send_one_channel_one_packet_mode){ //////////////////////////////////////////////////////////////////////// BOOST_AUTO_TEST_CASE(test_sph_send_one_channel_full_buffer_mode){ //////////////////////////////////////////////////////////////////////// - uhd::otw_type_t otw_type; - otw_type.width = 16; - otw_type.shift = 0; - otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; + uhd::convert::id_type id; + id.input_format = "fc32"; + id.num_inputs = 1; + id.output_format = "sc16_item32_be"; + id.num_outputs = 1; - dummy_send_xport_class dummy_send_xport(otw_type); + dummy_send_xport_class dummy_send_xport("big"); static const double TICK_RATE = 100e6; static const double SAMP_RATE = 10e6; @@ -167,7 +167,7 @@ BOOST_AUTO_TEST_CASE(test_sph_send_one_channel_full_buffer_mode){ handler.set_tick_rate(TICK_RATE); handler.set_samp_rate(SAMP_RATE); handler.set_xport_chan_get_buff(0, boost::bind(&dummy_send_xport_class::get_send_buff, &dummy_send_xport, _1)); - handler.set_converter(otw_type); + handler.set_converter(id); handler.set_max_samples_per_packet(20); //allocate metadata and buffer @@ -180,9 +180,7 @@ BOOST_AUTO_TEST_CASE(test_sph_send_one_channel_full_buffer_mode){ //generate the test data const size_t num_sent = handler.send( - &buff.front(), buff.size(), metadata, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::SEND_MODE_FULL_BUFF, 1.0 + &buff.front(), buff.size(), metadata, 1.0 ); BOOST_CHECK_EQUAL(num_sent, buff.size()); diff --git a/host/tests/wax_test.cpp b/host/tests/wax_test.cpp deleted file mode 100644 index 18730e0c2..000000000 --- a/host/tests/wax_test.cpp +++ /dev/null @@ -1,104 +0,0 @@ -// -// Copyright 2010-2011 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 <boost/shared_ptr.hpp> -#include <uhd/wax.hpp> -#include <iostream> - -enum opt_a_t{OPTION_A_0, OPTION_A_1}; -enum opt_b_t{OPTION_B_0, OPTION_B_1}; - -BOOST_AUTO_TEST_CASE(test_enums){ - wax::obj opta = OPTION_A_0; - BOOST_CHECK_THROW(opta.as<opt_b_t>(), std::exception); - BOOST_CHECK_EQUAL(opta.as<opt_a_t>(), OPTION_A_0); -} - -/*********************************************************************** - * demo class for wax framework - **********************************************************************/ -class wax_demo : public wax::obj{ -public: - typedef boost::shared_ptr<wax_demo> sptr; - - wax_demo(size_t sub_demos, size_t len){ - d_nums = std::vector<float>(len); - if (sub_demos != 0){ - for (size_t i = 0; i < len; i++){ - d_subs.push_back(sptr(new wax_demo(sub_demos-1, len))); - } - } - } - ~wax_demo(void){ - /* NOP */ - } -private: - std::vector<float> d_nums; - std::vector<sptr> d_subs; - - void get(const wax::obj &key, wax::obj &value){ - if (d_subs.size() == 0){ - value = d_nums[key.as<size_t>()]; - }else{ - value = d_subs[key.as<size_t>()]->get_link(); - } - } - void set(const wax::obj &key, const wax::obj &value){ - if (d_subs.size() == 0){ - d_nums[key.as<size_t>()] = value.as<float>(); - }else{ - throw std::runtime_error("cant set to a wax demo with sub demos"); - } - } -}; - -BOOST_AUTO_TEST_CASE(test_chaining){ - wax_demo wd(2, 1); - std::cout << "chain 1" << std::endl; - wd[size_t(0)]; - std::cout << "chain 2" << std::endl; - wd[size_t(0)][size_t(0)]; - std::cout << "chain 3" << std::endl; - wd[size_t(0)][size_t(0)][size_t(0)]; -} - -BOOST_AUTO_TEST_CASE(test_set_get){ - wax_demo wd(2, 10); - std::cout << "set and get all" << std::endl; - for (size_t i = 0; i < 10; i++){ - for (size_t j = 0; j < 10; j++){ - for (size_t k = 0; k < 10; k++){ - float val = float(i * j * k + i + j + k); - //std::cout << i << " " << j << " " << k << std::endl; - wd[i][j][k] = val; - BOOST_CHECK_EQUAL(val, wd[i][j][k].as<float>()); - } - } - } -} - -BOOST_AUTO_TEST_CASE(test_proxy){ - wax_demo wd(2, 1); - std::cout << "store proxy" << std::endl; - wax::obj p = wd[size_t(0)][size_t(0)]; - p[size_t(0)] = float(5); - - std::cout << "assign proxy" << std::endl; - wax::obj a = p[size_t(0)]; - BOOST_CHECK_EQUAL(a.as<float>(), float(5)); -} diff --git a/host/usrp_e_utils/CMakeLists.txt b/host/usrp_e_utils/CMakeLists.txt index 721a40093..2b099cc5d 100644 --- a/host/usrp_e_utils/CMakeLists.txt +++ b/host/usrp_e_utils/CMakeLists.txt @@ -29,8 +29,6 @@ IF(ENABLE_USRP_E_UTILS) SET(usrp_e_utils_sources usrp-e-loopback.cpp usrp-e-wb-test.cpp - usrp-e-debug-pins.c - usrp-e-gpio.c ) #for each source: build an executable and install diff --git a/host/usrp_e_utils/usrp-e-debug-pins.c b/host/usrp_e_utils/usrp-e-debug-pins.c deleted file mode 100644 index 570ae63d8..000000000 --- a/host/usrp_e_utils/usrp-e-debug-pins.c +++ /dev/null @@ -1,78 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <sys/types.h> -#include <fcntl.h> -#include <string.h> -#include <sys/ioctl.h> - -#include <linux/usrp_e.h> -#include "e100_regs.hpp" - -// Usage: usrp_e_gpio <string> - -static int fp; - -static int read_reg(__u16 reg) -{ - int ret; - struct usrp_e_ctl16 d; - - d.offset = reg; - d.count = 1; - ret = ioctl(fp, USRP_E_READ_CTL16, &d); - return d.buf[0]; -} - -static void write_reg(__u16 reg, __u16 val) -{ - int ret; - struct usrp_e_ctl16 d; - - d.offset = reg; - d.count = 1; - d.buf[0] = val; - ret = ioctl(fp, USRP_E_WRITE_CTL16, &d); -} - -int main(int argc, char *argv[]) -{ - int test; - - test = 0; - if (argc < 2) { - printf("%s 0|1|off\n", argv[0]); - return -1; - } - - fp = open("/dev/usrp_e0", O_RDWR); - printf("fp = %d\n", fp); - if (fp < 0) { - perror("Open failed"); - return -1; - } - - if (strcmp(argv[1], "0") == 0) { - printf("Selected 0 based on %s\n", argv[1]); - write_reg(E100_REG_GPIO_TX_DDR, 0xFFFF); - write_reg(E100_REG_GPIO_RX_DDR, 0xFFFF); - write_reg(E100_REG_GPIO_TX_SEL, 0x0); - write_reg(E100_REG_GPIO_RX_SEL, 0x0); - write_reg(E100_REG_GPIO_TX_DBG, 0xFFFF); - write_reg(E100_REG_GPIO_RX_DBG, 0xFFFF); - } else if (strcmp(argv[1], "1") == 0) { - printf("Selected 1 based on %s\n", argv[1]); - write_reg(E100_REG_GPIO_TX_DDR, 0xFFFF); - write_reg(E100_REG_GPIO_RX_DDR, 0xFFFF); - write_reg(E100_REG_GPIO_TX_SEL, 0xFFFF); - write_reg(E100_REG_GPIO_RX_SEL, 0xFFFF); - write_reg(E100_REG_GPIO_TX_DBG, 0xFFFF); - write_reg(E100_REG_GPIO_RX_DBG, 0xFFFF); - } else { - printf("Selected off based on %s\n", argv[1]); - write_reg(E100_REG_GPIO_TX_DDR, 0x0); - write_reg(E100_REG_GPIO_RX_DDR, 0x0); - } - - return 0; -} diff --git a/host/usrp_e_utils/usrp-e-gpio.c b/host/usrp_e_utils/usrp-e-gpio.c deleted file mode 100644 index 4b788e945..000000000 --- a/host/usrp_e_utils/usrp-e-gpio.c +++ /dev/null @@ -1,83 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <sys/types.h> -#include <fcntl.h> -#include <string.h> -#include <sys/ioctl.h> - -#include "linux/usrp_e.h" -#include "e100_regs.hpp" - -// Usage: usrp_e_gpio <string> - -static int fp; - -static int read_reg(__u16 reg) -{ - int ret; - struct usrp_e_ctl16 d; - - d.offset = reg; - d.count = 1; - ret = ioctl(fp, USRP_E_READ_CTL16, &d); - return d.buf[0]; -} - -static void write_reg(__u16 reg, __u16 val) -{ - int ret; - struct usrp_e_ctl16 d; - - d.offset = reg; - d.count = 1; - d.buf[0] = val; - ret = ioctl(fp, USRP_E_WRITE_CTL16, &d); -} - -int main(int argc, char *argv[]) -{ - int i, test, data_in; - - test = 0; - if (argc > 1) - test = 1; - - fp = open("/dev/usrp_e0", O_RDWR); - printf("fp = %d\n", fp); - - write_reg(E100_REG_GPIO_TX_DDR, 0x0); - write_reg(E100_REG_GPIO_RX_DDR, 0xFFFF); - - for (i=0; i < 16; i++) { - write_reg(E100_REG_GPIO_RX_IO, 1 << i); - sleep(1); - if (test) { - data_in = read_reg(E100_REG_GPIO_TX_IO); - if (data_in != (1 << i)) - printf("Read failed, wrote: %X read: %X\n", \ - 1 << i, data_in); - } - } - - write_reg(E100_REG_GPIO_RX_DDR, 0x0); - write_reg(E100_REG_GPIO_TX_DDR, 0xFFFF); - - sleep(1); - - for (i=0; i < 16; i++) { - write_reg(E100_REG_GPIO_TX_IO, 1 << i); - sleep(1); - if (test) { - data_in = read_reg(E100_REG_GPIO_RX_IO); - if (data_in != (1 << i)) - printf("Read failed, wrote: %X read: %X\n", \ - 1 << i, data_in); - } - } - - write_reg(E100_REG_GPIO_RX_DDR, 0x0); - write_reg(E100_REG_GPIO_TX_DDR, 0x0); - - return 0; -} |