40 Commits

Author SHA1 Message Date
Kirill Zakharenko
16bc2bdd18 bump version to 1.0.9 2016-02-27 20:52:08 +03:00
Kirill Zakharenko
9cf7377866 collectd: plugin to collect umtrx sensors 2016-02-26 17:52:07 +03:00
Kirill Zakharenko
a4568b4704 bumped version to 1.0.8 2015-12-28 15:15:16 +03:00
Kirill Zakharenko
b24a4d0bb8 Merge branch 'earwin/pythonpack' 2015-12-28 15:05:57 +03:00
Kirill Zakharenko
dc5718daea bumped version to 1.0.7 2015-12-28 15:02:06 +03:00
Kirill Zakharenko
25394541fa debian: packaged python utils in host/utils 2015-12-28 15:01:29 +03:00
Sergey Kostanbaev
b362710778 throw exception if incorrect DCDC version were provided 2015-12-25 20:47:12 +03:00
Sergey Kostanbaev
c9b7e8c884 turn off vin bypass to amplifiers by default 2015-12-24 21:08:38 +03:00
Sergey Kostanbaev
e7eb8e87cc add lmsvga2 args parameter to override UMTRX_VGA2_DEF 2015-12-24 21:04:07 +03:00
Sergey Kostanbaev
2a6b94bea9 autodetect DCDC translation version on startup 2015-12-24 19:34:07 +02:00
Sergey Kostanbaev
143a9008d1 add lmsvga1 device args parameter to override VGA1_DEF 2015-12-24 18:16:09 +03:00
Sergey Kostanbaev
1e1a889448 Merge branch 'umsel2_work' 2015-12-24 18:14:07 +03:00
Josh Blum
f385a4355a umsel: device args for enabling umsel2 and verbose 2015-12-21 15:12:23 -08:00
Alexander Chemeris
3c240a2ab2 host: Checking in umtrx_query_versions.py 2015-12-21 13:57:06 -05:00
Alexander Chemeris
e4c59df63e host: Add string getters/setters to the Python property tree library. 2015-12-21 13:57:06 -05:00
Alexander Chemeris
ad8ff4a345 host: Add "STRING" to umtrx_monitor error output. 2015-12-21 12:44:24 -05:00
Alexander Chemeris
4f909bcfa2 host: Properly handle most corner cases in VSWR calculations. 2015-12-21 12:32:28 -05:00
Kirill Zakharenko
9309e3c548 Bump version to 1.0.6 2015-12-21 14:40:33 +03:00
Josh Blum
6b5ff4a460 host: make boost property tree thread safe 2015-12-17 23:14:39 -08:00
Josh Blum
53e7e5597f host: support string type in JSON query 2015-12-17 23:06:26 -08:00
Kirill Zakharenko
a89917faae debian: build now produces an additional package with debug symbols 2015-12-16 22:08:58 +03:00
Josh Blum
09f323dc3c host: print locked for debugging 2015-12-13 18:39:54 -08:00
Josh Blum
fee336bc5a host: freq update sequence, copied registers from gui 2015-12-11 19:33:12 -08:00
Josh Blum
fc4efd5cb0 host: umsel2 adf355-2 tuning algorithm 2015-12-10 14:45:34 -08:00
Josh Blum
0f7b0cbeab host: umsel2 register work for adf355-2 2015-12-08 23:22:38 -08:00
Kirill Zakharenko
b600665303 umtrx_firmware: fixed typo preventing it from working 2015-12-01 02:48:10 +03:00
Kirill Zakharenko
2a89674c56 Bump version to 1.0.5 2015-11-23 15:09:10 +03:00
Josh Blum
6e30c16773 host: implement selection for umsel2 + lms 2015-11-17 21:35:46 -08:00
Josh Blum
401e64014c host: remove old umsel code from lsm6002
To avoid confusion and because umsel now has a dedicated support object.
2015-11-17 20:16:10 -08:00
Josh Blum
1dbd567102 host: integrate support class for umsel2 2015-11-16 22:05:22 -08:00
Josh Blum
77e9066bf8 fpga: updated 4x ddc image for spi work 2015-08-11 23:45:58 -07:00
Josh Blum
1e43f04790 Merge branch 'axi_shared_spi' 2015-08-11 17:04:51 -07:00
Kirill Zakharenko
cbff81745b debian: added firmware to package and umtrx_firmware script to handle it 2015-08-11 13:29:52 +03:00
Josh Blum
57d5ca4b51 fpga: simplify spi setting regs with generate loop 2015-07-29 19:21:28 -07:00
Josh Blum
b78ceeb9b1 fpga: connect both spi settings drivers 2015-07-29 01:59:54 -07:00
Josh Blum
fcd92d8f50 fpga: use axi stream spi core (still single dest) 2015-07-28 16:32:17 -07:00
Josh Blum
2727f62ab8 fpga: created axi stream controled spi core 2015-07-28 16:31:29 -07:00
Alexander Chemeris
75c3380ccf host: Fix getters in umtrx_property_tree.py. 2015-07-27 20:42:23 -04:00
Josh Blum
80e65f35cf host: disable umtrx_fifo_ctrl cache of spi config
This register can be modified by the firmware,
which invalidates the cached setting.
2015-07-24 00:50:35 -07:00
Kirill Zakharenko
f16599cfa9 debian: conflicts/replaces stanzas to replace renamed package
otherwise new packages will be installed alongside older one and we'll
get a file conflict
2015-07-23 03:00:19 +02:00
28 changed files with 1281 additions and 237 deletions

56
debian/changelog vendored
View File

@@ -1,3 +1,59 @@
umtrx (1.0.9) trusty; urgency=low
* collectd: osmo-nitb counter collection plugin
-- Kirill Zakharenko <earwin@gmail.com> Mon, 24 Feb 2016 19:35:56 +0300
umtrx (1.0.8) trusty; urgency=low
* host: integrate support class for umsel2
* host: integrate support class for umsel2
* host: implement selection for umsel2 + lms
* host: umsel2 register work for adf355-2
* host: umsel2 adf355-2 tuning algorithm
* host: freq update sequence, copied registers from gui
* host: print locked for debugging
* umsel: device args for enabling umsel2 and verbose
* add lmsvga1 device args parameter to override VGA1_DEF
* autodetect DCDC translation version on startup
* add lmsvga2 args parameter to override UMTRX_VGA2_DEF
* turn off vin bypass to amplifiers by default
* throw exception if incorrect DCDC version were provided
-- Kirill Zakharenko <earwin@gmail.com> Mon, 28 Dec 2015 15:07:56 +0300
umtrx (1.0.7) trusty; urgency=low
* host: Properly handle most corner cases in VSWR calculations
* host: Add "STRING" to umtrx_monitor error output
* host: Add string getters/setters to the Python property tree library
* host: Checking in umtrx_query_versions.py
* debian: packaged python utils in host/utils
-- Kirill Zakharenko <earwin@gmail.com> Mon, 25 Dec 2015 19:35:56 +0100
umtrx (1.0.6) trusty; urgency=low
* host: make boost property tree thread safe
* host: support string type in JSON query
* debian: build now produces an additional package with debug symbols
* umtrx_firmware: fixed typo preventing it from working
-- Kirill Zakharenko <earwin@gmail.com> Mon, 21 Dec 2015 14:23:56 +0300
umtrx (1.0.5) trusty; urgency=low
* host: disable umtrx_fifo_ctrl cache of spi config
* host: Fix getters in umtrx_property_tree.py.
* debian: added firmware to package and umtrx_firmware script to handle it
* fpga: created axi stream controled spi core
* fpga: use axi stream spi core (still single dest)
* fpga: connect both spi settings drivers
* fpga: simplify spi setting regs with generate loop
* fpga: updated 4x ddc image for spi work
-- Kirill Zakharenko <earwin@gmail.com> Mon, 23 Nov 2015 15:51:56 +0300
umtrx (1.0.4) unstable; urgency=low
* Do not add 'g' to a git id when creating a version string.

11
debian/control vendored
View File

@@ -17,6 +17,8 @@ Section: libs
Architecture: any
Pre-Depends: multiarch-support, ${misc:Pre-Depends}
Depends: ${shlibs:Depends}, ${misc:Depends}, uhd-umtrx
Conflicts: umtrx-uhd
Replaces: umtrx-uhd
Recommends: python
Description: Fairwaves UmTRX driver - runtime utilities
The industrial grade dual-channel wide-band SDR transceiver.
@@ -25,5 +27,14 @@ Package: uhd-umtrx
Section: libs
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Conflicts: umtrx-uhd
Replaces: umtrx-uhd
Description: Fairwaves UmTRX driver - UHD plugin module
The industrial grade dual-channel wide-band SDR transceiver.
Package: umtrx-dbg
Section: debug
Architecture: any
Depends: umtrx, uhd-umtrx, ${misc:Depends}
Description: Fairwaves UmTRX driver - debug symbols
The industrial grade dual-channel wide-band SDR transceiver.

3
debian/rules vendored
View File

@@ -16,3 +16,6 @@ export DH_OPTIONS
override_dh_auto_configure:
dh_auto_configure -- -DLIB_SUFFIX="/$(DEB_HOST_MULTIARCH)"
override_dh_strip:
dh_strip --dbg-package=umtrx-dbg

View File

@@ -1 +1,8 @@
usr/bin
images/u2plus_umtrx_v2.bin images/umtrx_txrx_uhd.bin usr/share/umtrx/firmware
host/utils/umtrx_property_tree.py host/utils/umtrx_vswr.py usr/share/umtrx
host/utils/umtrx_query_sensors.py host/utils/umtrx_query_versions.py host/utils/umtrx_net_burner.py usr/share/umtrx
host/utils/collectd/umtrx.types.db usr/share/collectd
host/utils/collectd/umtrx2collectd.py usr/share/umtrx
host/utils/collectd/umtrx.conf etc/collectd/collectd.conf.d

3
debian/umtrx.links vendored Normal file
View File

@@ -0,0 +1,3 @@
/usr/share/umtrx/umtrx_net_burner.py /usr/bin/umtrx_net_burner
/usr/share/umtrx/umtrx_query_sensors.py /usr/bin/umtrx_query_sensors
/usr/share/umtrx/umtrx_query_versions.py /usr/bin/umtrx_query_versions

View File

@@ -58,5 +58,6 @@ gpio_atr.v \
user_settings.v \
settings_fifo_ctrl.v \
simple_spi_core.v \
axis_spi_core.v \
simple_i2c_core.v \
))

View File

@@ -0,0 +1,243 @@
//
// Copyright 2012 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// Simple SPI core, the simplest, yet complete spi core I can think of
// Settings register controlled.
// 2 settings regs, control and data
// 1 32-bit readback and status signal
// Settings reg map:
//
// BASE+0 divider setting
// bits [15:0] spi clock divider
//
// BASE+1 configuration input
// bits [23:0] slave select, bit0 = slave0 enabled
// bits [29:24] num bits (1 through 32)
// bit [30] data input edge = in data bit latched on rising edge of clock
// bit [31] data output edge = out data bit latched on rising edge of clock
//
// BASE+2 input data
// Writing this register begins a spi transaction.
// Bits are latched out from bit 0.
// Therefore, load this register in reverse.
//
// Readback
// Bits are latched into bit 0.
// Therefore, data will be in-order.
module axis_spi_core
#(
//set to 1 for ILA
parameter DEBUG = 0,
//tdest width for number of core users
parameter DESTW = 1,
//width of serial enables (up to 24 is possible)
parameter WIDTH = 8,
//idle state of the spi clock
parameter CLK_IDLE = 0,
//idle state of the serial enables
parameter SEN_IDLE = 24'hffffff
)
(
//clock and synchronous reset
input clock, input reset,
//configuration settings bus
input [DESTW-1:0] CONFIG_tdest,
input [79:0] CONFIG_tdata,
input CONFIG_tvalid,
output CONFIG_tready,
//32-bit data readback
output reg [DESTW-1:0] READBACK_tdest,
output [31:0] READBACK_tdata,
output READBACK_tvalid,
input READBACK_tready,
//spi interface, slave selects, clock, data in, data out
output [WIDTH-1:0] sen,
output sclk,
output mosi,
input miso
);
//state
localparam WAIT_CONFIG = 0;
localparam PRE_IDLE = 1;
localparam CLK_REG = 2;
localparam CLK_INV = 3;
localparam POST_IDLE = 4;
localparam IDLE_SEN = 5;
localparam WAIT_READBACK = 6;
reg [2:0] state;
//configuration settings
reg [15:0] sclk_divider;
reg [23:0] slave_select;
reg [5:0] num_bits;
reg datain_edge, dataout_edge;
//output ready/valid signals
assign CONFIG_tready = (state == WAIT_CONFIG);
assign READBACK_tvalid = (state == WAIT_READBACK);
//serial clock either idles or is in one of two clock states
reg sclk_reg;
assign sclk = sclk_reg;
//serial enables either idle or enabled based on state
wire sen_is_idle = (state == WAIT_CONFIG) || (state == IDLE_SEN);
wire [23:0] sen24 = (sen_is_idle)? SEN_IDLE : (SEN_IDLE ^ slave_select);
reg [WIDTH-1:0] sen_reg;
always @(posedge clock) sen_reg <= sen24[WIDTH-1:0];
assign sen = sen_reg;
//data output shift register
reg [31:0] dataout_reg;
wire [31:0] dataout_next = {dataout_reg[30:0], 1'b0};
assign mosi = dataout_reg[31];
//data input shift register
reg [31:0] datain_reg;
wire [31:0] datain_next = {datain_reg[30:0], miso};
assign READBACK_tdata = datain_reg;
//counter for spi clock
reg [15:0] sclk_counter;
wire sclk_counter_done = (sclk_counter == sclk_divider);
wire [15:0] sclk_counter_next = (sclk_counter_done)? 0 : sclk_counter + 1;
//counter for latching bits miso/mosi
reg [6:0] bit_counter;
wire [6:0] bit_counter_next = bit_counter + 1;
wire bit_counter_done = (bit_counter_next == num_bits);
always @(posedge clock) begin
if (reset) begin
state <= WAIT_CONFIG;
sclk_reg <= CLK_IDLE;
end
else begin
case (state)
WAIT_CONFIG: begin
if (CONFIG_tvalid && CONFIG_tready) begin
state <= PRE_IDLE;
end
{sclk_divider, dataout_edge, datain_edge, num_bits, slave_select, dataout_reg} <= CONFIG_tdata;
READBACK_tdest <= CONFIG_tdest;
sclk_counter <= 0;
bit_counter <= 0;
sclk_reg <= CLK_IDLE;
end
PRE_IDLE: begin
if (sclk_counter_done) state <= CLK_REG;
sclk_counter <= sclk_counter_next;
sclk_reg <= CLK_IDLE;
end
CLK_REG: begin
if (sclk_counter_done) begin
state <= CLK_INV;
if (datain_edge != CLK_IDLE) datain_reg <= datain_next;
if (dataout_edge != CLK_IDLE && bit_counter != 0) dataout_reg <= dataout_next;
sclk_reg <= ~CLK_IDLE; //transition to rising when CLK_IDLE == 0
end
sclk_counter <= sclk_counter_next;
end
CLK_INV: begin
if (sclk_counter_done) begin
state <= (bit_counter_done)? POST_IDLE : CLK_REG;
bit_counter <= bit_counter_next;
if (datain_edge == CLK_IDLE) datain_reg <= datain_next;
if (dataout_edge == CLK_IDLE && ~bit_counter_done) dataout_reg <= dataout_next;
sclk_reg <= CLK_IDLE; //transition to falling when CLK_IDLE == 0
end
sclk_counter <= sclk_counter_next;
end
POST_IDLE: begin
if (sclk_counter_done) state <= IDLE_SEN;
sclk_counter <= sclk_counter_next;
sclk_reg <= CLK_IDLE;
end
IDLE_SEN: begin
if (sclk_counter_done) state <= WAIT_READBACK;
sclk_counter <= sclk_counter_next;
sclk_reg <= CLK_IDLE;
end
WAIT_READBACK: begin
if (READBACK_tready && READBACK_tvalid) begin
state <= WAIT_CONFIG;
end
end
default: state <= WAIT_CONFIG;
endcase //state
end
end
/*******************************************************************
* Debug
******************************************************************/
generate
if (DEBUG == 1) begin
wire [35:0] CONTROL0;
chipscope_icon chipscope_icon
(
.CONTROL0(CONTROL0)
);
wire [255:0] DATA;
wire [7:0] TRIG0;
chipscope_ila chipscope_ila
(
.CONTROL(CONTROL0),
.CLK(clock),
.DATA(DATA),
.TRIG0(TRIG0)
);
assign TRIG0 =
{
4'b0,
CONFIG_tvalid, CONFIG_tready,
READBACK_tvalid, READBACK_tready
};
assign DATA[79:0] = CONFIG_tdata;
assign DATA[111:80] = READBACK_tdata;
assign DATA[112] = CONFIG_tvalid;
assign DATA[113] = CONFIG_tready;
assign DATA[114] = READBACK_tvalid;
assign DATA[115] = READBACK_tready;
assign DATA[127:120] = state;
end
endgenerate
endmodule //axis_spi_core

View File

@@ -174,6 +174,10 @@ module umtrx_core
wire [7:0] set_addr, set_addr_dsp, set_addr_sys, set_addr_fe, set_addr_udp_wb, set_addr_udp_sys;
wire [31:0] set_data, set_data_dsp, set_data_sys, set_data_fe, set_data_udp_wb, set_data_udp_sys;
wire set_stb, set_stb_dsp, set_stb_sys, set_stb_fe, set_stb_udp_wb, set_stb_udp_sys;
wire set_stb_dsp0, set_stb_dsp1;
wire [31:0] set_data_dsp0, set_data_dsp1;
wire [7:0] set_addr_dsp0, set_addr_dsp1;
reg wb_rst;
wire dsp_rst, sys_rst, fe_rst;
@@ -412,17 +416,80 @@ module umtrx_core
// /////////////////////////////////////////////////////////////////////////
// SPI -- Slave #2
wire [31:0] spi_debug;
wire [31:0] spi_readback;
reg [31:0] spi_readback0;
reg [31:0] spi_readback1;
wire spi_ready;
simple_spi_core #(.BASE(SR_SPI_CORE), .WIDTH(5)) shared_spi(
wire [0:0] AXIS_SPI_CONFIG_tdest;
wire [79:0] AXIS_SPI_CONFIG_tdata;
wire AXIS_SPI_CONFIG_tvalid;
wire AXIS_SPI_CONFIG_tready;
wire [0:0] AXIS_SPI_READBACK_tdest;
wire [31:0] AXIS_SPI_READBACK_tdata;
wire AXIS_SPI_READBACK_tvalid;
wire AXIS_SPI_READBACK_tready;
axis_spi_core #(.DESTW(1), .WIDTH(5), .DEBUG(0)) axis_shared_spi(
.clock(dsp_clk), .reset(dsp_rst),
.set_stb(set_stb_dsp), .set_addr(set_addr_dsp), .set_data(set_data_dsp),
.readback(spi_readback), .ready(spi_ready),
.CONFIG_tdest(AXIS_SPI_CONFIG_tdest),
.CONFIG_tdata(AXIS_SPI_CONFIG_tdata),
.CONFIG_tvalid(AXIS_SPI_CONFIG_tvalid),
.CONFIG_tready(AXIS_SPI_CONFIG_tready),
.READBACK_tdest(AXIS_SPI_READBACK_tdest),
.READBACK_tdata(AXIS_SPI_READBACK_tdata),
.READBACK_tvalid(AXIS_SPI_READBACK_tvalid),
.READBACK_tready(AXIS_SPI_READBACK_tready),
.sen({aux_sen2,aux_sen1,sen_dac,sen_lms2,sen_lms1}),
.sclk(sclk), .mosi(mosi), .miso(miso), .debug(spi_debug)
.sclk(sclk), .mosi(mosi), .miso(miso)
);
//setting register block for spi dest 0 (wishbone) and spi dest 1 (ctrl fifo)
//Note: the strobes are exclusive (settings fifo cross clock)
wire [79:0] spi_config [0:1];
wire [0:1] spi_trigger;
wire [0:1] set_stb_dsp_n = {set_stb_dsp0, set_stb_dsp1};
genvar i;
generate for (i=0; i <= 1; i=i+1) begin
setting_reg #(.my_addr(SR_SPI_CORE+2),.width(32)) axis_shared_spi_sr0(
.clk(dsp_clk),.rst(dsp_rst),.strobe(set_stb_dsp_n[i]),.addr(set_addr_dsp),.in(set_data_dsp),
.out(spi_config[i][31:0]),.changed(spi_trigger[i]));
setting_reg #(.my_addr(SR_SPI_CORE+1),.width(32)) axis_shared_spi_sr1(
.clk(dsp_clk),.rst(dsp_rst),.strobe(set_stb_dsp_n[i]),.addr(set_addr_dsp),.in(set_data_dsp),
.out(spi_config[i][63:32]),.changed());
setting_reg #(.my_addr(SR_SPI_CORE+0),.width(16)) axis_shared_spi_sr2(
.clk(dsp_clk),.rst(dsp_rst),.strobe(set_stb_dsp_n[i]),.addr(set_addr_dsp),.in(set_data_dsp),
.out(spi_config[i][79:64]),.changed());
end endgenerate
//assign config bus from setting register sources
//Note: the triggers are exclusive (settings fifo cross clock)
assign AXIS_SPI_CONFIG_tdest = (spi_trigger[0])?1'b0:1'b1;
assign AXIS_SPI_CONFIG_tdata = (spi_trigger[0])?spi_config[0]:spi_config[1];
assign AXIS_SPI_CONFIG_tvalid = (spi_trigger != 0);
//create spi ready to block the ctrl fifo ASAP
wire spi_ready_now = AXIS_SPI_CONFIG_tready && !AXIS_SPI_CONFIG_tvalid;
assign spi_ready = spi_ready_now && spi_ready_prev;
reg spi_ready_prev;
always @(posedge dsp_clk) begin
spi_ready_prev <= spi_ready_now;
end
//readback output bus latches values into readback register
assign AXIS_SPI_READBACK_tready = 1'b1;
always @(posedge dsp_clk) begin
if (AXIS_SPI_READBACK_tvalid && AXIS_SPI_READBACK_tready) begin
if (AXIS_SPI_READBACK_tdest == 1'b0) spi_readback0 <= AXIS_SPI_READBACK_tdata;
if (AXIS_SPI_READBACK_tdest == 1'b1) spi_readback1 <= AXIS_SPI_READBACK_tdata;
end
end
// /////////////////////////////////////////////////////////////////////////
// I2C -- Slave #3
i2c_master_top #(.ARST_LVL(1))
@@ -448,7 +515,7 @@ module umtrx_core
// Buffer Pool Status -- Slave #5
//compatibility number -> increment when the fpga has been sufficiently altered
localparam compat_num = {16'd9, 16'd1}; //major, minor
localparam compat_num = {16'd9, 16'd2}; //major, minor
wire [31:0] irq_readback = {16'b0, aux_ld2, aux_ld1, button, spi_ready, 12'b0};
@@ -456,7 +523,7 @@ module umtrx_core
(.wb_clk_i(wb_clk), .wb_rst_i(wb_rst), .wb_stb_i(s5_stb),
.wb_adr_i(s5_adr), .wb_dat_o(s5_dat_i), .wb_ack_o(s5_ack),
.word00(spi_readback),.word01(`NUMDDC),.word02(`NUMDUC),.word03(32'b0),
.word00(spi_readback0),.word01(`NUMDDC),.word02(`NUMDUC),.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_readback),
@@ -498,10 +565,6 @@ module umtrx_core
(.clk_i(dsp_clk), .rst_i(dsp_rst), .set_stb_i(set_stb_dsp), .set_addr_i(set_addr_dsp), .set_data_i(set_data_dsp),
.clk_o(fe_clk), .rst_o(fe_rst), .set_stb_o(set_stb_fe), .set_addr_o(set_addr_fe), .set_data_o(set_data_fe));
wire set_stb_dsp0, set_stb_dsp1;
wire [31:0] set_data_dsp0, set_data_dsp1;
wire [7:0] set_addr_dsp0, set_addr_dsp1;
//mux settings_bus_crossclock and settings_readback_bus_fifo_ctrl with prio
assign set_stb_dsp = set_stb_dsp0 | set_stb_dsp1;
assign set_addr_dsp = set_stb_dsp1? set_addr_dsp1 : set_addr_dsp0;
@@ -542,7 +605,7 @@ module umtrx_core
.in_data(ctrl_data_dsp), .in_valid(ctrl_valid_dsp), .in_ready(ctrl_ready_dsp),
.out_data(resp_data_dsp), .out_valid(resp_valid_dsp), .out_ready(resp_ready_dsp),
.strobe(set_stb_dsp1), .addr(set_addr_dsp1), .data(set_data_dsp1),
.word00(spi_readback),.word01(32'b0),.word02(32'b0),.word03(32'b0),
.word00(spi_readback1),.word01(32'b0),.word02(32'b0),.word03(32'b0),
.word04(32'b0),.word05(32'b0),.word06(32'b0),.word07(32'b0),
.word08(32'b0),.word09(32'b0),.word10(vita_time[63:32]),
.word11(vita_time[31:0]),.word12(32'b0),.word13(irq_readback),

View File

@@ -58,6 +58,7 @@ list(APPEND UMTRX_SOURCES
cores/time64_core_200.cpp
cores/validate_subdev_spec.cpp
cores/apply_corrections.cpp
umsel2_ctrl.cpp
)
########################################################################
@@ -128,6 +129,10 @@ if (UNIX)
list(APPEND UMTRX_LIBRARIES ${CMAKE_THREAD_LIBS_INIT})
endif()
#make boost property tree thread safe
#http://stackoverflow.com/questions/8156948/is-boostproperty-treeptree-thread-safe
add_definitions(-DBOOST_SPIRIT_THREADSAFE)
########################################################################
# Helpful compiler flags
########################################################################

View File

@@ -131,7 +131,7 @@ public:
class lms6002d_ctrl_impl : public lms6002d_ctrl {
public:
lms6002d_ctrl_impl(uhd::spi_iface::sptr spiface, const int lms_spi_number, const int adf4350_spi_number, const double clock_rate);
lms6002d_ctrl_impl(uhd::spi_iface::sptr spiface, const int lms_spi_number, const double clock_rate);
double set_rx_freq(const double freq)
{
@@ -237,19 +237,7 @@ protected:
if (unit==dboard_iface::UNIT_TX) {
actual_freq = lms.tx_pll_tune(ref_freq, f);
} else if (unit==dboard_iface::UNIT_RX) {
#if 1
actual_freq = lms.rx_pll_tune(ref_freq, f);
#else
// New beta version of the code for UmSEL support.
const double umsel_if = 359.5e6;
double actual_lms_freq = lms.rx_pll_tune(ref_freq, umsel_if);
if (verbosity>0) printf("lms6002d_ctrl_impl::set_freq() actual_lms_freq=%f\n", actual_lms_freq);
double adf4350_freq = f - actual_lms_freq;
actual_freq = tune_adf4350(adf4350_freq);
if (verbosity>0) printf("lms6002d_ctrl_impl::set_freq() adf4350 freq=%f\n", actual_freq);
actual_freq += actual_lms_freq;
if (verbosity>0) printf("lms6002d_ctrl_impl::set_freq() actual_freq=%f\n", actual_freq);
#endif
} else {
assert(!"Wrong units_t value passed to lms6002d_ctrl_impl::set_freq()");
}
@@ -410,202 +398,27 @@ private:
int tx_vga1gain, tx_vga2gain; // Stored values of Tx VGA1 and VGA2 gains.
bool rf_loopback_enabled; // Whether RF loopback is enabled.
// Tune ADF4350 on an UmSEL
double tune_adf4350(double target_freq);
uhd::spi_iface::sptr _spiface;
const int _lms_spi_number;
const int _adf4350_spi_number;
const double _clock_rate;
boost::recursive_mutex _mutex;
};
lms6002d_ctrl::sptr lms6002d_ctrl::make(uhd::spi_iface::sptr spiface, const int lms_spi_number, const int adf4350_spi_number, const double clock_rate)
lms6002d_ctrl::sptr lms6002d_ctrl::make(uhd::spi_iface::sptr spiface, const int lms_spi_number, const double clock_rate)
{
return sptr(new lms6002d_ctrl_impl(spiface, lms_spi_number, adf4350_spi_number, clock_rate));
}
/***********************************************************************
* Tuning
**********************************************************************/
double lms6002d_ctrl_impl::tune_adf4350(double target_freq) {
UHD_LOGV(often) << boost::format(
"UmSEL tune: target frequency %f Mhz"
) % (target_freq/1e6) << std::endl;
//clip the input
// TODO::::::::::::::::::::::::::::::::
// 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;
// TODO:: May be *2? Check
double ref_freq = _clock_rate * 2;
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);
// TODO:: get_locked!
// UHD_LOGV(often)
std::cout
<< boost::format("UmSEL 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("UmSEL 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 % true /* this->get_locked(unit).to_pp_string() */ << std::endl
<< boost::format("UmSEL 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;
// TODO:: What?
// 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];
regs.mute_till_lock_detect = adf4350_regs_t::MUTE_TILL_LOCK_DETECT_MUTE_ENABLED;
regs.charge_pump_current = adf4350_regs_t::CHARGE_PUMP_CURRENT_2_50MA;
regs.double_buffer = adf4350_regs_t::DOUBLE_BUFFER_ENABLED;
regs.muxout = adf4350_regs_t::MUXOUT_3STATE;
regs.low_noise_and_spur = adf4350_regs_t::LOW_NOISE_AND_SPUR_LOW_NOISE;
//write the registers
//correct power-up sequence to write registers (5, 4, 3, 2, 1, 0)
#if 0
for(addr=5; addr>=0; addr--){
// UHD_LOGV(often)
std::cout << boost::format(
"UmSEL SPI Reg (0x%02x): 0x%08x"
) % addr % regs.get_reg(addr) << std::endl;
this->get_iface()->write_spi(
uhd::usrp::dboard_iface::UNIT_SYNT, spi_config_t::EDGE_RISE,
regs.get_reg(addr), 32
);
}
#else
_spiface->write_spi(_adf4350_spi_number, spi_config_t::EDGE_RISE, 0x0580000|5, 32);
_spiface->write_spi(_adf4350_spi_number, spi_config_t::EDGE_RISE, 0x0BFF4F8|4, 32);
_spiface->write_spi(_adf4350_spi_number, spi_config_t::EDGE_RISE, 0x0040000|3, 32);
_spiface->write_spi(_adf4350_spi_number, spi_config_t::EDGE_RISE, 0x1006E40|2, 32);
_spiface->write_spi(_adf4350_spi_number, spi_config_t::EDGE_RISE, 0x8008208|1, 32);
_spiface->write_spi(_adf4350_spi_number, spi_config_t::EDGE_RISE, (325<<15)|(1<<3)|0, 32);
#endif
//return the actual frequency
// UHD_LOGV(often) << boost::format(
std::cout << boost::format(
"UmSEL tune: actual frequency %f Mhz"
) % (actual_freq/1e6) << std::endl;
return actual_freq;
return sptr(new lms6002d_ctrl_impl(spiface, lms_spi_number, clock_rate));
}
// LMS RX dboard configuration
lms6002d_ctrl_impl::lms6002d_ctrl_impl(uhd::spi_iface::sptr spiface, const int lms_spi_number, const int adf4350_spi_number, const double clock_rate) :
lms6002d_ctrl_impl::lms6002d_ctrl_impl(uhd::spi_iface::sptr spiface, const int lms_spi_number, const double clock_rate) :
lms(umtrx_lms6002d_dev(spiface, lms_spi_number)),
tx_vga1gain(lms.get_tx_vga1gain()),
tx_vga2gain(lms.get_tx_vga2gain()),
rf_loopback_enabled(false),
_spiface(spiface),
_lms_spi_number(lms_spi_number),
_adf4350_spi_number(adf4350_spi_number),
_clock_rate(clock_rate)
{
////////////////////////////////////////////////////////////////////

View File

@@ -15,7 +15,7 @@ class lms6002d_ctrl
{
public:
typedef boost::shared_ptr<lms6002d_ctrl> sptr;
static sptr make(uhd::spi_iface::sptr spiface, const int lms_spi_number, const int adf4350_spi_number, const double clock_rate);
static sptr make(uhd::spi_iface::sptr spiface, const int lms_spi_number, const double clock_rate);
virtual double set_rx_freq(const double freq) = 0;
virtual double set_tx_freq(const double freq) = 0;

478
host/umsel2_ctrl.cpp Normal file
View File

@@ -0,0 +1,478 @@
// Copyright 2015-2015 Fairwaves 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 "umsel2_ctrl.hpp"
#include "umtrx_regs.hpp"
#include <uhd/exception.hpp>
#include <boost/thread.hpp>
#include <iostream>
#include <cmath>
#include <map>
static const int REG0_NVALUE_SHIFT = 4;
static const int REG0_NVALUE_MASK = 0xffff;
static const int REG0_PRESCALER_SHIFT = 20;
static const int REG0_AUTOCAL_SHIFT = 21;
static const int REG1_MFRAC_SHIFT = 4;
static const int REG1_MFRAC_MASK = 0xffffff;
static const int REG2_AUX_FRAC_SHIFT = 18;
static const int REG2_AUX_FRAC_MASK = ((1<<14)-1);
static const int REG2_AUX_MOD_SHIFT = 4;
static const int REG2_AUX_MOD_MASK = ((1<<14)-1);
static const int REG3_SD_LOAD_SHIFT = 30;
static const int REG3_PHASE_RSYNC_SHIFT = 29;
static const int REG3_PHASE_ADJ_SHIFT = 28;
static const int REG3_PHASE_SHIFT = 4;
static const int REG3_PHASE_MASK = ((1<<24)-1);
static const int REG4_MUXOUT_SHIFT = 27;
static const int REG4_MUXOUT_MASK = ((1<<3)-1);
static const int REG4_REF_DBL_SHIFT = 26;
static const int REG4_REF_DIV_SHIFT = 25;
static const int REG4_R_SHIFT = 15;
static const int REG4_R_MASK = ((1<<10)-1);
static const int REG4_DBL_BUFF_SHIFT = 14;
static const int REG4_CURRENT_SHIFT = 10;
static const int REG4_CURRENT_MASK = ((1<<4)-1);
static const int REG4_REF_MODE_SHIFT = 9;
static const int REG4_MUX_LOGIC_SHIFT = 8;
static const int REG4_PD_POL_SHIFT = 7;
static const int REG4_PWR_DOWN_SHIFT = 6;
static const int REG4_CP_3STATE_SHIFT = 5;
static const int REG4_CNTR_RESET = 4;
static const int REG5_RESERVED = 0x00800025;
static const int REG6_GATED_BLEED_SHIFT = 30;
static const int REG6_NEG_BLEED_SHIFT = 29;
static const int REG6_RESERVED_SHIFT = 25;
static const int REG6_RESERVED_VALUE = 0xA;
static const int REG6_FB_SEL_SHIFT = 24;
static const int REG6_RF_DIV_SHIFT = 21;
static const int REG6_RF_DIV_MASK = ((1<<3)-1);
static const int REG6_CP_BLEED_CURR_SHIFT = 13;
static const int REG6_CP_BLEED_CURR_MASK = ((1<<8)-1);
static const int REG6_MLTD_SHIFT = 11;
static const int REG6_AUX_PWR_EN_SHIFT = 9;
static const int REG6_AUX_PWR_SHIFT = 7;
static const int REG6_AUX_PWR_MASK = ((1<<2)-1);
static const int REG6_PWR_EN_SHIFT = 6;
static const int REG6_PWR_SHIFT = 4;
static const int REG6_PWR_MASK = ((1<<2)-1);
static const int REG7_RESERVED_SHIFT = 26;
static const int REG7_RESERVED_VALUE = 0x4;
static const int REG7_LE_SYNC_SHIFT = 25;
static const int REG7_LD_CYCLE_CNT_SHIFT = 8;
static const int REG7_LD_CYCLE_CNT_MASK = ((1<<2)-1);
static const int REG7_LOL_MODE_SHIFT = 7;
static const int REG7_FRAC_N_PREC_SHIFT = 5;
static const int REG7_FRAC_N_PREC_MASK = ((1<<2)-1);
static const int REG7_LD_MODE_SHIFT = 4;
static const int REG8_RESERVED = 0x102D0428;
static const int REG9_VCO_BAND_SHIFT = 24;
static const int REG9_VCO_BAND_MASK = ((1<<8)-1);
static const int REG9_TIMEOUT_SHIFT = 14;
static const int REG9_TIMEOUT_MASK = ((1<<10)-1);
static const int REG9_AUTO_LVL_TO_SHIFT = 9;
static const int REG9_AUTO_LVL_TO_MASK = ((1<<5)-1);
static const int REG9_SYNT_LOCK_TO_SHIFT = 4;
static const int REG9_SYNT_LOCK_TO_MASK = ((1<<5)-1);
static const int REG10_RESERVED_SHIFT = 14;
static const int REG10_RESERVED_VALUE = 0x300;
static const int REG10_ADC_CLK_DIV_SHIFT = 6;
static const int REG10_ADC_CLK_DIV_MASK = ((1<<8)-1);
static const int REG10_ADC_CONV_SHIFT = 5;
static const int REG10_ADC_EN_SHIFT = 4;
static const int REG11_RESERVED = 0x0061300B;
static const int REG12_RESYNC_CLOCK_SHIFT = 16;
static const int REG12_RESYNC_CLOCK_MASK = ((1<<16)-1);
static const int REG12_RESERVED_SHIFT = 4;
static const int REG12_RESERVED_VALUE = 0x41;
#define MODIFY_FIELD(reg, val, mask, shift) \
reg = ((reg & ~(mask << shift)) | ((val & mask) << shift))
class umsel2_ctrl_impl : public umsel2_ctrl
{
public:
umsel2_ctrl_impl(uhd::wb_iface::sptr ctrl, uhd::spi_iface::sptr spiface, const double ref_clock, const bool verbose):
_ctrl(ctrl), _spiface(spiface), _ref_clock(ref_clock), verbose(verbose)
{
this->init_synth(SPI_SS_AUX1);
this->init_synth(SPI_SS_AUX2);
//--------- basic self tests, use the muxout to verify communication ----------//
//set mux out to ground in both cases
MODIFY_FIELD(_regs[SPI_SS_AUX1][4], 2, REG4_MUXOUT_MASK, REG4_MUXOUT_SHIFT);
MODIFY_FIELD(_regs[SPI_SS_AUX2][4], 2, REG4_MUXOUT_MASK, REG4_MUXOUT_SHIFT);
this->write_reg(SPI_SS_AUX1, 4);
this->write_reg(SPI_SS_AUX2, 4);
UHD_ASSERT_THROW((_ctrl->peek32(U2_REG_IRQ_RB) & AUX_LD1_IRQ_BIT) == 0);
UHD_ASSERT_THROW((_ctrl->peek32(U2_REG_IRQ_RB) & AUX_LD2_IRQ_BIT) == 0);
//set slave1 to muxout vdd
MODIFY_FIELD(_regs[SPI_SS_AUX1][4], 1, REG4_MUXOUT_MASK, REG4_MUXOUT_SHIFT);
MODIFY_FIELD(_regs[SPI_SS_AUX2][4], 2, REG4_MUXOUT_MASK, REG4_MUXOUT_SHIFT);
this->write_reg(SPI_SS_AUX1, 4);
this->write_reg(SPI_SS_AUX2, 4);
UHD_ASSERT_THROW((_ctrl->peek32(U2_REG_IRQ_RB) & AUX_LD1_IRQ_BIT) != 0);
UHD_ASSERT_THROW((_ctrl->peek32(U2_REG_IRQ_RB) & AUX_LD2_IRQ_BIT) == 0);
//set slave2 to muxout vdd
MODIFY_FIELD(_regs[SPI_SS_AUX1][4], 2, REG4_MUXOUT_MASK, REG4_MUXOUT_SHIFT);
MODIFY_FIELD(_regs[SPI_SS_AUX2][4], 1, REG4_MUXOUT_MASK, REG4_MUXOUT_SHIFT);
this->write_reg(SPI_SS_AUX1, 4);
this->write_reg(SPI_SS_AUX2, 4);
UHD_ASSERT_THROW((_ctrl->peek32(U2_REG_IRQ_RB) & AUX_LD1_IRQ_BIT) == 0);
UHD_ASSERT_THROW((_ctrl->peek32(U2_REG_IRQ_RB) & AUX_LD2_IRQ_BIT) != 0);
//restore lock detect out
MODIFY_FIELD(_regs[SPI_SS_AUX1][4], 6, REG4_MUXOUT_MASK, REG4_MUXOUT_SHIFT);
MODIFY_FIELD(_regs[SPI_SS_AUX2][4], 6, REG4_MUXOUT_MASK, REG4_MUXOUT_SHIFT);
this->write_reg(SPI_SS_AUX1, 4);
this->write_reg(SPI_SS_AUX2, 4);
}
~umsel2_ctrl_impl(void)
{
try
{
this->pd_synth(SPI_SS_AUX1);
this->pd_synth(SPI_SS_AUX2);
}
catch(...){}
}
uhd::freq_range_t get_rx_freq_range(const int)
{
return uhd::freq_range_t(54e6, 4400e6);
}
double set_rx_freq(const int which, const double freq)
{
const int slaveno = (which == 1)?SPI_SS_AUX1 : SPI_SS_AUX2;
return this->tune_synth(slaveno, freq);
}
uhd::sensor_value_t get_locked(const int which)
{
boost::uint32_t irq = _ctrl->peek32(U2_REG_IRQ_RB);
bool locked = false;
if (which == 1) locked = (irq & AUX_LD1_IRQ_BIT) != 0;
if (which == 2) locked = (irq & AUX_LD2_IRQ_BIT) != 0;
return uhd::sensor_value_t("LO", locked, "locked", "unlocked");
}
private:
void init_synth(const int slaveno)
{
//reset the registers
_regs[slaveno][0] = 0;
_regs[slaveno][1] = 0;
_regs[slaveno][2] = 0;
_regs[slaveno][3] = 0;
_regs[slaveno][4] = 0;
_regs[slaveno][5] = REG5_RESERVED;
_regs[slaveno][6] = REG6_RESERVED_VALUE << REG6_RESERVED_SHIFT;
_regs[slaveno][7] = REG7_RESERVED_VALUE << REG7_RESERVED_SHIFT;
_regs[slaveno][8] = REG8_RESERVED;
_regs[slaveno][9] = 0;
_regs[slaveno][10] = REG10_RESERVED_VALUE << REG10_RESERVED_SHIFT;
_regs[slaveno][11] = REG11_RESERVED;
_regs[slaveno][12] = REG12_RESERVED_VALUE << REG12_RESERVED_SHIFT;
//------------------------------------------------------------//
//----------------------- register 0 -------------------------//
//------------------------------------------------------------//
//autocal enabled
MODIFY_FIELD(_regs[slaveno][0], 1, 0x1, REG0_AUTOCAL_SHIFT);
//prescaler 4/5
MODIFY_FIELD(_regs[slaveno][0], 0, 0x1, REG0_PRESCALER_SHIFT);
//------------------------------------------------------------//
//----------------------- register 3 -------------------------//
//------------------------------------------------------------//
//sd load reset, phase resync, phase adjust = disabled
MODIFY_FIELD(_regs[slaveno][3], 0, 0x1, REG3_SD_LOAD_SHIFT);
MODIFY_FIELD(_regs[slaveno][3], 0, 0x1, REG3_PHASE_RSYNC_SHIFT);
MODIFY_FIELD(_regs[slaveno][3], 0, 0x1, REG3_PHASE_ADJ_SHIFT);
MODIFY_FIELD(_regs[slaveno][3], 0, REG3_PHASE_MASK, REG3_PHASE_SHIFT);
//------------------------------------------------------------//
//----------------------- register 4 -------------------------//
//------------------------------------------------------------//
//muxout to lock detect
MODIFY_FIELD(_regs[slaveno][4], 6, REG4_MUXOUT_MASK, REG4_MUXOUT_SHIFT);
//double buff disabled
MODIFY_FIELD(_regs[slaveno][4], 0, 0x1, REG4_DBL_BUFF_SHIFT);
//charge pump current 0.31mA@5.1k
MODIFY_FIELD(_regs[slaveno][4], 0, REG4_CURRENT_MASK, REG4_CURRENT_SHIFT);
//refin single ended
MODIFY_FIELD(_regs[slaveno][4], 0, 0x1, REG4_REF_MODE_SHIFT);
//mux level 3V
MODIFY_FIELD(_regs[slaveno][4], 1, 0x1, REG4_MUX_LOGIC_SHIFT);
//PD polarity positive
MODIFY_FIELD(_regs[slaveno][4], 1, 0x1, REG4_PD_POL_SHIFT);
//power down disabled
MODIFY_FIELD(_regs[slaveno][4], 0, 0x1, REG4_PWR_DOWN_SHIFT);
//charge-pump 3-state disabled
MODIFY_FIELD(_regs[slaveno][4], 0, 0x1, REG4_CP_3STATE_SHIFT);
//counter reset disabled
MODIFY_FIELD(_regs[slaveno][4], 0, 0x1, REG4_CNTR_RESET);
//------------------------------------------------------------//
//----------------------- register 6 -------------------------//
//------------------------------------------------------------//
//feedback fundamental
MODIFY_FIELD(_regs[slaveno][6], 1, 0x1, REG6_FB_SEL_SHIFT);
//bleed current 7.5uA
MODIFY_FIELD(_regs[slaveno][6], 2, REG6_CP_BLEED_CURR_MASK, REG6_CP_BLEED_CURR_SHIFT);
//mute until lock detect disabled
MODIFY_FIELD(_regs[slaveno][6], 0, 0x1, REG6_MLTD_SHIFT);
//aux output disabled (-1dBm)
MODIFY_FIELD(_regs[slaveno][6], 0, 0x1, REG6_AUX_PWR_EN_SHIFT);
MODIFY_FIELD(_regs[slaveno][6], 1, REG6_AUX_PWR_MASK, REG6_AUX_PWR_SHIFT);
//RF output power (5dBm)
MODIFY_FIELD(_regs[slaveno][6], 1, 0x1, REG6_PWR_EN_SHIFT);
MODIFY_FIELD(_regs[slaveno][6], 3, REG6_PWR_MASK, REG6_PWR_SHIFT);
//negative bleed enabled
MODIFY_FIELD(_regs[slaveno][6], 1, 0x1, REG6_NEG_BLEED_SHIFT);
//gated bleed disabled
MODIFY_FIELD(_regs[slaveno][6], 0, 0x1, REG6_GATED_BLEED_SHIFT);
//------------------------------------------------------------//
//----------------------- register 7 -------------------------//
//------------------------------------------------------------//
//LE Sync REFin
MODIFY_FIELD(_regs[slaveno][7], 1, 0x1, REG7_LE_SYNC_SHIFT);
//LD Cycles
MODIFY_FIELD(_regs[slaveno][7], 1024, REG7_LD_CYCLE_CNT_MASK, REG7_LD_CYCLE_CNT_SHIFT);
//LOL Mode disabled
MODIFY_FIELD(_regs[slaveno][7], 0, 0x1, REG7_LOL_MODE_SHIFT);
//Frac-N LD Prec 5.0ns
MODIFY_FIELD(_regs[slaveno][7], 0, REG7_FRAC_N_PREC_MASK, REG7_FRAC_N_PREC_SHIFT);
//LD Mode Frac-N
MODIFY_FIELD(_regs[slaveno][7], 0, 0x1, REG7_LD_MODE_SHIFT);
//------------------------------------------------------------//
//----------------------- register 10 ------------------------//
//------------------------------------------------------------//
//adc enable
MODIFY_FIELD(_regs[slaveno][10], 1, 0x1, REG10_ADC_EN_SHIFT);
//adc conversion enable
MODIFY_FIELD(_regs[slaveno][10], 1, 0x1, REG10_ADC_CONV_SHIFT);
//------------------------------------------------------------//
//----------------------- register 12 ------------------------//
//------------------------------------------------------------//
//phase resync 0
MODIFY_FIELD(_regs[slaveno][12], 0, REG12_RESYNC_CLOCK_MASK, REG12_RESYNC_CLOCK_SHIFT);
//write all registers
for (int addr = 12; addr >= 0; addr--)
this->write_reg(slaveno, addr);
}
void pd_synth(const int slaveno)
{
//muxout to lock 3state
MODIFY_FIELD(_regs[slaveno][4], 0, REG4_MUXOUT_MASK, REG4_MUXOUT_SHIFT);
//charge pump 3state
MODIFY_FIELD(_regs[slaveno][4], 1, 0x1, REG4_CP_3STATE_SHIFT);
//power down
MODIFY_FIELD(_regs[slaveno][4], 1, 0x1, REG4_PWR_DOWN_SHIFT);
//outputs off
MODIFY_FIELD(_regs[slaveno][6], 0, 0x1, REG6_AUX_PWR_EN_SHIFT);
MODIFY_FIELD(_regs[slaveno][6], 0, 0x1, REG6_PWR_EN_SHIFT);
//write all registers
for (int addr = 12; addr >= 0; addr--)
this->write_reg(slaveno, addr);
}
double tune_synth(const int slaveno, const double RFout)
{
if (verbose) std::cout << " tune_synth(slaveno=" << slaveno << ")" << std::endl;
if (verbose) std::cout << " RFout " << (RFout/1e6) << " MHz" << std::endl;
//determine the reference out divider and VCOout
double VCOout = 0;
int RFOUTDIVSEL = 0;
while (true)
{
int RFOUTDIV = 1 << RFOUTDIVSEL;
VCOout = RFout*RFOUTDIV;
if (VCOout < 3.4e9) RFOUTDIVSEL++;
else break;
}
if (verbose) std::cout << " RFOUTDIV " << (1 << RFOUTDIVSEL) << "" << std::endl;
if (verbose) std::cout << " VCOout " << (VCOout/1e6) << " MHz" << std::endl;
//use doubler to increase the pfd frequency (good for noise performance)
int REFDBL = 0;
int REFDIV = 0;
//prescaler settings
int PRESCALER = 0; //4/5
const int Nmin = (PRESCALER==0)?23:75;
//calculate the R divider, N divider, and PDF frequency
double NDIV = 0;
int RDIV = 1;
double fPFD = 0;
while (true)
{
fPFD = _ref_clock*(double(1+REFDBL)/double(RDIV*(1+REFDIV)));
NDIV = VCOout/fPFD;
if (NDIV < Nmin) RDIV++;
else break;
}
if (verbose) std::cout << " RDIV " << RDIV << "" << std::endl;
if (verbose) std::cout << " NDIV " << NDIV << "" << std::endl;
if (verbose) std::cout << " fPFD " << (fPFD/1e6) << " MHz" << std::endl;
//calculate the integer parts of the N divider
int NINT = int(NDIV);
double NFRAC = std::ldexp(NDIV-NINT, 24);
int FRAC1 = int(NFRAC);
int MOD2 = fPFD/1e6; //TODO MOD2 = fPFD/GCD(fPFD, fCHSP)
int FRAC2 = int((NFRAC-FRAC1)*MOD2);
if (verbose) std::cout << " NINT " << NINT << "" << std::endl;
if (verbose) std::cout << " FRAC1 " << FRAC1 << "" << std::endl;
if (verbose) std::cout << " MOD2 " << MOD2 << "" << std::endl;
if (verbose) std::cout << " FRAC2 " << FRAC2 << "" << std::endl;
//VCO Band Division
//PFD/(band division × 16) < 150 kHz
int VCObanddiv = 1;
while (not(fPFD/(VCObanddiv*16) < 150e3)) VCObanddiv++;
if (verbose) std::cout << " VCObanddiv " << VCObanddiv << "" << std::endl;
//Maximize ALC Wait (to reduce Timeout to minimize time) so
//that ALC Wait = 30 and Synthesizer Lock Timeout = 12.
int ALC = 30;
int SLT = 12;
int TIMEOUT = std::ceil((fPFD*50e-6)/ALC);
if (verbose) std::cout << " TIMEOUT " << TIMEOUT << "" << std::endl;
//ADC Clock Divider (ADC_CLK_DIV)
//PFD/((ADC_CLK_DIV × 4) × 2) < 100 kHz
/*
int ADC_CLK_DIV = 1;
while (not(fPFD/((ADC_CLK_DIV*4)*2) < 100e3)) ADC_CLK_DIV++;
if (verbose) std::cout << " ADC_CLK_DIV " << ADC_CLK_DIV << "" << std::endl;
const long sleepUs = long(1e6*16*ADC_CLK_DIV/fPFD);
*/
//Copied from the ADI GUI
//after trying to juxtapose the documentation with the GUI
int ADC_CLK_DIV = 65;
const long sleepUs = 160;
//load registers
MODIFY_FIELD(_regs[slaveno][0], NINT, REG0_NVALUE_MASK, REG0_NVALUE_SHIFT);
MODIFY_FIELD(_regs[slaveno][0], PRESCALER, 0x1, REG0_PRESCALER_SHIFT);
MODIFY_FIELD(_regs[slaveno][0], 1/*enb*/, 0x1, REG0_AUTOCAL_SHIFT);
MODIFY_FIELD(_regs[slaveno][1], FRAC1, REG1_MFRAC_MASK, REG1_MFRAC_SHIFT);
MODIFY_FIELD(_regs[slaveno][2], MOD2, REG2_AUX_MOD_MASK, REG2_AUX_MOD_SHIFT);
MODIFY_FIELD(_regs[slaveno][2], FRAC2, REG2_AUX_FRAC_MASK, REG2_AUX_FRAC_SHIFT);
MODIFY_FIELD(_regs[slaveno][4], RDIV, REG4_R_MASK, REG4_R_SHIFT);
MODIFY_FIELD(_regs[slaveno][4], REFDIV, 0x1, REG4_REF_DIV_SHIFT);
MODIFY_FIELD(_regs[slaveno][4], REFDBL, 0x1, REG4_REF_DBL_SHIFT);
MODIFY_FIELD(_regs[slaveno][6], RFOUTDIVSEL, REG6_RF_DIV_MASK, REG6_RF_DIV_SHIFT);
MODIFY_FIELD(_regs[slaveno][9], SLT, REG9_SYNT_LOCK_TO_MASK, REG9_SYNT_LOCK_TO_SHIFT);
MODIFY_FIELD(_regs[slaveno][9], ALC, REG9_AUTO_LVL_TO_MASK, REG9_AUTO_LVL_TO_SHIFT);
MODIFY_FIELD(_regs[slaveno][9], TIMEOUT, REG9_TIMEOUT_MASK, REG9_TIMEOUT_SHIFT);
MODIFY_FIELD(_regs[slaveno][9], VCObanddiv, REG9_VCO_BAND_MASK, REG9_VCO_BAND_SHIFT);
MODIFY_FIELD(_regs[slaveno][10], ADC_CLK_DIV, REG10_ADC_CLK_DIV_MASK, REG10_ADC_CLK_DIV_SHIFT);
//write other registers
this->write_reg(slaveno, 6);
this->write_reg(slaveno, 9);
this->write_reg(slaveno, 10);
//FREQUENCY UPDATE SEQUENCE
MODIFY_FIELD(_regs[slaveno][4], 1, 0x1, REG4_CNTR_RESET);
this->write_reg(slaveno, 4);
this->write_reg(slaveno, 2);
this->write_reg(slaveno, 1);
MODIFY_FIELD(_regs[slaveno][0], 0, 0x1, REG0_AUTOCAL_SHIFT);
this->write_reg(slaveno, 0);
MODIFY_FIELD(_regs[slaveno][4], 0, 0x1, REG4_CNTR_RESET);
this->write_reg(slaveno, 4);
boost::this_thread::sleep(boost::posix_time::microseconds(sleepUs));
if (verbose) std::cout << " sleep time " << (sleepUs) << " us" << std::endl;
MODIFY_FIELD(_regs[slaveno][0], 1, 0x1, REG0_AUTOCAL_SHIFT);
this->write_reg(slaveno, 0);
//calculate actual tune value
double Nactual = NINT + std::ldexp(double(FRAC1 + FRAC2/double(MOD2)), -24);
double RFoutactual = (fPFD*Nactual)/(1 << RFOUTDIVSEL);
if (verbose) std::cout << " Nactual " << Nactual << "" << std::endl;
if (verbose) std::cout << " RFoutactual " << (RFoutactual/1e6) << " MHz" << std::endl;
if (verbose) boost::this_thread::sleep(boost::posix_time::microseconds(10000));
if (verbose) std::cout << " Locked " << this->get_locked((slaveno==SPI_SS_AUX1)?1:2).value << "" << std::endl;
return RFoutactual;
}
void write_reg(const int slaveno, const int addr)
{
int value = (_regs[slaveno][addr] & ~0xf) | addr;
if (verbose) std::cout << "write_reg[" << addr << "] = 0x" << std::hex << value << std::dec << std::endl;
_spiface->write_spi(slaveno, uhd::spi_config_t::EDGE_RISE, value, 32);
}
uhd::wb_iface::sptr _ctrl;
uhd::spi_iface::sptr _spiface;
const double _ref_clock;
std::map<int, std::map<int, int> > _regs;
const bool verbose;
};
umsel2_ctrl::sptr umsel2_ctrl::make(uhd::wb_iface::sptr ctrl, uhd::spi_iface::sptr spiface, const double ref_clock, const bool verbose)
{
return umsel2_ctrl::sptr(new umsel2_ctrl_impl(ctrl, spiface, ref_clock, verbose));
}

61
host/umsel2_ctrl.hpp Normal file
View File

@@ -0,0 +1,61 @@
// Copyright 2015-2015 Fairwaves 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_UMSEL2_CTRL_HPP
#define INCLUDED_UMSEL2_CTRL_HPP
#include <uhd/types/serial.hpp>
#include <uhd/types/wb_iface.hpp>
#include <uhd/types/sensors.hpp>
#include <uhd/types/ranges.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/utility.hpp>
#define UMSEL2_CH1_LMS_IF 360e6
#define UMSEL2_CH2_LMS_IF 400e6
/*!
* Control UmSEL2 board.
*/
class umsel2_ctrl
{
public:
typedef boost::shared_ptr<umsel2_ctrl> sptr;
static sptr make(uhd::wb_iface::sptr ctrl, uhd::spi_iface::sptr spiface, const double ref_clock, const bool verbose);
/*!
* Query the tune range.
* \param which values 1 or 2
*/
virtual uhd::freq_range_t get_rx_freq_range(const int which) = 0;
/*!
* Tune the synthesizer
* \param which values 1 or 2
* \param freq the freq in Hz
* \return the actual freq in Hz
*/
virtual double set_rx_freq(const int which, const double freq) = 0;
/*!
* Query lock detect.
* \param which values 1 or 2
*/
virtual uhd::sensor_value_t get_locked(const int which) = 0;
};
#endif /* INCLUDED_UMSEL2_CTRL_HPP */

View File

@@ -38,8 +38,9 @@ const int umtrx_impl::UMTRX_VGA1_DEF = -20;
const int umtrx_impl::UMTRX_VGA2_DEF = 22;
const int umtrx_impl::UMTRX_VGA2_MIN = 0;
static const double _dcdc_val_to_volt_init[256] =
const double umtrx_impl::_dcdc_val_to_volt[umtrx_impl::DCDC_VER_COUNT][256] =
{
{
9.38, 9.38, 9.40, 9.42, 9.42, 9.44, 9.46, 9.46, 9.48, 9.50, // 10
9.50, 9.52, 9.54, 9.54, 9.56, 9.58, 9.58, 9.60, 9.60, 9.62, // 20
9.64, 9.66, 9.66, 9.68, 9.70, 9.70, 9.72, 9.74, 9.76, 9.76, // 30
@@ -66,8 +67,35 @@ static const double _dcdc_val_to_volt_init[256] =
20.02, 20.20, 20.38, 20.58, 20.76, 20.96, 21.18, 21.38, 21.60, 21.82, // 240
21.92, 22.16, 22.40, 22.66, 22.92, 23.18, 23.46, 23.74, 24.02, 24.30, // 250
24.62, 24.94, 25.28, 25.62, 25.98, 26.34
},{
4.84, 4.84, 4.86, 4.88, 4.88, 4.90, 4.92, 4.94, 4.94, 4.96, // 10
4.98, 5.00, 5.02, 5.02, 5.04, 5.06, 5.06, 5.08, 5.10, 5.12, // 20
5.12, 5.14, 5.16, 5.18, 5.20, 5.22, 5.22, 5.24, 5.26, 5.28, // 30
5.30, 5.32, 5.32, 5.34, 5.36, 5.38, 5.40, 5.42, 5.44, 5.46, // 40
5.48, 5.50, 5.50, 5.52, 5.54, 5.56, 5.58, 5.60, 5.62, 5.64, // 50
5.66, 5.68, 5.70, 5.72, 5.74, 5.76, 5.78, 5.80, 5.82, 5.86, // 60
5.88, 5.90, 5.92, 5.94, 5.96, 5.98, 6.00, 6.02, 6.04, 6.08, // 70
6.10, 6.12, 6.14, 6.16, 6.20, 6.22, 6.24, 6.28, 6.30, 6.32, // 80
6.34, 6.36, 6.40, 6.42, 6.44, 6.48, 6.50, 6.54, 6.56, 6.58, // 90
6.62, 6.64, 6.68, 6.70, 6.74, 6.76, 6.78, 6.82, 6.84, 6.88, // 100
6.92, 6.94, 6.98, 7.00, 7.04, 7.08, 7.12, 7.14, 7.18, 7.22, // 110
7.26, 7.28, 7.30, 7.34, 7.38, 7.42, 7.46, 7.50, 7.54, 7.58, // 120
7.62, 7.66, 7.70, 7.74, 7.78, 7.82, 7.86, 7.90, 7.92, 7.98, // 130
8.02, 8.06, 8.10, 8.16, 8.20, 8.26, 8.30, 8.34, 8.40, 8.44, // 140
8.50, 8.54, 8.60, 8.66, 8.68, 8.74, 8.78, 8.84, 8.90, 8.96, // 150
9.02, 9.08, 9.14, 9.20, 9.26, 9.32, 9.38, 9.44, 9.52, 9.58, // 160
9.62, 9.68, 9.76, 9.82, 9.90, 9.96, 10.04, 10.12, 10.18, 10.26, // 170
10.34, 10.42, 10.50, 10.58, 10.68, 10.76, 10.80, 10.88, 10.98, 11.08, // 180
11.16, 11.26, 11.36, 11.44, 11.54, 11.64, 11.74, 11.86, 11.96, 12.08, // 190
12.18, 12.30, 12.36, 12.48, 12.60, 12.72, 12.84, 12.96, 13.10, 13.24, // 200
13.36, 13.50, 13.64, 13.78, 13.94, 14.08, 14.24, 14.40, 14.48, 14.66, // 210
14.82, 15.00, 15.18, 15.36, 15.54, 15.74, 15.92, 16.12, 16.32, 16.54, // 220
16.76, 16.98, 17.22, 17.44, 17.58, 17.82, 18.08, 18.34, 18.62, 18.90, // 230
19.20, 19.48, 19.80, 20.10, 20.44, 20.78, 21.12, 21.50, 21.88, 22.26, // 240
22.48, 22.90, 23.34, 23.80, 24.26, 24.74, 25.26, 25.76, 26.32, 26.86, // 250
27.48, 28.12, 28.78, 29.50, 29.50, 29.50
}
};
const std::vector<double> umtrx_impl::_dcdc_val_to_volt(_dcdc_val_to_volt_init, &_dcdc_val_to_volt_init[256]);
/***********************************************************************
* Property tree "alias" function
@@ -179,6 +207,7 @@ static mtu_result_t determine_mtu(const std::string &addr, const mtu_result_t &u
**********************************************************************/
umtrx_impl::umtrx_impl(const device_addr_t &device_addr)
{
_umtrx_vga2_def = device_addr.cast<int>("lmsvga2", UMTRX_VGA2_DEF);
_device_ip_addr = device_addr["addr"];
UHD_MSG(status) << "UmTRX driver version: " << UMTRX_VERSION << std::endl;
UHD_MSG(status) << "Opening a UmTRX device... " << _device_ip_addr << std::endl;
@@ -270,6 +299,36 @@ umtrx_impl::umtrx_impl(const device_addr_t &device_addr)
_tree->create<std::string>(mb_path / "hwrev").set(get_hw_rev());
UHD_MSG(status) << "Detected UmTRX " << get_hw_rev() << std::endl;
_hw_dcdc_ver = device_addr.cast<int>("dcdc_ver", -1);
if (_hw_dcdc_ver < 0)
{
detect_hw_dcdc_ver(mb_path);
} else {
UHD_ASSERT_THROW(_hw_dcdc_ver < DCDC_VER_COUNT);
UHD_MSG(status) << "Using DCDC version " << _hw_dcdc_ver << std::endl;
}
_tree->create<int>(mb_path / "hwdcdc_ver").set(_hw_dcdc_ver);
////////////////////////////////////////////////////////////////////////
// setup umsel2 control when present
////////////////////////////////////////////////////////////////////////
const std::string detect_umsel = device_addr.get("umsel", "off");
if (detect_umsel != "off")
{
//TODO delect umsel2 automatically with I2C communication
const bool umsel_verbose = device_addr.has_key("umsel_verbose");
_umsel2 = umsel2_ctrl::make(_ctrl/*peek*/, _ctrl/*spi*/, this->get_master_clock_rate(), umsel_verbose);
}
//register lock detect for umsel2
if (_umsel2)
{
_tree->create<sensor_value_t>(mb_path / "dboards" / "A" / "rx_frontends" / "0" / "sensors" / "aux_lo_locked")
.publish(boost::bind(&umsel2_ctrl::get_locked, _umsel2, 1));
_tree->create<sensor_value_t>(mb_path / "dboards" / "B" / "rx_frontends" / "0" / "sensors" / "aux_lo_locked")
.publish(boost::bind(&umsel2_ctrl::get_locked, _umsel2, 2));
}
////////////////////////////////////////////////////////////////////////
// configure diversity switches
////////////////////////////////////////////////////////////////////////
@@ -490,8 +549,8 @@ umtrx_impl::umtrx_impl(const device_addr_t &device_addr)
////////////////////////////////////////////////////////////////////
// create RF frontend interfacing
////////////////////////////////////////////////////////////////////
_lms_ctrl["A"] = lms6002d_ctrl::make(_ctrl/*spi*/, SPI_SS_LMS1, SPI_SS_AUX1, this->get_master_clock_rate() / _pll_div);
_lms_ctrl["B"] = lms6002d_ctrl::make(_ctrl/*spi*/, SPI_SS_LMS2, SPI_SS_AUX2, this->get_master_clock_rate() / _pll_div);
_lms_ctrl["A"] = lms6002d_ctrl::make(_ctrl/*spi*/, SPI_SS_LMS1, this->get_master_clock_rate() / _pll_div);
_lms_ctrl["B"] = lms6002d_ctrl::make(_ctrl/*spi*/, SPI_SS_LMS2, this->get_master_clock_rate() / _pll_div);
// LMS dboard do not have physical eeprom so we just hardcode values from host/lib/usrp/dboard/db_lms.cpp
dboard_eeprom_t rx_db_eeprom, tx_db_eeprom, gdb_db_eeprom;
@@ -554,7 +613,8 @@ umtrx_impl::umtrx_impl(const device_addr_t &device_addr)
} else {
// Set LMS internal VGA1 gain to optimal value
// VGA2 will be set in the set_tx_power()
ctrl->set_tx_gain(UMTRX_VGA1_DEF, "VGA1");
const int vga1 = device_addr.cast<int>("lmsvga1", UMTRX_VGA1_DEF);
ctrl->set_tx_gain(vga1, "VGA1");
_tx_power_range[fe_name] = generate_tx_power_range(fe_name);
// Use PA control to control output power
@@ -570,9 +630,9 @@ umtrx_impl::umtrx_impl(const device_addr_t &device_addr)
//rx freq
_tree->create<double>(rx_rf_fe_path / "freq" / "value")
.coerce(boost::bind(&lms6002d_ctrl::set_rx_freq, ctrl, _1));
.coerce(boost::bind(&umtrx_impl::set_rx_freq, this, fe_name, _1));
_tree->create<meta_range_t>(rx_rf_fe_path / "freq" / "range")
.publish(boost::bind(&lms6002d_ctrl::get_rx_freq_range, ctrl));
.publish(boost::bind(&umtrx_impl::get_rx_freq_range, this, fe_name));
_tree->create<bool>(rx_rf_fe_path / "use_lo_offset").set(false);
//tx freq
@@ -712,12 +772,13 @@ umtrx_impl::~umtrx_impl(void)
int umtrx_impl::volt_to_dcdc_r(double v)
{
if (v <= _dcdc_val_to_volt[0])
if (v <= _dcdc_val_to_volt[_hw_dcdc_ver][0])
return 0;
else if (v >= _dcdc_val_to_volt[255])
else if (v >= _dcdc_val_to_volt[_hw_dcdc_ver][255])
return 255;
else
return std::lower_bound(_dcdc_val_to_volt.begin(), _dcdc_val_to_volt.end(), v) - _dcdc_val_to_volt.begin();
return std::lower_bound(&_dcdc_val_to_volt[_hw_dcdc_ver][0], &_dcdc_val_to_volt[_hw_dcdc_ver][256], v) -
&_dcdc_val_to_volt[_hw_dcdc_ver][0];
}
void umtrx_impl::set_pa_dcdc_r(uint8_t val)
@@ -737,7 +798,7 @@ uhd::gain_range_t umtrx_impl::generate_tx_power_range(const std::string &which)
// maintain high signal quality.
uhd::gain_range_t pa_range = generate_pa_power_range(which);
// UHD_MSG(status) << "Original PA output power range: " << pa_range.to_pp_string() << std::endl;
uhd::gain_range_t vga_range(pa_range.start() - (UMTRX_VGA2_DEF-UMTRX_VGA2_MIN), pa_range.start()-1, 1.0);
uhd::gain_range_t vga_range(pa_range.start() - (_umtrx_vga2_def-UMTRX_VGA2_MIN), pa_range.start()-1, 1.0);
uhd::gain_range_t res_range(vga_range);
res_range.insert(res_range.end(), pa_range.begin(), pa_range.end());
// UHD_MSG(status) << "Generated Tx output power range: " << res_range.to_pp_string() << std::endl;
@@ -763,16 +824,16 @@ double umtrx_impl::set_tx_power(double power, const std::string &which)
if (power >= min_pa_power)
{
UHD_MSG(status) << "Setting Tx power using PA (VGA2=" << UMTRX_VGA2_DEF << ", PA=" << power << ")" << std::endl;
UHD_MSG(status) << "Setting Tx power using PA (VGA2=" << _umtrx_vga2_def << ", PA=" << power << ")" << std::endl;
// Set VGA2 to the recommended value and use PA to control Tx power
_lms_ctrl[which]->set_tx_gain(UMTRX_VGA2_DEF, "VGA2");
_lms_ctrl[which]->set_tx_gain(_umtrx_vga2_def, "VGA2");
actual_power = set_pa_power(power, which);
} else {
double vga2_gain = UMTRX_VGA2_DEF - (min_pa_power-power);
double vga2_gain = _umtrx_vga2_def - (min_pa_power-power);
UHD_MSG(status) << "Setting Tx power using VGA2 (VGA2=" << vga2_gain << ", PA=" << min_pa_power << ")" << std::endl;
// Set PA output power to minimum and use VGA2 to control Tx power
actual_power = _lms_ctrl[which]->set_tx_gain(vga2_gain, "VGA2");
actual_power = set_pa_power(min_pa_power, which) - (UMTRX_VGA2_DEF-actual_power);
actual_power = set_pa_power(min_pa_power, which) - (_umtrx_vga2_def-actual_power);
}
return actual_power;
@@ -871,6 +932,49 @@ uint8_t umtrx_impl::dc_offset_double2int(double corr)
return (int)(corr*128 + 128.5);
}
double umtrx_impl::set_rx_freq(const std::string &which, const double freq)
{
if (_umsel2)
{
const double target_lms_freq = (which=="A")?UMSEL2_CH1_LMS_IF:UMSEL2_CH2_LMS_IF;
const double actual_lms_freq = _lms_ctrl[which]->set_rx_freq(target_lms_freq);
const double target_umsel_freq = freq - actual_lms_freq;
const double actual_umsel_freq = _umsel2->set_rx_freq((which=="A")?1:2, target_umsel_freq);
/*
std::cout << "target_total_freq " << freq/1e6 << " MHz" << std::endl;
std::cout << "target_lms_freq " << target_lms_freq/1e6 << " MHz" << std::endl;
std::cout << "actual_lms_freq " << actual_lms_freq/1e6 << " MHz" << std::endl;
std::cout << "target_umsel_freq " << target_umsel_freq/1e6 << " MHz" << std::endl;
std::cout << "actual_umsel_freq " << actual_umsel_freq/1e6 << " MHz" << std::endl;
std::cout << "actual_total_freq " << (actual_umsel_freq + actual_lms_freq)/1e6 << " MHz" << std::endl;
//*/
return actual_umsel_freq + actual_lms_freq;
}
else
{
return _lms_ctrl[which]->set_rx_freq(freq);
}
}
uhd::freq_range_t umtrx_impl::get_rx_freq_range(const std::string &which) const
{
if (_umsel2)
{
const double target_lms_freq = (which=="A")?UMSEL2_CH1_LMS_IF:UMSEL2_CH2_LMS_IF;
const uhd::freq_range_t range_umsel = _umsel2->get_rx_freq_range((which=="A")?1:2);
return uhd::freq_range_t(
range_umsel.start()+target_lms_freq,
range_umsel.stop()+target_lms_freq);
}
else
{
return _lms_ctrl[which]->get_rx_freq_range();
}
}
uhd::sensor_value_t umtrx_impl::read_temp_c(const std::string &which)
{
boost::recursive_mutex::scoped_lock l(_i2c_mutex);
@@ -1001,7 +1105,7 @@ void umtrx_impl::detect_hw_rev(const fs_path& mb_path)
pa_low = pa_low_env;
}
if (pa_low.empty())
_pa_nlow = false;
_pa_nlow = true; //Turn off Vin bypass by default
else
_pa_nlow = (boost::lexical_cast<int>(pa_low) == 0);
@@ -1061,3 +1165,34 @@ const char* umtrx_impl::get_hw_rev() const
default: return "[unknown]";
}
}
void umtrx_impl::detect_hw_dcdc_ver(const uhd::fs_path &)
{
_hw_dcdc_ver = DCDC_VER_2_3_1_OLD;
if (_hw_rev < UMTRX_VER_2_3_1)
{
return;
}
uint8_t old = _pa_dcdc_r;
bool old_pa_nlow = _pa_nlow;
set_nlow(true);
set_pa_dcdc_r(0);
boost::this_thread::sleep(boost::posix_time::seconds(1));
double v_actual = read_dc_v("DCOUT").to_real();
double err_min = std::abs(v_actual - _dcdc_val_to_volt[0][0]);
for (unsigned j = 1; j < DCDC_VER_COUNT; ++j) {
double err = std::abs(v_actual - _dcdc_val_to_volt[j][0]);
if (err < err_min) {
err_min = err;
_hw_dcdc_ver = j;
}
}
set_pa_dcdc_r(old);
set_nlow(old_pa_nlow);
UHD_MSG(status) << "Detected UmTRX DCDC ver. " << _hw_dcdc_ver
<< " (err: " << err_min << ")" << std::endl;
}

View File

@@ -31,6 +31,7 @@
#include "ads1015_ctrl.hpp"
#include "tmp102_ctrl.hpp"
#include "power_amp.hpp"
#include "umsel2_ctrl.hpp"
#include <uhd/usrp/mboard_eeprom.hpp>
#include <uhd/property_tree.hpp>
#include <uhd/device.hpp>
@@ -118,8 +119,17 @@ private:
UMTRX_VER_2_3_1
};
enum umtrx_dcdc_ver {
DCDC_VER_2_3_1_OLD = 0,
DCDC_VER_2_3_1_NEW = 1,
DCDC_VER_COUNT //Should be the last
};
// hardware revision
umtrx_hw_rev _hw_rev;
int _hw_dcdc_ver;
unsigned _pll_div;
const char* get_hw_rev() const;
@@ -128,16 +138,16 @@ private:
static const int UMTRX_VGA2_DEF;
static const int UMTRX_VGA2_MIN;
// Conversion table for converting DCDC_R values to actual voltages.
static const std::vector<double> _dcdc_val_to_volt;
// Find a dcdc_r value to approximate requested Vout voltage
static int volt_to_dcdc_r(double v);
static const double _dcdc_val_to_volt[umtrx_impl::DCDC_VER_COUNT][256];
ads1015_ctrl _sense_pwr;
ads1015_ctrl _sense_dc;
tmp102_ctrl _temp_side_a;
tmp102_ctrl _temp_side_b;
// Optimal device specific value
int _umtrx_vga2_def;
//PA control
bool _pa_nlow;
bool _pa_en1;
@@ -152,6 +162,7 @@ private:
std::string _device_ip_addr;
umtrx_iface::sptr _iface;
umtrx_fifo_ctrl::sptr _ctrl;
umsel2_ctrl::sptr _umsel2;
//controls for perifs
uhd::dict<std::string, lms6002d_ctrl::sptr> _lms_ctrl;
@@ -181,6 +192,7 @@ private:
void set_tx_fe_corrections(const std::string &mb, const std::string &board, const double);
void set_tcxo_dac(const umtrx_iface::sptr &, const uint16_t val);
void detect_hw_rev(const uhd::fs_path &mb_path);
void detect_hw_dcdc_ver(const uhd::fs_path &mb_path);
void commit_pa_state();
void set_enpa1(bool en);
void set_enpa2(bool en);
@@ -195,6 +207,11 @@ private:
uhd::transport::zero_copy_if::sptr make_xport(const size_t which, const uhd::device_addr_t &args);
std::complex<double> get_dc_offset_correction(const std::string &which) const;
void set_dc_offset_correction(const std::string &which, const std::complex<double> &corr);
double set_rx_freq(const std::string &which, const double freq);
uhd::freq_range_t get_rx_freq_range(const std::string &which) const;
// Find a dcdc_r value to approximate requested Vout voltage
int volt_to_dcdc_r(double v);
static double dc_offset_int2double(uint8_t corr);
static uint8_t dc_offset_double2int(double corr);

View File

@@ -187,7 +187,8 @@ void umtrx_impl::client_query_handle1(const boost::property_tree::ptree &request
else if (action == "GET")
{
const std::string type = request.get("type", "");
if (type.empty()) response.put("error", "type field not specified: BOOL, INT, DOUBLE, SENSOR, RANGE");
if (type.empty()) response.put("error", "type field not specified: STRING, BOOL, INT, DOUBLE, SENSOR, RANGE");
else if (type == "STRING") response.put("result", _tree->access<std::string>(path).get());
else if (type == "BOOL") response.put("result", _tree->access<bool>(path).get());
else if (type == "INT") response.put("result", _tree->access<int>(path).get());
else if (type == "DOUBLE") response.put("result", _tree->access<double>(path).get());
@@ -218,7 +219,8 @@ void umtrx_impl::client_query_handle1(const boost::property_tree::ptree &request
else if (action == "SET")
{
const std::string type = request.get("type", "");
if (type.empty()) response.put("error", "type field not specified: BOOL, INT, DOUBLE");
if (type.empty()) response.put("error", "type field not specified: STRING, BOOL, INT, DOUBLE");
else if (type == "STRING") _tree->access<std::string>(path).set(request.get<std::string>("value"));
else if (type == "BOOL") _tree->access<bool>(path).set(request.get<bool>("value"));
else if (type == "INT") _tree->access<int>(path).set(request.get<int>("value"));
else if (type == "DOUBLE") _tree->access<double>(path).set(request.get<double>("value"));

View File

@@ -104,6 +104,9 @@ localparam SR_SPI_CORE = 185; // 3
#define U2_REG_TIME64_HI_RB_PPS READBACK_BASE + 4*14
#define U2_REG_TIME64_LO_RB_PPS READBACK_BASE + 4*15
#define AUX_LD1_IRQ_BIT (1 << 14)
#define AUX_LD2_IRQ_BIT (1 << 15)
/////////////////////////////////////////////////
// LMS regs
////////////////////////////////////////////////

View File

@@ -2,10 +2,10 @@
# Build and Install the UmTRX utils
########################################################################
INSTALL(PROGRAMS
umtrx_net_burner
umtrx_nmea
umtrx_gps_coord
umtrx_auto_calibration
umtrx_firmware
DESTINATION bin
)

View File

@@ -0,0 +1,6 @@
TypesDB "/usr/share/collectd/umtrx.types.db"
LoadPlugin exec
<Plugin exec>
Exec "fairwaves:fairwaves" "/usr/share/umtrx/umtrx2collectd.py"
</Plugin>

View File

@@ -0,0 +1,5 @@
Voltages1 PR1:GAUGE:0:32, PR2:GAUGE:0:32, PF1:GAUGE:0:32, PF2:GAUGE:0:32
Voltages2 Zero:GAUGE:0:32, Vin:GAUGE:0:32, VinPA:GAUGE:0:32, DCOUT:GAUGE:0:32
Temperature A:GAUGE:-40:120, B:GAUGE:-40:120
ReturnLoss C1:GAUGE:0:32, C2:GAUGE:0:32
VSWR C1:GAUGE:0:32, C2:GAUGE:0:32

View File

@@ -0,0 +1,76 @@
#!/usr/bin/python -u
# -*- coding: utf-8 -*-
import os
import sched,time
from umtrx_property_tree import umtrx_property_tree
from umtrx_vswr import umtrx_vswr
HOSTNAME = os.environ['COLLECTD_HOSTNAME'] if 'COLLECTD_HOSTNAME' in os.environ else 'localhost'
INTERVAL = os.environ['COLLECTD_INTERVAL'] if 'COLLECTD_INTERVAL' in os.environ else '60'
umtrx = umtrx_property_tree()
umtrx.connect()
sensors_path = "/mboards/0/sensors"
res = umtrx.list_path_raw(sensors_path)
# ['tempA', 'tempB', 'voltagePR1', 'voltagePF1', 'voltagePR2', 'voltagePF2', 'voltagezero', 'voltageVin', 'voltageVinPA', 'voltageDCOUT']
sensors_list = res.get('result', [])
# Voltages1 PR1:GAUGE:0:32, PR2:GAUGE:0:32, PF1:GAUGE:0:32, PF2:GAUGE:0:32
# Voltages2 Zero:GAUGE:0:32, Vin:GAUGE:0:32, VinPA:GAUGE:0:32, DCOUT:GAUGE:0:32
# Temperature A:GAUGE:-40:120, B:GAUGE:-40:120,
# ReturnLoss C1:GAUGE:0:32, C2:GAUGE:0:32
# VSWR C1:GAUGE:0:32, C2:GAUGE:0:32
def publish_set(name, values):
res = [HOSTNAME, name, INTERVAL]
res.extend(values)
src = "PUTVAL \"%s/UmTRX/%s\" interval=%s N:" + ":".join(["%s"] * len(values))
print src % tuple(res)
def publish():
sets = {'Voltages1': ['VoltagePR1', 'VoltagePR2', 'VoltagePF1', 'VoltagePF2'],
'Voltages2': ['Voltagezero', 'VoltageVin', 'VoltageVinPA', 'VoltageDCOUT'],
'Temperature': ['TempA', 'TempB'], 'VSWR': ['Channel_1_VSWR', 'Channel_2_VSWR'],
'ReturnLoss': ['Channel_1_ReturnLoss', 'Channel_2_ReturnLoss']}
current_sensors = {}
for sensor in sensors_list:
sensor_data = umtrx.query_sensor_raw(sensors_path + "/" + sensor)['result']
current_sensors[sensor_data['name']] = sensor_data['value']
# vswr_calibration = TM10_VSWR_cal
vswr_calibration = 0
for num in [1, 2]:
vpr_name = 'voltagePR' + str(num)
vpf_name = 'voltagePF' + str(num)
if vpr_name in sensors_list and vpf_name in sensors_list:
vpr = float(umtrx.query_sensor_value(sensors_path + '/' + vpr_name))
vpf = float(umtrx.query_sensor_value(sensors_path + '/' + vpf_name))
vswr = umtrx_vswr(vpf, vpr, vswr_calibration)
current_sensors['Channel_%d_VSWR' % num] = vswr.vswr()
current_sensors['Channel_%d_ReturnLoss' % num] = vswr.return_loss()
for key, values in sets.iteritems():
publish_set(key, [current_sensors[name] for name in values])
s = sched.scheduler(time.time, time.sleep)
def timer_loop():
s.enter(float(INTERVAL), 1, timer_loop, ())
publish()
timer_loop()
s.run()
umtrx.close()

16
host/utils/umtrx_firmware Executable file
View File

@@ -0,0 +1,16 @@
#!/bin/bash
case $1 in
flash )
umtrx_net_burner --addr=192.168.10.2 --fpga=/usr/share/umtrx/firmware/u2plus_umtrx_v2.bin --fw=/usr/share/umtrx/firmware/umtrx_txrx_uhd.bin --reset
;;
check )
;;
* )
cat <<-EOF
Usage:
$0 flash - burn packaged firmware to umtrx
$0 check - compare versions of packaged firmware and one installed on umtrx
EOF
;;
esac

View File

@@ -51,37 +51,45 @@ class umtrx_property_tree:
self._send_request('GET', path, value_type='DOUBLE')
return self._recv_response()
def query_sensor_raw(self, sensor_path):
self._send_request('GET', sensor_path, value_type='SENSOR')
def query_sensor_raw(self, path):
self._send_request('GET', path, value_type='SENSOR')
return self._recv_response()
def query_range_raw(self, path):
self._send_request('GET', path, value_type='RANGE')
return self._recv_response()
def query_string_raw(self, path):
self._send_request('GET', path, value_type='STRING')
return self._recv_response()
#
# Getters (value)
#
def query_bool_value(self, path):
res = self.query_bool_raw(sensor_path)
res = self.query_bool_raw(path)
return res['result']['value']
def query_int_value(self, path):
res = self.query_int_raw(sensor_path)
res = self.query_int_raw(path)
return res['result']['value']
def query_double_value(self, path):
res = self.query_double_raw(sensor_path)
res = self.query_double_raw(path)
return res['result']['value']
def query_sensor_value(self, sensor_path):
res = self.query_sensor_raw(sensor_path)
def query_sensor_value(self, path):
res = self.query_sensor_raw(path)
return res['result']['value']
def query_range_value(self, path):
res = self.query_range_raw(sensor_path)
return res['result']['value']
res = self.query_range_raw(path)
return res['result']
def query_string_value(self, path):
res = self.query_string_raw(path)
return res['result']
#
# Setters
@@ -99,6 +107,10 @@ class umtrx_property_tree:
self._send_request('SET', path, value_type='DOUBLE', value=val)
return self._recv_response()
def set_string(self, path, val):
self._send_request('SET', path, value_type='STRING', value=val)
return self._recv_response()
#
# Check path presence and list paths
#

View File

@@ -0,0 +1,20 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
##########################
### Query sensors
##########################
from umtrx_property_tree import umtrx_property_tree
s = umtrx_property_tree()
s.connect()
mb_path="/mboards/0"
fpga_version = s.query_string_value(mb_path+"/fpga_version")
fw_version = s.query_string_value(mb_path+"/fw_version")
print "FPGA bitstream version: %s" % fpga_version
print "ZPU firmware version: %s" % fw_version
s.close()

View File

@@ -46,6 +46,8 @@ class umtrx_vswr:
gamma = self._gamma
if gamma == 1.0:
return float("inf")
elif gamma > 1.0:
return float("nan")
else:
return (1+gamma)/(1-gamma)
@@ -54,15 +56,21 @@ class umtrx_vswr:
gamma = self._gamma
if gamma == 1.0:
return float("-inf")
elif gamma > 1.0:
return float("nan")
else:
return -10.0 * math.log(1.0-gamma*gamma, 10)
def pf_rate(self):
''' Estimated reflected power rate, % '''
gamma = self._gamma
if gamma > 1.0:
return float("nan")
return 1.0 - gamma*gamma
def pr_rate(self):
''' Estimated reflected power rate, % '''
gamma = self._gamma
if gamma > 1.0:
return float("nan")
return gamma*gamma

Binary file not shown.

Binary file not shown.