mirror of
				https://github.com/fairwaves/UHD-Fairwaves.git
				synced 2025-10-31 12:03:39 +00:00 
			
		
		
		
	Compare commits
	
		
			40 Commits
		
	
	
		
			fifo_ctrl_
			...
			1.0.9
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 16bc2bdd18 | ||
|  | 9cf7377866 | ||
|  | a4568b4704 | ||
|  | b24a4d0bb8 | ||
|  | dc5718daea | ||
|  | 25394541fa | ||
|  | b362710778 | ||
|  | c9b7e8c884 | ||
|  | e7eb8e87cc | ||
|  | 2a6b94bea9 | ||
|  | 143a9008d1 | ||
|  | 1e1a889448 | ||
|  | f385a4355a | ||
|  | 3c240a2ab2 | ||
|  | e4c59df63e | ||
|  | ad8ff4a345 | ||
|  | 4f909bcfa2 | ||
|  | 9309e3c548 | ||
|  | 6b5ff4a460 | ||
|  | 53e7e5597f | ||
|  | a89917faae | ||
|  | 09f323dc3c | ||
|  | fee336bc5a | ||
|  | fc4efd5cb0 | ||
|  | 0f7b0cbeab | ||
|  | b600665303 | ||
|  | 2a89674c56 | ||
|  | 6e30c16773 | ||
|  | 401e64014c | ||
|  | 1dbd567102 | ||
|  | 77e9066bf8 | ||
|  | 1e43f04790 | ||
|  | cbff81745b | ||
|  | 57d5ca4b51 | ||
|  | b78ceeb9b1 | ||
|  | fcd92d8f50 | ||
|  | 2727f62ab8 | ||
|  | 75c3380ccf | ||
|  | 80e65f35cf | ||
|  | f16599cfa9 | 
							
								
								
									
										56
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										56
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							| @@ -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
									
									
								
							
							
						
						
									
										11
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							| @@ -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
									
									
								
							
							
						
						
									
										3
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							| @@ -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 | ||||
|   | ||||
							
								
								
									
										7
									
								
								debian/umtrx.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								debian/umtrx.install
									
									
									
									
										vendored
									
									
								
							| @@ -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
									
								
							
							
						
						
									
										3
									
								
								debian/umtrx.links
									
									
									
									
										vendored
									
									
										Normal 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 | ||||
| @@ -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 \ | ||||
| )) | ||||
|   | ||||
							
								
								
									
										243
									
								
								fpga/control_lib/axis_spi_core.v
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								fpga/control_lib/axis_spi_core.v
									
									
									
									
									
										Normal 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 | ||||
| @@ -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), | ||||
|   | ||||
| @@ -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 | ||||
| ######################################################################## | ||||
|   | ||||
| @@ -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) | ||||
| { | ||||
|     //////////////////////////////////////////////////////////////////// | ||||
|   | ||||
| @@ -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
									
								
							
							
						
						
									
										478
									
								
								host/umsel2_ctrl.cpp
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										61
									
								
								host/umsel2_ctrl.hpp
									
									
									
									
									
										Normal 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 */ | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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")); | ||||
|   | ||||
| @@ -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 | ||||
| //////////////////////////////////////////////// | ||||
|   | ||||
| @@ -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 | ||||
| ) | ||||
|  | ||||
|   | ||||
							
								
								
									
										6
									
								
								host/utils/collectd/umtrx.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								host/utils/collectd/umtrx.conf
									
									
									
									
									
										Normal 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> | ||||
							
								
								
									
										5
									
								
								host/utils/collectd/umtrx.types.db
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								host/utils/collectd/umtrx.types.db
									
									
									
									
									
										Normal 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 | ||||
							
								
								
									
										76
									
								
								host/utils/collectd/umtrx2collectd.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										76
									
								
								host/utils/collectd/umtrx2collectd.py
									
									
									
									
									
										Executable 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
									
								
							
							
						
						
									
										16
									
								
								host/utils/umtrx_firmware
									
									
									
									
									
										Executable 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 | ||||
| @@ -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 | ||||
|   # | ||||
|   | ||||
							
								
								
									
										20
									
								
								host/utils/umtrx_query_versions.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										20
									
								
								host/utils/umtrx_query_versions.py
									
									
									
									
									
										Executable 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() | ||||
| @@ -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.
										
									
								
							
		Reference in New Issue
	
	Block a user