diff options
Diffstat (limited to 'host/lib')
73 files changed, 9503 insertions, 202 deletions
diff --git a/host/lib/convert/convert_pack_sc12.cpp b/host/lib/convert/convert_pack_sc12.cpp index 92cd5d152..081f60a89 100644 --- a/host/lib/convert/convert_pack_sc12.cpp +++ b/host/lib/convert/convert_pack_sc12.cpp @@ -80,13 +80,26 @@ struct convert_star_1_to_sc12_item32_1 : public converter void operator()(const input_type &inputs, const output_type &outputs, const size_t nsamps) { const std::complex<type> *input = reinterpret_cast<const std::complex<type> *>(inputs[0]); - item32_sc12_3x *output = reinterpret_cast<item32_sc12_3x *>(size_t(outputs[0]) & ~0x3); + + /* + * Effectively outputs will point to a managed_buffer instance. These buffers are 32 bit aligned. + * For a detailed description see comments in 'convert_unpack_sc12.cpp'. + */ + const size_t head_samps = size_t(inputs[0]) & 0x3; + size_t rewind = 0; + switch(head_samps) + { + case 0: break; + case 1: rewind = 9; break; + case 2: rewind = 6; break; + case 3: rewind = 3; break; + } + item32_sc12_3x *output = reinterpret_cast<item32_sc12_3x *>(size_t(outputs[0]) - rewind); //helper variables size_t i = 0, o = 0; //handle the head case - const size_t head_samps = size_t(outputs[0]) & 0x3; switch (head_samps) { case 0: break; //no head diff --git a/host/lib/device.cpp b/host/lib/device.cpp index c5bc047da..ff5163f2d 100644 --- a/host/lib/device.cpp +++ b/host/lib/device.cpp @@ -153,3 +153,9 @@ device::sptr device::make(const device_addr_t &hint, size_t which){ return dev; } } + +uhd::property_tree::sptr +device::get_tree(void) const +{ + return _tree; +} diff --git a/host/lib/ic_reg_maps/CMakeLists.txt b/host/lib/ic_reg_maps/CMakeLists.txt index b6e121db3..889c725db 100644 --- a/host/lib/ic_reg_maps/CMakeLists.txt +++ b/host/lib/ic_reg_maps/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2013 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 @@ -98,6 +98,11 @@ LIBUHD_PYTHON_GEN_SOURCE( ) LIBUHD_PYTHON_GEN_SOURCE( + ${CMAKE_CURRENT_SOURCE_DIR}/gen_ads62p48_regs.py + ${CMAKE_CURRENT_BINARY_DIR}/ads62p48_regs.hpp +) + +LIBUHD_PYTHON_GEN_SOURCE( ${CMAKE_CURRENT_SOURCE_DIR}/gen_tuner_4937di5_regs.py ${CMAKE_CURRENT_BINARY_DIR}/tuner_4937di5_regs.hpp ) @@ -107,4 +112,9 @@ LIBUHD_PYTHON_GEN_SOURCE( ${CMAKE_CURRENT_BINARY_DIR}/tda18272hnm_regs.hpp ) +LIBUHD_PYTHON_GEN_SOURCE( + ${CMAKE_CURRENT_SOURCE_DIR}/gen_lmk04816_regs.py + ${CMAKE_CURRENT_BINARY_DIR}/lmk04816_regs.hpp +) + UNSET(LIBUHD_PYTHON_GEN_SOURCE_DEPS) diff --git a/host/lib/ic_reg_maps/gen_ads62p48_regs.py b/host/lib/ic_reg_maps/gen_ads62p48_regs.py new file mode 100644 index 000000000..c38ce8ff1 --- /dev/null +++ b/host/lib/ic_reg_maps/gen_ads62p48_regs.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# +# Copyright 2013 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# Template for raw text data describing registers +# name addr[bit range inclusive] default optional enums +######################################################################## +REGS_TMPL="""\ +reset 0[7] 0 +serial_readout 0[0] 0 +enable_low_speed_mode 0x20[2] 0 +ref 0x3f[6:5] 0 internal=0, external=3 +standby 0x3f[1] 0 normal, standby +power_down 0x40[3:0] 0 pins=0, normal=8, chb=9, cha=10, chab=11, global=12, chb_standby=13, cha_standby=14, mux=15 +lvds_cmos 0x41[7] 0 parallel_cmos, ddr_lvds +clk_out_pos_edge 0x44[7:5] 0 normal=0, plus4_26=5, minus4_26=7, minus7_26=6 +clk_out_neg_edge 0x44[4:2] 0 normal=0, plus4_26=5, minus4_26=7, minus7_26=6 +channel_control 0x50[6] 0 common, independent +data_format 0x50[2:1] 2 2s_compliment=2, offset_binary=3 +custom_pattern_low 0x51[7:0] 0 +custom_pattern_high 0x52[5:0] 0 +enable_offset_corr_chA 0x53[6] 0 +gain_chA 0x55[7:4] 0 +offset_corr_time_const_chA 0x55[3:0] 0 +fine_gain_adjust_chA 0x57[6:0] 0 +test_patterns_chA 0x62[2:0] 0 normal, zeros, ones, toggle, ramp, custom +offset_pedestal_chA 0x63[5:0] 0 +enable_offset_corr_chB 0x66[6] 0 +gain_chB 0x68[7:4] 0 +offset_corr_time_const_chB 0x68[3:0] 0 +fine_gain_adjust_chB 0x6A[6:0] 0 +test_patterns_chB 0x75[2:0] 0 normal, zeros, ones, toggle, ramp, custom +offset_pedestal_chB 0x76[5:0] 0 +""" + +######################################################################## +# Template for methods in the body of the struct +######################################################################## +BODY_TMPL="""\ +boost::uint8_t get_reg(boost::uint8_t addr){ + boost::uint8_t reg = 0; + switch(addr){ + #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) + case $addr: + #for $reg in filter(lambda r: r.get_addr() == addr, $regs) + reg |= (boost::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); + #end for + break; + #end for + } + return reg; +} + +boost::uint16_t get_write_reg(boost::uint8_t addr){ + return (boost::uint16_t(addr) << 8) | get_reg(addr); +} + +boost::uint16_t get_read_reg(boost::uint8_t addr){ + return (boost::uint16_t(addr) << 8) | (1 << 7); +} +""" + +if __name__ == '__main__': + import common; common.generate( + name='ads62p48_regs', + regs_tmpl=REGS_TMPL, + body_tmpl=BODY_TMPL, + file=__file__, + ) diff --git a/host/lib/ic_reg_maps/gen_lmk04816_regs.py b/host/lib/ic_reg_maps/gen_lmk04816_regs.py new file mode 100644 index 000000000..e89a82671 --- /dev/null +++ b/host/lib/ic_reg_maps/gen_lmk04816_regs.py @@ -0,0 +1,407 @@ +#Copyright 2010 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# Template for raw text data describing registers +# name addr[bit range inclusive] default optional enums +######################################################################## +REGS_TMPL="""\ +######################################################################## +## address 0 +######################################################################## +address0 0[0:4] 0 +CLKout0_1_DIV 0[5:15] 25 +CLKout0_1_HS 0[16] 0 +RESET 0[17] 0 no_reset, reset +CLKout0_1_DDLY 0[18:27] 0 five +CLKout0_ADLY_SEL 0[28] 0 d_pd, d_ev_x, d_odd_y, d_both +CLKout1_ADLY_SEL 0[29] 0 d_pd, d_ev_x, d_odd_y, d_both +Required_0 0[30] 0 +CLKout0_1_PD 0[31] 1 power_up, power_down +######################################################################## +## address 1 +######################################################################## +address1 1[0:4] 1 +CLKout2_3_DIV 1[5:15] 25 +CLKout2_3_HS 1[16] 0 +Powerdown 1[17] 0 normal, disabled +CLKout2_3_DDLY 1[18:27] 0 +CLKout2_ADLY_SEL 1[28] 0 d_pd, d_ev_x, d_odd_y, d_both +CLKout3_ADLY_SEL 1[29] 0 d_pd, d_ev_x, d_odd_y, d_both +Required_1 1[30] 0 +CLKout2_3_PD 1[31] 1 power_up, power_down +######################################################################## +## address 2 +######################################################################## +address2 2[0:4] 2 +CLKout4_5_DIV 2[5:15] 25 +CLKout4_5_HS 2[16] 0 +Required_2_17 2[17] 0 +CLKout4_5_DDLY 2[18:27] 0 +CLKout4_ADLY_SEL 2[28] 0 d_pd, d_ev_x, d_odd_y, d_both +CLKout5_ADLY_SEL 2[29] 0 d_pd, d_ev_x, d_odd_y, d_both +Required_2_30 2[30] 0 +CLKout4_5_PD 2[31] 1 power_up, power_down +######################################################################## +## address 3 +######################################################################## +address3 3[0:4] 3 +CLKout6_7_DIV 3[5:15] 1 +CLKout6_7_HS 3[16] 0 +Required_3_17 3[17] 0 +CLKout6_7_DDLY 3[18:27] 0 +CLKout6_ADLY_SEL 3[28] 0 d_pd, d_ev_x, d_odd_y, d_both +CLKout7_ADLY_SEL 3[29] 0 d_pd, d_ev_x, d_odd_y, d_both +CLKout6_7_OSCin_Sel 3[30] 1 VCO,OSCin +CLKout6_7_PD 3[31] 0 power_up, power_down +######################################################################## +## address 4 +######################################################################## +address4 4[0:4] 4 +CLKout8_9_DIV 4[5:15] 25 +CLKout8_9_HS 4[16] 0 +Required_4_17 4[17] 0 +CLKout8_9_DDLY 4[18:27] 0 +CLKout8_ADLY_SEL 4[28] 0 d_pd, d_ev_x, d_odd_y, d_both +CLKout9_ADLY_SEL 4[29] 0 d_pd, d_ev_x, d_odd_y, d_both +CLKout8_9_OSCin_Sel 4[30] 0 VCO, OSCin +CLKout8_9_PD 4[31] 0 power_up, power_down +######################################################################## +## address 5 +######################################################################## +address5 5[0:4] 5 +CLKout10_11_DIV 5[5:15] 25 +CLKout10_11_HS 5[16] 0 +Required_5_17 5[17] 0 +CLKout10_11_DDLY 5[18:27] 0 +CLKout10_ADLY_SEL 5[28] 0 d_pd, d_ev_x, d_odd_y, d_both +CLKout11_ADLY_SEL 5[29] 0 d_pd, d_ev_x, d_odd_y, d_both +Required_5_30 5[30] 0 +CLKout10_11_PD 5[31] 1 normal, power_down +######################################################################## +## address 6 +######################################################################## +#set $CLKoutX_TYPE_ENUMS = "p_down=0, LVDS=1, LVPECL_700mVpp=2, LVPECL_1200mVpp=3, LVPECL_1600mVpp=4, LVPECL_200mVpp=5, LVCMOS=6, LVCMOS_IN=7, LVCMOS_NN=8, LVCMOS_II=9, LVCMOS_LN=10, LVCMOS_LI=11, LVCMOS_NL=12, LVCMOS_IL=13, LVCMOS_LL=1" +address6 6[0:4] 6 +CLKout0_1_ADLY 6[5:9] 0 +Required_6_10 6[10] 0 +CLKout2_3_ADLY 6[11:15] 0 +CLKout0_TYPE 6[16:19] 0 $(CLKoutX_TYPE_ENUMS) +CLKout1_TYPE 6[20:23] 0 $(CLKoutX_TYPE_ENUMS) +CLKout2_TYPE 6[24:27] 0 $(CLKoutX_TYPE_ENUMS) +CLKout3_TYPE 6[28:31] 0 $(CLKoutX_TYPE_ENUMS) +######################################################################## +## address 7 +######################################################################## +address7 7[0:4] 7 +CLKout4_5_ADLY 7[5:9] 0 +Required_7_10 7[10] 0 +CLKout6_7_ADLY 7[11:15] 0 +CLKout4_TYPE 7[16:19] 0 $(CLKoutX_TYPE_ENUMS) +CLKout5_TYPE 7[20:23] 0 $(CLKoutX_TYPE_ENUMS) +CLKout6_TYPE 7[24:27] 0 $(CLKoutX_TYPE_ENUMS) +CLKout7_TYPE 7[28:31] 0 $(CLKoutX_TYPE_ENUMS) +######################################################################## +## address 8 +######################################################################## +address8 8[0:4] 8 +CLKout8_9_ADLY 8[5:9] 0 +Required_8_10 8[10] 0 +CLKout10_11_ADLY 8[11:15] 0 $(CLKoutX_TYPE_ENUMS) +CLKout8_TYPE 8[16:19] 0 $(CLKoutX_TYPE_ENUMS) +CLKout9_TYPE 8[20:23] 0 $(CLKoutX_TYPE_ENUMS) +CLKout10_TYPE 8[24:27] 0 $(CLKoutX_TYPE_ENUMS) +CLKout11_TYPE 8[28:31] 0 $(CLKoutX_TYPE_ENUMS) +######################################################################## +## address 9 +######################################################################## +address9 9[0:4] 9 +Required_9_5 9[5] 0 +Required_9_6 9[6] 1 +Required_9_7 9[7] 0 +Required_9_8 9[8] 1 +Required_9_9 9[9] 0 +Required_9_10 9[10] 1 +Required_9_11 9[11] 0 +Required_9_12 9[12] 1 +Required_9_13 9[13] 0 +Required_9_14 9[14] 1 +Required_9_15 9[15] 0 +Required_9_16 9[16] 1 +Required_9_17 9[17] 0 +Required_9_18 9[18] 1 +Required_9_19 9[19] 0 +Required_9_20 9[20] 1 +Required_9_21 9[21] 0 +Required_9_22 9[22] 1 +Required_9_23 9[23] 0 +Required_9_24 9[24] 1 +Required_9_25 9[25] 0 +Required_9_26 9[26] 1 +Required_9_27 9[27] 0 +Required_9_28 9[28] 1 +Required_9_29 9[29] 0 +Required_9_30 9[30] 1 +Required_9_31 9[31] 0 +######################################################################## +## address 10 +######################################################################## +address10 10[0:4] 10 +FEEDBACK_MUX 10[5:7] 0 +VCO_DIV 10[8:10] 2 +EN_FEEDBACK_MUX 10[11] 0 powered_down, enabled +VCO_MUX 10[12] 0 just_vco, vco_divider +Required_10_13 10[13] 0 +Required_10_14 10[14] 1 +Required_10_15 10[15] 0 +OSCout_DIV 10[16:18] 0 +PD_OSCin 10[19] 0 normal, power_down +OSCout10_MUX 10[20] 0 bypass_div, divided +Required_10_21 10[21] 0 +EN_OSCout0 10[22] 1 disabled, enabled +Required_10_23 10[23] 0 +OSCout0_TYPE 10[24:27] 0 +Required_10_28 10[28] 1 +Required_10_range 10[29:31] 0 +######################################################################## +## address 11 +######################################################################## +address11 11[0:4] 11 +EN_PLL2_XTAL 11[5] 0 osc_disabled=0,osc_enabled=1 +Required_11 11[6:11] 0 +SYNC_TYPE 11[12:14] 1 input=0,in_pull_up=1,in_pull_down=2,out_push_pull=3,out_inverted=4,out_open_sources=5,out_open_drains=6 +SYNC_EN_AUTO 11[15] 0 man_sync=0,sync_int_gen=1 +SYNC_POL_INV 11[16] 1 sync_high=0,sync_low=1 +SYNC_QUAL 11[17] 0 not_qual=0,fb_mux=1 +SYNC_CLKin2_MUX 11[18:19] 0 log_low=0,CLKin2_LOS=1,CLKin2_Selected=2,uWire_RB=3 +NO_SYNC_CLKout0_1 11[20] 0 clock_xy_sync=0,clock_xy_nosync=1 +NO_SYNC_CLKout2_3 11[21] 0 clock_xy_sync=0,clock_xy_nosync=1 +NO_SYNC_CLKout4_5 11[22] 1 clock_xy_sync=0,clock_xy_nosync=1 +NO_SYNC_CLKout6_7 11[23] 1 clock_xy_sync=0,clock_xy_nosync=1 +NO_SYNC_CLKout8_9 11[24] 0 clock_xy_sync=0,clock_xy_nosync=1 +NO_SYNC_CLKout10_11 11[25] 0 clock_xy_sync=0,clock_xy_nosync=1 +EN_SYNC 11[26] 1 disable=0,enable=1 +MODE 11[27:31] 0 dual_int=0, dual_int_zer_delay=2,dual_ext=3,dual_ext_zer_delay=5,pll_two_int=6,pll_two_int_zer_delay=8,pll_two_ext=11,clock_dist=16 +######################################################################## +## address 12 +######################################################################## +address12 12[0:4] 12 +HOLDOVER_MODE 12[6:7] 2 disabled=1,enabled=2 +EN_TRACK 12[8] 1 disabled=0,enabled=1 +Required_12_range917 12[9:17] 0 +Required_12_range1819 12[18:19] 1 +Required_12_20 12[20] 0 +Required_LE_12 12[21] 0 +SYNC_PLL1_DLD 12[22] 0 sync_mode_noforce=0,sync_mode_force=1 +SYNC_PLL2_DLD 12[23] 0 sync_mode_noforce=0,sync_mode_force=1 +LD_TYPE 12[24:26] 3 out_push_pull=3,out_inverted=4,out_open_sources=5,out_open_drains=6 +LD_MUX 12[27:31] 3 log_low=0,pll1_dld=1,pll2_dld=2,both=3,holdover=4,locked_dac=5,uWire_RB=7,rail=8,low=9,high=10,pll1_N=11,pll1_Nhalf=12,pll2_N=13,pll2_Nhalf=14,pll1_R=15,pll1_Rhalf=16,pll2_R=17,pll2_Rhalf=18 +######################################################################## +## address 13 +######################################################################## +address13 13[0:4] 13 +EN_CLKin0 13[5] 1 no_valid_use=0,valid_use=1 +EN_CLKin1 13[6] 1 no_valid_use=0,valid_use=1 +EN_CLKin2 13[7] 1 no_valid_use=0,valid_use=1 +CLKin_Sel_INV 13[8] 0 active_high=0,active_low=1 +CLKin_Select_MODE 13[9:11] 3 CLKin0_man=0,CLKin1_man=1,CLKin2_man=2,pin_sel_mode=3,auto_mode=4,auto_mode_w_next_block_sel=6 +Status_CLKin0_MUX 13[14:12] 0 log_low=0,CLKin0_LOS=1,CLKin0_Selected=2,dac_lock=3,dac_low=4,dac_high=5,uWire_RB=6 +DISABLE_DLD1_DET 13[15] 0 pll1_dld_cause_event=0,pll1_dld_not_cause=1 +Status_CLKin0_TYPE 13[16:18] 2 input=0,in_pull_up=1,in_pull_down=2,out_push_pull=3,out_inverted=4,out_open_sources=5,out_open_drains=6 +Required_13_19 13[19] 0 +Status_CLKin1_MUX 13[20:22] 0 log_low=0,CLKin1_LOS=1,CLKin1_Selected=2,DAC_lock=3,DAC_low=4,DAC_high=5,uWire_RB=6 +Required_19_23 13[23] 0 +HOLDOVER_TYPE 13[24:26] 3 out_push_pull=3,out_inverted=4,out_open_sources=5,out_open_drains=6 +HOLDOVER_MUX 13[27:31] 7 log_low=0,pll1_dld=1,pll2_dld=2,both=3,holdover=4,locked_dac=5,uWire_RB=7,rail=8,low=9,high=10,pll1_N=11,pll1_Nhalf=12,pll2_N=13,pll2_Nhalf=14,pll1_R=15,pll1_Rhalf=16,pll2_R=17,pll2_Rhalf=18 +######################################################################## +## address 14 +######################################################################## +address14 14[0:4] 14 +EN_VTUNE_RAIL_DET 14[5] 0 disabled=0,enabled=1 +DAC_LOW_TRIP 14[6:11] 0 +Required_14_1213 14[12:13] 0 +DAC_HIGH_TRIP 14[14:19] 0 +CLKin0_BUF_TYPE 14[20] 0 bipolar=0,cmos=1 +CLKin1_BUF_TYPE 14[21] 0 bipolar=0,cmos=1 +CLKin2_BUF_TYPE 14[22] 0 bipolar=0,cmos=1 +Required_14_23 14[23] 0 +Status_CLKin1_TYPE 14[24:26] 2 input=0,in_pull_up=1,in_pull_down=2,out_push_pull=3,out_inverted=4,out_open_sources=5,out_open_drains=6 +Required_14_27 14[27] 0 +EN_LOS 14[28] 1 disable_timeout=0,enable_timeout=1 +Required_14_29 14[29] 0 +LOS_TIMEOUT 14[30:31] 0 1200ns_at_4p2KHz=0,206ns_at_2p5MHz=1,52p9at10MHz=2,23p7_at_22MHz=3 +######################################################################## +## address 15 +######################################################################## +address15 15[0:4] 15 +FORCE_HOLDOVER 15[5] 0 disabled=0,enabled=1 +HOLDOVER_DLD_CNT 15[6:19] 512 +EN_MAN_DAC 15[20] 0 disabled=0,enabled=1 +Required_15 15[21] 0 +MAN_DAC 15[22:31] 512 +######################################################################## +## address 16 +######################################################################## +address16 16[0:4] 16 +Required_16_5 16[5] 0 +Required_16_6 16[6] 0 +Required_16_7 16[7] 0 +Required_16_8 16[8] 0 +Required_16_9 16[9] 0 +Required_16_10 16[10] 1 +Required_16_11 16[11] 0 +Required_16_12 16[12] 0 +Required_16_13 16[13] 0 +Required_16_14 16[14] 0 +Required_16_15 16[15] 0 +Required_16_16 16[16] 1 +Required_16_17 16[17] 0 +Required_16_18 16[18] 1 +Required_16_19 16[19] 0 +Required_16_20 16[20] 1 +Required_16_21 16[21] 0 +Required_16_22 16[22] 1 +Required_16_23 16[23] 0 +Required_16_24 16[24] 1 +Required_16_25 16[25] 0 +Required_16_26 16[26] 0 +Required_16_27 16[27] 0 +Required_16_28 16[28] 0 +Required_16_29 16[29] 0 +XTAL_LVL 16[30:31] 0 1p65VPP=0, 1p75VPP=1, 1p90VPP=2, 2p05VPP=3 +######################################################################## +## address 24 +######################################################################## +address24 24[0:4] 24 +PLL1_WND_SIZE 24[6:7] 3 5p5ns=0, 10ns=1, 18p6=2, 40ns=3 +PLL1_R_DLY 24[8:10] 0 0ps=0, 205ps=1, 410ps=2, 615ps=3, 820ps=4, 1025ps=5, 1230ps=6, 1345ps=7 +Required_24_11 24[11] 0 +PLL2_N_DLY 24[12:14] 0 0ps=0, 205ps=1, 410ps=2, 615ps=3, 820ps=4, 1025ps=5, 1230ps=6, 1345ps=7 +Required_24_15 24[15] 0 +PLL2_R3_LF 24[16:18] 0 200ohm=0, 1kilo_ohm=1, 2kilo_ohm=2, 4kilo_ohm=3, 16kilo_ohm=4 +Required_24_19 24[19] 0 +PLL2_R4_LF 24[20:22] 0 200ohm=0, 1kilo_ohm=1, 2kilo_ohm=2, 4kilo_ohm=3, 16kilo_ohm=4 +Required_24_23 24[23] 0 +PLL2_C3_LF 24[24:27] 0 10pF=0, 11pF=1, 15pF=2, 16pF=3, 19pF=4, 20pF=5, 24pF=6, 25pF=7, 29pF=8, 30pF=9, 33pF=10, 34pF=11, 38pF=12, 39pF=13 +PLL2_C4_LF 24[28:31] 0 10pF=0, 15pF=1, 29pF=2, 34pF=3, 47pF=4, 52pF=5, 66pF=6, 71pF=7, 103pF=8, 108pF=9, 122pF=10, 126pF=11, 141pF=12, 146pF=13 +######################################################################### +## address 25 +######################################################################### +addres25 25[0:4] 25 +PLL2_CP_TRI_25 25[5] 0 +PLL1_DLD_CNT_25 25[6:19] 1024 +Required_25 25[20:21] 0 +DAC_CLK_DIV 25[22:31] 4 +######################################################################### +## address 26 +######################################################################### +address26 26[0:4] 26 +PLL2_CP_TRI_26 26[5] 0 PLL2_cpout2_active=0,PLL2cpout2T=1 +PLL2_DLD_CNT_26 26[6:19] 81 +Required_26_20 26[20] 0 +Required_26_21 26[21] 1 +Required_26_22 26[22] 0 +Required_26_23 26[23] 1 +Required_26_24 26[24] 1 +Required_26_25 26[25] 1 +PLL2_CP_GAIN_26 26[26:27] 3 100uA=0, 400uA=1, 1600uA=2, 3200uA=3 +PLL2_CP_POL_26 26[28] 0 neg_slope=0, pos_slope=1 +EN_PLL2_REF_2X 26[29] 0 normal_freq_ref=0, doubled_freq_ref=1 +PLL2_WND_SIZE 26[30:31] 2 3p7ns=2 +######################################################################### +## address 27 +######################################################################### +address27 27[0:4] 27 +PLL1_CP_TRI_27 27[5] 0 PLL2_cpout2_active=0,PLL2_cpout2TRI=1 +PLL1_R_27 27[6:19] 96 +CLKin0_PreR_DIV 27[20:21] 0 div1=0,div2=1,div4=2,div8=3 +CLKin1_PreR_DIV 27[22:23] 0 div1=0,div2=1,div4=2,div8=3 +CLKin2_PreR_DIV 27[24:25] 0 div1=0,div2=1,div4=2,div8=3 +PLL1_CP_GAIN_27 27[26:27] 0 100uA=0, 200uA=1, 400uA=2, 1600uA=3 +PLL1_CP_POL_27 27[28] 1 neg_slove=0, pos_slope=1 +Reqiured_27 27[29:31] 0 +######################################################################### +## address 28 +######################################################################### +address28 28[0:4] 28 +Required_28 28[5] 0 +PLL1_N_28 28[6:19] 192 +PLL2_R_28 28[20:31] 4 +######################################################################### +## address 29 +######################################################################### +address29 29[0:4] 29 +PLL2_N_CAL_29 29[5:22] 48 +PLL2_FAST_PDF_29 29[23] 1 100MHz_less=0, 100MHz_more=1 +OSCin_FREQ_29 29[24:26] 1 zero_to_63MHz=0, 63_to_127MHz=1, 127_to_255MHz=2, 255_to_400MHz=4 +Required29 29[27:31] 0 +######################################################################### +## address 30 +######################################################################### +address30 30[0:4] 30 +PLL2_N_30 30[5:22] 48 +Required_30_522 30[23] 0 +PLL2_P_30 30[24:26] 2 div_8=0, div_2=1, div_2a=2, div_3=3, div_4=4, div_5=5, div_6=6, div_7=7 +Required_2731 30[27:31] 0 +######################################################################### +## address 31 +######################################################################### +address31 31[0:4] 31 +uWire_LOCK 31[5] 0 reg_locked=0, reg_unlocked=1 +Required_615 31[6:15] 0 +READBACK_ADDR 31[16:20] 31 R0=0, R1=1, R2=2, R3=3, R4=4, R5=5, R6=6, R7=7, R8=8, R10=10, R11=11, R12=12, R13=13, R14=14, R15=15, R24=24, R25=25, R26=26, R27=27, R28=28, R29=29, R30=30, R31=31 +READBACK_LE 31[21] 0 LE_low=0, LE_high=1 +Required_2231 31[22:31] 0 +""" +######################################################################## +# Template for methods in the body of the struct +######################################################################## + +BODY_TMPL = """\ + + + + +boost::uint32_t get_reg(int addr){ + boost::uint32_t reg = 0; + switch(addr){ + #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) + case $addr: + #for $reg in filter(lambda r: r.get_addr() == addr, $regs) + reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); + #end for + break; + #end for + } + return reg; +} +""" + + + + + +if __name__ == '__main__': + import common; common.generate( + name='lmk04816_regs', + regs_tmpl=REGS_TMPL, + body_tmpl=BODY_TMPL, + file=__file__, + ) + + + + diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index 963edcf85..5920f3d78 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -20,6 +20,11 @@ ######################################################################## ######################################################################## +# Include subdirectories (different than add) +######################################################################## +INCLUDE_SUBDIRECTORY(nirio) + +######################################################################## # Setup libusb ######################################################################## MESSAGE(STATUS "") @@ -119,7 +124,18 @@ LIBUHD_PYTHON_GEN_SOURCE( ) LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/tcp_zero_copy.cpp ${CMAKE_CURRENT_SOURCE_DIR}/buffer_pool.cpp ${CMAKE_CURRENT_SOURCE_DIR}/if_addrs.cpp ${CMAKE_CURRENT_SOURCE_DIR}/udp_simple.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/nirio_zero_copy.cpp ) + +# Verbose Debug output for send/recv +SET( UHD_TXRX_DEBUG_PRINTS OFF CACHE BOOL "Use verbose debug output for send/recv" ) +OPTION( UHD_TXRX_DEBUG_PRINTS "Use verbose debug output for send/recv" "" ) +IF(UHD_TXRX_DEBUG_PRINTS) + MESSAGE(STATUS "Using verbose debug output for send/recv") + ADD_DEFINITIONS(-DUHD_TXRX_DEBUG_PRINTS) +ENDIF() + diff --git a/host/lib/transport/buffer_pool.cpp b/host/lib/transport/buffer_pool.cpp index 971bbb75a..0fcebecb1 100644 --- a/host/lib/transport/buffer_pool.cpp +++ b/host/lib/transport/buffer_pool.cpp @@ -16,11 +16,21 @@ // #include <uhd/transport/buffer_pool.hpp> +#include <uhd/transport/zero_copy.hpp> #include <boost/shared_array.hpp> #include <vector> using namespace uhd::transport; +#ifdef UHD_TXRX_DEBUG_PRINTS +/* + * This is the implementation for the static variable 's_buffer_count' + * located in uhd/transport/zero_copy.hpp. + * It is used in the managed_buffer class. + */ +boost::detail::atomic_count managed_buffer::s_buffer_count(0); +#endif // UHD_TXRX_DEBUG_PRINTS + //! pad the byte count to a multiple of alignment static size_t pad_to_boundary(const size_t bytes, const size_t alignment){ return bytes + (alignment - bytes)%alignment; diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp index 2d18e1623..4ea2032e3 100644 --- a/host/lib/transport/libusb1_zero_copy.cpp +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -31,6 +31,12 @@ #include <boost/thread/condition_variable.hpp> #include <list> +#ifdef UHD_TXRX_DEBUG_PRINTS +#include <vector> +#include <fstream> +#include <boost/format.hpp> +#endif + using namespace uhd; using namespace uhd::transport; @@ -69,12 +75,23 @@ struct lut_result_t completed = 0; status = LIBUSB_TRANSFER_COMPLETED; actual_length = 0; +#ifdef UHD_TXRX_DEBUG_PRINTS + start_time = 0; + buff_num = -1; +#endif } int completed; libusb_transfer_status status; int actual_length; boost::mutex mut; boost::condition_variable usb_transfer_complete; + +#ifdef UHD_TXRX_DEBUG_PRINTS + // These are fore debugging + long start_time; + int buff_num; + bool is_recv; +#endif }; // Created to be used as an argument to boost::condition_variable::timed_wait() function @@ -84,6 +101,14 @@ struct lut_result_completed { bool operator()() const {return (_result.completed ? true : false);} }; +#ifdef UHD_TXRX_DEBUG_PRINTS +static std::string dbg_prefix("libusb1_zero_copy,"); +static void libusb1_zerocopy_dbg_print_err(std::string msg){ + msg = dbg_prefix + msg; + fprintf(stderr, "%s\n", msg.c_str()); +} +#endif + /*! * All libusb callback functions should be marked with the LIBUSB_CALL macro * to ensure that they are compiled with the same calling convention as libusb. @@ -98,6 +123,10 @@ static void LIBUSB_CALL libusb_async_cb(libusb_transfer *lut) r->actual_length = lut->actual_length; r->completed = 1; r->usb_transfer_complete.notify_one(); // wake up thread waiting in wait_for_completion() member function below +#ifdef UHD_TXRX_DEBUG_PRINTS + long end_time = boost::get_system_time().time_of_day().total_microseconds(); + libusb1_zerocopy_dbg_print_err( (boost::format("libusb_async_cb,%s,%i,%i,%i,%ld,%ld") % (r->is_recv ? "rx":"tx") % r->buff_num % r->actual_length % r->status % end_time % r->start_time).str() ); +#endif } /*********************************************************************** @@ -113,11 +142,18 @@ public: _ctx(libusb::session::get_global_session()->get_context()), _lut(lut), _frame_size(frame_size) { /* NOP */ } - void release(void){_release_cb(this);} + void release(void){ + _release_cb(this); + } UHD_INLINE void submit(void) { - _lut->length = (_is_recv)? _frame_size : size(); //always set length + _lut->length = (_is_recv)? _frame_size : size(); //always set length +#ifdef UHD_TXRX_DEBUG_PRINTS + result.start_time = boost::get_system_time().time_of_day().total_microseconds(); + result.buff_num = num(); + result.is_recv = _is_recv; +#endif const int ret = libusb_submit_transfer(_lut); if (ret != 0) throw uhd::runtime_error(str(boost::format( "usb %s submit failed: %s") % _name % libusb_error_name(ret))); @@ -160,7 +196,6 @@ public: } private: - boost::function<void(libusb_zero_copy_mb *)> _release_cb; const bool _is_recv; const std::string _name; @@ -214,7 +249,7 @@ public: UHD_ASSERT_THROW(lut != NULL); _mb_pool.push_back(boost::make_shared<libusb_zero_copy_mb>( - lut, this->get_frame_size(), boost::bind(&libusb_zero_copy_single::enqueue_damn_buffer, this, _1), is_recv, name + lut, this->get_frame_size(), boost::bind(&libusb_zero_copy_single::enqueue_buffer, this, _1), is_recv, name )); libusb_fill_bulk_transfer( @@ -304,7 +339,7 @@ private: //! why 2 queues? there is room in the future to have > N buffers but only N in flight boost::circular_buffer<libusb_zero_copy_mb *> _enqueued, _released; - void enqueue_damn_buffer(libusb_zero_copy_mb *mb) + void enqueue_buffer(libusb_zero_copy_mb *mb) { boost::mutex::scoped_lock l(_mutex); _released.push_back(mb); diff --git a/host/lib/transport/nirio/CMakeLists.txt b/host/lib/transport/nirio/CMakeLists.txt new file mode 100644 index 000000000..6a33da6c5 --- /dev/null +++ b/host/lib/transport/nirio/CMakeLists.txt @@ -0,0 +1,48 @@ +# +# Copyright 2013-2014 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## + +######################################################################## +# Include subdirectories (different than add) +######################################################################## +INCLUDE_SUBDIRECTORY(lvbitx) +INCLUDE_SUBDIRECTORY(rpc) + +######################################################################## +# Append to the list of sources for lib uhd +######################################################################## + +LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/nifpga_lvbitx.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/niusrprio_session.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/niriok_proxy.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/nirio_resource_manager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/status.cpp +) + +IF(WIN32) + LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_SOURCE_DIR}/nirio_driver_iface_win.cpp) +ELSE(WIN32) + IF(APPLE) + LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_SOURCE_DIR}/nirio_driver_iface_macos.cpp) + ELSE(APPLE) + LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_SOURCE_DIR}/nirio_driver_iface_linux.cpp) + ENDIF(APPLE) +ENDIF(WIN32) diff --git a/host/lib/transport/nirio/lvbitx/CMakeLists.txt b/host/lib/transport/nirio/lvbitx/CMakeLists.txt new file mode 100644 index 000000000..35cfaa456 --- /dev/null +++ b/host/lib/transport/nirio/lvbitx/CMakeLists.txt @@ -0,0 +1,65 @@ +# +# Copyright 2013 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/>. +# + +MACRO(LIBUHD_LVBITX_GEN_SOURCE_AND_BITSTREAM lvbitx binfile) + GET_FILENAME_COMPONENT(lvbitxprefix ${lvbitx} NAME_WE) + + IF( ${binfile} STREQUAL "OFF" ) + SET( GEN_OPTIONS ) + MESSAGE( STATUS " Using ${lvbitx} for codegen" ) + ELSE( ${binfile} STREQUAL "OFF" ) + SET( GEN_OPTIONS --merge-bin=${CMAKE_SOURCE_DIR}/../binaries/${binfile} --output-lvbitx-path=${CMAKE_SOURCE_DIR}/../binaries ) + MESSAGE( STATUS " Merging ${lvbitx} with ${binfile} for codegen" ) + ENDIF( ${binfile} STREQUAL "OFF" ) + + SET(OUTPUT_PATH_OPT --output-src-path=${CMAKE_CURRENT_BINARY_DIR}) + SET(IMAGES_PATH_OPT --uhd-images-path=${FPGA_IMAGES_DIR}) + + ADD_CUSTOM_COMMAND( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${lvbitxprefix}_lvbitx.hpp + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${lvbitxprefix}_lvbitx.cpp + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/process-lvbitx.py + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/template_lvbitx.hpp + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/template_lvbitx.cpp + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${lvbitx} + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/process-lvbitx.py ${OUTPUT_PATH_OPT} ${IMAGES_PATH_OPT} ${GEN_OPTIONS} ${CMAKE_CURRENT_SOURCE_DIR}/${lvbitx} + COMMENT "Generating ${CMAKE_CURRENT_BINARY_DIR}/${lvbitxprefix}_lvbitx.cpp" + ) + + #make libuhd depend on the output file + LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_BINARY_DIR}/${lvbitxprefix}_lvbitx.cpp) +ENDMACRO(LIBUHD_LVBITX_GEN_SOURCE_AND_BITSTREAM) + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) + +######################################################################## +# Generation code +######################################################################## + +MESSAGE(STATUS "") +MESSAGE(STATUS "Processing NI-RIO FPGA LVBITX Bitstreams...") + +FILE(TO_NATIVE_PATH ${CMAKE_INSTALL_PREFIX}/share/uhd/images default_images_dir) +SET( FPGA_IMAGES_DIR ${default_images_dir} CACHE STRING "Path to installed FPGA image files." ) +OPTION( FPGA_IMAGES_DIR "Path to installed FPGA image files." "" ) +MESSAGE( STATUS " LVBITX install directory: ${FPGA_IMAGES_DIR}" ) + +# X300 Stuff +LIBUHD_LVBITX_GEN_SOURCE_AND_BITSTREAM(x300.lvbitx_base OFF) + +# X310 Stuff +LIBUHD_LVBITX_GEN_SOURCE_AND_BITSTREAM(x310.lvbitx_base OFF) diff --git a/host/lib/transport/nirio/lvbitx/process-lvbitx.py b/host/lib/transport/nirio/lvbitx/process-lvbitx.py new file mode 100755 index 000000000..a3d88d0bb --- /dev/null +++ b/host/lib/transport/nirio/lvbitx/process-lvbitx.py @@ -0,0 +1,185 @@ +#!/usr/bin/python +# +# Copyright 2013-2014 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/>. +# + +from xml.etree import ElementTree +from collections import namedtuple +import optparse +import base64 +import hashlib +import os +import sys + +# Parse options +parser = optparse.OptionParser() +parser.add_option("--uhd-images-path", type="string", dest="uhd_images_path", help="Install location for UHD images", default='') +parser.add_option("--merge-bin", type="string", dest="merge_bin", help="Path to bin file that needs to be merged with the LVBITX before exporting", default=None) +parser.add_option("--output-bin", action="store_true", dest="output_bin", help="Generate a binary FPGA programming bitstream file", default=False) +parser.add_option("--output-lvbitx-path", type="string", dest="output_lvbitx_path", help="Output path for autogenerated LVBITX file", default=None) +parser.add_option("--output-src-path", type="string", dest="output_src_path", help="Output path for autogenerated src file", default=None) +(options, args) = parser.parse_args() + +# Args +if (len(args) < 1): + print 'ERROR: Please specify the input LVBITX file name' + sys.exit(1) + +lvbitx_filename = args[0] +input_filename = os.path.abspath(lvbitx_filename) +autogen_src_path = os.path.abspath(options.output_src_path) if (options.output_src_path is not None) else os.path.dirname(input_filename) +class_name = os.path.splitext(os.path.basename(input_filename))[0] + +if (not os.path.isfile(input_filename)): + print 'ERROR: FPGA File ' + input_filename + ' could not be accessed or is not a file.' + sys.exit(1) +if (options.merge_bin is not None and not os.path.isfile(os.path.abspath(options.merge_bin))): + print 'ERROR: FPGA Bin File ' + options.merge_bin + ' could not be accessed or is not a file.' + sys.exit(1) +if (not os.path.exists(autogen_src_path)): + print 'ERROR: Output path ' + autogen_src_path + ' could not be accessed.' + sys.exit(1) +if (options.output_lvbitx_path is not None and input_filename == os.path.join(autogen_src_path, class_name + '.lvbitx')): + print 'ERROR: Input and output LVBITX files were the same. Choose a difference input file or output path.' + sys.exit(1) + +# Get XML Tree Node +tree = ElementTree.parse(input_filename) +root = tree.getroot() +codegen_transform = {} + +# General info +codegen_transform['autogen_msg'] = '// Auto-generated file: DO NOT EDIT!\n// Generated from a LabVIEW FPGA LVBITX image using "process-lvbitx.py"' +codegen_transform['lvbitx_search_paths'] = options.uhd_images_path.replace('\\', '\\\\') +codegen_transform['lvbitx_classname'] = class_name +codegen_transform['lvbitx_classname_u'] = class_name.upper() +bitstream_version = root.find('BitstreamVersion').text + +# Enumerate registers (controls and indicators) +register_list = root.find('VI').find('RegisterList') + +reg_init_seq = '' +control_list = '' +indicator_list = '' +control_idx = 0 +indicator_idx = 0 +for register in register_list.findall('Register'): + reg_type = 'INDICATOR' if (register.find('Indicator').text.lower() == 'true') else 'CONTROL' + reg_name = '\"' + register.find('Name').text + '\"' + + if (reg_type == 'INDICATOR'): + indicator_list += '\n ' + reg_name + ',' + idx = indicator_idx + indicator_idx += 1 + else: + control_list += '\n ' + reg_name + ',' + idx = control_idx + control_idx += 1 + + reg_init_seq += '\n vtr.push_back(nirio_register_info_t(' + reg_init_seq += hex(int(register.find('Offset').text)) + ', ' + reg_init_seq += reg_type + 'S[' + str(idx) + '], ' + reg_init_seq += reg_type + reg_init_seq += ')); //' + reg_name + + +codegen_transform['register_init'] = reg_init_seq +codegen_transform['control_list'] = control_list +codegen_transform['indicator_list'] = indicator_list + +# Enumerate FIFOs +nifpga_metadata = root.find('Project').find('CompilationResultsTree').find('CompilationResults').find('NiFpga') +dma_channel_list = nifpga_metadata.find('DmaChannelAllocationList') +reg_block_list = nifpga_metadata.find('RegisterBlockList') + +fifo_init_seq = '' +out_fifo_list = '' +in_fifo_list = '' +out_fifo_idx = 0 +in_fifo_idx = 0 +for dma_channel in dma_channel_list: + fifo_name = '\"' + dma_channel.attrib['name'] + '\"' + direction = 'OUTPUT_FIFO' if (dma_channel.find('Direction').text == 'HostToTarget') else 'INPUT_FIFO' + for reg_block in reg_block_list.findall('RegisterBlock'): + if (reg_block.attrib['name'] == dma_channel.find('BaseAddressTag').text): + base_addr = reg_block.find('Offset').text + break + + if (direction == 'OUTPUT_FIFO'): + out_fifo_list += '\n ' + fifo_name + ',' + idx = out_fifo_idx + out_fifo_idx += 1 + else: + in_fifo_list += '\n ' + fifo_name + ',' + idx = in_fifo_idx + in_fifo_idx += 1 + + fifo_init_seq += '\n vtr.push_back(nirio_fifo_info_t(' + fifo_init_seq += dma_channel.find('Number').text + ', ' + fifo_init_seq += direction + 'S[' + str(idx) + '], ' + fifo_init_seq += direction + ', ' + fifo_init_seq += str.lower(base_addr) + ', ' + fifo_init_seq += dma_channel.find('NumberOfElements').text + ', ' + fifo_init_seq += 'SCALAR_' + dma_channel.find('DataType').find('SubType').text + ', ' + fifo_init_seq += dma_channel.find('DataType').find('WordLength').text + ', ' + fifo_init_seq += bitstream_version + fifo_init_seq += ')); //' + fifo_name + + +codegen_transform['fifo_init'] = fifo_init_seq +codegen_transform['out_fifo_list'] = out_fifo_list +codegen_transform['in_fifo_list'] = in_fifo_list + +# Merge bitstream into LVBITX +if (options.merge_bin is not None): + with open(os.path.abspath(options.merge_bin), 'rb') as bin_file: + bitstream = bin_file.read() + bitstream_md5 = hashlib.md5(bitstream).hexdigest() + bitstream_b64 = base64.b64encode(bitstream) + bitstream_b64_lb = '' + for i in range(0, len(bitstream_b64), 76): + bitstream_b64_lb += bitstream_b64[i:i+76] + '\n' + + root.find('Bitstream').text = bitstream_b64_lb + root.find('BitstreamMD5').text = bitstream_md5 + +codegen_transform['lvbitx_signature'] = str.upper(root.find('SignatureRegister').text) + +# Write BIN file +bitstream = base64.b64decode(root.find('Bitstream').text) +if (options.output_lvbitx_path is not None and hashlib.md5(bitstream).hexdigest() != root.find('BitstreamMD5').text): + print 'ERROR: The MD5 sum for the output LVBITX was incorrect. Make sure that the bitstream in the input LVBITX or BIN file is valid.' + sys.exit(1) +if (options.output_bin): + fpga_bin_file = open(os.path.join(options.output_lvbitx_path, class_name + '.bin'), 'w') + fpga_bin_file.write(bitstream) + fpga_bin_file.close() + +# Save LVBITX +if (options.output_lvbitx_path is not None): + tree.write(os.path.join(options.output_lvbitx_path, class_name + '_fpga.lvbitx'), encoding="utf-8", xml_declaration=True, default_namespace=None, method="xml") + +# Save HPP and CPP +with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'template_lvbitx.hpp'), 'r') as template_file: + template_string = template_file.read() +with open(os.path.join(autogen_src_path, class_name + '_lvbitx.hpp'), 'w') as source_file: + source_file.write(template_string.format(**codegen_transform)) + +with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'template_lvbitx.cpp'), 'r') as template_file: + template_string = template_file.read() +with open(os.path.join(autogen_src_path, class_name + '_lvbitx.cpp'), 'w') as source_file: + source_file.write(template_string.format(**codegen_transform)) + diff --git a/host/lib/transport/nirio/lvbitx/template_lvbitx.cpp b/host/lib/transport/nirio/lvbitx/template_lvbitx.cpp new file mode 100644 index 000000000..a1899c771 --- /dev/null +++ b/host/lib/transport/nirio/lvbitx/template_lvbitx.cpp @@ -0,0 +1,86 @@ +{autogen_msg} + +#include "{lvbitx_classname}_lvbitx.hpp" +#include <string> +#include <iostream> +#include <fstream> +#include <streambuf> +#include <boost/filesystem/path.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/regex.hpp> + +namespace uhd {{ namespace niusrprio {{ + +#define SEARCH_PATHS "{lvbitx_search_paths}" + +const char* {lvbitx_classname}_lvbitx::CONTROLS[] = {{{control_list} +}}; + +const char* {lvbitx_classname}_lvbitx::INDICATORS[] = {{{indicator_list} +}}; + +const char* {lvbitx_classname}_lvbitx::OUTPUT_FIFOS[] = {{{out_fifo_list} +}}; + +const char* {lvbitx_classname}_lvbitx::INPUT_FIFOS[] = {{{in_fifo_list} +}}; + +{lvbitx_classname}_lvbitx::{lvbitx_classname}_lvbitx(const std::string& option) +{{ + boost::filesystem::path fpga_path(_get_fpga_images_dir(SEARCH_PATHS)); + fpga_path /= "usrp_{lvbitx_classname}_fpga_" + option + ".lvbitx"; + _fpga_file_name = fpga_path.string(); + _bitstream_checksum = _get_bitstream_checksum(_fpga_file_name); +}} + +const char* {lvbitx_classname}_lvbitx::get_bitfile_path() {{ + return _fpga_file_name.c_str(); +}} + +const char* {lvbitx_classname}_lvbitx::get_signature() {{ + return "{lvbitx_signature}"; +}} + +const char* {lvbitx_classname}_lvbitx::get_bitstream_checksum() {{ + return _bitstream_checksum.c_str(); +}} + +size_t {lvbitx_classname}_lvbitx::get_input_fifo_count() {{ + return sizeof(INPUT_FIFOS)/sizeof(*INPUT_FIFOS); +}} + +const char** {lvbitx_classname}_lvbitx::get_input_fifo_names() {{ + return INPUT_FIFOS; +}} + +size_t {lvbitx_classname}_lvbitx::get_output_fifo_count() {{ + return sizeof(OUTPUT_FIFOS)/sizeof(*OUTPUT_FIFOS); +}} + +const char** {lvbitx_classname}_lvbitx::get_output_fifo_names() {{ + return OUTPUT_FIFOS; +}} + +size_t {lvbitx_classname}_lvbitx::get_control_count() {{ + return sizeof(CONTROLS)/sizeof(*CONTROLS); +}} + +const char** {lvbitx_classname}_lvbitx::get_control_names() {{ + return CONTROLS; +}} + +size_t {lvbitx_classname}_lvbitx::get_indicator_count() {{ + return sizeof(INDICATORS)/sizeof(*INDICATORS); +}} + +const char** {lvbitx_classname}_lvbitx::get_indicator_names() {{ + return INDICATORS; +}} + +void {lvbitx_classname}_lvbitx::init_register_info(nirio_register_info_vtr& vtr) {{ {register_init} +}} + +void {lvbitx_classname}_lvbitx::init_fifo_info(nirio_fifo_info_vtr& vtr) {{ {fifo_init} +}} + +}}}} diff --git a/host/lib/transport/nirio/lvbitx/template_lvbitx.hpp b/host/lib/transport/nirio/lvbitx/template_lvbitx.hpp new file mode 100644 index 000000000..d8872e15c --- /dev/null +++ b/host/lib/transport/nirio/lvbitx/template_lvbitx.hpp @@ -0,0 +1,48 @@ +{autogen_msg} + +#ifndef INCLUDED_{lvbitx_classname_u}_LVBITX_HPP +#define INCLUDED_{lvbitx_classname_u}_LVBITX_HPP + +#include <uhd/transport/nirio/nifpga_lvbitx.h> + +namespace uhd {{ namespace niusrprio {{ + +class {lvbitx_classname}_lvbitx : public nifpga_lvbitx {{ +public: + {lvbitx_classname}_lvbitx(const std::string& option); + + virtual ~{lvbitx_classname}_lvbitx() {{}}; + + virtual const char* get_bitfile_path(); + virtual const char* get_signature(); + virtual const char* get_bitstream_checksum(); + + virtual size_t get_input_fifo_count(); + virtual const char** get_input_fifo_names(); + + virtual size_t get_output_fifo_count(); + virtual const char** get_output_fifo_names(); + + virtual size_t get_control_count(); + virtual const char** get_control_names(); + + virtual size_t get_indicator_count(); + virtual const char** get_indicator_names(); + + virtual void init_register_info(nirio_register_info_vtr& vtr); + virtual void init_fifo_info(nirio_fifo_info_vtr& vtr); + + static const char* CONTROLS[]; + static const char* INDICATORS[]; + static const char* OUTPUT_FIFOS[]; + static const char* INPUT_FIFOS[]; + +private: + std::string _fpga_file_name; + std::string _bitstream_checksum; +}}; + +}}}} + +#endif /* INCLUDED_{lvbitx_classname_u}_LVBITX_HPP */ + diff --git a/host/lib/transport/nirio/lvbitx/x300.lvbitx_base b/host/lib/transport/nirio/lvbitx/x300.lvbitx_base new file mode 100755 index 000000000..c264e7157 --- /dev/null +++ b/host/lib/transport/nirio/lvbitx/x300.lvbitx_base @@ -0,0 +1,469 @@ +<?xml version="1.0" encoding="utf-8"?> +<Bitfile> + <BitfileVersion>4.0</BitfileVersion> + <Documentation> + <BuildSpecVersion/> + <BuildSpecDescription/> + </Documentation> + <SignatureRegister>97C6D9F4F4829001B83378F93CAB0C94</SignatureRegister> + <SignatureGuids>7BAD6AEB9741248079F13147B3F8AD94</SignatureGuids> + <SignatureNames>AE54C47F787D92DB46F7DC973338D786</SignatureNames> + <TimeStamp/> + <CompilationStatus/> + <BitstreamVersion>2</BitstreamVersion> + <VI> + <Name>USRP_X3x0_Top.vi</Name> + <RegisterList> + <Register> + <Name>ViSignature</Name> + <Hidden>true</Hidden> + <Indicator>true</Indicator> + <Datatype> + <Array> + <Name/> + <Size>4</Size> + <Type> + <U32> + <Name/> + </U32> + </Type> + </Array> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262132</Offset> + <SizeInBits>128</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>false</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + <Register> + <Name>DiagramReset</Name> + <Hidden>false</Hidden> + <Indicator>false</Indicator> + <Datatype> + <U32> + <Name/> + </U32> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262140</Offset> + <SizeInBits>32</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>true</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + <Register> + <Name>ViControl</Name> + <Hidden>false</Hidden> + <Indicator>false</Indicator> + <Datatype> + <U32> + <Name/> + </U32> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262136</Offset> + <SizeInBits>32</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>true</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + <Register> + <Name>InterruptEnable</Name> + <Hidden>false</Hidden> + <Indicator>false</Indicator> + <Datatype> + <U32> + <Name/> + </U32> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262116</Offset> + <SizeInBits>32</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>true</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + <Register> + <Name>InterruptMask</Name> + <Hidden>false</Hidden> + <Indicator>false</Indicator> + <Datatype> + <U32> + <Name/> + </U32> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262124</Offset> + <SizeInBits>32</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>true</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + <Register> + <Name>InterruptStatus</Name> + <Hidden>false</Hidden> + <Indicator>false</Indicator> + <Datatype> + <U32> + <Name/> + </U32> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262128</Offset> + <SizeInBits>32</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>true</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + </RegisterList> + <Icon> + <ImageType>0</ImageType> + <ImageDepth>8</ImageDepth> + <Image>////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAA/////////////////////////wAAAAAAAAAAAP//AAD/+fn5+fn5+fn5+fn59/ks+SzgAAAAAAAAAAAA//8AAP/5///////////////3+Sz5LP8AAAAAAAAAAAD//wAA//n/6OTo////6OTo//f8K/ws/wAAAAAAAAAAAP//AAD/+f/k/+T////k/+T/9ywsLCzgAAAAAAAAAAAA//8AAP/56OT/5Oj/6OT/5Oj3K/wrLP8AAAAAAAAAAAD//wAA//nk6P/o5P/k6P/o5Pf8CPws/wAAAAAAAAAAAP//AAD/+eT////k/+T////k9/wI/Cz/AAAAAAAAAAAA//8AAP/5/////+jk6P/////3K/wrLP8AAAAAAAAAAAD//wAA//n///////////////csLCws/wAAAAAAAAAAAP//AAD/9/f39/f39/f39/f39ywsg4P/AAAAAAAAAAAA//8AAP8sLCwsLCwsLCwsLCwsLCyDBYODAAAAAAAAAAD//wAA/yz8LCwsLCz8LCwsLCMjI4MFBQWDgwAAAAAAAP//AAD//PD8LCws/CP8LCMjLCwsgwUF/wUFg4MAAAAA//8AAP8s7ywsLCwjLCwjLCwsLCyDBf///wUFBYMjIwD//wAA///w////I///I////////4MFBf8FBYODAAAAAP//AAAAAO8AAAAjAAAjAADw7+/wgwUFBYODAAAAAAAA//8AAAAAAPAAAAAjIwAA7wAAAACDBYODAAAAAAAAAAD//wAAAAAAAO/vAAAAAPAAAAAAAIODAAAAAAAAAAAAAP//AAAAAAAAAADw7/DvAAAAAAAAAAAAAAAAAP8AAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////////////////////////////////////w==</Image> + <Mask>//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8=</Mask> + <Colors>AP///wD//8wA//+ZAP//ZgD//zMA//8AAP/M/wD/zMwA/8yZAP/MZgD/zDMA/8wAAP+Z/wD/mcwA/5mZAP+ZZgD/mTMA/5kAAP9m/wD/ZswA/2aZAP9mZgD/ZjMA/2YAAP8z/wD/M8wA/zOZAP8zZgD/MzMA/zMAAP8A/wD/AMwA/wCZAP8AZgD/ADMA/wAAAMz//wDM/8wAzP+ZAMz/ZgDM/zMAzP8AAMzM/wDMzMwAzMyZAMzMZgDMzDMAzMwAAMyZ/wDMmcwAzJmZAMyZZgDMmTMAzJkAAMxm/wDMZswAzGaZAMxmZgDMZjMAzGYAAMwz/wDMM8wAzDOZAMwzZgDMMzMAzDMAAMwA/wDMAMwAzACZAMwAZgDMADMAzAAAAJn//wCZ/8wAmf+ZAJn/ZgCZ/zMAmf8AAJnM/wCZzMwAmcyZAJnMZgCZzDMAmcwAAJmZ/wCZmcwAmZmZAJmZZgCZmTMAmZkAAJlm/wCZZswAmWaZAJlmZgCZZjMAmWYAAJkz/wCZM8wAmTOZAJkzZgCZMzMAmTMAAJkA/wCZAMwAmQCZAJkAZgCZADMAmQAAAGb//wBm/8wAZv+ZAGb/ZgBm/zMAZv8AAGbM/wBmzMwAZsyZAGbMZgBmzDMAZswAAGaZ/wBmmcwAZpmZAGaZZgBmmTMAZpkAAGZm/wBmZswAZmaZAGZmZgBmZjMAZmYAAGYz/wBmM8wAZjOZAGYzZgBmMzMAZjMAAGYA/wBmAMwAZgCZAGYAZgBmADMAZgAAADP//wAz/8wAM/+ZADP/ZgAz/zMAM/8AADPM/wAzzMwAM8yZADPMZgAzzDMAM8wAADOZ/wAzmcwAM5mZADOZZgAzmTMAM5kAADNm/wAzZswAM2aZADNmZgAzZjMAM2YAADMz/wAzM8wAMzOZADMzZgAzMzMAMzMAADMA/wAzAMwAMwCZADMAZgAzADMAMwAAAAD//wAA/8wAAP+ZAAD/ZgAA/zMAAP8AAADM/wAAzMwAAMyZAADMZgAAzDMAAMwAAACZ/wAAmcwAAJmZAACZZgAAmTMAAJkAAABm/wAAZswAAGaZAABmZgAAZjMAAGYAAAAz/wAAM8wAADOZAAAzZgAAMzMAADMAAAAA/wAAAMwAAACZAAAAZgAAADMA7gAAAN0AAAC7AAAAqgAAAIgAAAB3AAAAVQAAAEQAAAAiAAAAEQAAAADuAAAA3QAAALsAAACqAAAAiAAAAHcAAABVAAAARAAAACIAAAARAAAAAO4AAADdAAAAuwAAAKoAAACIAAAAdwAAAFUAAABEAAAAIgAAABEA7u7uAN3d3QC7u7sAqqqqAIiIiAB3d3cAVVVVAERERAAiIiIAERERAAAAAA==</Colors> + <Rectangle> + <Left>0</Left> + <Top>0</Top> + <Right>32</Right> + <Bottom>32</Bottom> + </Rectangle> + </Icon> + </VI> + <Project> + <TargetClass>294XR; 295XR</TargetClass> + <AutoRunWhenDownloaded>false</AutoRunWhenDownloaded> + <CompilationResultsTree> + <CompilationResults> + <NiFpga> + <BaseAddressOnDevice>0</BaseAddressOnDevice> + <DmaChannelAllocationList> + <Channel name="RX FIFO 0"> + <BaseAddressTag>NiLvFpgaFIFO 0</BaseAddressTag> + <ControlSet>0</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>0</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="RX FIFO 1"> + <BaseAddressTag>NiLvFpgaFIFO 1</BaseAddressTag> + <ControlSet>1</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>1</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="RX FIFO 2"> + <BaseAddressTag>NiLvFpgaFIFO 2</BaseAddressTag> + <ControlSet>2</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>2</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="RX FIFO 3"> + <BaseAddressTag>NiLvFpgaFIFO 3</BaseAddressTag> + <ControlSet>3</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>3</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="RX FIFO 4"> + <BaseAddressTag>NiLvFpgaFIFO 4</BaseAddressTag> + <ControlSet>4</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>4</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="RX FIFO 5"> + <BaseAddressTag>NiLvFpgaFIFO 5</BaseAddressTag> + <ControlSet>5</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>5</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 0"> + <BaseAddressTag>NiLvFpgaFIFO 6</BaseAddressTag> + <ControlSet>6</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>6</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 1"> + <BaseAddressTag>NiLvFpgaFIFO 7</BaseAddressTag> + <ControlSet>7</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>7</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 2"> + <BaseAddressTag>NiLvFpgaFIFO 8</BaseAddressTag> + <ControlSet>8</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>8</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 3"> + <BaseAddressTag>NiLvFpgaFIFO 9</BaseAddressTag> + <ControlSet>9</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>9</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 4"> + <BaseAddressTag>NiLvFpgaFIFO 10</BaseAddressTag> + <ControlSet>10</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>10</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 5"> + <BaseAddressTag>NiLvFpgaFIFO 11</BaseAddressTag> + <ControlSet>11</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>11</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + </DmaChannelAllocationList> + <RegisterBlockList> + <RegisterBlock name="NiLvFpgaFIFO 0"> + <Offset>0xFF80</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 1"> + <Offset>0xFF40</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 2"> + <Offset>0xFF00</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 3"> + <Offset>0xFEC0</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 4"> + <Offset>0xFE80</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 5"> + <Offset>0xFE40</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 6"> + <Offset>0xFE00</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 7"> + <Offset>0xFDC0</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 8"> + <Offset>0xFD80</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 9"> + <Offset>0xFD40</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 10"> + <Offset>0xFD00</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 11"> + <Offset>0xFCC0</Offset> + </RegisterBlock> + </RegisterBlockList> + <UsedBaseClockList> + <BaseClock name="ReliableClkIn"> + </BaseClock> + <BaseClock name="ChinchClk"> + </BaseClock> + <BaseClock name="40 MHz Onboard Clock"> + </BaseClock> + </UsedBaseClockList> + <version>1</version> + </NiFpga> + </CompilationResults> + </CompilationResultsTree> + <MultipleUserClocks>false</MultipleUserClocks> + <AllowImplicitEnableRemoval>false</AllowImplicitEnableRemoval> + </Project> + <ClientData/> + <BitstreamMD5>a72ba1716893a0bd02f88dac3ab28e1b</BitstreamMD5> + <Bitstream>a72ba1716893a0bd02f88dac3ab28e1b</Bitstream> +</Bitfile> diff --git a/host/lib/transport/nirio/lvbitx/x310.lvbitx_base b/host/lib/transport/nirio/lvbitx/x310.lvbitx_base new file mode 100755 index 000000000..c264e7157 --- /dev/null +++ b/host/lib/transport/nirio/lvbitx/x310.lvbitx_base @@ -0,0 +1,469 @@ +<?xml version="1.0" encoding="utf-8"?> +<Bitfile> + <BitfileVersion>4.0</BitfileVersion> + <Documentation> + <BuildSpecVersion/> + <BuildSpecDescription/> + </Documentation> + <SignatureRegister>97C6D9F4F4829001B83378F93CAB0C94</SignatureRegister> + <SignatureGuids>7BAD6AEB9741248079F13147B3F8AD94</SignatureGuids> + <SignatureNames>AE54C47F787D92DB46F7DC973338D786</SignatureNames> + <TimeStamp/> + <CompilationStatus/> + <BitstreamVersion>2</BitstreamVersion> + <VI> + <Name>USRP_X3x0_Top.vi</Name> + <RegisterList> + <Register> + <Name>ViSignature</Name> + <Hidden>true</Hidden> + <Indicator>true</Indicator> + <Datatype> + <Array> + <Name/> + <Size>4</Size> + <Type> + <U32> + <Name/> + </U32> + </Type> + </Array> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262132</Offset> + <SizeInBits>128</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>false</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + <Register> + <Name>DiagramReset</Name> + <Hidden>false</Hidden> + <Indicator>false</Indicator> + <Datatype> + <U32> + <Name/> + </U32> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262140</Offset> + <SizeInBits>32</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>true</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + <Register> + <Name>ViControl</Name> + <Hidden>false</Hidden> + <Indicator>false</Indicator> + <Datatype> + <U32> + <Name/> + </U32> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262136</Offset> + <SizeInBits>32</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>true</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + <Register> + <Name>InterruptEnable</Name> + <Hidden>false</Hidden> + <Indicator>false</Indicator> + <Datatype> + <U32> + <Name/> + </U32> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262116</Offset> + <SizeInBits>32</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>true</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + <Register> + <Name>InterruptMask</Name> + <Hidden>false</Hidden> + <Indicator>false</Indicator> + <Datatype> + <U32> + <Name/> + </U32> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262124</Offset> + <SizeInBits>32</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>true</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + <Register> + <Name>InterruptStatus</Name> + <Hidden>false</Hidden> + <Indicator>false</Indicator> + <Datatype> + <U32> + <Name/> + </U32> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262128</Offset> + <SizeInBits>32</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>true</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + </RegisterList> + <Icon> + <ImageType>0</ImageType> + <ImageDepth>8</ImageDepth> + <Image>////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAA/////////////////////////wAAAAAAAAAAAP//AAD/+fn5+fn5+fn5+fn59/ks+SzgAAAAAAAAAAAA//8AAP/5///////////////3+Sz5LP8AAAAAAAAAAAD//wAA//n/6OTo////6OTo//f8K/ws/wAAAAAAAAAAAP//AAD/+f/k/+T////k/+T/9ywsLCzgAAAAAAAAAAAA//8AAP/56OT/5Oj/6OT/5Oj3K/wrLP8AAAAAAAAAAAD//wAA//nk6P/o5P/k6P/o5Pf8CPws/wAAAAAAAAAAAP//AAD/+eT////k/+T////k9/wI/Cz/AAAAAAAAAAAA//8AAP/5/////+jk6P/////3K/wrLP8AAAAAAAAAAAD//wAA//n///////////////csLCws/wAAAAAAAAAAAP//AAD/9/f39/f39/f39/f39ywsg4P/AAAAAAAAAAAA//8AAP8sLCwsLCwsLCwsLCwsLCyDBYODAAAAAAAAAAD//wAA/yz8LCwsLCz8LCwsLCMjI4MFBQWDgwAAAAAAAP//AAD//PD8LCws/CP8LCMjLCwsgwUF/wUFg4MAAAAA//8AAP8s7ywsLCwjLCwjLCwsLCyDBf///wUFBYMjIwD//wAA///w////I///I////////4MFBf8FBYODAAAAAP//AAAAAO8AAAAjAAAjAADw7+/wgwUFBYODAAAAAAAA//8AAAAAAPAAAAAjIwAA7wAAAACDBYODAAAAAAAAAAD//wAAAAAAAO/vAAAAAPAAAAAAAIODAAAAAAAAAAAAAP//AAAAAAAAAADw7/DvAAAAAAAAAAAAAAAAAP8AAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////////////////////////////////////w==</Image> + <Mask>//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8=</Mask> + <Colors>AP///wD//8wA//+ZAP//ZgD//zMA//8AAP/M/wD/zMwA/8yZAP/MZgD/zDMA/8wAAP+Z/wD/mcwA/5mZAP+ZZgD/mTMA/5kAAP9m/wD/ZswA/2aZAP9mZgD/ZjMA/2YAAP8z/wD/M8wA/zOZAP8zZgD/MzMA/zMAAP8A/wD/AMwA/wCZAP8AZgD/ADMA/wAAAMz//wDM/8wAzP+ZAMz/ZgDM/zMAzP8AAMzM/wDMzMwAzMyZAMzMZgDMzDMAzMwAAMyZ/wDMmcwAzJmZAMyZZgDMmTMAzJkAAMxm/wDMZswAzGaZAMxmZgDMZjMAzGYAAMwz/wDMM8wAzDOZAMwzZgDMMzMAzDMAAMwA/wDMAMwAzACZAMwAZgDMADMAzAAAAJn//wCZ/8wAmf+ZAJn/ZgCZ/zMAmf8AAJnM/wCZzMwAmcyZAJnMZgCZzDMAmcwAAJmZ/wCZmcwAmZmZAJmZZgCZmTMAmZkAAJlm/wCZZswAmWaZAJlmZgCZZjMAmWYAAJkz/wCZM8wAmTOZAJkzZgCZMzMAmTMAAJkA/wCZAMwAmQCZAJkAZgCZADMAmQAAAGb//wBm/8wAZv+ZAGb/ZgBm/zMAZv8AAGbM/wBmzMwAZsyZAGbMZgBmzDMAZswAAGaZ/wBmmcwAZpmZAGaZZgBmmTMAZpkAAGZm/wBmZswAZmaZAGZmZgBmZjMAZmYAAGYz/wBmM8wAZjOZAGYzZgBmMzMAZjMAAGYA/wBmAMwAZgCZAGYAZgBmADMAZgAAADP//wAz/8wAM/+ZADP/ZgAz/zMAM/8AADPM/wAzzMwAM8yZADPMZgAzzDMAM8wAADOZ/wAzmcwAM5mZADOZZgAzmTMAM5kAADNm/wAzZswAM2aZADNmZgAzZjMAM2YAADMz/wAzM8wAMzOZADMzZgAzMzMAMzMAADMA/wAzAMwAMwCZADMAZgAzADMAMwAAAAD//wAA/8wAAP+ZAAD/ZgAA/zMAAP8AAADM/wAAzMwAAMyZAADMZgAAzDMAAMwAAACZ/wAAmcwAAJmZAACZZgAAmTMAAJkAAABm/wAAZswAAGaZAABmZgAAZjMAAGYAAAAz/wAAM8wAADOZAAAzZgAAMzMAADMAAAAA/wAAAMwAAACZAAAAZgAAADMA7gAAAN0AAAC7AAAAqgAAAIgAAAB3AAAAVQAAAEQAAAAiAAAAEQAAAADuAAAA3QAAALsAAACqAAAAiAAAAHcAAABVAAAARAAAACIAAAARAAAAAO4AAADdAAAAuwAAAKoAAACIAAAAdwAAAFUAAABEAAAAIgAAABEA7u7uAN3d3QC7u7sAqqqqAIiIiAB3d3cAVVVVAERERAAiIiIAERERAAAAAA==</Colors> + <Rectangle> + <Left>0</Left> + <Top>0</Top> + <Right>32</Right> + <Bottom>32</Bottom> + </Rectangle> + </Icon> + </VI> + <Project> + <TargetClass>294XR; 295XR</TargetClass> + <AutoRunWhenDownloaded>false</AutoRunWhenDownloaded> + <CompilationResultsTree> + <CompilationResults> + <NiFpga> + <BaseAddressOnDevice>0</BaseAddressOnDevice> + <DmaChannelAllocationList> + <Channel name="RX FIFO 0"> + <BaseAddressTag>NiLvFpgaFIFO 0</BaseAddressTag> + <ControlSet>0</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>0</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="RX FIFO 1"> + <BaseAddressTag>NiLvFpgaFIFO 1</BaseAddressTag> + <ControlSet>1</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>1</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="RX FIFO 2"> + <BaseAddressTag>NiLvFpgaFIFO 2</BaseAddressTag> + <ControlSet>2</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>2</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="RX FIFO 3"> + <BaseAddressTag>NiLvFpgaFIFO 3</BaseAddressTag> + <ControlSet>3</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>3</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="RX FIFO 4"> + <BaseAddressTag>NiLvFpgaFIFO 4</BaseAddressTag> + <ControlSet>4</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>4</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="RX FIFO 5"> + <BaseAddressTag>NiLvFpgaFIFO 5</BaseAddressTag> + <ControlSet>5</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>5</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 0"> + <BaseAddressTag>NiLvFpgaFIFO 6</BaseAddressTag> + <ControlSet>6</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>6</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 1"> + <BaseAddressTag>NiLvFpgaFIFO 7</BaseAddressTag> + <ControlSet>7</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>7</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 2"> + <BaseAddressTag>NiLvFpgaFIFO 8</BaseAddressTag> + <ControlSet>8</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>8</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 3"> + <BaseAddressTag>NiLvFpgaFIFO 9</BaseAddressTag> + <ControlSet>9</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>9</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 4"> + <BaseAddressTag>NiLvFpgaFIFO 10</BaseAddressTag> + <ControlSet>10</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>10</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 5"> + <BaseAddressTag>NiLvFpgaFIFO 11</BaseAddressTag> + <ControlSet>11</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>11</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + </DmaChannelAllocationList> + <RegisterBlockList> + <RegisterBlock name="NiLvFpgaFIFO 0"> + <Offset>0xFF80</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 1"> + <Offset>0xFF40</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 2"> + <Offset>0xFF00</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 3"> + <Offset>0xFEC0</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 4"> + <Offset>0xFE80</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 5"> + <Offset>0xFE40</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 6"> + <Offset>0xFE00</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 7"> + <Offset>0xFDC0</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 8"> + <Offset>0xFD80</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 9"> + <Offset>0xFD40</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 10"> + <Offset>0xFD00</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 11"> + <Offset>0xFCC0</Offset> + </RegisterBlock> + </RegisterBlockList> + <UsedBaseClockList> + <BaseClock name="ReliableClkIn"> + </BaseClock> + <BaseClock name="ChinchClk"> + </BaseClock> + <BaseClock name="40 MHz Onboard Clock"> + </BaseClock> + </UsedBaseClockList> + <version>1</version> + </NiFpga> + </CompilationResults> + </CompilationResultsTree> + <MultipleUserClocks>false</MultipleUserClocks> + <AllowImplicitEnableRemoval>false</AllowImplicitEnableRemoval> + </Project> + <ClientData/> + <BitstreamMD5>a72ba1716893a0bd02f88dac3ab28e1b</BitstreamMD5> + <Bitstream>a72ba1716893a0bd02f88dac3ab28e1b</Bitstream> +</Bitfile> diff --git a/host/lib/transport/nirio/nifpga_lvbitx.cpp b/host/lib/transport/nirio/nifpga_lvbitx.cpp new file mode 100644 index 000000000..289a44d4a --- /dev/null +++ b/host/lib/transport/nirio/nifpga_lvbitx.cpp @@ -0,0 +1,138 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/nirio/nifpga_lvbitx.h> +#include <string> +#include <iostream> +#include <fstream> +#include <streambuf> +#include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <boost/filesystem.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/regex.hpp> + +namespace uhd { namespace niusrprio { + +std::string nifpga_lvbitx::_get_bitstream_checksum(const std::string& file_path) +{ + std::string checksum; + std::ifstream lvbitx_stream(file_path.c_str()); + if (lvbitx_stream.is_open()) { + std::string lvbitx_contents; + lvbitx_stream.seekg(0, std::ios::end); + lvbitx_contents.reserve(static_cast<size_t>(lvbitx_stream.tellg())); + lvbitx_stream.seekg(0, std::ios::beg); + lvbitx_contents.assign((std::istreambuf_iterator<char>(lvbitx_stream)), std::istreambuf_iterator<char>()); + try { + boost::smatch md5_match; + if (boost::regex_search(lvbitx_contents, md5_match, boost::regex("<BitstreamMD5>([a-zA-Z0-9]{32})<\\/BitstreamMD5>", boost::regex::icase))) { + checksum = std::string(md5_match[1].first, md5_match[1].second); + } + } catch (boost::exception&) { + checksum = ""; + } + } + boost::to_upper(checksum); + return checksum; +} + +#ifdef UHD_PLATFORM_WIN32 +#include <windows.h> + +std::string _get_path_from_registry(const std::string& registry_key_path) +{ + boost::smatch reg_key_match; + //If a substring in the search path is enclosed in [] (square brackets) then it is interpreted as a registry path + if (not boost::regex_search(registry_key_path, reg_key_match, boost::regex("\\[(.+)\\](.*)", boost::regex::icase))) + return std::string(); + std::string reg_key_path = std::string(reg_key_match[1].first, reg_key_match[1].second); + std::string path_suffix = std::string(reg_key_match[2].first, reg_key_match[2].second); + + //Split the registry path into parent, key-path and value. + boost::smatch reg_parent_match; + if (not boost::regex_search(reg_key_path, reg_parent_match, boost::regex("^(.+?)\\\\(.+)\\\\(.+)$", boost::regex::icase))) + return std::string(); + std::string reg_parent = std::string(reg_parent_match[1].first, reg_parent_match[1].second); + std::string reg_path = std::string(reg_parent_match[2].first, reg_parent_match[2].second); + std::string reg_val_name = std::string(reg_parent_match[3].first, reg_parent_match[3].second); + + HKEY hkey_parent = HKEY_LOCAL_MACHINE; + if (reg_parent == "HKEY_LOCAL_MACHINE") hkey_parent = HKEY_LOCAL_MACHINE; + else if (reg_parent == "HKEY_CURRENT_USER") hkey_parent = HKEY_CURRENT_USER; + else if (reg_parent == "HKEY_CLASSES_ROOT") hkey_parent = HKEY_CLASSES_ROOT; + else if (reg_parent == "HKEY_CURRENT_CONFIG") hkey_parent = HKEY_CURRENT_CONFIG; + else if (reg_parent == "HKEY_USERS") hkey_parent = HKEY_CURRENT_USER; + + TCHAR value_buff[1024]; + DWORD value_buff_size = 1024*sizeof(TCHAR); + + //Get a handle to the key location + HKEY hkey_location; + if (RegOpenKeyExA(hkey_parent, reg_path.c_str(), NULL, KEY_QUERY_VALUE, &hkey_location) != ERROR_SUCCESS) + return std::string(); + + //Query key value + DWORD dw_type = REG_SZ; + if(RegQueryValueExA(hkey_location, reg_val_name.c_str(), NULL, &dw_type, (LPBYTE)value_buff, &value_buff_size) == ERROR_SUCCESS) { + RegCloseKey(hkey_location); + if (value_buff_size >= 1024*sizeof(TCHAR)) { + return std::string(); + } else { + std::string return_value(value_buff, value_buff_size-1); //value_buff_size includes the null terminator + return_value += path_suffix; + return return_value; + } + } else { + return std::string(); + } +} + +#endif /*UHD_PLATFORM_WIN32*/ + +std::string nifpga_lvbitx::_get_fpga_images_dir(const std::string search_paths) +{ + std::vector<std::string> search_path_vtr; + boost::split(search_path_vtr, search_paths, boost::is_any_of(",")); + + std::string lvbitx_dir; + //Traverse through the list of search paths. Priority: lexical + BOOST_FOREACH(std::string& search_path, search_path_vtr) { + boost::algorithm::trim(search_path); + if (search_path.empty()) continue; + +#ifdef UHD_PLATFORM_WIN32 + lvbitx_dir = _get_path_from_registry(search_path); + if (lvbitx_dir.empty()) { + //Could not read from the registry due to missing key, invalid values, etc + //Just use the search path. The is_directory check will fail if this is a + //registry path and we will move on to the next item in the list. + lvbitx_dir = search_path; + } +#else + lvbitx_dir = search_path; +#endif + + //If the current directory exists then stop traversing the search path list. + if (boost::filesystem::is_directory(lvbitx_dir)) break; + } + + return lvbitx_dir; +} + + +}} diff --git a/host/lib/transport/nirio/nirio_driver_iface_linux.cpp b/host/lib/transport/nirio/nirio_driver_iface_linux.cpp new file mode 100644 index 000000000..b1f4bb49f --- /dev/null +++ b/host/lib/transport/nirio/nirio_driver_iface_linux.cpp @@ -0,0 +1,111 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/nirio/nirio_driver_iface.h> +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/mman.h> + +namespace nirio_driver_iface { + +nirio_status rio_open( + const std::string& device_path, + rio_dev_handle_t& device_handle) +{ + device_handle = ::open(device_path.c_str(), O_RDWR | O_CLOEXEC); + return (device_handle < 0) ? NiRio_Status_InvalidParameter : NiRio_Status_Success; +} + +void rio_close(rio_dev_handle_t& device_handle) +{ + ::close(device_handle); + device_handle = -1; +} + +bool rio_isopen(rio_dev_handle_t device_handle) +{ + return (device_handle >= 0); +} + +nirio_status rio_ioctl( + rio_dev_handle_t device_handle, + uint32_t ioctl_code, + const void *write_buf, + size_t write_buf_len, + void *read_buf, + size_t read_buf_len) +{ + nirio_ioctl_block_t ioctl_block = {0,0,0,0,0,0}; + + // two-casts necessary to prevent pointer sign-extension + ioctl_block.inBuf = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(write_buf)); + ioctl_block.inBufLength = write_buf_len; + ioctl_block.outBuf = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(read_buf)); + ioctl_block.outBufLength = read_buf_len; + + int status = ::ioctl(device_handle, ioctl_code, &ioctl_block); + if (status == -1) { + switch (errno) { + case EINVAL: return NiRio_Status_InvalidParameter; + case EFAULT: return NiRio_Status_MemoryFull; + default: return NiRio_Status_SoftwareFault; + } + } else { + return NiRio_Status_Success; + } +} + +nirio_status rio_mmap( + rio_dev_handle_t device_handle, + uint16_t memory_type, + size_t size, + bool writable, + rio_mmap_t &map) +{ + int access_mode = PROT_READ; //Write-only mode not supported + if (writable) access_mode |= PROT_WRITE; + map.addr = ::mmap(NULL, size, access_mode, MAP_SHARED, device_handle, (off_t) memory_type * sysconf(_SC_PAGESIZE)); + map.size = size; + + if (map.addr == MAP_FAILED) { + map.addr = NULL; + map.size = 0; + switch (errno) { + case EINVAL: return NiRio_Status_InvalidParameter; + case EFAULT: return NiRio_Status_MemoryFull; + default: return NiRio_Status_SoftwareFault; + } + } + return NiRio_Status_Success; +} + +nirio_status rio_munmap(rio_mmap_t &map) +{ + nirio_status status = 0; + if (map.addr != NULL) { + status = ::munmap(map.addr, map.size); + + map.addr = NULL; + map.size = 0; + } + return status; +} + +} diff --git a/host/lib/transport/nirio/nirio_driver_iface_macos.cpp b/host/lib/transport/nirio/nirio_driver_iface_macos.cpp new file mode 100644 index 000000000..1a1142525 --- /dev/null +++ b/host/lib/transport/nirio/nirio_driver_iface_macos.cpp @@ -0,0 +1,63 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +#include <uhd/transport/nirio/nirio_driver_iface.h> + +namespace nirio_driver_iface { + +nirio_status rio_open( + const std::string& device_path, + rio_dev_handle_t& device_handle) +{ + return NiRio_Status_FeatureNotSupported; +} + +void rio_close(rio_dev_handle_t& device_handle) +{ +} + +bool rio_isopen(rio_dev_handle_t device_handle) +{ + return false; +} + +nirio_status rio_ioctl( + rio_dev_handle_t device_handle, + uint32_t ioctl_code, + const void *write_buf, + size_t write_buf_len, + void *read_buf, + size_t read_buf_len) +{ + return NiRio_Status_FeatureNotSupported; +} + +nirio_status rio_mmap( + rio_dev_handle_t device_handle, + uint16_t memory_type, + size_t size, + bool writable, + rio_mmap_t &map) +{ + return NiRio_Status_FeatureNotSupported; +} + +nirio_status rio_munmap(rio_mmap_t &map) +{ + return NiRio_Status_FeatureNotSupported; +} + +} diff --git a/host/lib/transport/nirio/nirio_driver_iface_win.cpp b/host/lib/transport/nirio/nirio_driver_iface_win.cpp new file mode 100644 index 000000000..b47c6ce85 --- /dev/null +++ b/host/lib/transport/nirio/nirio_driver_iface_win.cpp @@ -0,0 +1,148 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/nirio/nirio_driver_iface.h> +#include <process.h> + +#define NIRIO_IOCTL_MAP_MEMORY CTL_CODE(FILE_DEVICE_UNKNOWN, 0xF00, METHOD_BUFFERED, (FILE_READ_ACCESS | FILE_WRITE_ACCESS)) +#define NIRIO_IOCTL_UNMAP_MEMORY CTL_CODE(FILE_DEVICE_UNKNOWN, 0xF01, METHOD_BUFFERED, (FILE_READ_ACCESS | FILE_WRITE_ACCESS)) + +namespace nirio_driver_iface { + +nirio_status rio_open( + const std::string& device_path, + rio_dev_handle_t& device_handle) +{ + device_handle = CreateFileA(device_path.c_str(), GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, /* default security */ + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL /* template file */); + + return (device_handle == INVALID_HANDLE_VALUE) ? NiRio_Status_InvalidParameter : NiRio_Status_Success; +} + +void rio_close(rio_dev_handle_t& device_handle) +{ + ::CloseHandle(device_handle); + device_handle = INVALID_HANDLE_VALUE; +} + +bool rio_isopen(rio_dev_handle_t device_handle) +{ + return (device_handle != INVALID_HANDLE_VALUE); +} + +nirio_status rio_ioctl( + rio_dev_handle_t device_handle, + uint32_t ioctl_code, + const void *write_buf, + size_t write_buf_len, + void *read_buf, + size_t read_buf_len) +{ + if (!rio_isopen(device_handle)) return NiRio_Status_ResourceNotInitialized; + + /* Note, if the file handle was opened with the OVERLAPPED flag, you must + * supply an OVERLAPPED structure to ReadFile, WriteFile, and + * DeviceIoControl, even when doing synchronous IO. */ + OVERLAPPED zeroedOverlapped = {0}; + DWORD outLen = 0; + int_fast32_t lastError = 0; + + if (!(DeviceIoControl(device_handle, ioctl_code, + const_cast<void*>(write_buf), static_cast<DWORD>(write_buf_len), + read_buf, static_cast<DWORD>(read_buf_len), + &outLen, &zeroedOverlapped ))) + { + lastError = GetLastError(); + return NiRio_Status_SoftwareFault; + } + + return NiRio_Status_Success; +} + +unsigned int __stdcall memory_map_thread_routine(void *context) +{ + rio_mmap_threadargs_t *args = (rio_mmap_threadargs_t*)context; + args->status = rio_ioctl(args->device_handle, NIRIO_IOCTL_MAP_MEMORY, &(args->params), sizeof(args->params), NULL, 0); + if (nirio_status_fatal(args->status)) + { + SetEvent(reinterpret_cast<HANDLE>(args->params.map_ready_event_handle)); + } + return 0; +} + +nirio_status rio_mmap( + rio_dev_handle_t device_handle, + uint16_t memory_type, + size_t size, + bool writable, + rio_mmap_t &map) +{ + if (!rio_isopen(device_handle)) return NiRio_Status_ResourceNotInitialized; + + access_mode_t access_mode = writable ? ACCESS_MODE_WRITE : ACCESS_MODE_READ; + + uint64_t mapped_addr = 0; + map.map_thread_args.device_handle = device_handle; + map.map_thread_args.status = NiRio_Status_Success; + map.map_thread_args.params.memoryType = memory_type; + map.map_thread_args.params.size = (uint32_t)size; + map.map_thread_args.params.mapped_va_ptr = reinterpret_cast<uintptr_t>(&mapped_addr); + map.map_thread_args.params.access_mode = (uint8_t)access_mode; + HANDLE map_ready_event_handle = CreateEventA(NULL, TRUE, FALSE, NULL); + if (map_ready_event_handle == NULL) { + map.addr = NULL; + return NiRio_Status_SoftwareFault; + } + map.map_thread_args.params.map_ready_event_handle = reinterpret_cast<uint64_t>(map_ready_event_handle); + map.map_thread_handle = (HANDLE) _beginthreadex(NULL, 0, memory_map_thread_routine, &(map.map_thread_args), 0, NULL); + + nirio_status status = NiRio_Status_Success; + if (map.map_thread_handle == NULL) { + map.addr = NULL; + return NiRio_Status_SoftwareFault; + } else { + WaitForSingleObject(map_ready_event_handle, INFINITE); + map.addr = reinterpret_cast<void*>(mapped_addr); + if (map.addr == NULL) { + WaitForSingleObject(map.map_thread_handle, INFINITE); + CloseHandle(map.map_thread_handle); + nirio_status_chain(map.map_thread_args.status, status); + } + } + CloseHandle(map_ready_event_handle); + return status; +} + +nirio_status rio_munmap(rio_mmap_t &map) +{ + if (!rio_isopen(map.map_thread_args.device_handle)) return NiRio_Status_ResourceNotInitialized; + + nirio_status status = NiRio_Status_Success; + if (map.addr != NULL) { + uint64_t mapped_addr = reinterpret_cast<uintptr_t>(map.addr); + status = rio_ioctl(map.map_thread_args.device_handle, NIRIO_IOCTL_UNMAP_MEMORY, &mapped_addr, sizeof(mapped_addr), NULL, 0); + if (nirio_status_not_fatal(status)) { + WaitForSingleObject(map.map_thread_handle, INFINITE); + } + CloseHandle(map.map_thread_handle); + map.addr = NULL; + } + return status; +} + +} diff --git a/host/lib/transport/nirio/nirio_resource_manager.cpp b/host/lib/transport/nirio/nirio_resource_manager.cpp new file mode 100644 index 000000000..85e789087 --- /dev/null +++ b/host/lib/transport/nirio/nirio_resource_manager.cpp @@ -0,0 +1,120 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + + +#include <uhd/transport/nirio/nirio_resource_manager.h> + +#ifdef __clang__ + #pragma GCC diagnostic push ignored "-Wmissing-field-initializers" +#elif defined(__GNUC__) + #pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +namespace uhd { namespace niusrprio +{ + +nirio_resource_manager::nirio_resource_manager( + niriok_proxy& proxy) : _kernel_proxy(proxy), _fifo_info_map(), _reg_info_map() +{ +} + +nirio_resource_manager::~nirio_resource_manager() +{ + finalize(); +} + +nirio_status nirio_resource_manager::initialize( + const nirio_register_info_vtr& reg_info_vtr, + const nirio_fifo_info_vtr& fifo_info_vtr) +{ + nirio_status status = 0; + for (nirio_fifo_info_vtr::const_iterator it = fifo_info_vtr.begin(); it != fifo_info_vtr.end(); it++) { + const nirio_fifo_info_t& fifo_info = *it; + status = _add_fifo_resource(fifo_info); + if (nirio_status_fatal(status)) return status; + + _fifo_info_map.insert(fifo_info_map_t::value_type(fifo_info.name, fifo_info)); + } + for (nirio_register_info_vtr::const_iterator it = reg_info_vtr.begin(); it != reg_info_vtr.end(); it++) { + const nirio_register_info_t& reg_info = *it; + + _reg_info_map.insert(register_info_map_t::value_type(reg_info.name, reg_info)); + } + return _set_driver_config(); +} + +void nirio_resource_manager::finalize() +{ + _fifo_info_map.clear(); +} + +nirio_status nirio_resource_manager::get_register_offset( + const char* register_name, + uint32_t& offset) +{ + register_info_map_t::const_iterator it = _reg_info_map.find(fifo_info_map_t::key_type(register_name)); + if (it == _reg_info_map.end()) return NiRio_Status_InvalidParameter; + + offset = (*it).second.offset; + + return NiRio_Status_Success; +} + + +nirio_status nirio_resource_manager::_add_fifo_resource( + const nirio_fifo_info_t& fifo_info) +{ + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::ADD_RESOURCE; + in.subfunction = (fifo_info.direction == OUTPUT_FIFO) ? + nirio_driver_iface::NIRIO_RESOURCE::OUTPUT_FIFO : + nirio_driver_iface::NIRIO_RESOURCE::INPUT_FIFO; + + in.params.add.fifoWithDataType.channel = fifo_info.channel; + in.params.add.fifoWithDataType.baseAddress = fifo_info.base_addr; + in.params.add.fifoWithDataType.depthInSamples = fifo_info.depth; + in.params.add.fifoWithDataType.scalarType = fifo_info.scalar_type; + in.params.add.fifoWithDataType.bitWidth = fifo_info.width; + in.params.add.fifoWithDataType.version = fifo_info.version; + + return _kernel_proxy.sync_operation(&in, sizeof(in), &out, sizeof(out)); +} + +nirio_status nirio_resource_manager::_set_driver_config() +{ + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + in.function = nirio_driver_iface::NIRIO_FUNC::SET_DRIVER_CONFIG; + in.subfunction = 0; + + return _kernel_proxy.sync_operation(&in, sizeof(in), &out, sizeof(out)); +} + +nirio_fifo_info_t* nirio_resource_manager::_lookup_fifo_info(const char* fifo_name) { + fifo_info_map_t::iterator it = _fifo_info_map.find(fifo_info_map_t::key_type(fifo_name)); + if (it == _fifo_info_map.end()) return NULL; + + return &((*it).second); +} + +}} + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif diff --git a/host/lib/transport/nirio/niriok_proxy.cpp b/host/lib/transport/nirio/niriok_proxy.cpp new file mode 100644 index 000000000..031623c9a --- /dev/null +++ b/host/lib/transport/nirio/niriok_proxy.cpp @@ -0,0 +1,300 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + + +#include <uhd/transport/nirio/niriok_proxy.h> +#include <cstring> + +#define NI_VENDOR_NUM 0x1093 + +#define VERSION_BUILD_SHIFT 0 +#define VERSION_PHASE_SHIFT 14 +#define VERSION_MAINT_SHIFT 16 +#define VERSION_UPGRD_SHIFT 20 +#define VERSION_MAJOR_SHIFT 24 +#define VERSION_BUILD_MASK 0x00003FFF +#define VERSION_PHASE_MASK 0x0000C000 +#define VERSION_MAINT_MASK 0x000F0000 +#define VERSION_UPGRD_MASK 0x00F00000 +#define VERSION_MAJOR_MASK 0xFF000000 + +#define GET_FIFO_MEMORY_TYPE(fifo_inst) (static_cast<uint16_t>(0x0100 | static_cast<uint16_t>(fifo_inst))) + +#ifdef __clang__ + #pragma GCC diagnostic push ignored "-Wmissing-field-initializers" +#elif defined(__GNUC__) + #pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +namespace uhd { namespace niusrprio +{ + //------------------------------------------------------- + // niriok_proxy + //------------------------------------------------------- + niriok_proxy::niriok_proxy(): _device_handle(nirio_driver_iface::INVALID_RIO_HANDLE) + { + } + + niriok_proxy::~niriok_proxy() + { + close(); + } + + nirio_status niriok_proxy::open(const std::string& interface_path) + { + if (interface_path.empty()) return NiRio_Status_ResourceNotFound; + + //close if already open. + close(); + + nirio_status status = NiRio_Status_Success; + nirio_status_chain(nirio_driver_iface::rio_open( + interface_path, _device_handle), status); + if (nirio_status_not_fatal(status)) { + nirio_status_chain(nirio_driver_iface::rio_ioctl(_device_handle, + nirio_driver_iface::NIRIO_IOCTL_POST_OPEN, + NULL, 0, NULL, 0), status); + nirio_driver_iface::nirio_ioctl_packet_t out(&_interface_num, sizeof(_interface_num), 0); + nirio_status_chain(nirio_driver_iface::rio_ioctl(_device_handle, + nirio_driver_iface::NIRIO_IOCTL_GET_IFACE_NUM, + NULL, 0, + &out, sizeof(out)), status); + + if (nirio_status_fatal(status)) close(); + } + return status; + } + + void niriok_proxy::close(void) + { + if(nirio_driver_iface::rio_isopen(_device_handle)) + { + nirio_driver_iface::rio_ioctl( + _device_handle, nirio_driver_iface::NIRIO_IOCTL_PRE_CLOSE, NULL, 0, NULL, 0); + nirio_driver_iface::rio_close(_device_handle); + } + } + + nirio_status niriok_proxy::reset() + { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::RESET; + + return sync_operation(&in, sizeof(in), &out, sizeof(out)); + } + + nirio_status niriok_proxy::get_cached_session( + uint32_t& session) + { + nirio_driver_iface::nirio_ioctl_packet_t out(&session, sizeof(session), 0); + return nirio_driver_iface::rio_ioctl(_device_handle, + nirio_driver_iface::NIRIO_IOCTL_GET_SESSION, + NULL, 0, + &out, sizeof(out)); + } + + nirio_status niriok_proxy::get_version( + nirio_version_t type, + uint32_t& major, + uint32_t& upgrade, + uint32_t& maintenance, + char& phase, + uint32_t& build) + { + nirio_device_attr_32_t version_attr = (type==CURRENT)?CURRENT_VERSION:OLDEST_COMPATIBLE_VERSION; + uint32_t raw_version = 0; + nirio_status status = get_attribute(version_attr, raw_version); + + major = (raw_version & VERSION_MAJOR_MASK) >> VERSION_MAJOR_SHIFT; + upgrade = (raw_version & VERSION_UPGRD_MASK) >> VERSION_UPGRD_SHIFT; + maintenance = (raw_version & VERSION_MAINT_MASK) >> VERSION_MAINT_SHIFT; + build = (raw_version & VERSION_BUILD_MASK) >> VERSION_BUILD_SHIFT; + + uint32_t phase_num = (raw_version & VERSION_PHASE_MASK) >> VERSION_PHASE_SHIFT; + switch (phase_num) { + case 0: phase = 'd'; break; + case 1: phase = 'a'; break; + case 2: phase = 'b'; break; + case 3: phase = 'f'; break; + } + + return status; + } + + nirio_status niriok_proxy::sync_operation( + const void *writeBuffer, + size_t writeBufferLength, + void *readBuffer, + size_t readBufferLength) + { + nirio_driver_iface::nirio_ioctl_packet_t out(readBuffer, readBufferLength, 0); + nirio_status ioctl_status = nirio_driver_iface::rio_ioctl(_device_handle, + nirio_driver_iface::NIRIO_IOCTL_SYNCOP, + writeBuffer, writeBufferLength, + &out, sizeof(out)); + if (nirio_status_fatal(ioctl_status)) return ioctl_status; + + return out.statusCode; + } + + nirio_status niriok_proxy::get_attribute( + const nirio_device_attr_32_t attribute, + uint32_t& attrValue) + { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::GET32; + in.params.attribute32.attribute = attribute; + + nirio_status status = sync_operation(&in, sizeof(in), &out, sizeof(out)); + + attrValue = out.params.attribute32.value; + return status; + } + + nirio_status niriok_proxy::get_attribute( + const nirio_device_attr_str_t attribute, + char *buf, + const uint32_t bufLen, + uint32_t& stringLen) + { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + nirio_driver_iface::init_syncop_out_params(out, buf, bufLen); + + in.function = nirio_driver_iface::NIRIO_FUNC::GET_STRING; + in.params.attributeStr.attribute = attribute; + + nirio_status status = sync_operation(&in, sizeof(in), &out, sizeof(out)); + + stringLen = out.params.stringLength; + return status; + } + + nirio_status niriok_proxy::set_attribute( + const nirio_device_attr_32_t attribute, + const uint32_t value) + { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::SET32; + in.params.attribute32.attribute = attribute; + in.params.attribute32.value = value; + + return sync_operation(&in, sizeof(in), &out, sizeof(out)); + } + + nirio_status niriok_proxy::set_attribute( + const nirio_device_attr_str_t attribute, + const char* const buffer) + { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::init_syncop_in_params(in, buffer, strlen(buffer) + 1); + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::SET_STRING; + in.params.attributeStr.attribute = attribute; + + return sync_operation(&in, sizeof(in), &out, sizeof(out)); + } + + nirio_status niriok_proxy::peek(uint32_t offset, uint32_t& value) + { + if (offset % 4 != 0) return NiRio_Status_MisalignedAccess; + + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::IO; + in.subfunction = nirio_driver_iface::NIRIO_IO::PEEK32; + in.params.io.offset = offset; + + nirio_status status = sync_operation(&in, sizeof(in), &out, sizeof(out)); + value = out.params.io.value.value32; + return status; + } + + nirio_status niriok_proxy::peek(uint32_t offset, uint64_t& value) + { + if (offset % 8 != 0) return NiRio_Status_MisalignedAccess; + + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::IO; + in.subfunction = nirio_driver_iface::NIRIO_IO::PEEK64; + in.params.io.offset = offset; + + nirio_status status = sync_operation(&in, sizeof(in), &out, sizeof(out)); + value = out.params.io.value.value64; + return status; + } + + nirio_status niriok_proxy::poke(uint32_t offset, const uint32_t& value) + { + if (offset % 4 != 0) return NiRio_Status_MisalignedAccess; + + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::IO; + in.subfunction = nirio_driver_iface::NIRIO_IO::POKE32; + in.params.io.offset = offset; + in.params.io.value.value32 = value; + + return sync_operation(&in, sizeof(in), &out, sizeof(out)); + } + + nirio_status niriok_proxy::poke(uint32_t offset, const uint64_t& value) + { + if (offset % 8 != 0) return NiRio_Status_MisalignedAccess; + + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::IO; + in.subfunction = nirio_driver_iface::NIRIO_IO::POKE64; + in.params.io.offset = offset; + in.params.io.value.value64 = value; + + return sync_operation(&in, sizeof(in), &out, sizeof(out)); + } + + nirio_status niriok_proxy::map_fifo_memory( + uint32_t fifo_instance, + size_t size, + nirio_driver_iface::rio_mmap_t& map) + { + return nirio_driver_iface::rio_mmap(_device_handle, + GET_FIFO_MEMORY_TYPE(fifo_instance), + size, true, map); + } + + nirio_status niriok_proxy::unmap_fifo_memory( + nirio_driver_iface::rio_mmap_t& map) + { + return nirio_driver_iface::rio_munmap(map); + } +}} + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif diff --git a/host/lib/transport/nirio/niusrprio_session.cpp b/host/lib/transport/nirio/niusrprio_session.cpp new file mode 100644 index 000000000..a07bc4fdf --- /dev/null +++ b/host/lib/transport/nirio/niusrprio_session.cpp @@ -0,0 +1,199 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + + +#include <uhd/transport/nirio/niusrprio_session.h> +#include <uhd/transport/nirio/nirio_fifo.h> +#include <uhd/transport/nirio/status.h> +#include <boost/format.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/lexical_cast.hpp> +#include <stdio.h> +#include <fstream> +//@TODO: Move the register defs required by the class to a common location +#include "../../usrp/x300/x300_regs.hpp" + +namespace uhd { namespace niusrprio { + +niusrprio_session::niusrprio_session(const std::string& resource_name, const std::string& rpc_port_name) : + _resource_name(resource_name), + _session_open(false), + _resource_manager(_riok_proxy), + _rpc_client("localhost", rpc_port_name) +{ +} + +niusrprio_session::~niusrprio_session() +{ + close(); +} + +nirio_status niusrprio_session::enumerate(const std::string& rpc_port_name, device_info_vtr& device_info_vtr) +{ + usrprio_rpc::usrprio_rpc_client temp_rpc_client("localhost", rpc_port_name); + nirio_status status = temp_rpc_client.get_ctor_status(); + nirio_status_chain(temp_rpc_client.niusrprio_enumerate(device_info_vtr), status); + return status; +} + +nirio_status niusrprio_session::open( + nifpga_lvbitx::sptr lvbitx, + bool force_download) +{ + boost::unique_lock<boost::recursive_mutex> lock(_session_mutex); + + _lvbitx = lvbitx; + + nirio_status status = NiRio_Status_Success; + std::string bitfile_path(_lvbitx->get_bitfile_path()); + std::string signature(_lvbitx->get_signature()); + + //Make sure that the RPC client connected to the server properly + nirio_status_chain(_rpc_client.get_ctor_status(), status); + //Get a handle to the kernel driver + nirio_status_chain(_rpc_client.niusrprio_get_interface_path(_resource_name, _interface_path), status); + nirio_status_chain(_riok_proxy.open(_interface_path), status); + + if (nirio_status_not_fatal(status)) { + //Bitfile build for a particular LVFPGA interface will have the same signature + //because the API of the bitfile does not change. Two files with the same signature + //can however have different bitstreams because of non-LVFPGA code differences. + //That is why we need another identifier to qualify the signature. The BIN + //checksum is a good candidate. + std::string lvbitx_checksum(_lvbitx->get_bitstream_checksum()); + boost::uint16_t download_fpga = (force_download || (_read_bitstream_checksum() != lvbitx_checksum)) ? 1 : 0; + + nirio_status_chain(_rpc_client.niusrprio_open_session( + _resource_name, bitfile_path, signature, download_fpga), status); + _session_open = nirio_status_not_fatal(status); + + if (nirio_status_not_fatal(status)) { + nirio_register_info_vtr reg_vtr; + nirio_fifo_info_vtr fifo_vtr; + _lvbitx->init_register_info(reg_vtr); + _lvbitx->init_fifo_info(fifo_vtr); + _resource_manager.initialize(reg_vtr, fifo_vtr); + + nirio_status_chain(_verify_signature(), status); + nirio_status_chain(_write_bitstream_checksum(lvbitx_checksum), status); + } + } + + return status; +} + +void niusrprio_session::close(bool skip_reset) +{ + boost::unique_lock<boost::recursive_mutex> lock(_session_mutex); + + if (_session_open) { + nirio_status status = NiRio_Status_Success; + if (!skip_reset) reset(); + nirio_status_chain(_rpc_client.niusrprio_close_session(_resource_name), status); + _session_open = false; + } +} + +nirio_status niusrprio_session::reset() +{ + boost::unique_lock<boost::recursive_mutex> lock(_session_mutex); + return _rpc_client.niusrprio_reset_device(_resource_name); +} + +nirio_status niusrprio_session::download_bitstream_to_flash(const std::string& bitstream_path) +{ + boost::unique_lock<boost::recursive_mutex> lock(_session_mutex); + return _rpc_client.niusrprio_download_fpga_to_flash(_resource_name, bitstream_path); +} + +niriok_proxy::sptr niusrprio_session::create_kernel_proxy( + const std::string& resource_name, + const std::string& rpc_port_name) +{ + usrprio_rpc::usrprio_rpc_client temp_rpc_client("localhost", rpc_port_name); + nirio_status status = temp_rpc_client.get_ctor_status(); + + std::string interface_path; + nirio_status_chain(temp_rpc_client.niusrprio_get_interface_path(resource_name, interface_path), status); + + niriok_proxy::sptr proxy; + if (nirio_status_not_fatal(status)) { + proxy.reset(new niriok_proxy()); + if (proxy) nirio_status_chain(proxy->open(interface_path), status); + } + + return proxy; +} + +nirio_status niusrprio_session::_verify_signature() +{ + //Validate the signature using the kernel proxy + nirio_status status = NiRio_Status_Success; + boost::uint32_t sig_offset = 0; + nirio_status_chain(_riok_proxy.get_attribute(DEFAULT_FPGA_SIGNATURE_OFFSET, sig_offset), status); + niriok_scoped_addr_space(_riok_proxy, FPGA, status); + std::string signature; + for (boost::uint32_t i = 0; i < 8; i++) { + boost::uint32_t quarter_sig; + nirio_status_chain(_riok_proxy.peek(sig_offset, quarter_sig), status); + signature += boost::str(boost::format("%08x") % quarter_sig); + } + + std::string expected_signature(_lvbitx->get_signature()); + boost::to_upper(signature); + boost::to_upper(expected_signature); + if (signature.find(expected_signature) == std::string::npos) { + nirio_status_chain(NiRio_Status_SignatureMismatch, status); + } + + return status; +} + +std::string niusrprio_session::_read_bitstream_checksum() +{ + nirio_status status = NiRio_Status_Success; + niriok_scoped_addr_space(_riok_proxy, BUS_INTERFACE, status); + std::string usr_signature; + for (boost::uint32_t i = 0; i < FPGA_USR_SIG_REG_SIZE; i+=4) { + boost::uint32_t quarter_sig; + nirio_status_chain(_riok_proxy.peek(FPGA_USR_SIG_REG_BASE + i, quarter_sig), status); + usr_signature += boost::str(boost::format("%08x") % quarter_sig); + } + boost::to_upper(usr_signature); + + return usr_signature; +} + +nirio_status niusrprio_session::_write_bitstream_checksum(const std::string& checksum) +{ + nirio_status status = NiRio_Status_Success; + niriok_scoped_addr_space(_riok_proxy, BUS_INTERFACE, status); + for (boost::uint32_t i = 0; i < FPGA_USR_SIG_REG_SIZE; i+=4) { + boost::uint32_t quarter_sig; + try { + std::stringstream ss; + ss << std::hex << checksum.substr(i*2,8); + ss >> quarter_sig; + } catch (std::exception&) { + quarter_sig = 0; + } + nirio_status_chain(_riok_proxy.poke(FPGA_USR_SIG_REG_BASE + i, quarter_sig), status); + } + return status; +} + +}} diff --git a/host/lib/transport/nirio/rpc/CMakeLists.txt b/host/lib/transport/nirio/rpc/CMakeLists.txt new file mode 100644 index 000000000..02c16d2ff --- /dev/null +++ b/host/lib/transport/nirio/rpc/CMakeLists.txt @@ -0,0 +1,29 @@ +# +# Copyright 2013 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## + +######################################################################## +# Append to the list of sources for lib uhd +######################################################################## + +LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/rpc_client.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/usrprio_rpc_client.cpp +) diff --git a/host/lib/transport/nirio/rpc/rpc_client.cpp b/host/lib/transport/nirio/rpc/rpc_client.cpp new file mode 100644 index 000000000..a5f8cf412 --- /dev/null +++ b/host/lib/transport/nirio/rpc/rpc_client.cpp @@ -0,0 +1,201 @@ +/// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/nirio/rpc/rpc_client.hpp> +#include <boost/bind.hpp> +#include <boost/format.hpp> + +#define CHAIN_BLOCKING_XFER(func, exp, status) \ + if (status) { \ + status = (static_cast<size_t>((func)) == exp); \ + } else { \ + UHD_LOG << "rpc_client operation skipped: " #func "\n"; \ + } \ + +namespace uhd { namespace usrprio_rpc { + +using boost::asio::ip::tcp; + +rpc_client::rpc_client ( + const std::string& server, + const std::string& port, + boost::uint32_t process_id, + boost::uint32_t host_id +) : _socket(_io_service) +{ + //Fill in handshake info + _hshake_args_client.version = CURRENT_VERSION; + _hshake_args_client.oldest_comp_version = OLDEST_COMPATIBLE_VERSION; + _hshake_args_client.client_id = build_client_id(host_id, process_id); + _hshake_args_client.boost_archive_version = boost_serialization_archive_utils::get_version(); + + try { + //Synchronous resolve + connect + tcp::resolver resolver(_io_service); + tcp::resolver::query query(tcp::v4(), server, port); + tcp::resolver::iterator iterator = resolver.resolve(query); + boost::asio::connect(_socket, iterator); + UHD_LOG << "rpc_client connected to server." << std::endl; + + try { + //Perform handshake + bool status = true; + CHAIN_BLOCKING_XFER( + boost::asio::write(_socket, boost::asio::buffer(&_hshake_args_client, sizeof(_hshake_args_client))), + sizeof(_hshake_args_client), status); + CHAIN_BLOCKING_XFER( + boost::asio::read(_socket, boost::asio::buffer(&_hshake_args_server, sizeof(_hshake_args_server))), + sizeof(_hshake_args_server), status); + + _request.header.client_id = _hshake_args_server.client_id; + + if (_hshake_args_server.version >= _hshake_args_client.oldest_comp_version && + _hshake_args_client.version >= _hshake_args_server.oldest_comp_version && + status) + { + UHD_LOG << "rpc_client bound to server." << std::endl; + _wait_for_next_response_header(); + + //Spawn a thread for the io_service callback handler. This thread will run until rpc_client is destroyed. + _io_service_thread.reset(new boost::thread(boost::bind(&boost::asio::io_service::run, &_io_service))); + } else { + UHD_LOG << "rpc_client handshake failed." << std::endl; + _exec_err.assign(boost::asio::error::connection_refused, boost::system::system_category()); + } + UHD_LOG << boost::format("rpc_client archive = %d, rpc_server archive = %d\n.") % + _hshake_args_client.boost_archive_version % + _hshake_args_server.boost_archive_version; + } catch (boost::exception&) { + UHD_LOG << "rpc_client handshake aborted." << std::endl; + _exec_err.assign(boost::asio::error::connection_refused, boost::system::system_category()); + } + } catch (boost::exception&) { + UHD_LOG << "rpc_client connection request cancelled/aborted." << std::endl; + _exec_err.assign(boost::asio::error::connection_aborted, boost::system::system_category()); + } +} + +rpc_client::~rpc_client () { + _stop_io_service(); +} + +const boost::system::error_code& rpc_client::call( + func_id_t func_id, + const func_args_writer_t& in_args, + func_args_reader_t &out_args, + boost::posix_time::milliseconds timeout +) +{ + boost::mutex::scoped_lock lock(_mutex); + + if (_io_service_thread.get()) { + _request.header.func_id = func_id; + in_args.store(_request.data); + _request.header.func_args_size = _request.data.size(); + + _exec_err.clear(); + + //Send function call header and args + bool status = true; + try { + CHAIN_BLOCKING_XFER( + boost::asio::write(_socket, boost::asio::buffer(&_request.header, sizeof(_request.header))), + sizeof(_request.header), status); + CHAIN_BLOCKING_XFER( + boost::asio::write(_socket, boost::asio::buffer(&(*_request.data.begin()), _request.data.size())), + _request.data.size(), status); + } catch (boost::exception&) { + status = false; + } + + //Wait for response using condition variable + if (status) { + if (!_exec_gate.timed_wait(lock, timeout)) { + UHD_LOG << "rpc_client function timed out." << std::endl; + _exec_err.assign(boost::asio::error::timed_out, boost::system::system_category()); + } + } else { + UHD_LOG << "rpc_client connection dropped." << std::endl; + _exec_err.assign(boost::asio::error::connection_aborted, boost::system::system_category()); + _stop_io_service(); + } + + //Verify that we are talking to the correct endpoint + if ((_request.header.client_id != _response.header.client_id) && !_exec_err) { + UHD_LOG << "rpc_client confused about who its talking to." << std::endl; + _exec_err.assign(boost::asio::error::operation_aborted, boost::system::system_category()); + } + + if (!_exec_err) out_args.load(_response.data); + } + + return _exec_err; +} + +void rpc_client::_handle_response_hdr(const boost::system::error_code& err, size_t transferred, size_t expected) +{ + boost::mutex::scoped_lock lock(_mutex); + _exec_err = err; + if (!_exec_err && (transferred == expected)) { + //Response header received. Verify that it is expected + if (func_args_header_t::match_function(_request.header, _response.header)) { + _response.data.resize(_response.header.func_args_size); + + //Wait for response data + boost::asio::async_read(_socket, + boost::asio::buffer(&(*_response.data.begin()), _response.data.size()), + boost::bind(&rpc_client::_handle_response_data, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred, + _response.data.size())); + } else { + //Unexpected response. Ignore it. + UHD_LOG << "rpc_client received garbage responses." << std::endl; + _exec_err.assign(boost::asio::error::operation_aborted, boost::system::system_category()); + + _wait_for_next_response_header(); + } + } + + if (_exec_err) _exec_gate.notify_all(); +} + +void rpc_client::_handle_response_data(const boost::system::error_code& err, size_t transferred, size_t expected) +{ + boost::mutex::scoped_lock lock(_mutex); + _exec_err = err; + if (transferred != expected) { + _exec_err.assign(boost::asio::error::operation_aborted, boost::system::system_category()); + } + + _exec_gate.notify_all(); + + _wait_for_next_response_header(); +} + +void rpc_client::_wait_for_next_response_header() { + //_mutex must be locked when this call is made + boost::asio::async_read( + _socket, + boost::asio::buffer(&_response.header, sizeof(_response.header)), + boost::bind(&rpc_client::_handle_response_hdr, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred, + sizeof(_response.header))); +} + +}} diff --git a/host/lib/transport/nirio/rpc/usrprio_rpc_client.cpp b/host/lib/transport/nirio/rpc/usrprio_rpc_client.cpp new file mode 100644 index 000000000..1a1f1cd21 --- /dev/null +++ b/host/lib/transport/nirio/rpc/usrprio_rpc_client.cpp @@ -0,0 +1,229 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/nirio/rpc/usrprio_rpc_client.hpp> +#include <uhd/utils/platform.hpp> + +namespace uhd { namespace usrprio_rpc { + +usrprio_rpc_client::usrprio_rpc_client( + std::string server, + std::string port +) : _rpc_client(server, port, uhd::get_process_id(), uhd::get_host_id()), + _timeout(boost::posix_time::milliseconds(DEFAULT_TIMEOUT_IN_MS)) +{ + _ctor_status = _rpc_client.status() ? NiRio_Status_RpcConnectionError : NiRio_Status_Success; +} + +usrprio_rpc_client::~usrprio_rpc_client() +{ +} + +nirio_status usrprio_rpc_client::niusrprio_enumerate(NIUSRPRIO_ENUMERATE_ARGS) +/* +#define NIUSRPRIO_ENUMERATE_ARGS \ + usrprio_device_info_vtr& device_info_vtr +*/ +{ + usrprio_rpc::func_args_writer_t in_args; + usrprio_rpc::func_args_reader_t out_args; + nirio_status status = NiRio_Status_Success; + boost::uint32_t vtr_size = 0; + + status = _boost_error_to_nirio_status( + _rpc_client.call(NIUSRPRIO_ENUMERATE, in_args, out_args, _timeout)); + + if (nirio_status_not_fatal(status)) { + out_args >> status; + out_args >> vtr_size; + } + if (nirio_status_not_fatal(status) && vtr_size > 0) { + device_info_vtr.resize(vtr_size); + for (size_t i = 0; i < (size_t)vtr_size; i++) { + usrprio_device_info info; + out_args >> info; + device_info_vtr[i] = info; + } + } + return status; +} + +nirio_status usrprio_rpc_client::niusrprio_open_session(NIUSRPRIO_OPEN_SESSION_ARGS) +/* +#define NIUSRPRIO_OPEN_SESSION_ARGS \ + const std::string& resource, \ + const std::string& path, \ + const std::string& signature, \ + const boost::uint16_t& download_fpga +*/ +{ + usrprio_rpc::func_args_writer_t in_args; + usrprio_rpc::func_args_reader_t out_args; + nirio_status status = NiRio_Status_Success; + + in_args << resource; + in_args << path; + in_args << signature; + in_args << download_fpga; + + //Open needs a longer timeout because the FPGA download can take upto 6 secs and the NiFpga libload can take 4. + static const boost::uint32_t OPEN_TIMEOUT = 15000; + status = _boost_error_to_nirio_status( + _rpc_client.call(NIUSRPRIO_OPEN_SESSION, in_args, out_args, boost::posix_time::milliseconds(OPEN_TIMEOUT))); + + if (nirio_status_not_fatal(status)) { + out_args >> status; + } + + return status; +} + +nirio_status usrprio_rpc_client::niusrprio_close_session(NIUSRPRIO_CLOSE_SESSION_ARGS) +/* +#define NIUSRPRIO_CLOSE_SESSION_ARGS \ + const std::string& resource +*/ +{ + usrprio_rpc::func_args_writer_t in_args; + usrprio_rpc::func_args_reader_t out_args; + nirio_status status = NiRio_Status_Success; + + in_args << resource; + + status = _boost_error_to_nirio_status( + _rpc_client.call(NIUSRPRIO_CLOSE_SESSION, in_args, out_args, _timeout)); + + if (nirio_status_not_fatal(status)) { + out_args >> status; + } + + return status; +} + +nirio_status usrprio_rpc_client::niusrprio_reset_device(NIUSRPRIO_RESET_SESSION_ARGS) +/* +#define NIUSRPRIO_RESET_SESSION_ARGS \ + const std::string& resource +*/ +{ + usrprio_rpc::func_args_writer_t in_args; + usrprio_rpc::func_args_reader_t out_args; + nirio_status status = NiRio_Status_Success; + + in_args << resource; + + status = _boost_error_to_nirio_status( + _rpc_client.call(NIUSRPRIO_RESET_SESSION, in_args, out_args, _timeout)); + + if (nirio_status_not_fatal(status)) { + out_args >> status; + } + + return status; +} + +nirio_status usrprio_rpc_client::niusrprio_get_interface_path(NIUSRPRIO_GET_INTERFACE_PATH_ARGS) +/* +#define NIUSRPRIO_GET_INTERFACE_PATH_ARGS \ + const std::string& resource, \ + std::string& interface_path +*/ +{ + usrprio_rpc::func_args_writer_t in_args; + usrprio_rpc::func_args_reader_t out_args; + nirio_status status = NiRio_Status_Success; + + in_args << resource; + + status = _boost_error_to_nirio_status( + _rpc_client.call(NIUSRPRIO_GET_INTERFACE_PATH, in_args, out_args, _timeout)); + + if (nirio_status_not_fatal(status)) { + out_args >> status; + out_args >> interface_path; + } + + return status; +} + +nirio_status usrprio_rpc_client::niusrprio_download_fpga_to_flash(NIUSRPRIO_DOWNLOAD_FPGA_TO_FLASH_ARGS) +/* +#define NIUSRPRIO_DOWNLOAD_FPGA_TO_FLASH_ARGS \ + const boost::uint32_t& interface_num, \ + const std::string& bitstream_path +*/ +{ + usrprio_rpc::func_args_writer_t in_args; + usrprio_rpc::func_args_reader_t out_args; + nirio_status status = NiRio_Status_Success; + + in_args << resource; + in_args << bitstream_path; + + static const boost::uint32_t DOWNLOAD_FPGA_TIMEOUT = 1200000; + status = _boost_error_to_nirio_status( + _rpc_client.call(NIUSRPRIO_DOWNLOAD_FPGA_TO_FLASH, in_args, out_args, + boost::posix_time::milliseconds(DOWNLOAD_FPGA_TIMEOUT))); + + if (nirio_status_not_fatal(status)) { + out_args >> status; + } + + return status; +} + +nirio_status usrprio_rpc_client::niusrprio_download_bitstream_to_fpga(NIUSRPRIO_DOWNLOAD_BITSTREAM_TO_FPGA_ARGS) +/* +#define NIUSRPRIO_DOWNLOAD_BITSTREAM_TO_FPGA_ARGS \ + const std::string& resource +*/ +{ + usrprio_rpc::func_args_writer_t in_args; + usrprio_rpc::func_args_reader_t out_args; + nirio_status status = NiRio_Status_Success; + + in_args << resource; + + status = _boost_error_to_nirio_status( + _rpc_client.call(NIUSRPRIO_DOWNLOAD_BITSTREAM_TO_FPGA, in_args, out_args, _timeout)); + + if (nirio_status_not_fatal(status)) { + out_args >> status; + } + + return status; +} + +nirio_status usrprio_rpc_client::_boost_error_to_nirio_status(const boost::system::error_code& err) { + if (err) { + switch (err.value()) { + case boost::asio::error::connection_aborted: + case boost::asio::error::connection_refused: + case boost::asio::error::eof: + return NiRio_Status_RpcSessionError; + case boost::asio::error::timed_out: + case boost::asio::error::operation_aborted: + return NiRio_Status_RpcOperationError; + default: + return NiRio_Status_SoftwareFault; + } + } else { + return NiRio_Status_Success; + } +} + +}} diff --git a/host/lib/transport/nirio/status.cpp b/host/lib/transport/nirio/status.cpp new file mode 100644 index 000000000..f3f8d4cd1 --- /dev/null +++ b/host/lib/transport/nirio/status.cpp @@ -0,0 +1,55 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + + +#include <uhd/transport/nirio/status.h> +#include <boost/format.hpp> + +namespace uhd { namespace niusrprio { + +#define NIRIO_ERR_INFO(CONST_NAME, ERR_CODE, ERR_MSG) \ + nirio_err_info(ERR_CODE, ERR_MSG), + +const nirio_err_info nirio_err_info::NIRIO_ERROR_TABLE[] = { + #include "../../../include/uhd/transport/nirio/nirio_err_template.h" +}; + +#undef NIRIO_ERR_INFO + +const size_t nirio_err_info::NIRIO_ERROR_TABLE_SIZE = sizeof(NIRIO_ERROR_TABLE)/sizeof(*NIRIO_ERROR_TABLE); + +const std::string lookup_err_msg(nirio_status code) { + std::string error_msg = (boost::format("Unknown error. (Error code %d)") % code).str(); + for (size_t i = 0; i < nirio_err_info::NIRIO_ERROR_TABLE_SIZE; i++) { + if (nirio_err_info::NIRIO_ERROR_TABLE[i].code == code) { + error_msg = (boost::format("%s (Error code %d)") % nirio_err_info::NIRIO_ERROR_TABLE[i].msg % code).str(); + break; + } + } + return error_msg; +} + +void nirio_status_to_exception(const nirio_status& status, const std::string& message) { + if (nirio_status_fatal(status)) { + throw uhd::runtime_error((boost::format("%s %s") % message % lookup_err_msg(status)).str()); + } +} + +}} + + + diff --git a/host/lib/transport/nirio_zero_copy.cpp b/host/lib/transport/nirio_zero_copy.cpp new file mode 100644 index 000000000..7b1e32fe0 --- /dev/null +++ b/host/lib/transport/nirio_zero_copy.cpp @@ -0,0 +1,352 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/nirio_zero_copy.hpp> +#include <stdio.h> +#include <uhd/transport/nirio/nirio_fifo.h> +#include <uhd/transport/nirio/nirio_fifo.h> +#include <uhd/transport/buffer_pool.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/atomic.hpp> +#include <boost/format.hpp> +#include <boost/make_shared.hpp> +#include <boost/thread/thread.hpp> //sleep +#include <vector> +#include <algorithm> // std::max +//@TODO: Move the register defs required by the class to a common location +#include "../usrp/x300/x300_regs.hpp" + +using namespace uhd; +using namespace uhd::transport; +using namespace uhd::niusrprio; + +typedef uint64_t fifo_data_t; + +class nirio_zero_copy_mrb : public managed_recv_buffer +{ +public: + nirio_zero_copy_mrb(nirio_fifo<fifo_data_t>& fifo, const size_t frame_size): + _fifo(fifo), _frame_size(frame_size) { } + + void release(void) + { + _fifo.release(_frame_size / sizeof(fifo_data_t)); + } + + UHD_INLINE sptr get_new(const double timeout, size_t &index) + { + nirio_status status = 0; + size_t elems_acquired, elems_remaining; + nirio_status_chain(_fifo.acquire( + _typed_buffer, _frame_size / sizeof(fifo_data_t), + static_cast<uint32_t>(timeout*1000), + elems_acquired, elems_remaining), status); + _length = elems_acquired * sizeof(fifo_data_t); + _buffer = static_cast<void*>(_typed_buffer); + + if (nirio_status_not_fatal(status)) { + index++; //Advances the caller's buffer + return make(this, _buffer, _length); + } else if (status == NiRio_Status_CommunicationTimeout) { + nirio_status_to_exception(status, "NI-RIO PCIe data transfer failed."); + return sptr(); + } else { + return sptr(); //NULL for timeout or error. + } + } + +private: + nirio_fifo<fifo_data_t>& _fifo; + fifo_data_t* _typed_buffer; + const size_t _frame_size; + size_t _num_frames; +}; + +class nirio_zero_copy_msb : public managed_send_buffer +{ +public: + nirio_zero_copy_msb(nirio_fifo<fifo_data_t>& fifo, const size_t frame_size): + _fifo(fifo), _frame_size(frame_size) { } + + void release(void) + { + _fifo.release(_frame_size / sizeof(fifo_data_t)); + } + + UHD_INLINE sptr get_new(const double timeout, size_t &index) + { + nirio_status status = 0; + size_t elems_acquired, elems_remaining; + nirio_status_chain(_fifo.acquire( + _typed_buffer, _frame_size / sizeof(fifo_data_t), + static_cast<uint32_t>(timeout*1000), + elems_acquired, elems_remaining), status); + _length = elems_acquired * sizeof(fifo_data_t); + _buffer = static_cast<void*>(_typed_buffer); + + if (nirio_status_not_fatal(status)) { + index++; //Advances the caller's buffer + return make(this, _buffer, _length); + } else if (status == NiRio_Status_CommunicationTimeout) { + nirio_status_to_exception(status, "NI-RIO PCIe data transfer failed."); + return sptr(); + } else { + return sptr(); //NULL for timeout or error. + } + } + +private: + nirio_fifo<fifo_data_t>& _fifo; + fifo_data_t* _typed_buffer; + const size_t _frame_size; + size_t _num_frames; +}; + +class nirio_zero_copy_impl : public nirio_zero_copy { +public: + typedef boost::shared_ptr<nirio_zero_copy_impl> sptr; + + nirio_zero_copy_impl( + uhd::niusrprio::niusrprio_session::sptr fpga_session, + uint32_t instance, + const zero_copy_xport_params& xport_params + ): + _fpga_session(fpga_session), + _fifo_instance(instance), + _xport_params(xport_params), + _next_recv_buff_index(0), _next_send_buff_index(0) + { + UHD_LOG << boost::format("Creating PCIe transport for channel %d") % instance << std::endl; + UHD_LOG << boost::format("nirio zero-copy RX transport configured with frame size = %u, #frames = %u, buffer size = %u\n") + % _xport_params.recv_frame_size % _xport_params.num_recv_frames % + (_xport_params.recv_frame_size * _xport_params.num_recv_frames); + UHD_LOG << boost::format("nirio zero-copy TX transport configured with frame size = %u, #frames = %u, buffer size = %u\n") + % _xport_params.send_frame_size % _xport_params.num_send_frames % (_xport_params.send_frame_size * _xport_params.num_send_frames); + + _recv_buffer_pool = buffer_pool::make(_xport_params.num_recv_frames, _xport_params.recv_frame_size); + _send_buffer_pool = buffer_pool::make(_xport_params.num_send_frames, _xport_params.send_frame_size); + + nirio_status status = 0; + size_t actual_depth = 0, actual_size = 0; + + //Configure frame width + nirio_status_chain( + _proxy().poke(PCIE_TX_DMA_REG(DMA_FRAME_SIZE_REG, _fifo_instance), + static_cast<uint32_t>(_xport_params.send_frame_size/sizeof(fifo_data_t))), + status); + nirio_status_chain( + _proxy().poke(PCIE_RX_DMA_REG(DMA_FRAME_SIZE_REG, _fifo_instance), + static_cast<uint32_t>(_xport_params.recv_frame_size/sizeof(fifo_data_t))), + status); + //Config 32-bit word flipping and Reset DMA streams + nirio_status_chain( + _proxy().poke(PCIE_TX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), + DMA_CTRL_SW_BUF_U32 | DMA_CTRL_RESET), + status); + nirio_status_chain( + _proxy().poke(PCIE_RX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), + DMA_CTRL_SW_BUF_U32 | DMA_CTRL_RESET), + status); + + //Create FIFOs + nirio_status_chain( + _fpga_session->create_rx_fifo(_fifo_instance, _recv_fifo), + status); + nirio_status_chain( + _fpga_session->create_tx_fifo(_fifo_instance, _send_fifo), + status); + + if ((_recv_fifo.get() != NULL) && (_send_fifo.get() != NULL)) { + //Initialize FIFOs + nirio_status_chain( + _recv_fifo->initialize( + (_xport_params.recv_frame_size*_xport_params.num_recv_frames)/sizeof(fifo_data_t), + actual_depth, actual_size), + status); + nirio_status_chain( + _send_fifo->initialize( + (_xport_params.send_frame_size*_xport_params.num_send_frames)/sizeof(fifo_data_t), + actual_depth, actual_size), + status); + + _proxy().get_rio_quirks().add_tx_fifo(_fifo_instance); + + nirio_status_chain(_recv_fifo->start(), status); + nirio_status_chain(_send_fifo->start(), status); + + if (nirio_status_not_fatal(status)) { + //Flush RX kernel buffers in case some cruft was + //left behind from the last run + _flush_rx_buff(); + + //allocate re-usable managed receive buffers + for (size_t i = 0; i < get_num_recv_frames(); i++){ + _mrb_pool.push_back(boost::shared_ptr<nirio_zero_copy_mrb>(new nirio_zero_copy_mrb( + *_recv_fifo, get_recv_frame_size()))); + } + + //allocate re-usable managed send buffers + for (size_t i = 0; i < get_num_send_frames(); i++){ + _msb_pool.push_back(boost::shared_ptr<nirio_zero_copy_msb>(new nirio_zero_copy_msb( + *_send_fifo, get_send_frame_size()))); + } + } + } else { + nirio_status_chain(NiRio_Status_ResourceNotInitialized, status); + } + + nirio_status_to_exception(status, "Could not create nirio_zero_copy transport."); + } + + virtual ~nirio_zero_copy_impl() + { + _proxy().get_rio_quirks().remove_tx_fifo(_fifo_instance); + + //Reset DMA streams (Teardown, so don't status chain) + _proxy().poke(PCIE_TX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), DMA_CTRL_RESET); + _proxy().poke(PCIE_RX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), DMA_CTRL_RESET); + + _flush_rx_buff(); + + //Stop DMA channels. Stop is called in the fifo dtor but + //it doesn't hurt to do it here. + _send_fifo->stop(); + _recv_fifo->stop(); + } + + /******************************************************************* + * Receive implementation: + * Block on the managed buffer's get call and advance the index. + ******************************************************************/ + managed_recv_buffer::sptr get_recv_buff(double timeout) + { + if (_next_recv_buff_index == _xport_params.num_recv_frames) _next_recv_buff_index = 0; + return _mrb_pool[_next_recv_buff_index]->get_new(timeout, _next_recv_buff_index); + } + + size_t get_num_recv_frames(void) const {return _xport_params.num_recv_frames;} + size_t get_recv_frame_size(void) const {return _xport_params.recv_frame_size;} + + /******************************************************************* + * Send implementation: + * Block on the managed buffer's get call and advance the index. + ******************************************************************/ + managed_send_buffer::sptr get_send_buff(double timeout) + { + if (_next_send_buff_index == _xport_params.num_send_frames) _next_send_buff_index = 0; + return _msb_pool[_next_send_buff_index]->get_new(timeout, _next_send_buff_index); + } + + size_t get_num_send_frames(void) const {return _xport_params.num_send_frames;} + size_t get_send_frame_size(void) const {return _xport_params.send_frame_size;} + +private: + + UHD_INLINE niriok_proxy& _proxy() { return _fpga_session->get_kernel_proxy(); } + + UHD_INLINE void _flush_rx_buff() + { + nirio_status flush_status = 0; + while (nirio_status_not_fatal(flush_status)) { + static const size_t NUM_ELEMS_TO_FLUSH = 1; + static const uint32_t FLUSH_TIMEOUT_IN_MS = 0; + + fifo_data_t* flush_data_ptr = NULL; + size_t flush_elems_acquired = 0, flush_elems_remaining = 0; + flush_status = _recv_fifo->acquire( + flush_data_ptr, NUM_ELEMS_TO_FLUSH, FLUSH_TIMEOUT_IN_MS, + flush_elems_acquired, flush_elems_remaining); + if (nirio_status_not_fatal(flush_status)) { + _recv_fifo->release(flush_elems_acquired); + } + } + } + + //memory management -> buffers and fifos + niusrprio::niusrprio_session::sptr _fpga_session; + uint32_t _fifo_instance; + nirio_fifo<fifo_data_t>::sptr _recv_fifo, _send_fifo; + const zero_copy_xport_params _xport_params; + buffer_pool::sptr _recv_buffer_pool, _send_buffer_pool; + std::vector<boost::shared_ptr<nirio_zero_copy_msb> > _msb_pool; + std::vector<boost::shared_ptr<nirio_zero_copy_mrb> > _mrb_pool; + size_t _next_recv_buff_index, _next_send_buff_index; +}; + + +nirio_zero_copy::sptr nirio_zero_copy::make( + uhd::niusrprio::niusrprio_session::sptr fpga_session, + const uint32_t instance, + const zero_copy_xport_params& default_buff_args, + const device_addr_t &hints +){ + //Initialize xport_params + zero_copy_xport_params xport_params = default_buff_args; + + //The kernel buffer for this transport must be (num_frames * frame_size) big. Unlike ethernet, + //where the kernel buffer size is independent of the circular buffer size for the transport, + //it is possible for users to over constrain the system when they set the num_frames and the buff_size + //So we give buff_size priority over num_frames and throw an error if they conflict. + + //RX + xport_params.recv_frame_size = size_t(hints.cast<double>("recv_frame_size", default_buff_args.recv_frame_size)); + + size_t usr_num_recv_frames = static_cast<size_t>( + hints.cast<double>("num_recv_frames", default_buff_args.num_recv_frames)); + size_t usr_recv_buff_size = static_cast<size_t>( + hints.cast<double>("recv_buff_size", default_buff_args.num_recv_frames)); + + if (hints.has_key("num_recv_frames") and hints.has_key("recv_buff_size")) { + if (usr_recv_buff_size < xport_params.recv_frame_size) + throw uhd::value_error("recv_buff_size must be equal to or greater than (num_recv_frames * recv_frame_size)"); + + if ((usr_recv_buff_size/xport_params.recv_frame_size) != usr_num_recv_frames) + throw uhd::value_error("Conflicting values for recv_buff_size and num_recv_frames"); + } + + if (hints.has_key("recv_buff_size")) { + xport_params.num_recv_frames = std::max<size_t>(1, usr_recv_buff_size/xport_params.recv_frame_size); //Round down + } else if (hints.has_key("num_recv_frames")) { + xport_params.num_recv_frames = usr_num_recv_frames; + } + + //TX + xport_params.send_frame_size = size_t(hints.cast<double>("send_frame_size", default_buff_args.send_frame_size)); + + size_t usr_num_send_frames = static_cast<size_t>( + hints.cast<double>("num_send_frames", default_buff_args.num_send_frames)); + size_t usr_send_buff_size = static_cast<size_t>( + hints.cast<double>("send_buff_size", default_buff_args.num_send_frames)); + + if (hints.has_key("num_send_frames") and hints.has_key("send_buff_size")) { + if (usr_send_buff_size < xport_params.send_frame_size) + throw uhd::value_error("send_buff_size must be equal to or greater than (num_send_frames * send_frame_size)"); + + if ((usr_send_buff_size/xport_params.send_frame_size) != usr_num_send_frames) + throw uhd::value_error("Conflicting values for send_buff_size and num_send_frames"); + } + + if (hints.has_key("send_buff_size")) { + xport_params.num_send_frames = std::max<size_t>(1, usr_send_buff_size/xport_params.send_frame_size); //Round down + } else if (hints.has_key("num_send_frames")) { + xport_params.num_send_frames = usr_num_send_frames; + } + + return nirio_zero_copy::sptr(new nirio_zero_copy_impl(fpga_session, instance, xport_params)); +} + diff --git a/host/lib/transport/super_recv_packet_handler.hpp b/host/lib/transport/super_recv_packet_handler.hpp index 5080182d6..5fdf2594d 100644 --- a/host/lib/transport/super_recv_packet_handler.hpp +++ b/host/lib/transport/super_recv_packet_handler.hpp @@ -39,6 +39,13 @@ #include <iostream> #include <vector> +// Included for debugging +#ifdef UHD_TXRX_DEBUG_PRINTS +#include <boost/format.hpp> +#include <boost/thread/thread.hpp> +#include "boost/date_time/posix_time/posix_time.hpp" +#endif + namespace uhd{ namespace transport{ namespace sph{ UHD_INLINE boost::uint32_t get_context_code( @@ -76,6 +83,10 @@ public: _queue_error_for_next_call(false), _buffers_infos_index(0) { + #ifdef ERROR_INJECT_DROPPED_PACKETS + recvd_packets = 0; + #endif + this->resize(size); set_alignment_failure_threshold(1000); } @@ -142,6 +153,32 @@ public: } /*! + * Flush all transports in the streamer: + * This calls into get_and_process_single_packet(), + * so the sequence and flow control are handled. + * However, the packet payload is discarded. + */ + void flush_all(const double timeout = 0.0) + { + increment_buffer_info(); //increment to next buffer + + for (size_t i = 0; i < _props.size(); i++) + { + while (true) //while (_props.at(i).get_buff(timeout)); + { + //receive a single packet from the transport + try + { + if (get_and_process_single_packet(i, + get_prev_buffer_info(), + get_curr_buffer_info(), + timeout) == PACKET_TIMEOUT_ERROR) break; + }catch(...){} + } + } + } + + /*! * Set the function to handle flow control * \param xport_chan which transport channel * \param handle_flowctrl the callback function @@ -213,7 +250,12 @@ public: buffs, nsamps_per_buff, metadata, timeout ); - if (one_packet) return accum_num_samps; + if (one_packet){ +#ifdef UHD_TXRX_DEBUG_PRINTS + dbg_gather_data(nsamps_per_buff, accum_num_samps, metadata, timeout, one_packet); +#endif + return accum_num_samps; + } //first recv had an error code set, return immediately if (metadata.error_code != rx_metadata_t::ERROR_CODE_NONE) return accum_num_samps; @@ -232,11 +274,13 @@ public: } accum_num_samps += num_samps; } +#ifdef UHD_TXRX_DEBUG_PRINTS + dbg_gather_data(nsamps_per_buff, accum_num_samps, metadata, timeout, one_packet); +#endif return accum_num_samps; } private: - vrt_unpacker_type _vrt_unpacker; size_t _header_offset_words32; double _tick_rate, _samp_rate; @@ -264,6 +308,13 @@ private: //! information stored for a received buffer struct per_buffer_info_type{ + void reset() + { + buff.reset(); + vrt_hdr = NULL; + time = time_spec_t(0.0); + copy_buff = NULL; + } managed_recv_buffer::sptr buff; const boost::uint32_t *vrt_hdr; vrt::if_packet_info_t ifpi; @@ -280,6 +331,17 @@ private: data_bytes_to_copy(0), fragment_offset_in_samps(0) {/* NOP */} + void reset() + { + indexes_todo.set(); + alignment_time = time_spec_t(0.0); + alignment_time_valid = false; + data_bytes_to_copy = 0; + fragment_offset_in_samps = 0; + metadata.reset(); + for (size_t i = 0; i < size(); i++) + at(i).reset(); + } boost::dynamic_bitset<> indexes_todo; //used in alignment logic time_spec_t alignment_time; //used in alignment logic bool alignment_time_valid; //used in alignment logic @@ -305,6 +367,10 @@ private: PACKET_SEQUENCE_ERROR }; + #ifdef ERROR_INJECT_DROPPED_PACKETS + int recvd_packets; + #endif + /******************************************************************* * Get and process a single packet from the transport: * Receive a single packet at the given index. @@ -322,6 +388,16 @@ private: buff = _props[index].get_buff(timeout); if (buff.get() == NULL) return PACKET_TIMEOUT_ERROR; + #ifdef ERROR_INJECT_DROPPED_PACKETS + if (++recvd_packets > 1000) + { + recvd_packets = 0; + buff.reset(); + buff = _props[index].get_buff(timeout); + if (buff.get() == NULL) return PACKET_TIMEOUT_ERROR; + } + #endif + //bounds check before extract size_t num_packet_words32 = buff->size()/sizeof(boost::uint32_t); if (num_packet_words32 <= _header_offset_words32){ @@ -339,7 +415,7 @@ private: //handle flow control if (_props[index].handle_flowctrl) { - if ((info.ifpi.packet_count % _props[index].fc_update_window/2) == 0) + if ((info.ifpi.packet_count % _props[index].fc_update_window) == 0) { _props[index].handle_flowctrl(info.ifpi.packet_count); } @@ -411,7 +487,10 @@ private: ******************************************************************/ UHD_INLINE void get_aligned_buffs(double timeout){ + get_prev_buffer_info().reset(); // no longer need the previous info - reset it for future use + increment_buffer_info(); //increment to next buffer + buffers_info_type &prev_info = get_prev_buffer_info(); buffers_info_type &curr_info = get_curr_buffer_info(); buffers_info_type &next_info = get_next_buffer_info(); @@ -440,12 +519,6 @@ private: "The receive packet handler caught an exception.\n%s" ) % e.what() << std::endl; std::swap(curr_info, next_info); //save progress from curr -> next - curr_info.metadata.has_time_spec = false; - curr_info.metadata.time_spec = time_spec_t(0.0); - curr_info.metadata.more_fragments = false; - curr_info.metadata.fragment_offset = 0; - curr_info.metadata.start_of_burst = false; - curr_info.metadata.end_of_burst = false; curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_BAD_PACKET; return; } @@ -470,10 +543,6 @@ private: std::swap(curr_info, next_info); //save progress from curr -> next curr_info.metadata.has_time_spec = next_info[index].ifpi.has_tsf; curr_info.metadata.time_spec = next_info[index].time; - curr_info.metadata.more_fragments = false; - curr_info.metadata.fragment_offset = 0; - curr_info.metadata.start_of_burst = false; - curr_info.metadata.end_of_burst = false; curr_info.metadata.error_code = rx_metadata_t::error_code_t(get_context_code(next_info[index].vrt_hdr, next_info[index].ifpi)); if (curr_info.metadata.error_code == rx_metadata_t::ERROR_CODE_OVERFLOW){ _props[index].handle_overflow(); @@ -483,12 +552,6 @@ private: case PACKET_TIMEOUT_ERROR: std::swap(curr_info, next_info); //save progress from curr -> next - curr_info.metadata.has_time_spec = false; - curr_info.metadata.time_spec = time_spec_t(0.0); - curr_info.metadata.more_fragments = false; - curr_info.metadata.fragment_offset = 0; - curr_info.metadata.start_of_burst = false; - curr_info.metadata.end_of_burst = false; curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_TIMEOUT; return; @@ -498,10 +561,7 @@ private: curr_info.metadata.has_time_spec = prev_info.metadata.has_time_spec; curr_info.metadata.time_spec = prev_info.metadata.time_spec + time_spec_t::from_ticks( prev_info[index].ifpi.num_payload_words32*sizeof(boost::uint32_t)/_bytes_per_otw_item, _samp_rate); - curr_info.metadata.more_fragments = false; - curr_info.metadata.fragment_offset = 0; - curr_info.metadata.start_of_burst = false; - curr_info.metadata.end_of_burst = false; + curr_info.metadata.out_of_sequence = true; curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_OVERFLOW; UHD_MSG(fastpath) << "D"; return; @@ -516,12 +576,6 @@ private: "However, a timestamp match could not be determined.\n" ) % iterations << std::endl; std::swap(curr_info, next_info); //save progress from curr -> next - curr_info.metadata.has_time_spec = false; - curr_info.metadata.time_spec = time_spec_t(0.0); - curr_info.metadata.more_fragments = false; - curr_info.metadata.fragment_offset = 0; - curr_info.metadata.start_of_burst = false; - curr_info.metadata.end_of_burst = false; curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_ALIGNMENT; _props[index].handle_overflow(); return; @@ -554,13 +608,8 @@ private: const size_t buffer_offset_bytes = 0 ){ //get the next buffer if the current one has expired - if (get_curr_buffer_info().data_bytes_to_copy == 0){ - - //reset current buffer info members for reuse - get_curr_buffer_info().fragment_offset_in_samps = 0; - get_curr_buffer_info().alignment_time_valid = false; - get_curr_buffer_info().indexes_todo.set(); - + if (get_curr_buffer_info().data_bytes_to_copy == 0) + { //perform receive with alignment logic get_aligned_buffs(timeout); } @@ -641,6 +690,65 @@ private: size_t _convert_buffer_offset_bytes; size_t _convert_bytes_to_copy; + /* + * This last section is only for debugging purposes. + * It causes a lot of prints to stderr which can be piped to a file. + * Gathered data can be used to post process it with external tools. + */ +#ifdef UHD_TXRX_DEBUG_PRINTS + struct dbg_recv_stat_t { + dbg_recv_stat_t(long wc, size_t nspb, size_t nsr, uhd::rx_metadata_t md, double to, bool op, double rate): + wallclock(wc), nsamps_per_buff(nspb), nsamps_recv(nsr), metadata(md), timeout(to), one_packet(op), samp_rate(rate) + {} + long wallclock; + size_t nsamps_per_buff; + size_t nsamps_recv; + uhd::rx_metadata_t metadata; + double timeout; + bool one_packet; + double samp_rate; + // Create a formatted print line for all the info gathered in this struct. + std::string print_line() { + boost::format fmt("recv,%ld,%f,%i,%i,%s,%i,%s,%s,%s,%i,%s,%ld"); + fmt % wallclock; + fmt % timeout % (int)nsamps_per_buff % (int) nsamps_recv; + fmt % (one_packet ? "true":"false"); + fmt % metadata.error_code; + fmt % (metadata.start_of_burst ? "true":"false") % (metadata.end_of_burst ? "true":"false"); + fmt % (metadata.more_fragments ? "true":"false") % (int)metadata.fragment_offset; + fmt % (metadata.has_time_spec ? "true":"false") % metadata.time_spec.to_ticks(samp_rate); + return fmt.str(); + } + }; + + void dbg_gather_data(const size_t nsamps_per_buff, const size_t nsamps_recv, + uhd::rx_metadata_t &metadata, const double timeout, + const bool one_packet, + bool dbg_print_directly = true + ) + { + // Initialize a struct with all available data. It can return a formatted string with all infos if wanted. + dbg_recv_stat_t data(boost::get_system_time().time_of_day().total_microseconds(), + nsamps_per_buff, + nsamps_recv, + metadata, + timeout, + one_packet, + _samp_rate + ); + if(dbg_print_directly) { + dbg_print_err(data.print_line()); + } + } + + + + void dbg_print_err(std::string msg) { + std::string dbg_prefix("super_recv_packet_handler,"); + msg = dbg_prefix + msg; + fprintf(stderr, "%s\n", msg.c_str()); + } +#endif }; class recv_packet_streamer : public recv_packet_handler, public rx_streamer{ diff --git a/host/lib/transport/super_send_packet_handler.hpp b/host/lib/transport/super_send_packet_handler.hpp index ae483d1f3..c2810842e 100644 --- a/host/lib/transport/super_send_packet_handler.hpp +++ b/host/lib/transport/super_send_packet_handler.hpp @@ -35,7 +35,18 @@ #include <iostream> #include <vector> -namespace uhd{ namespace transport{ namespace sph{ +#ifdef UHD_TXRX_DEBUG_PRINTS +// Included for debugging +#include <boost/format.hpp> +#include <boost/thread/thread.hpp> +#include "boost/date_time/posix_time/posix_time.hpp" +#include <map> +#include <fstream> +#endif + +namespace uhd { +namespace transport { +namespace sph { /*********************************************************************** * Super send packet handler @@ -56,7 +67,7 @@ public: * \param size the number of transport channels */ send_packet_handler(const size_t size = 1): - _next_packet_seq(0) + _next_packet_seq(0), _cached_metadata(false) { this->set_enable_trailer(true); this->resize(size); @@ -183,6 +194,23 @@ public: if_packet_info.sob = metadata.start_of_burst; if_packet_info.eob = metadata.end_of_burst; + /* + * Metadata is cached when we get a send requesting a start of burst with no samples. + * It is applied here on the next call to send() that actually has samples to send. + */ + if (_cached_metadata && nsamps_per_buff != 0) + { + // If the new metada has a time_spec, do not use the cached time_spec. + if (!metadata.has_time_spec) + { + if_packet_info.has_tsf = _metadata_cache.has_time_spec; + if_packet_info.tsf = _metadata_cache.time_spec.to_ticks(_tick_rate); + } + if_packet_info.sob = _metadata_cache.start_of_burst; + if_packet_info.eob = _metadata_cache.end_of_burst; + _cached_metadata = false; + } + if (nsamps_per_buff <= _max_samples_per_packet){ //TODO remove this code when sample counts of zero are supported by hardware @@ -190,13 +218,27 @@ public: static const boost::uint64_t zero = 0; _zero_buffs.resize(buffs.size(), &zero); - if (nsamps_per_buff == 0) return send_one_packet( - _zero_buffs, 1, if_packet_info, timeout - ) & 0x0; + if (nsamps_per_buff == 0) + { + // if this is a start of a burst and there are no samples + if (metadata.start_of_burst) + { + // cache metadata and apply on the next send() + _metadata_cache = metadata; + _cached_metadata = true; + return 0; + } else { + // send requests with no samples are handled here (such as end of burst) + return send_one_packet(_zero_buffs, 1, if_packet_info, timeout) & 0x0; + } + } #endif - return send_one_packet(buffs, nsamps_per_buff, if_packet_info, timeout); - } + size_t nsamps_sent = send_one_packet(buffs, nsamps_per_buff, if_packet_info, timeout); +#ifdef UHD_TXRX_DEBUG_PRINTS + dbg_print_send(nsamps_per_buff, nsamps_sent, metadata, timeout); +#endif + return nsamps_sent; } size_t total_num_samps_sent = 0; //false until final fragment @@ -226,11 +268,14 @@ public: //send the final fragment with the helper function if_packet_info.eob = metadata.end_of_burst; - return total_num_samps_sent + send_one_packet( - buffs, final_length, - if_packet_info, timeout, - total_num_samps_sent*_bytes_per_cpu_item - ); + size_t nsamps_sent = total_num_samps_sent + + send_one_packet(buffs, final_length, if_packet_info, timeout, + total_num_samps_sent * _bytes_per_cpu_item); +#ifdef UHD_TXRX_DEBUG_PRINTS + dbg_print_send(nsamps_per_buff, nsamps_sent, metadata, timeout); + +#endif + return nsamps_sent; } private: @@ -255,6 +300,53 @@ private: size_t _next_packet_seq; bool _has_tlr; async_receiver_type _async_receiver; + bool _cached_metadata; + uhd::tx_metadata_t _metadata_cache; + +#ifdef UHD_TXRX_DEBUG_PRINTS + struct dbg_send_stat_t { + dbg_send_stat_t(long wc, size_t nspb, size_t nss, uhd::tx_metadata_t md, double to, double rate): + wallclock(wc), nsamps_per_buff(nspb), nsamps_sent(nss), metadata(md), timeout(to), samp_rate(rate) + {} + long wallclock; + size_t nsamps_per_buff; + size_t nsamps_sent; + uhd::tx_metadata_t metadata; + double timeout; + double samp_rate; + // Create a formatted print line for all the info gathered in this struct. + std::string print_line() { + boost::format fmt("send,%ld,%f,%i,%i,%s,%s,%s,%ld"); + fmt % wallclock; + fmt % timeout % (int)nsamps_per_buff % (int) nsamps_sent; + fmt % (metadata.start_of_burst ? "true":"false") % (metadata.end_of_burst ? "true":"false"); + fmt % (metadata.has_time_spec ? "true":"false") % metadata.time_spec.to_ticks(samp_rate); + return fmt.str(); + } + }; + + void dbg_print_send(size_t nsamps_per_buff, size_t nsamps_sent, + const uhd::tx_metadata_t &metadata, const double timeout, + bool dbg_print_directly = true) + { + dbg_send_stat_t data(boost::get_system_time().time_of_day().total_microseconds(), + nsamps_per_buff, + nsamps_sent, + metadata, + timeout, + _samp_rate + ); + if(dbg_print_directly){ + dbg_print_err(data.print_line()); + } + } + void dbg_print_err(std::string msg) { + msg = "super_send_packet_handler," + msg; + fprintf(stderr, "%s\n", msg.c_str()); + } + + +#endif /******************************************************************* * Send a single packet: @@ -266,6 +358,7 @@ private: const double timeout, const size_t buffer_offset_bytes = 0 ){ + //load the rest of the if_packet_info in here if_packet_info.num_payload_bytes = nsamps_per_buff*_num_inputs*_bytes_per_otw_item; if_packet_info.num_payload_words32 = (if_packet_info.num_payload_bytes + 3/*round up*/)/sizeof(boost::uint32_t); @@ -374,6 +467,8 @@ private: size_t _max_num_samps; }; -}}} //namespace +} // namespace sph +} // namespace transport +} // namespace uhd #endif /* INCLUDED_LIBUHD_TRANSPORT_SUPER_SEND_PACKET_HANDLER_HPP */ diff --git a/host/lib/transport/tcp_zero_copy.cpp b/host/lib/transport/tcp_zero_copy.cpp new file mode 100644 index 000000000..402bda1e8 --- /dev/null +++ b/host/lib/transport/tcp_zero_copy.cpp @@ -0,0 +1,225 @@ +// +// Copyright 2010-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "udp_common.hpp" +#include <uhd/transport/tcp_zero_copy.hpp> +#include <uhd/transport/buffer_pool.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/atomic.hpp> +#include <boost/format.hpp> +#include <boost/make_shared.hpp> +#include <boost/thread/thread.hpp> //sleep +#include <vector> + +using namespace uhd; +using namespace uhd::transport; +namespace asio = boost::asio; + +static const size_t DEFAULT_NUM_FRAMES = 32; +static const size_t DEFAULT_FRAME_SIZE = 2048; + +/*********************************************************************** + * Reusable managed receiver buffer: + * - get_new performs the recv operation + **********************************************************************/ +class tcp_zero_copy_asio_mrb : public managed_recv_buffer{ +public: + tcp_zero_copy_asio_mrb(void *mem, int sock_fd, const size_t frame_size): + _mem(mem), _sock_fd(sock_fd), _frame_size(frame_size) { /*NOP*/ } + + void release(void){ + _claimer.release(); + } + + UHD_INLINE sptr get_new(const double timeout, size_t &index){ + if (not _claimer.claim_with_wait(timeout)) return sptr(); + + #ifdef MSG_DONTWAIT //try a non-blocking recv() if supported + _len = ::recv(_sock_fd, (char *)_mem, _frame_size, MSG_DONTWAIT); + if (_len > 0){ + index++; //advances the caller's buffer + return make(this, _mem, size_t(_len)); + } + #endif + + if (wait_for_recv_ready(_sock_fd, timeout)){ + _len = ::recv(_sock_fd, (char *)_mem, _frame_size, 0); + index++; //advances the caller's buffer + return make(this, _mem, size_t(_len)); + } + + _claimer.release(); //undo claim + return sptr(); //null for timeout + } + +private: + void *_mem; + int _sock_fd; + size_t _frame_size; + ssize_t _len; + simple_claimer _claimer; +}; + +/*********************************************************************** + * Reusable managed send buffer: + * - commit performs the send operation + **********************************************************************/ +class tcp_zero_copy_asio_msb : public managed_send_buffer{ +public: + tcp_zero_copy_asio_msb(void *mem, int sock_fd, const size_t frame_size): + _mem(mem), _sock_fd(sock_fd), _frame_size(frame_size) { /*NOP*/ } + + void release(void){ + //Retry logic because send may fail with ENOBUFS. + //This is known to occur at least on some OSX systems. + //But it should be safe to always check for the error. + while (true) + { + this->commit(_frame_size); //always full size frames to avoid pkt coalescing + const ssize_t ret = ::send(_sock_fd, (const char *)_mem, size(), 0); + if (ret == ssize_t(size())) break; + if (ret == -1 and errno == ENOBUFS) + { + boost::this_thread::sleep(boost::posix_time::microseconds(1)); + continue; //try to send again + } + UHD_ASSERT_THROW(ret == ssize_t(size())); + } + _claimer.release(); + } + + UHD_INLINE sptr get_new(const double timeout, size_t &index){ + if (not _claimer.claim_with_wait(timeout)) return sptr(); + index++; //advances the caller's buffer + return make(this, _mem, _frame_size); + } + +private: + void *_mem; + int _sock_fd; + size_t _frame_size; + simple_claimer _claimer; +}; + +/*********************************************************************** + * Zero Copy TCP implementation with ASIO: + * This is the portable zero copy implementation for systems + * where a faster, platform specific solution is not available. + * However, it is not a true zero copy implementation as each + * send and recv requires a copy operation to/from userspace. + **********************************************************************/ +class tcp_zero_copy_asio_impl : public tcp_zero_copy{ +public: + typedef boost::shared_ptr<tcp_zero_copy_asio_impl> sptr; + + tcp_zero_copy_asio_impl( + const std::string &addr, + const std::string &port, + const device_addr_t &hints + ): + _recv_frame_size(size_t(hints.cast<double>("recv_frame_size", DEFAULT_FRAME_SIZE))), + _num_recv_frames(size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_FRAMES))), + _send_frame_size(size_t(hints.cast<double>("send_frame_size", DEFAULT_FRAME_SIZE))), + _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_FRAMES))), + _recv_buffer_pool(buffer_pool::make(_num_recv_frames, _recv_frame_size)), + _send_buffer_pool(buffer_pool::make(_num_send_frames, _send_frame_size)), + _next_recv_buff_index(0), _next_send_buff_index(0) + { + UHD_LOG << boost::format("Creating tcp transport for %s %s") % addr % port << std::endl; + + //resolve the address + asio::ip::tcp::resolver resolver(_io_service); + asio::ip::tcp::resolver::query query(asio::ip::tcp::v4(), addr, port); + asio::ip::tcp::endpoint receiver_endpoint = *resolver.resolve(query); + + //create, open, and connect the socket + _socket.reset(new asio::ip::tcp::socket(_io_service)); + _socket->connect(receiver_endpoint); + _sock_fd = _socket->native(); + + //packets go out ASAP + asio::ip::tcp::no_delay option(true); + _socket->set_option(option); + + //allocate re-usable managed receive buffers + for (size_t i = 0; i < get_num_recv_frames(); i++){ + _mrb_pool.push_back(boost::make_shared<tcp_zero_copy_asio_mrb>( + _recv_buffer_pool->at(i), _sock_fd, get_recv_frame_size() + )); + } + + //allocate re-usable managed send buffers + for (size_t i = 0; i < get_num_send_frames(); i++){ + _msb_pool.push_back(boost::make_shared<tcp_zero_copy_asio_msb>( + _send_buffer_pool->at(i), _sock_fd, get_send_frame_size() + )); + } + } + + /******************************************************************* + * Receive implementation: + * Block on the managed buffer's get call and advance the index. + ******************************************************************/ + managed_recv_buffer::sptr get_recv_buff(double timeout){ + if (_next_recv_buff_index == _num_recv_frames) _next_recv_buff_index = 0; + return _mrb_pool[_next_recv_buff_index]->get_new(timeout, _next_recv_buff_index); + } + + size_t get_num_recv_frames(void) const {return _num_recv_frames;} + size_t get_recv_frame_size(void) const {return _recv_frame_size;} + + /******************************************************************* + * Send implementation: + * Block on the managed buffer's get call and advance the index. + ******************************************************************/ + managed_send_buffer::sptr get_send_buff(double timeout){ + if (_next_send_buff_index == _num_send_frames) _next_send_buff_index = 0; + return _msb_pool[_next_send_buff_index]->get_new(timeout, _next_send_buff_index); + } + + size_t get_num_send_frames(void) const {return _num_send_frames;} + size_t get_send_frame_size(void) const {return _send_frame_size;} + +private: + //memory management -> buffers and fifos + const size_t _recv_frame_size, _num_recv_frames; + const size_t _send_frame_size, _num_send_frames; + buffer_pool::sptr _recv_buffer_pool, _send_buffer_pool; + std::vector<boost::shared_ptr<tcp_zero_copy_asio_msb> > _msb_pool; + std::vector<boost::shared_ptr<tcp_zero_copy_asio_mrb> > _mrb_pool; + size_t _next_recv_buff_index, _next_send_buff_index; + + //asio guts -> socket and service + asio::io_service _io_service; + boost::shared_ptr<asio::ip::tcp::socket> _socket; + int _sock_fd; +}; + +/*********************************************************************** + * TCP zero copy make function + **********************************************************************/ +zero_copy_if::sptr tcp_zero_copy::make( + const std::string &addr, + const std::string &port, + const device_addr_t &hints +){ + zero_copy_if::sptr xport; + xport.reset(new tcp_zero_copy_asio_impl(addr, port, hints)); + while (xport->get_recv_buff(0.0)){} //flush + return xport; +} diff --git a/host/lib/transport/udp_wsa_zero_copy.cpp b/host/lib/transport/udp_wsa_zero_copy.cpp index 6fe4e3cad..031d26374 100644 --- a/host/lib/transport/udp_wsa_zero_copy.cpp +++ b/host/lib/transport/udp_wsa_zero_copy.cpp @@ -182,14 +182,15 @@ public: udp_zero_copy_wsa_impl( const std::string &addr, const std::string &port, + zero_copy_xport_params& xport_params, const device_addr_t &hints ): - _recv_frame_size(size_t(hints.cast<double>("recv_frame_size", udp_simple::mtu))), - _num_recv_frames(size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_FRAMES))), - _send_frame_size(size_t(hints.cast<double>("send_frame_size", udp_simple::mtu))), - _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_FRAMES))), - _recv_buffer_pool(buffer_pool::make(_num_recv_frames, _recv_frame_size)), - _send_buffer_pool(buffer_pool::make(_num_send_frames, _send_frame_size)), + _recv_frame_size(xport_params.recv_frame_size), + _num_recv_frames(xport_params.num_recv_frames), + _send_frame_size(xport_params.send_frame_size), + _num_send_frames(xport_params.num_send_frames), + _recv_buffer_pool(buffer_pool::make(xport_params.num_recv_frames, xport_params.recv_frame_size)), + _send_buffer_pool(buffer_pool::make(xport_params.num_send_frames, xport_params.send_frame_size)), _next_recv_buff_index(0), _next_send_buff_index(0) { #ifdef CHECK_REG_SEND_THRESH @@ -294,7 +295,17 @@ private: udp_zero_copy::sptr udp_zero_copy::make( const std::string &addr, const std::string &port, + const zero_copy_xport_params &default_buff_args, + udp_zero_copy::buff_params& buff_params_out, const device_addr_t &hints ){ - return sptr(new udp_zero_copy_wsa_impl(addr, port, hints)); + //Initialize xport_params + zero_copy_xport_params xport_params = default_buff_args; + + xport_params.recv_frame_size = size_t(hints.cast<double>("recv_frame_size", default_buff_args.recv_frame_size)); + xport_params.num_recv_frames = size_t(hints.cast<double>("num_recv_frames", default_buff_args.num_recv_frames)); + xport_params.send_frame_size = size_t(hints.cast<double>("send_frame_size", default_buff_args.send_frame_size)); + xport_params.num_send_frames = size_t(hints.cast<double>("num_send_frames", default_buff_args.num_send_frames)); + + return sptr(new udp_zero_copy_wsa_impl(addr, port, xport_params, hints)); } diff --git a/host/lib/transport/udp_zero_copy.cpp b/host/lib/transport/udp_zero_copy.cpp index 7b6a476f5..adc7d5585 100644 --- a/host/lib/transport/udp_zero_copy.cpp +++ b/host/lib/transport/udp_zero_copy.cpp @@ -158,14 +158,14 @@ public: udp_zero_copy_asio_impl( const std::string &addr, const std::string &port, - const device_addr_t &hints + const zero_copy_xport_params& xport_params ): - _recv_frame_size(size_t(hints.cast<double>("recv_frame_size", udp_simple::mtu))), - _num_recv_frames(size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_FRAMES))), - _send_frame_size(size_t(hints.cast<double>("send_frame_size", udp_simple::mtu))), - _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_FRAMES))), - _recv_buffer_pool(buffer_pool::make(_num_recv_frames, _recv_frame_size)), - _send_buffer_pool(buffer_pool::make(_num_send_frames, _send_frame_size)), + _recv_frame_size(xport_params.recv_frame_size), + _num_recv_frames(xport_params.num_recv_frames), + _send_frame_size(xport_params.send_frame_size), + _num_send_frames(xport_params.num_send_frames), + _recv_buffer_pool(buffer_pool::make(xport_params.num_recv_frames, xport_params.recv_frame_size)), + _send_buffer_pool(buffer_pool::make(xport_params.num_send_frames, xport_params.send_frame_size)), _next_recv_buff_index(0), _next_send_buff_index(0) { UHD_LOG << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; @@ -256,11 +256,12 @@ private: /*********************************************************************** * UDP zero copy make function **********************************************************************/ -template<typename Opt> static void resize_buff_helper( +template<typename Opt> static size_t resize_buff_helper( udp_zero_copy_asio_impl::sptr udp_trans, const size_t target_size, const std::string &name ){ + size_t actual_size = 0; std::string help_message; #if defined(UHD_PLATFORM_LINUX) help_message = str(boost::format( @@ -270,7 +271,7 @@ template<typename Opt> static void resize_buff_helper( //resize the buffer if size was provided if (target_size > 0){ - size_t actual_size = udp_trans->resize_buff<Opt>(target_size); + actual_size = udp_trans->resize_buff<Opt>(target_size); UHD_LOG << boost::format( "Target %s sock buff size: %d bytes\n" "Actual %s sock buff size: %d bytes" @@ -282,24 +283,54 @@ template<typename Opt> static void resize_buff_helper( "See the transport application notes on buffer resizing.\n%s" ) % name % target_size % actual_size % help_message; } + + return actual_size; } udp_zero_copy::sptr udp_zero_copy::make( const std::string &addr, const std::string &port, + const zero_copy_xport_params &default_buff_args, + udp_zero_copy::buff_params& buff_params_out, const device_addr_t &hints ){ - udp_zero_copy_asio_impl::sptr udp_trans( - new udp_zero_copy_asio_impl(addr, port, hints) - ); + //Initialize xport_params + zero_copy_xport_params xport_params = default_buff_args; + + xport_params.recv_frame_size = size_t(hints.cast<double>("recv_frame_size", default_buff_args.recv_frame_size)); + xport_params.num_recv_frames = size_t(hints.cast<double>("num_recv_frames", default_buff_args.num_recv_frames)); + xport_params.send_frame_size = size_t(hints.cast<double>("send_frame_size", default_buff_args.send_frame_size)); + xport_params.num_send_frames = size_t(hints.cast<double>("num_send_frames", default_buff_args.num_send_frames)); //extract buffer size hints from the device addr - size_t recv_buff_size = size_t(hints.cast<double>("recv_buff_size", 0.0)); - size_t send_buff_size = size_t(hints.cast<double>("send_buff_size", 0.0)); + size_t usr_recv_buff_size = size_t(hints.cast<double>("recv_buff_size", 0.0)); + size_t usr_send_buff_size = size_t(hints.cast<double>("send_buff_size", 0.0)); + + if (hints.has_key("recv_buff_size")) { + if (usr_recv_buff_size < xport_params.recv_frame_size * xport_params.num_recv_frames) { + throw uhd::value_error((boost::format( + "recv_buff_size must be equal to or greater than (num_recv_frames * recv_frame_size) where num_recv_frames=%d, recv_frame_size=%d") + % xport_params.num_recv_frames % xport_params.recv_frame_size).str()); + } + } + + if (hints.has_key("send_buff_size")) { + if (usr_send_buff_size < xport_params.send_frame_size * xport_params.num_send_frames) { + throw uhd::value_error((boost::format( + "send_buff_size must be equal to or greater than (num_send_frames * send_frame_size) where num_send_frames=%d, send_frame_size=%d") + % xport_params.num_send_frames % xport_params.send_frame_size).str()); + } + } + + udp_zero_copy_asio_impl::sptr udp_trans( + new udp_zero_copy_asio_impl(addr, port, xport_params) + ); //call the helper to resize send and recv buffers - resize_buff_helper<asio::socket_base::receive_buffer_size>(udp_trans, recv_buff_size, "recv"); - resize_buff_helper<asio::socket_base::send_buffer_size> (udp_trans, send_buff_size, "send"); + buff_params_out.recv_buff_size = + resize_buff_helper<asio::socket_base::receive_buffer_size>(udp_trans, usr_recv_buff_size, "recv"); + buff_params_out.send_buff_size = + resize_buff_helper<asio::socket_base::send_buffer_size> (udp_trans, usr_send_buff_size, "send"); return udp_trans; } diff --git a/host/lib/transport/xport_benchmarker.cpp b/host/lib/transport/xport_benchmarker.cpp new file mode 100644 index 000000000..d58dbea47 --- /dev/null +++ b/host/lib/transport/xport_benchmarker.cpp @@ -0,0 +1,155 @@ +// +// Copyright 2010-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "xport_benchmarker.hpp" + +namespace uhd { namespace transport { + +const device_addr_t& xport_benchmarker::benchmark_throughput_chdr +( + zero_copy_if::sptr tx_transport, + zero_copy_if::sptr rx_transport, + boost::uint32_t sid, + bool big_endian, + boost::uint32_t duration_ms) +{ + vrt::if_packet_info_t pkt_info; + _initialize_chdr(tx_transport, rx_transport, sid, pkt_info); + _reset_counters(); + boost::posix_time::ptime start_time(boost::posix_time::microsec_clock::local_time()); + + _tx_thread.reset(new boost::thread(boost::bind(&xport_benchmarker::_stream_tx, this, tx_transport.get(), &pkt_info, big_endian))); + _rx_thread.reset(new boost::thread(boost::bind(&xport_benchmarker::_stream_rx, this, rx_transport.get(), &pkt_info, big_endian))); + + boost::this_thread::sleep(boost::posix_time::milliseconds(duration_ms)); + + _tx_thread->interrupt(); + _rx_thread->interrupt(); + _tx_thread->join(); + _rx_thread->join(); + + boost::posix_time::ptime stop_time(boost::posix_time::microsec_clock::local_time()); + double duration_s = ((double)(stop_time-start_time).total_microseconds())/1e6; + + boost::uint64_t tx_bytes = pkt_info.num_payload_words32*sizeof(uint32_t)*_num_tx_packets; + boost::uint64_t rx_bytes = pkt_info.num_payload_words32*sizeof(uint32_t)*_num_rx_packets; + double tx_rate = (((double)tx_bytes)/duration_s); + double rx_rate = (((double)rx_bytes)/duration_s); + + _results["TX-Bytes"] = (boost::format("%.2fMB") % (tx_bytes/(1024*1024))).str(); + _results["RX-Bytes"] = (boost::format("%.2fMB") % (rx_bytes/(1024*1024))).str(); + _results["TX-Throughput"] = (boost::format("%.2fMB/s") % (tx_rate/(1024*1024))).str(); + _results["RX-Throughput"] = (boost::format("%.2fMB/s") % (rx_rate/(1024*1024))).str(); + _results["TX-Timeouts"] = boost::lexical_cast<std::string>(_num_tx_timeouts); + _results["RX-Timeouts"] = boost::lexical_cast<std::string>(_num_rx_timeouts); + _results["Data-Errors"] = boost::lexical_cast<std::string>(_num_data_errors); + + return _results; +} + +void xport_benchmarker::_stream_tx(zero_copy_if* transport, vrt::if_packet_info_t* pkt_info, bool big_endian) +{ + while (not boost::this_thread::interruption_requested()) { + managed_send_buffer::sptr buff = transport->get_send_buff(_tx_timeout); + if (buff) { + boost::uint32_t *packet_buff = buff->cast<boost::uint32_t *>(); + //Populate packet + if (big_endian) { + vrt::if_hdr_pack_be(packet_buff, *pkt_info); + } else { + vrt::if_hdr_pack_le(packet_buff, *pkt_info); + } + //send the buffer over the interface + buff->commit(sizeof(boost::uint32_t)*(pkt_info->num_packet_words32)); + _num_tx_packets++; + } else { + _num_tx_timeouts++; + } + } +} + +void xport_benchmarker::_stream_rx(zero_copy_if* transport, const vrt::if_packet_info_t* exp_pkt_info, bool big_endian) +{ + while (not boost::this_thread::interruption_requested()) { + managed_recv_buffer::sptr buff = transport->get_recv_buff(_rx_timeout); + if (buff) { + //Extract packet info + vrt::if_packet_info_t pkt_info; + pkt_info.link_type = exp_pkt_info->link_type; + pkt_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); + const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>(); + + _num_rx_packets++; + + //unpacking can fail + try { + if (big_endian) { + vrt::if_hdr_unpack_be(packet_buff, pkt_info); + } else { + vrt::if_hdr_unpack_le(packet_buff, pkt_info); + } + + if (exp_pkt_info->packet_type != pkt_info.packet_type || + exp_pkt_info->num_payload_bytes != pkt_info.num_payload_bytes) { + _num_data_errors++; + } + } catch(const std::exception &ex) { + _num_data_errors++; + } + } else { + _num_rx_timeouts++; + } + } +} + +void xport_benchmarker::_reset_counters(void) +{ + _num_tx_packets = 0; + _num_rx_packets = 0; + _num_tx_timeouts = 0; + _num_rx_timeouts = 0; + _num_data_errors = 0; +} + +void xport_benchmarker::_initialize_chdr( + zero_copy_if::sptr tx_transport, + zero_copy_if::sptr rx_transport, + boost::uint32_t sid, + vrt::if_packet_info_t& pkt_info) +{ + _tx_timeout = 0.5; + _rx_timeout = 0.5; + + size_t frame_size = std::min(tx_transport->get_send_frame_size(), rx_transport->get_recv_frame_size()); + + pkt_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; + pkt_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA; + pkt_info.num_packet_words32 = (frame_size/sizeof(boost::uint32_t)); + pkt_info.num_payload_words32 = pkt_info.num_packet_words32 - 2; + pkt_info.num_payload_bytes = pkt_info.num_payload_words32*sizeof(boost::uint32_t); + pkt_info.packet_count = 0; + pkt_info.sob = false; + pkt_info.eob = false; + pkt_info.sid = sid; + pkt_info.has_sid = true; + pkt_info.has_cid = false; + pkt_info.has_tsi = false; + pkt_info.has_tsf = false; + pkt_info.has_tlr = false; +} + +}} diff --git a/host/lib/transport/xport_benchmarker.hpp b/host/lib/transport/xport_benchmarker.hpp new file mode 100644 index 000000000..9fca8d1fb --- /dev/null +++ b/host/lib/transport/xport_benchmarker.hpp @@ -0,0 +1,77 @@ +// +// Copyright 2010-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_XPORT_BENCHMARKER_HPP +#define INCLUDED_LIBUHD_XPORT_BENCHMARKER_HPP + +#include <uhd/transport/zero_copy.hpp> +#include <uhd/types/device_addr.hpp> +#include <uhd/utils/msg.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/thread/thread.hpp> +#include <uhd/transport/vrt_if_packet.hpp> + +namespace uhd { namespace transport { + +//Test class to benchmark a low-level transport object with a VITA/C-VITA data stream +class xport_benchmarker : boost::noncopyable { +public: + const device_addr_t& benchmark_throughput_chdr( + zero_copy_if::sptr tx_transport, + zero_copy_if::sptr rx_transport, + boost::uint32_t sid, + bool big_endian, + boost::uint32_t duration_ms); + +private: + void _stream_tx( + zero_copy_if* transport, + vrt::if_packet_info_t* pkt_info, + bool big_endian); + + void _stream_rx( + zero_copy_if* transport, + const vrt::if_packet_info_t* exp_pkt_info, + bool big_endian); + + void _initialize_chdr( + zero_copy_if::sptr tx_transport, + zero_copy_if::sptr rx_transport, + boost::uint32_t sid, + vrt::if_packet_info_t& pkt_info); + + void _reset_counters(void); + + boost::shared_ptr<boost::thread> _tx_thread; + boost::shared_ptr<boost::thread> _rx_thread; + + boost::uint64_t _num_tx_packets; + boost::uint64_t _num_rx_packets; + boost::uint64_t _num_tx_timeouts; + boost::uint64_t _num_rx_timeouts; + boost::uint64_t _num_data_errors; + + double _tx_timeout; + double _rx_timeout; + + device_addr_t _results; +}; + + +}} //namespace + +#endif /* INCLUDED_LIBUHD_XPORT_BENCHMARKER_HPP */ diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt index f8c817df5..c8c2e6a8d 100644 --- a/host/lib/usrp/CMakeLists.txt +++ b/host/lib/usrp/CMakeLists.txt @@ -37,4 +37,5 @@ INCLUDE_SUBDIRECTORY(usrp1) INCLUDE_SUBDIRECTORY(usrp2) INCLUDE_SUBDIRECTORY(b100) INCLUDE_SUBDIRECTORY(e100) +INCLUDE_SUBDIRECTORY(x300) INCLUDE_SUBDIRECTORY(b200) diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp index 305ba42a7..a47856b07 100644 --- a/host/lib/usrp/b100/b100_impl.cpp +++ b/host/lib/usrp/b100/b100_impl.cpp @@ -52,9 +52,9 @@ static device_addrs_t b100_find(const device_addr_t &hint) //return an empty list of addresses when type is set to non-b100 if (hint.has_key("type") and hint["type"] != "b100") return b100_addrs; - //Return an empty list of addresses when an address is specified, - //since an address is intended for a different, non-USB, device. - if (hint.has_key("addr")) return b100_addrs; + //Return an empty list of addresses when an address or resource is specified, + //since an address and resource is intended for a different, non-USB, device. + if (hint.has_key("addr") || hint.has_key("resource")) return b100_addrs; unsigned int vid, pid; diff --git a/host/lib/usrp/b100/b100_impl.hpp b/host/lib/usrp/b100/b100_impl.hpp index ab83c80a3..7d71d5ec3 100644 --- a/host/lib/usrp/b100/b100_impl.hpp +++ b/host/lib/usrp/b100/b100_impl.hpp @@ -105,8 +105,6 @@ public: bool recv_async_msg(uhd::async_metadata_t &, double); private: - uhd::property_tree::sptr _tree; - //controllers fifo_ctrl_excelsior::sptr _fifo_ctrl; i2c_core_200::sptr _fpga_i2c_ctrl; @@ -129,11 +127,6 @@ private: uhd::usrp::dboard_manager::sptr _dboard_manager; uhd::usrp::dboard_iface::sptr _dboard_iface; - //device properties interface - uhd::property_tree::sptr get_tree(void) const{ - return _tree; - } - std::vector<boost::weak_ptr<uhd::rx_streamer> > _rx_streamers; std::vector<boost::weak_ptr<uhd::tx_streamer> > _tx_streamers; diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp index ab437af6b..8ed8e99af 100644 --- a/host/lib/usrp/b200/b200_impl.cpp +++ b/host/lib/usrp/b200/b200_impl.cpp @@ -53,9 +53,9 @@ static device_addrs_t b200_find(const device_addr_t &hint) //return an empty list of addresses when type is set to non-b200 if (hint.has_key("type") and hint["type"] != "b200") return b200_addrs; - //Return an empty list of addresses when an address is specified, - //since an address is intended for a different, non-USB, device. - if (hint.has_key("addr")) return b200_addrs; + //Return an empty list of addresses when an address or resource is specified, + //since an address and resource is intended for a different, non-USB, device. + if (hint.has_key("addr") || hint.has_key("resource")) return b200_addrs; unsigned int vid, pid; @@ -332,7 +332,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) data_xport_args // param hints ); while (_data_transport->get_recv_buff(0.0)){} //flush ctrl xport - _demux.reset(new recv_packet_demuxer_3000(_data_transport)); + _demux = recv_packet_demuxer_3000::make(_data_transport); //////////////////////////////////////////////////////////////////// // Init codec - turns on clocks diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp index bee42679b..c88d14ad5 100644 --- a/host/lib/usrp/b200/b200_impl.hpp +++ b/host/lib/usrp/b200/b200_impl.hpp @@ -80,8 +80,9 @@ static const boost::uint32_t B200_LOCAL_RESP_SID = FLIP_SID(B200_LOCAL_CTRL_SID) **********************************************************************/ //! Implementation guts -struct b200_impl : public uhd::device +class b200_impl : public uhd::device { +public: //structors b200_impl(const uhd::device_addr_t &); ~b200_impl(void); @@ -91,8 +92,7 @@ struct b200_impl : public uhd::device uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args); bool recv_async_msg(uhd::async_metadata_t &, double); - uhd::property_tree::sptr _tree; - +private: //controllers b200_iface::sptr _iface; radio_ctrl_core_3000::sptr _local_ctrl; @@ -104,13 +104,7 @@ struct b200_impl : public uhd::device //transports uhd::transport::zero_copy_if::sptr _data_transport; uhd::transport::zero_copy_if::sptr _ctrl_transport; - boost::shared_ptr<uhd::usrp::recv_packet_demuxer_3000> _demux; - - //device properties interface - uhd::property_tree::sptr get_tree(void) const - { - return _tree; - } + uhd::usrp::recv_packet_demuxer_3000::sptr _demux; boost::weak_ptr<uhd::rx_streamer> _rx_streamer; boost::weak_ptr<uhd::tx_streamer> _tx_streamer; diff --git a/host/lib/usrp/common/recv_packet_demuxer_3000.hpp b/host/lib/usrp/common/recv_packet_demuxer_3000.hpp index 77bdebab0..ec930f3ad 100644 --- a/host/lib/usrp/common/recv_packet_demuxer_3000.hpp +++ b/host/lib/usrp/common/recv_packet_demuxer_3000.hpp @@ -28,11 +28,18 @@ #include <uhd/utils/byteswap.hpp> #include <queue> #include <map> +#include <boost/enable_shared_from_this.hpp> namespace uhd{ namespace usrp{ - struct recv_packet_demuxer_3000 + struct recv_packet_demuxer_3000 : boost::enable_shared_from_this<recv_packet_demuxer_3000> { + typedef boost::shared_ptr<recv_packet_demuxer_3000> sptr; + static sptr make(transport::zero_copy_if::sptr xport) + { + return sptr(new recv_packet_demuxer_3000(xport)); + } + recv_packet_demuxer_3000(transport::zero_copy_if::sptr xport): _xport(xport) {/*NOP*/} @@ -120,6 +127,8 @@ namespace uhd{ namespace usrp{ } } + transport::zero_copy_if::sptr make_proxy(const boost::uint32_t sid); + typedef std::queue<transport::managed_recv_buffer::sptr> queue_type_t; std::map<boost::uint32_t, queue_type_t> _queues; transport::zero_copy_if::sptr _xport; @@ -130,6 +139,42 @@ namespace uhd{ namespace usrp{ boost::mutex mutex; }; + struct recv_packet_demuxer_proxy_3000 : transport::zero_copy_if + { + recv_packet_demuxer_proxy_3000(recv_packet_demuxer_3000::sptr demux, transport::zero_copy_if::sptr xport, const boost::uint32_t sid): + _demux(demux), _xport(xport), _sid(sid) + { + _demux->realloc_sid(_sid); //causes clear + } + + ~recv_packet_demuxer_proxy_3000(void) + { + _demux->realloc_sid(_sid); //causes clear + } + + size_t get_num_recv_frames(void) const {return _xport->get_num_recv_frames();} + size_t get_recv_frame_size(void) const {return _xport->get_recv_frame_size();} + transport::managed_recv_buffer::sptr get_recv_buff(double timeout) + { + return _demux->get_recv_buff(_sid, timeout); + } + size_t get_num_send_frames(void) const {return _xport->get_num_send_frames();} + size_t get_send_frame_size(void) const {return _xport->get_send_frame_size();} + transport::managed_send_buffer::sptr get_send_buff(double timeout) + { + return _xport->get_send_buff(timeout); + } + + recv_packet_demuxer_3000::sptr _demux; + transport::zero_copy_if::sptr _xport; + const boost::uint32_t _sid; + }; + + inline transport::zero_copy_if::sptr recv_packet_demuxer_3000::make_proxy(const boost::uint32_t sid) + { + return transport::zero_copy_if::sptr(new recv_packet_demuxer_proxy_3000(this->shared_from_this(), _xport, sid)); + } + }} //namespace uhd::usrp #endif /* INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_3000_HPP */ diff --git a/host/lib/usrp/cores/gpio_core_200.cpp b/host/lib/usrp/cores/gpio_core_200.cpp index 51c23aa4b..e55f1f51e 100644 --- a/host/lib/usrp/cores/gpio_core_200.cpp +++ b/host/lib/usrp/cores/gpio_core_200.cpp @@ -45,8 +45,8 @@ public: void set_gpio_ddr(const unit_t unit, const boost::uint16_t value){ _gpio_ddr[unit] = value; //shadow _iface->poke32(REG_GPIO_DDR, //update the 32 bit register - (boost::uint32_t(_gpio_ddr[dboard_iface::UNIT_RX]) << unit2shit(dboard_iface::UNIT_RX)) | - (boost::uint32_t(_gpio_ddr[dboard_iface::UNIT_TX]) << unit2shit(dboard_iface::UNIT_TX)) + (boost::uint32_t(_gpio_ddr[dboard_iface::UNIT_RX]) << shift_by_unit(dboard_iface::UNIT_RX)) | + (boost::uint32_t(_gpio_ddr[dboard_iface::UNIT_TX]) << shift_by_unit(dboard_iface::UNIT_TX)) ); } @@ -56,7 +56,7 @@ public: } boost::uint16_t read_gpio(const unit_t unit){ - return boost::uint16_t(_iface->peek32(_rb_addr) >> unit2shit(unit)); + return boost::uint16_t(_iface->peek32(_rb_addr) >> shift_by_unit(unit)); } private: @@ -68,7 +68,7 @@ private: uhd::dict<unit_t, boost::uint16_t> _pin_ctrl, _gpio_out, _gpio_ddr; uhd::dict<unit_t, uhd::dict<atr_reg_t, boost::uint16_t> > _atr_regs; - unsigned unit2shit(const unit_t unit){ + unsigned shift_by_unit(const unit_t unit){ return (unit == dboard_iface::UNIT_RX)? 0 : 16; } @@ -81,16 +81,16 @@ private: void update(const atr_reg_t atr, const size_t addr){ const boost::uint32_t atr_val = - (boost::uint32_t(_atr_regs[dboard_iface::UNIT_RX][atr]) << unit2shit(dboard_iface::UNIT_RX)) | - (boost::uint32_t(_atr_regs[dboard_iface::UNIT_TX][atr]) << unit2shit(dboard_iface::UNIT_TX)); + (boost::uint32_t(_atr_regs[dboard_iface::UNIT_RX][atr]) << shift_by_unit(dboard_iface::UNIT_RX)) | + (boost::uint32_t(_atr_regs[dboard_iface::UNIT_TX][atr]) << shift_by_unit(dboard_iface::UNIT_TX)); const boost::uint32_t gpio_val = - (boost::uint32_t(_gpio_out[dboard_iface::UNIT_RX]) << unit2shit(dboard_iface::UNIT_RX)) | - (boost::uint32_t(_gpio_out[dboard_iface::UNIT_TX]) << unit2shit(dboard_iface::UNIT_TX)); + (boost::uint32_t(_gpio_out[dboard_iface::UNIT_RX]) << shift_by_unit(dboard_iface::UNIT_RX)) | + (boost::uint32_t(_gpio_out[dboard_iface::UNIT_TX]) << shift_by_unit(dboard_iface::UNIT_TX)); const boost::uint32_t ctrl = - (boost::uint32_t(_pin_ctrl[dboard_iface::UNIT_RX]) << unit2shit(dboard_iface::UNIT_RX)) | - (boost::uint32_t(_pin_ctrl[dboard_iface::UNIT_TX]) << unit2shit(dboard_iface::UNIT_TX)); + (boost::uint32_t(_pin_ctrl[dboard_iface::UNIT_RX]) << shift_by_unit(dboard_iface::UNIT_RX)) | + (boost::uint32_t(_pin_ctrl[dboard_iface::UNIT_TX]) << shift_by_unit(dboard_iface::UNIT_TX)); const boost::uint32_t val = (ctrl & atr_val) | ((~ctrl) & gpio_val); if (not _update_cache.has_key(addr) or _update_cache[addr] != val) { diff --git a/host/lib/usrp/cores/radio_ctrl_core_3000.cpp b/host/lib/usrp/cores/radio_ctrl_core_3000.cpp index 27fac3471..2005bcf33 100644 --- a/host/lib/usrp/cores/radio_ctrl_core_3000.cpp +++ b/host/lib/usrp/cores/radio_ctrl_core_3000.cpp @@ -88,9 +88,6 @@ public: boost::mutex::scoped_lock lock(_mutex); UHD_LOGV(always) << _name << std::hex << " addr 0x" << addr << std::dec << std::endl; this->send_pkt(SR_READBACK, addr/8); - this->wait_for_ack(false); - - this->send_pkt(0); const boost::uint64_t res = this->wait_for_ack(true); const boost::uint32_t lo = boost::uint32_t(res & 0xffffffff); const boost::uint32_t hi = boost::uint32_t(res >> 32); @@ -103,9 +100,6 @@ public: UHD_LOGV(always) << _name << std::hex << " addr 0x" << addr << std::dec << std::endl; this->send_pkt(SR_READBACK, addr/8); - this->wait_for_ack(false); - - this->send_pkt(0); return this->wait_for_ack(true); } diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.cpp b/host/lib/usrp/cores/rx_dsp_core_3000.cpp index 525916032..67d0be017 100644 --- a/host/lib/usrp/cores/rx_dsp_core_3000.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_3000.cpp @@ -32,6 +32,8 @@ #define REG_DSP_RX_SCALE_IQ _dsp_base + 4 #define REG_DSP_RX_DECIM _dsp_base + 8 #define REG_DSP_RX_MUX _dsp_base + 12 +#define REG_DSP_RX_COEFFS _dsp_base + 16 +//FIXME: Add code to support REG_DSP_RX_COEFFS #define FLAG_DSP_RX_MUX_SWAP_IQ (1 << 0) #define FLAG_DSP_RX_MUX_REAL_MODE (1 << 1) @@ -88,6 +90,9 @@ public: uhd::meta_range_t get_host_rates(void){ meta_range_t range; + for (int rate = 1024; rate > 512; rate -= 8){ + range.push_back(range_t(_tick_rate/rate)); + } for (int rate = 512; rate > 256; rate -= 4){ range.push_back(range_t(_tick_rate/rate)); } @@ -105,7 +110,7 @@ public: size_t decim = decim_rate; //determine which half-band filters are activated - int hb0 = 0, hb1 = 0; + int hb0 = 0, hb1 = 0, hb2 = 0, hb_enable=0; if (decim % 2 == 0){ hb0 = 1; decim /= 2; @@ -114,14 +119,30 @@ public: hb1 = 1; decim /= 2; } + if (decim % 2 == 0){ + hb2 = 1; + decim /= 2; + } + + // Encode Halfband config for setting register programming. + if (hb2) { // Implies HB1 and HB0 also asserted + hb_enable=3; + } else if (hb1) { // Implies HB0 is also asserted + hb_enable=2; + } else if (hb0) { + hb_enable=1; + } else { + hb_enable=0; + } - _iface->poke32(REG_DSP_RX_DECIM, (hb1 << 9) | (hb0 << 8) | (decim & 0xff)); + _iface->poke32(REG_DSP_RX_DECIM, (hb_enable << 8) | (decim & 0xff)); - if (decim > 1 and hb0 == 0 and hb1 == 0) + if (decim > 1 and hb0 == 0 and hb1 == 0 and hb2 == 0) { UHD_MSG(warning) << boost::format( - "The requested decimation is odd; the user should expect CIC rolloff.\n" + "The requested decimation is odd; the user should expect passband CIC rolloff.\n" "Select an even decimation to ensure that a halfband filter is enabled.\n" + "Decimations factorable by 4 will enable 2 halfbands, those factorable by 8 will enable 3 halfbands.\n" "decimation = dsp_rate/samp_rate -> %d = (%f MHz)/(%f MHz)\n" ) % decim_rate % (_tick_rate/1e6) % (rate/1e6); } @@ -137,7 +158,7 @@ public: void update_scalar(void){ const double factor = 1.0 + std::max(ceil_log2(_scaling_adjustment), 0.0); - const double target_scalar = (1 << 17)*_scaling_adjustment/_dsp_extra_scaling/factor; + const double target_scalar = (1 << 15)*_scaling_adjustment/_dsp_extra_scaling/factor; const boost::int32_t actual_scalar = boost::math::iround(target_scalar); _fxpt_scalar_correction = target_scalar/actual_scalar*factor; //should be small _iface->poke32(REG_DSP_RX_SCALE_IQ, actual_scalar); diff --git a/host/lib/usrp/dboard/db_sbx_common.cpp b/host/lib/usrp/dboard/db_sbx_common.cpp index 49e30949e..b67910e9a 100644 --- a/host/lib/usrp/dboard/db_sbx_common.cpp +++ b/host/lib/usrp/dboard/db_sbx_common.cpp @@ -33,6 +33,7 @@ UHD_STATIC_BLOCK(reg_sbx_dboards){ dboard_manager::register_dboard(0x0054, 0x0055, &make_sbx, "SBX"); dboard_manager::register_dboard(0x0065, 0x0064, &make_sbx, "SBX v4"); dboard_manager::register_dboard(0x0067, 0x0066, &make_sbx, "CBX"); + dboard_manager::register_dboard(0x0069, 0x0068, &make_sbx, "SBX v5"); dboard_manager::register_dboard(0x0083, 0x0082, &make_sbx, "SBX-120"); dboard_manager::register_dboard(0x0085, 0x0084, &make_sbx, "CBX-120"); } @@ -127,6 +128,10 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){ db_actual = sbx_versionx_sptr(new cbx(this)); freq_range = cbx_freq_range; break; + case 0x0069: + db_actual = sbx_versionx_sptr(new sbx_version4(this)); + freq_range = sbx_freq_range; + break; case 0x0083: db_actual = sbx_versionx_sptr(new sbx_version4(this)); freq_range = sbx_freq_range; diff --git a/host/lib/usrp/dboard/db_tvrx2.cpp b/host/lib/usrp/dboard/db_tvrx2.cpp index 0bfa5229a..c593c5437 100644 --- a/host/lib/usrp/dboard/db_tvrx2.cpp +++ b/host/lib/usrp/dboard/db_tvrx2.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010,2012 Ettus Research LLC +// Copyright 2010,2012-2013 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 @@ -1005,6 +1005,17 @@ tvrx2::tvrx2(ctor_args_t args) : rx_dboard_base(args){ _freq_scalar = (4*16.0e6)/(this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX)); } else if (ref_clock == 100e6) { + + this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, REFCLOCK_DIV8); + + UHD_LOGV(often) << boost::format( + "TVRX2 (%s): Dividing Refclock by 6" + ) % (get_subdev_name()) << std::endl; + + _freq_scalar = (6*16.0e6)/this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX); + } else if (ref_clock == 200e6) { + UHD_MSG(warning) << boost::format("ref_clock was 200e6, setting ref_clock divider for 100e6.") % ref_clock << std::endl; + this->get_iface()->set_clock_rate(dboard_iface::UNIT_RX, 100e6); this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, REFCLOCK_DIV6); UHD_LOGV(often) << boost::format( @@ -1014,7 +1025,7 @@ tvrx2::tvrx2(ctor_args_t args) : rx_dboard_base(args){ _freq_scalar = (6*16.0e6)/this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX); } else { this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, REFCLOCK_DIV6); - UHD_MSG(warning) << boost::format("Unsupported ref_clock %0.2f, valid options 64e6 and 100e6") % ref_clock << std::endl; + UHD_MSG(warning) << boost::format("Unsupported ref_clock %0.2f, valid options 64e6, 100e6, 200e6") % ref_clock << std::endl; _freq_scalar = 1.0; } diff --git a/host/lib/usrp/e100/e100_impl.cpp b/host/lib/usrp/e100/e100_impl.cpp index 619ea8f8e..b49ba64a2 100644 --- a/host/lib/usrp/e100/e100_impl.cpp +++ b/host/lib/usrp/e100/e100_impl.cpp @@ -51,6 +51,10 @@ static device_addrs_t e100_find(const device_addr_t &hint){ //return an empty list of addresses when type is set to non-usrp-e if (hint.has_key("type") and hint["type"] != "e100") return e100_addrs; + //Return an empty list of addresses when a resource is specified, + //since a resource is intended for a different, non-USB, device. + if (hint.has_key("resource")) return e100_addrs; + //device node not provided, assume its 0 if (not hint.has_key("node")){ device_addr_t new_addr = hint; diff --git a/host/lib/usrp/e100/e100_impl.hpp b/host/lib/usrp/e100/e100_impl.hpp index 813f9cc8f..d499c4c03 100644 --- a/host/lib/usrp/e100/e100_impl.hpp +++ b/host/lib/usrp/e100/e100_impl.hpp @@ -85,8 +85,6 @@ public: bool recv_async_msg(uhd::async_metadata_t &, double); private: - uhd::property_tree::sptr _tree; - //controllers fifo_ctrl_excelsior::sptr _fifo_ctrl; i2c_core_200::sptr _fpga_i2c_ctrl; @@ -111,11 +109,6 @@ private: uhd::usrp::dboard_manager::sptr _dboard_manager; uhd::usrp::dboard_iface::sptr _dboard_iface; - //device properties interface - uhd::property_tree::sptr get_tree(void) const{ - return _tree; - } - std::vector<boost::weak_ptr<uhd::rx_streamer> > _rx_streamers; std::vector<boost::weak_ptr<uhd::tx_streamer> > _tx_streamers; diff --git a/host/lib/usrp/gps_ctrl.cpp b/host/lib/usrp/gps_ctrl.cpp index 98bc90a3f..4bf8eb3a3 100644 --- a/host/lib/usrp/gps_ctrl.cpp +++ b/host/lib/usrp/gps_ctrl.cpp @@ -38,7 +38,7 @@ using namespace boost::algorithm; using namespace boost::this_thread; /*! - * A GPS control for NMEA compatible GPSes + * A control for GPSDO devices */ class gps_ctrl_impl : public gps_ctrl{ @@ -63,7 +63,7 @@ private: } std::string update_cached_sensors(const std::string sensor) { - if(not gps_detected() || (gps_type != GPS_TYPE_ER_GPSDO)) { + if(not gps_detected() || (gps_type != GPS_TYPE_INTERNAL_GPSDO)) { UHD_MSG(error) << "get_stat(): unsupported GPS or no GPS detected"; return std::string(); } @@ -105,18 +105,19 @@ public: bool i_heard_some_nmea = false, i_heard_something_weird = false; gps_type = GPS_TYPE_NONE; + //first we look for an internal GPSDO _flush(); //get whatever junk is in the rx buffer right now, and throw it away _send("HAAAY GUYYYYS\n"); //to elicit a response from the GPSDO //wait for _send(...) to return - sleep(milliseconds(200)); + sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); //then we loop until we either timeout, or until we get a response that indicates we're a JL device const boost::system_time comm_timeout = boost::get_system_time() + milliseconds(GPS_COMM_TIMEOUT_MS); while(boost::get_system_time() < comm_timeout) { reply = _recv(); if(reply.find("Command Error") != std::string::npos) { - gps_type = GPS_TYPE_ER_GPSDO; + gps_type = GPS_TYPE_INTERNAL_GPSDO; break; } else if(reply.substr(0, 3) == "$GP") i_heard_some_nmea = true; //but keep looking for that "Command Error" response @@ -124,24 +125,25 @@ public: sleep(milliseconds(GPS_TIMEOUT_DELAY_MS)); } - if((i_heard_some_nmea) && (gps_type != GPS_TYPE_ER_GPSDO)) gps_type = GPS_TYPE_GENERIC_NMEA; + if((i_heard_some_nmea) && (gps_type != GPS_TYPE_INTERNAL_GPSDO)) gps_type = GPS_TYPE_GENERIC_NMEA; if((gps_type == GPS_TYPE_NONE) && i_heard_something_weird) { UHD_MSG(error) << "GPS invalid reply \"" << reply << "\", assuming none available" << std::endl; } switch(gps_type) { - case GPS_TYPE_ER_GPSDO: - UHD_MSG(status) << "Found an Ettus Research NMEA-capable GPSDO" << std::endl; + case GPS_TYPE_INTERNAL_GPSDO: + UHD_MSG(status) << "Found an internal GPSDO" << std::endl; init_gpsdo(); break; case GPS_TYPE_GENERIC_NMEA: - if(gps_type == GPS_TYPE_GENERIC_NMEA) UHD_MSG(status) << "Found a generic NMEA GPS device" << std::endl; + UHD_MSG(status) << "Found a generic NMEA GPS device" << std::endl; break; case GPS_TYPE_NONE: default: + UHD_MSG(status) << "No GPSDO found" << std::endl; break; } @@ -189,19 +191,19 @@ private: //issue some setup stuff so it spits out the appropriate data //none of these should issue replies so we don't bother looking for them //we have to sleep between commands because the JL device, despite not acking, takes considerable time to process each command. - sleep(milliseconds(200)); + sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); _send("SYST:COMM:SER:ECHO OFF\n"); - sleep(milliseconds(200)); + sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); _send("SYST:COMM:SER:PRO OFF\n"); - sleep(milliseconds(200)); + sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); _send("GPS:GPGGA 1\n"); - sleep(milliseconds(200)); + sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); _send("GPS:GGAST 0\n"); - sleep(milliseconds(200)); + sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); _send("GPS:GPRMC 1\n"); - sleep(milliseconds(200)); + sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); _send("SERV:TRAC 0\n"); - sleep(milliseconds(200)); + sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); } //retrieve a raw NMEA sentence @@ -304,7 +306,7 @@ private: //enable servo reporting _send("SERV:TRAC 1\n"); - sleep(milliseconds(200)); + sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); std::string reply; @@ -339,7 +341,7 @@ private: } enum { - GPS_TYPE_ER_GPSDO, + GPS_TYPE_INTERNAL_GPSDO, GPS_TYPE_GENERIC_NMEA, GPS_TYPE_NONE } gps_type; @@ -351,6 +353,7 @@ private: static const int GPS_SERVO_FRESHNESS = 2500; static const int GPS_LOCK_FRESHNESS = 2500; static const int GPS_TIMEOUT_DELAY_MS = 200; + static const int GPSDO_STUPID_DELAY_MS = 200; }; /*********************************************************************** diff --git a/host/lib/usrp/mboard_eeprom.cpp b/host/lib/usrp/mboard_eeprom.cpp index dc25379f9..68c084589 100644 --- a/host/lib/usrp/mboard_eeprom.cpp +++ b/host/lib/usrp/mboard_eeprom.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2012 Ettus Research LLC +// Copyright 2010-2013 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 @@ -229,6 +229,137 @@ static void store_n100(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ } /*********************************************************************** + * Implementation of X300 load/store + **********************************************************************/ +static const boost::uint8_t X300_EEPROM_ADDR = 0x50; + +struct x300_eeprom_map +{ + //indentifying numbers + unsigned char revision[2]; + unsigned char product[2]; + boost::uint8_t _pad0[4]; + + //all the mac addrs + boost::uint8_t mac_addr0[6]; + boost::uint8_t _pad1[2]; + boost::uint8_t mac_addr1[6]; + boost::uint8_t _pad2[2]; + + //all the IP addrs + boost::uint32_t gateway; + boost::uint32_t subnet[4]; + boost::uint32_t ip_addr[4]; + boost::uint8_t _pad3[16]; + + //names and serials + unsigned char name[NAME_MAX_LEN]; + unsigned char serial[SERIAL_LEN]; +}; + +static void load_x300(mboard_eeprom_t &mb_eeprom, i2c_iface &iface) +{ + //extract the revision number + mb_eeprom["revision"] = uint16_bytes_to_string( + iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, revision), 2) + ); + + //extract the product code + mb_eeprom["product"] = uint16_bytes_to_string( + iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, product), 2) + ); + + //extract the mac addresses + mb_eeprom["mac-addr0"] = mac_addr_t::from_bytes(iface.read_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, mac_addr0), 6 + )).to_string(); + mb_eeprom["mac-addr1"] = mac_addr_t::from_bytes(iface.read_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, mac_addr1), 6 + )).to_string(); + + //extract the ip addresses + boost::asio::ip::address_v4::bytes_type ip_addr_bytes; + byte_copy(iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, gateway), 4), ip_addr_bytes); + mb_eeprom["gateway"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + for (size_t i = 0; i < 4; i++) + { + const std::string n(1, i+'0'); + byte_copy(iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, ip_addr)+(i*4), 4), ip_addr_bytes); + mb_eeprom["ip-addr"+n] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + + byte_copy(iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, subnet)+(i*4), 4), ip_addr_bytes); + mb_eeprom["subnet"+n] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + } + + //extract the serial + mb_eeprom["serial"] = bytes_to_string(iface.read_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, serial), SERIAL_LEN + )); + + //extract the name + mb_eeprom["name"] = bytes_to_string(iface.read_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, name), NAME_MAX_LEN + )); +} + +static void store_x300(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface) +{ + //parse the revision number + if (mb_eeprom.has_key("revision")) iface.write_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, revision), + string_to_uint16_bytes(mb_eeprom["revision"]) + ); + + //parse the product code + if (mb_eeprom.has_key("product")) iface.write_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, product), + string_to_uint16_bytes(mb_eeprom["product"]) + ); + + //store the mac addresses + if (mb_eeprom.has_key("mac-addr0")) iface.write_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, mac_addr0), + mac_addr_t::from_string(mb_eeprom["mac-addr0"]).to_bytes() + ); + if (mb_eeprom.has_key("mac-addr1")) iface.write_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, mac_addr1), + mac_addr_t::from_string(mb_eeprom["mac-addr1"]).to_bytes() + ); + + //store the ip addresses + byte_vector_t ip_addr_bytes(4); + if (mb_eeprom.has_key("gateway")){ + byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["gateway"]).to_bytes(), ip_addr_bytes); + iface.write_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, gateway), ip_addr_bytes); + } + for (size_t i = 0; i < 4; i++) + { + const std::string n(1, i+'0'); + if (mb_eeprom.has_key("ip-addr"+n)){ + byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["ip-addr"+n]).to_bytes(), ip_addr_bytes); + iface.write_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, ip_addr)+(i*4), ip_addr_bytes); + } + + if (mb_eeprom.has_key("subnet"+n)){ + byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["subnet"+n]).to_bytes(), ip_addr_bytes); + iface.write_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, subnet)+(i*4), ip_addr_bytes); + } + } + + //store the serial + if (mb_eeprom.has_key("serial")) iface.write_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, serial), + string_to_bytes(mb_eeprom["serial"], SERIAL_LEN) + ); + + //store the name + if (mb_eeprom.has_key("name")) iface.write_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, name), + string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN) + ); +} + +/*********************************************************************** * Implementation of B000 load/store **********************************************************************/ static const boost::uint8_t B000_EEPROM_ADDR = 0x50; @@ -512,6 +643,7 @@ mboard_eeprom_t::mboard_eeprom_t(void){ mboard_eeprom_t::mboard_eeprom_t(i2c_iface &iface, const std::string &which){ if (which == "N100") load_n100(*this, iface); + if (which == "X300") load_x300(*this, iface); if (which == "B000") load_b000(*this, iface); if (which == "B100") load_b100(*this, iface); if (which == "B200") load_b200(*this, iface); @@ -520,6 +652,7 @@ mboard_eeprom_t::mboard_eeprom_t(i2c_iface &iface, const std::string &which){ void mboard_eeprom_t::commit(i2c_iface &iface, const std::string &which) const{ if (which == "N100") store_n100(*this, iface); + if (which == "X300") store_x300(*this, iface); if (which == "B000") store_b000(*this, iface); if (which == "B100") store_b100(*this, iface); if (which == "B200") store_b200(*this, iface); diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index 4bd7d1bfe..182e774fc 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -517,6 +517,46 @@ public: return _tree->access<std::vector<std::string> >(mb_root(mboard) / "clock_source" / "options").get(); } + void set_clock_source_out(const bool enb, const size_t mboard) + { + if (mboard != ALL_MBOARDS) + { + if (_tree->exists(mb_root(mboard) / "clock_source" / "output")) + { + _tree->access<bool>(mb_root(mboard) / "clock_source" / "output").set(enb); + } + else + { + throw uhd::runtime_error("multi_usrp::set_clock_source_out - not supported on this device"); + } + return; + } + for (size_t m = 0; m < get_num_mboards(); m++) + { + this->set_clock_source_out(enb, m); + } + } + + void set_time_source_out(const bool enb, const size_t mboard) + { + if (mboard != ALL_MBOARDS) + { + if (_tree->exists(mb_root(mboard) / "time_source" / "output")) + { + _tree->access<bool>(mb_root(mboard) / "time_source" / "output").set(enb); + } + else + { + throw uhd::runtime_error("multi_usrp::set_time_source_out - not supported on this device"); + } + return; + } + for (size_t m = 0; m < get_num_mboards(); m++) + { + this->set_time_source_out(enb, m); + } + } + size_t get_num_mboards(void){ return _tree->list("/mboards").size(); } @@ -910,6 +950,74 @@ public: } } + /******************************************************************* + * GPIO methods + ******************************************************************/ + std::vector<std::string> get_gpio_banks(const size_t mboard) + { + std::vector<std::string> banks; + if (_tree->exists(mb_root(mboard) / "gpio")) + { + BOOST_FOREACH(const std::string &name, _tree->list(mb_root(mboard) / "gpio")) + { + banks.push_back(name); + } + } + BOOST_FOREACH(const std::string &name, _tree->list(mb_root(mboard) / "dboards")) + { + banks.push_back("RX"+name); + banks.push_back("TX"+name); + } + return banks; + } + + void set_gpio_attr(const std::string &bank, const std::string &attr, const boost::uint32_t value, const boost::uint32_t mask, const size_t mboard) + { + if (_tree->exists(mb_root(mboard) / "gpio" / bank)) + { + const boost::uint32_t current = _tree->access<boost::uint32_t>(mb_root(mboard) / "gpio" / bank / attr).get(); + const boost::uint32_t new_value = (current & ~mask) | (value & mask); + _tree->access<boost::uint32_t>(mb_root(mboard) / "gpio" / bank / attr).set(new_value); + return; + } + if (bank.size() > 2 and bank[1] == 'X') + { + const std::string name = bank.substr(2); + const dboard_iface::unit_t unit = (bank[0] == 'R')? dboard_iface::UNIT_RX : dboard_iface::UNIT_TX; + dboard_iface::sptr iface = _tree->access<dboard_iface::sptr>(mb_root(mboard) / "dboards" / name / "iface").get(); + if (attr == "CTRL") iface->set_pin_ctrl(unit, boost::uint16_t(value), boost::uint16_t(mask)); + if (attr == "DDR") iface->set_gpio_ddr(unit, boost::uint16_t(value), boost::uint16_t(mask)); + if (attr == "OUT") iface->set_gpio_out(unit, boost::uint16_t(value), boost::uint16_t(mask)); + if (attr == "ATR_0X") iface->set_atr_reg(unit, dboard_iface::ATR_REG_IDLE, boost::uint16_t(value), boost::uint16_t(mask)); + if (attr == "ATR_RX") iface->set_atr_reg(unit, dboard_iface::ATR_REG_RX_ONLY, boost::uint16_t(value), boost::uint16_t(mask)); + if (attr == "ATR_TX") iface->set_atr_reg(unit, dboard_iface::ATR_REG_TX_ONLY, boost::uint16_t(value), boost::uint16_t(mask)); + if (attr == "ATR_XX") iface->set_atr_reg(unit, dboard_iface::ATR_REG_FULL_DUPLEX, boost::uint16_t(value), boost::uint16_t(mask)); + } + } + + boost::uint32_t get_gpio_attr(const std::string &bank, const std::string &attr, const size_t mboard) + { + if (_tree->exists(mb_root(mboard) / "gpio" / bank)) + { + return _tree->access<boost::uint64_t>(mb_root(mboard) / "gpio" / bank / attr).get(); + } + if (bank.size() > 2 and bank[1] == 'X') + { + const std::string name = bank.substr(2); + const dboard_iface::unit_t unit = (bank[0] == 'R')? dboard_iface::UNIT_RX : dboard_iface::UNIT_TX; + dboard_iface::sptr iface = _tree->access<dboard_iface::sptr>(mb_root(mboard) / "dboards" / name / "iface").get(); + if (attr == "CTRL") return iface->get_pin_ctrl(unit); + if (attr == "DDR") return iface->get_gpio_ddr(unit); + if (attr == "OUT") return iface->get_gpio_out(unit); + if (attr == "ATR_0X") return iface->get_atr_reg(unit, dboard_iface::ATR_REG_IDLE); + if (attr == "ATR_RX") return iface->get_atr_reg(unit, dboard_iface::ATR_REG_RX_ONLY); + if (attr == "ATR_TX") return iface->get_atr_reg(unit, dboard_iface::ATR_REG_TX_ONLY); + if (attr == "ATR_XX") return iface->get_atr_reg(unit, dboard_iface::ATR_REG_FULL_DUPLEX); + if (attr == "READBACK") return iface->read_gpio(unit); + } + return 0; + } + private: device::sptr _dev; property_tree::sptr _tree; diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index 625926f36..3b902b343 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -55,9 +55,9 @@ static device_addrs_t usrp1_find(const device_addr_t &hint) //return an empty list of addresses when type is set to non-usrp1 if (hint.has_key("type") and hint["type"] != "usrp1") return usrp1_addrs; - //Return an empty list of addresses when an address is specified, - //since an address is intended for a different, non-USB, device. - if (hint.has_key("addr")) return usrp1_addrs; + //Return an empty list of addresses when an address or resource is specified, + //since an address and resource is intended for a different, non-USB, device. + if (hint.has_key("addr") || hint.has_key("resource")) return usrp1_addrs; unsigned int vid, pid; diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp index 0be8ebca9..da9fe8b16 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.hpp +++ b/host/lib/usrp/usrp1/usrp1_impl.hpp @@ -84,13 +84,6 @@ public: bool recv_async_msg(uhd::async_metadata_t &, double); private: - uhd::property_tree::sptr _tree; - - //device properties interface - uhd::property_tree::sptr get_tree(void) const{ - return _tree; - } - //controllers uhd::usrp::fx2_ctrl::sptr _fx2_ctrl; usrp1_iface::sptr _iface; diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp index 5f97045e1..7297a30d1 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.cpp +++ b/host/lib/usrp/usrp2/usrp2_iface.cpp @@ -36,6 +36,7 @@ #include <boost/filesystem.hpp> #include <algorithm> #include <iostream> +#include <uhd/utils/platform.hpp> using namespace uhd; using namespace uhd::usrp; @@ -62,33 +63,6 @@ static const boost::uint32_t MIN_PROTO_COMPAT_I2C = 7; static const boost::uint32_t MIN_PROTO_COMPAT_REG = 10; static const boost::uint32_t MIN_PROTO_COMPAT_UART = 7; -//Define get_gpid() to get a globally unique identifier for this process. -//The gpid is implemented as a hash of the pid and a unique machine identifier. -#ifdef UHD_PLATFORM_WIN32 -#include <Windows.h> -static inline size_t get_gpid(void){ - //extract volume serial number - char szVolName[MAX_PATH+1], szFileSysName[MAX_PATH+1]; - DWORD dwSerialNumber, dwMaxComponentLen, dwFileSysFlags; - GetVolumeInformation("C:\\", szVolName, MAX_PATH, - &dwSerialNumber, &dwMaxComponentLen, - &dwFileSysFlags, szFileSysName, sizeof(szFileSysName)); - - size_t hash = 0; - boost::hash_combine(hash, GetCurrentProcessId()); - boost::hash_combine(hash, dwSerialNumber); - return hash; -} -#else -#include <unistd.h> -static inline size_t get_gpid(void){ - size_t hash = 0; - boost::hash_combine(hash, getpid()); - boost::hash_combine(hash, gethostid()); - return hash; -} -#endif - class usrp2_iface_impl : public usrp2_iface{ public: /*********************************************************************** @@ -122,7 +96,7 @@ public: void lock_device(bool lock){ if (lock){ - this->pokefw(U2_FW_REG_LOCK_GPID, boost::uint32_t(get_gpid())); + this->pokefw(U2_FW_REG_LOCK_GPID, get_process_hash()); _lock_task = task::make(boost::bind(&usrp2_iface_impl::lock_task, this)); } else{ @@ -147,7 +121,7 @@ public: if (time_diff >= lock_timeout_time) return false; //otherwise only lock if the device hash is different that ours - return lock_gpid != boost::uint32_t(get_gpid()); + return lock_gpid != get_process_hash(); } void lock_task(void){ diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 3afb3aac7..16d9b9a54 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -41,6 +41,9 @@ using namespace uhd::usrp; using namespace uhd::transport; namespace asio = boost::asio; +//A reasonable number of frames for send/recv and async/sync +static const size_t DEFAULT_NUM_FRAMES = 32; + /*********************************************************************** * Discovery over the udp transport **********************************************************************/ @@ -49,13 +52,16 @@ static device_addrs_t usrp2_find(const device_addr_t &hint_){ device_addrs_t hints = separate_device_addr(hint_); if (hints.size() > 1){ device_addrs_t found_devices; + std::string error_msg; BOOST_FOREACH(const device_addr_t &hint_i, hints){ device_addrs_t found_devices_i = usrp2_find(hint_i); - if (found_devices_i.size() != 1) throw uhd::value_error(str(boost::format( + if (found_devices_i.size() != 1) error_msg += str(boost::format( "Could not resolve device hint \"%s\" to a single device." - ) % hint_i.to_string())); - found_devices.push_back(found_devices_i[0]); + ) % hint_i.to_string()); + else found_devices.push_back(found_devices_i[0]); } + if (found_devices.empty()) return device_addrs_t(); + if (not error_msg.empty()) throw uhd::value_error(error_msg); return device_addrs_t(1, combine_device_addrs(found_devices)); } @@ -68,6 +74,10 @@ static device_addrs_t usrp2_find(const device_addr_t &hint_){ //return an empty list of addresses when type is set to non-usrp2 if (hint.has_key("type") and hint["type"] != "usrp2") return usrp2_addrs; + //Return an empty list of addresses when a resource is specified, + //since a resource is intended for a different, non-USB, device. + if (hint.has_key("resource")) return usrp2_addrs; + //if no address was specified, send a broadcast on each interface if (not hint.has_key("addr")){ BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()){ @@ -278,8 +288,15 @@ static zero_copy_if::sptr make_xport( filtered_hints[key] = hints[key]; } + zero_copy_xport_params default_buff_args; + default_buff_args.send_frame_size = transport::udp_simple::mtu; + default_buff_args.recv_frame_size = transport::udp_simple::mtu; + default_buff_args.num_send_frames = DEFAULT_NUM_FRAMES; + default_buff_args.num_recv_frames = DEFAULT_NUM_FRAMES; + //make the transport object with the filtered hints - zero_copy_if::sptr xport = udp_zero_copy::make(addr, port, filtered_hints); + udp_zero_copy::buff_params ignored_params; + zero_copy_if::sptr xport = udp_zero_copy::make(addr, port, default_buff_args, ignored_params, filtered_hints); //Send a small data packet so the usrp2 knows the udp source port. //This setup must happen before further initialization occurs diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index f9988287f..d7b53e56b 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -79,7 +79,6 @@ public: bool recv_async_msg(uhd::async_metadata_t &, double); private: - uhd::property_tree::sptr _tree; struct mb_container_type{ usrp2_iface::sptr iface; usrp2_fifo_ctrl::sptr fifo_ctrl; @@ -115,11 +114,6 @@ private: void set_rx_fe_corrections(const std::string &mb, const double); void set_tx_fe_corrections(const std::string &mb, const double); - //device properties interface - uhd::property_tree::sptr get_tree(void) const{ - return _tree; - } - //io impl methods and members UHD_PIMPL_DECL(io_impl) _io_impl; void io_init(void); diff --git a/host/lib/usrp/x300/CMakeLists.txt b/host/lib/usrp/x300/CMakeLists.txt new file mode 100644 index 000000000..a588f901b --- /dev/null +++ b/host/lib/usrp/x300/CMakeLists.txt @@ -0,0 +1,38 @@ +# +# Copyright 2013 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## + +######################################################################## +# Conditionally configure the X300 support +######################################################################## +LIBUHD_REGISTER_COMPONENT("X300" ENABLE_X300 ON "ENABLE_LIBUHD" OFF) + +IF(ENABLE_X300) + LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/x300_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/x300_fw_ctrl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/x300_fw_uart.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/x300_adc_ctrl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/x300_dac_ctrl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/x300_io_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/x300_dboard_iface.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/x300_clock_ctrl.cpp + ) +ENDIF(ENABLE_X300) diff --git a/host/lib/usrp/x300/x300_adc_ctrl.cpp b/host/lib/usrp/x300/x300_adc_ctrl.cpp new file mode 100644 index 000000000..75bfb048c --- /dev/null +++ b/host/lib/usrp/x300/x300_adc_ctrl.cpp @@ -0,0 +1,133 @@ +// +// Copyright 2010-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "x300_adc_ctrl.hpp" +#include "ads62p48_regs.hpp" +#include <uhd/types/ranges.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/safe_call.hpp> +#include <uhd/exception.hpp> +#include <boost/foreach.hpp> + +using namespace uhd; + +/*! + * A X300 codec control specific to the ads62p48 ic. + */ +class x300_adc_ctrl_impl : public x300_adc_ctrl +{ +public: + x300_adc_ctrl_impl(uhd::spi_iface::sptr iface, const size_t slaveno): + _iface(iface), _slaveno(slaveno) + { + //power-up adc + _ads62p48_regs.reset = 1; + this->send_ads62p48_reg(0x00); //issue a reset to the ADC + _ads62p48_regs.reset = 0; + + _ads62p48_regs.enable_low_speed_mode = 0; + _ads62p48_regs.ref = ads62p48_regs_t::REF_INTERNAL; + _ads62p48_regs.standby = ads62p48_regs_t::STANDBY_NORMAL; + _ads62p48_regs.power_down = ads62p48_regs_t::POWER_DOWN_NORMAL; + _ads62p48_regs.lvds_cmos = ads62p48_regs_t::LVDS_CMOS_DDR_LVDS; + _ads62p48_regs.channel_control = ads62p48_regs_t::CHANNEL_CONTROL_INDEPENDENT; + _ads62p48_regs.data_format = ads62p48_regs_t::DATA_FORMAT_2S_COMPLIMENT; + _ads62p48_regs.clk_out_pos_edge = ads62p48_regs_t::CLK_OUT_POS_EDGE_MINUS7_26; + _ads62p48_regs.clk_out_neg_edge = ads62p48_regs_t::CLK_OUT_NEG_EDGE_MINUS7_26; + + + this->send_ads62p48_reg(0); + this->send_ads62p48_reg(0x20); + this->send_ads62p48_reg(0x3f); + this->send_ads62p48_reg(0x40); + this->send_ads62p48_reg(0x41); + this->send_ads62p48_reg(0x44); + this->send_ads62p48_reg(0x50); + this->send_ads62p48_reg(0x51); + this->send_ads62p48_reg(0x52); + this->send_ads62p48_reg(0x53); + this->send_ads62p48_reg(0x55); + this->send_ads62p48_reg(0x57); + this->send_ads62p48_reg(0x62); + this->send_ads62p48_reg(0x63); + this->send_ads62p48_reg(0x66); + this->send_ads62p48_reg(0x68); + this->send_ads62p48_reg(0x6a); + this->send_ads62p48_reg(0x75); + this->send_ads62p48_reg(0x76); + + } + + double set_gain(const double &gain) + { + const meta_range_t gain_range = meta_range_t(0, 6.0, 0.5); + const int gain_bits = int((gain_range.clip(gain)*2.0) + 0.5); + _ads62p48_regs.gain_chA = gain_bits; + _ads62p48_regs.gain_chB = gain_bits; + this->send_ads62p48_reg(0x55); + this->send_ads62p48_reg(0x68); + return gain_bits/2; + } + + void set_test_word(const std::string &patterna, const std::string &patternb, const boost::uint32_t num) + { + _ads62p48_regs.custom_pattern_low = num & 0xff; + _ads62p48_regs.custom_pattern_high = num >> 8; + if (patterna == "ones") _ads62p48_regs.test_patterns_chA = ads62p48_regs_t::TEST_PATTERNS_CHA_ONES; + if (patterna == "zeros") _ads62p48_regs.test_patterns_chA = ads62p48_regs_t::TEST_PATTERNS_CHA_ZEROS; + if (patterna == "custom") _ads62p48_regs.test_patterns_chA = ads62p48_regs_t::TEST_PATTERNS_CHA_CUSTOM; + if (patterna == "ramp") _ads62p48_regs.test_patterns_chA = ads62p48_regs_t::TEST_PATTERNS_CHA_RAMP; + if (patterna == "normal") _ads62p48_regs.test_patterns_chA = ads62p48_regs_t::TEST_PATTERNS_CHA_NORMAL; + if (patternb == "ones") _ads62p48_regs.test_patterns_chB = ads62p48_regs_t::TEST_PATTERNS_CHB_ONES; + if (patternb == "zeros") _ads62p48_regs.test_patterns_chB = ads62p48_regs_t::TEST_PATTERNS_CHB_ZEROS; + if (patternb == "custom") _ads62p48_regs.test_patterns_chB = ads62p48_regs_t::TEST_PATTERNS_CHB_CUSTOM; + if (patterna == "ramp") _ads62p48_regs.test_patterns_chB = ads62p48_regs_t::TEST_PATTERNS_CHB_RAMP; + if (patterna == "normal") _ads62p48_regs.test_patterns_chB = ads62p48_regs_t::TEST_PATTERNS_CHB_NORMAL; + this->send_ads62p48_reg(0x51); + this->send_ads62p48_reg(0x52); + this->send_ads62p48_reg(0x62); + this->send_ads62p48_reg(0x75); + } + + ~x300_adc_ctrl_impl(void) + { + _ads62p48_regs.power_down = ads62p48_regs_t::POWER_DOWN_GLOBAL; + UHD_SAFE_CALL + ( + this->send_ads62p48_reg(0x40); + ) + } + +private: + ads62p48_regs_t _ads62p48_regs; + uhd::spi_iface::sptr _iface; + const size_t _slaveno; + + void send_ads62p48_reg(boost::uint8_t addr) + { + boost::uint16_t reg = _ads62p48_regs.get_write_reg(addr); + _iface->write_spi(_slaveno, spi_config_t::EDGE_FALL, reg, 16); + } +}; + +/*********************************************************************** + * Public make function for the ADC control + **********************************************************************/ +x300_adc_ctrl::sptr x300_adc_ctrl::make(uhd::spi_iface::sptr iface, const size_t slaveno) +{ + return sptr(new x300_adc_ctrl_impl(iface, slaveno)); +} diff --git a/host/lib/usrp/x300/x300_adc_ctrl.hpp b/host/lib/usrp/x300/x300_adc_ctrl.hpp new file mode 100644 index 000000000..fce40a434 --- /dev/null +++ b/host/lib/usrp/x300/x300_adc_ctrl.hpp @@ -0,0 +1,44 @@ +// +// Copyright 2010-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_X300_ADC_CTRL_HPP +#define INCLUDED_X300_ADC_CTRL_HPP + +#include <uhd/types/serial.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> + +class x300_adc_ctrl : boost::noncopyable +{ +public: + typedef boost::shared_ptr<x300_adc_ctrl> sptr; + + /*! + * Make a codec control for the ADC. + * \param iface a pointer to the interface object + * \param spiface the interface to spi + * \return a new codec control object + */ + static sptr make(uhd::spi_iface::sptr iface, const size_t slaveno); + + virtual double set_gain(const double &) = 0; + + virtual void set_test_word(const std::string &patterna, const std::string &patternb, const boost::uint32_t = 0) = 0; + +}; + +#endif /* INCLUDED_X300_ADC_CTRL_HPP */ diff --git a/host/lib/usrp/x300/x300_clock_ctrl.cpp b/host/lib/usrp/x300/x300_clock_ctrl.cpp new file mode 100644 index 000000000..1a4cd4668 --- /dev/null +++ b/host/lib/usrp/x300/x300_clock_ctrl.cpp @@ -0,0 +1,396 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "lmk04816_regs.hpp" +#include "x300_clock_ctrl.hpp" +#include <uhd/utils/safe_call.hpp> +#include <boost/cstdint.hpp> +#include <boost/format.hpp> +#include <stdexcept> +#include <cmath> +#include <cstdlib> + +using namespace uhd; + +class x300_clock_ctrl_impl : public x300_clock_ctrl { + +public: + +~x300_clock_ctrl_impl(void) {} + +x300_clock_ctrl_impl(uhd::spi_iface::sptr spiface, + const size_t slaveno, + const size_t hw_rev, + const double master_clock_rate, + const double system_ref_rate): + _spiface(spiface), + _slaveno(slaveno), + _hw_rev(hw_rev), + _master_clock_rate(master_clock_rate), + _system_ref_rate(system_ref_rate) +{ + set_master_clock_rate(master_clock_rate); +} + +void sync_clocks(void) { + //soft sync: + //put the sync IO into output mode - FPGA must be input + //write low, then write high - this triggers a soft sync + _lmk04816_regs.SYNC_POL_INV = lmk04816_regs_t::SYNC_POL_INV_SYNC_LOW; + this->write_regs(11); + _lmk04816_regs.SYNC_POL_INV = lmk04816_regs_t::SYNC_POL_INV_SYNC_HIGH; + this->write_regs(11); +} + +double get_master_clock_rate(void) { + return _master_clock_rate; +} + +double get_sysref_clock_rate(void) { + return _system_ref_rate; +} + +double get_refout_clock_rate(void) { + //We support only one reference output rate + return 10e6; +} + +void set_dboard_rate(const x300_clock_which_t, double rate) { + if(not doubles_are_equal(rate, get_master_clock_rate())) { + throw uhd::not_implemented_error("x3xx set dboard clock rate does not support setting an arbitrary clock rate"); + } +} + +std::vector<double> get_dboard_rates(const x300_clock_which_t) { + /* Right now, the only supported daughterboard clock rate is the master clock + * rate. TODO Implement divider settings for lower clock rates for legacy + * daughterboard support. */ + + std::vector<double> rates; + rates.push_back(get_master_clock_rate()); + return rates; +} + +void set_ref_out(const bool enable) { + // TODO Implement divider configuration to allow for configurable output + // rates + if (enable) + _lmk04816_regs.CLKout10_TYPE = lmk04816_regs_t::CLKOUT10_TYPE_LVDS; + else + _lmk04816_regs.CLKout10_TYPE = lmk04816_regs_t::CLKOUT10_TYPE_P_DOWN; + this->write_regs(8); +} + +void write_regs(boost::uint8_t addr) { + boost::uint32_t data = _lmk04816_regs.get_reg(addr); + _spiface->write_spi(_slaveno, spi_config_t::EDGE_RISE, data,32); +} + + +private: + +void set_master_clock_rate(double clock_rate) { + /* The X3xx has two primary rates. The first is the + * _system_ref_rate, which is sourced from the "clock_source"/"value" field + * of the property tree, and whose value can be 10e6, 30.72e6, or 200e6. + * The _system_ref_rate is the input to the clocking system, and + * what comes out is a disciplined master clock running at the + * _master_clock_rate. As such, only certain combinations of + * system reference rates and master clock rates are supported. + * Additionally, a subset of these will operate in "zero delay" mode. */ + + enum opmode_t { INVALID, + m10M_200M_NOZDEL, // used for debug purposes only + m10M_200M_ZDEL, // Normal mode + m30_72M_184_32M_ZDEL, // LTE with external ref, aka CPRI Mode + m10M_184_32M_NOZDEL, // LTE with 10 MHz ref + m10M_120M_ZDEL }; // NI USRP 120 MHz Clocking + + /* The default clocking mode is 10MHz reference generating a 200 MHz master + * clock, in zero-delay mode. */ + opmode_t clocking_mode = INVALID; + + if(doubles_are_equal(_system_ref_rate, 10e6)) { + if(doubles_are_equal(clock_rate, 184.32e6)) { + /* 10MHz reference, 184.32 MHz master clock out, NOT Zero Delay. */ + clocking_mode = m10M_184_32M_NOZDEL; + } else if(doubles_are_equal(clock_rate, 200e6)) { + /* 10MHz reference, 200 MHz master clock out, Zero Delay */ + clocking_mode = m10M_200M_ZDEL; + } else if(doubles_are_equal(clock_rate, 120e6)) { + /* 10MHz reference, 120 MHz master clock rate, Zero Delay */ + clocking_mode = m10M_120M_ZDEL; + } + } else if(doubles_are_equal(_system_ref_rate, 30.72e6)) { + if(doubles_are_equal(clock_rate, 184.32e6)) { + /* 30.72MHz reference, 184.32 MHz master clock out, Zero Delay */ + clocking_mode = m30_72M_184_32M_ZDEL; + } + } + + if(clocking_mode == INVALID) { + throw uhd::runtime_error(str(boost::format("A master clock rate of %f cannot be derived from a system reference rate of %f") % clock_rate % _system_ref_rate)); + } + + // For 200 MHz output, the VCO is run at 2400 MHz + // For the LTE/CPRI rate of 184.32 MHz, the VCO runs at 2580.48 MHz + + int vco_div = 0; + + // Note: PLL2 N2 prescaler is enabled for all cases + // PLL2 reference doubler is enabled for all cases + + /* All LMK04816 settings are from the LMK datasheet for our clocking + * architecture. Please refer to the datasheet for more information. */ + switch (clocking_mode) { + case m10M_200M_NOZDEL: + vco_div = 12; + _lmk04816_regs.MODE = lmk04816_regs_t::MODE_DUAL_INT; + + // PLL1 - 2 MHz compare frequency + _lmk04816_regs.PLL1_N_28 = 48; + _lmk04816_regs.PLL1_R_27 = 5; + _lmk04816_regs.PLL1_CP_GAIN_27 = lmk04816_regs_t::PLL1_CP_GAIN_27_100UA; + + // PLL2 - 48 MHz compare frequency + _lmk04816_regs.PLL2_N_30 = 25; + _lmk04816_regs.PLL2_P_30 = lmk04816_regs_t::PLL2_P_30_DIV_2A; + _lmk04816_regs.PLL2_R_28 = 4; + _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_3200UA; + + break; + + case m10M_200M_ZDEL: + vco_div = 12; + _lmk04816_regs.MODE = lmk04816_regs_t::MODE_DUAL_INT_ZER_DELAY; + + // PLL1 - 2 MHz compare frequency + _lmk04816_regs.PLL1_N_28 = 100; + _lmk04816_regs.PLL1_R_27 = 5; + _lmk04816_regs.PLL1_CP_GAIN_27 = lmk04816_regs_t::PLL1_CP_GAIN_27_100UA; + + // PLL2 - 96 MHz compare frequency + _lmk04816_regs.PLL2_N_30 = 5; + _lmk04816_regs.PLL2_P_30 = lmk04816_regs_t::PLL2_P_30_DIV_5; + _lmk04816_regs.PLL2_R_28 = 2; + + if(_hw_rev <= 4) + _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_1600UA; + else + _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_400UA; + + break; + + case m30_72M_184_32M_ZDEL: + vco_div=14; + _lmk04816_regs.MODE = lmk04816_regs_t::MODE_DUAL_INT_ZER_DELAY; + + // PLL1 - 2.048 MHz compare frequency + _lmk04816_regs.PLL1_N_28 = 90; + _lmk04816_regs.PLL1_R_27 = 15; + _lmk04816_regs.PLL1_CP_GAIN_27 = lmk04816_regs_t::PLL1_CP_GAIN_27_100UA; + + // PLL2 - 7.68 MHz compare frequency + _lmk04816_regs.PLL2_N_30 = 168; + _lmk04816_regs.PLL2_P_30 = lmk04816_regs_t::PLL2_P_30_DIV_2A; + _lmk04816_regs.PLL2_R_28 = 25; + _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_3200UA; + + _lmk04816_regs.PLL2_R3_LF = lmk04816_regs_t::PLL2_R3_LF_1KILO_OHM; + _lmk04816_regs.PLL2_C3_LF = lmk04816_regs_t::PLL2_C3_LF_39PF; + + _lmk04816_regs.PLL2_R4_LF = lmk04816_regs_t::PLL2_R4_LF_1KILO_OHM; + _lmk04816_regs.PLL2_C4_LF = lmk04816_regs_t::PLL2_C4_LF_34PF; + + break; + + case m10M_184_32M_NOZDEL: + vco_div=14; + _lmk04816_regs.MODE = lmk04816_regs_t::MODE_DUAL_INT; + + // PLL1 - 2 MHz compare frequency + _lmk04816_regs.PLL1_N_28 = 48; + _lmk04816_regs.PLL1_R_27 = 5; + _lmk04816_regs.PLL1_CP_GAIN_27 = lmk04816_regs_t::PLL1_CP_GAIN_27_100UA; + + // PLL2 - 7.68 MHz compare frequency + _lmk04816_regs.PLL2_N_30 = 168; + _lmk04816_regs.PLL2_P_30 = lmk04816_regs_t::PLL2_P_30_DIV_2A; + _lmk04816_regs.PLL2_R_28 = 25; + _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_3200UA; + + _lmk04816_regs.PLL2_R3_LF = lmk04816_regs_t::PLL2_R3_LF_4KILO_OHM; + _lmk04816_regs.PLL2_C3_LF = lmk04816_regs_t::PLL2_C3_LF_39PF; + + _lmk04816_regs.PLL2_R4_LF = lmk04816_regs_t::PLL2_R4_LF_1KILO_OHM; + _lmk04816_regs.PLL2_C4_LF = lmk04816_regs_t::PLL2_C4_LF_71PF; + + break; + + case m10M_120M_ZDEL: + vco_div = 20; + _lmk04816_regs.MODE = lmk04816_regs_t::MODE_DUAL_INT_ZER_DELAY; + + // PLL1 - 2 MHz compare frequency + _lmk04816_regs.PLL1_N_28 = 60; + _lmk04816_regs.PLL1_R_27 = 5; + _lmk04816_regs.PLL1_CP_GAIN_27 = lmk04816_regs_t::PLL1_CP_GAIN_27_100UA; + + // PLL2 - 96 MHz compare frequency + _lmk04816_regs.PLL2_N_30 = 5; + _lmk04816_regs.PLL2_P_30 = lmk04816_regs_t::PLL2_P_30_DIV_5; + _lmk04816_regs.PLL2_R_28 = 2; + + if(_hw_rev <= 4) + _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_1600UA; + else + _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_400UA; + + break; + + default: + UHD_THROW_INVALID_CODE_PATH(); + break; + }; + + /* Reset the LMK clock controller. */ + _lmk04816_regs.RESET = lmk04816_regs_t::RESET_RESET; + this->write_regs(0); + _lmk04816_regs.RESET = lmk04816_regs_t::RESET_NO_RESET; + this->write_regs(0); + + /* Initial power-up */ + _lmk04816_regs.CLKout0_1_PD = lmk04816_regs_t::CLKOUT0_1_PD_POWER_UP; + this->write_regs(0); + _lmk04816_regs.CLKout0_1_DIV = vco_div; + this->write_regs(0); + + // Register 1 + _lmk04816_regs.CLKout2_3_PD = lmk04816_regs_t::CLKOUT2_3_PD_POWER_UP; + _lmk04816_regs.CLKout2_3_DIV = vco_div; + // Register 2 + _lmk04816_regs.CLKout4_5_PD = lmk04816_regs_t::CLKOUT4_5_PD_POWER_UP; + _lmk04816_regs.CLKout4_5_DIV = vco_div; + // Register 3 + _lmk04816_regs.CLKout6_7_DIV = vco_div; + _lmk04816_regs.CLKout6_7_OSCin_Sel = lmk04816_regs_t::CLKOUT6_7_OSCIN_SEL_VCO; + // Register 4 + _lmk04816_regs.CLKout8_9_DIV = vco_div; + // Register 5 + _lmk04816_regs.CLKout10_11_PD = lmk04816_regs_t::CLKOUT10_11_PD_NORMAL; + _lmk04816_regs.CLKout10_11_DIV = vco_div; + + // Register 6 + _lmk04816_regs.CLKout0_TYPE = lmk04816_regs_t::CLKOUT0_TYPE_LVDS; //FPGA + _lmk04816_regs.CLKout1_TYPE = lmk04816_regs_t::CLKOUT1_TYPE_P_DOWN; //CPRI feedback clock, use LVDS + _lmk04816_regs.CLKout2_TYPE = lmk04816_regs_t::CLKOUT2_TYPE_LVPECL_700MVPP; //DB_0_RX + _lmk04816_regs.CLKout3_TYPE = lmk04816_regs_t::CLKOUT3_TYPE_LVPECL_700MVPP; //DB_1_RX + // Register 7 + _lmk04816_regs.CLKout4_TYPE = lmk04816_regs_t::CLKOUT4_TYPE_LVPECL_700MVPP; //DB_1_TX + _lmk04816_regs.CLKout5_TYPE = lmk04816_regs_t::CLKOUT5_TYPE_LVPECL_700MVPP; //DB_0_TX + _lmk04816_regs.CLKout6_TYPE = lmk04816_regs_t::CLKOUT6_TYPE_LVPECL_700MVPP; //DB0_DAC + _lmk04816_regs.CLKout7_TYPE = lmk04816_regs_t::CLKOUT7_TYPE_LVPECL_700MVPP; //DB1_DAC + _lmk04816_regs.CLKout8_TYPE = lmk04816_regs_t::CLKOUT8_TYPE_LVPECL_700MVPP; //DB0_ADC + // Register 8 + _lmk04816_regs.CLKout9_TYPE = lmk04816_regs_t::CLKOUT9_TYPE_LVPECL_700MVPP; //DB1_ADC + _lmk04816_regs.CLKout10_TYPE = lmk04816_regs_t::CLKOUT10_TYPE_LVDS; //REF_CLKOUT + _lmk04816_regs.CLKout11_TYPE = lmk04816_regs_t::CLKOUT11_TYPE_P_DOWN; //Debug header, use LVPECL + + + // Register 10 + _lmk04816_regs.EN_OSCout0 = lmk04816_regs_t::EN_OSCOUT0_DISABLED; //Debug header + _lmk04816_regs.FEEDBACK_MUX = 0; //use output 0 (FPGA clock) for feedback + _lmk04816_regs.EN_FEEDBACK_MUX = lmk04816_regs_t::EN_FEEDBACK_MUX_ENABLED; + + // Register 11 + // MODE set in individual cases above + _lmk04816_regs.SYNC_QUAL = lmk04816_regs_t::SYNC_QUAL_FB_MUX; + _lmk04816_regs.EN_SYNC = lmk04816_regs_t::EN_SYNC_ENABLE; + _lmk04816_regs.NO_SYNC_CLKout0_1 = lmk04816_regs_t::NO_SYNC_CLKOUT0_1_CLOCK_XY_SYNC; + _lmk04816_regs.NO_SYNC_CLKout2_3 = lmk04816_regs_t::NO_SYNC_CLKOUT2_3_CLOCK_XY_SYNC; + _lmk04816_regs.NO_SYNC_CLKout4_5 = lmk04816_regs_t::NO_SYNC_CLKOUT4_5_CLOCK_XY_SYNC; + _lmk04816_regs.NO_SYNC_CLKout8_9 = lmk04816_regs_t::NO_SYNC_CLKOUT8_9_CLOCK_XY_SYNC; + _lmk04816_regs.NO_SYNC_CLKout10_11 = lmk04816_regs_t::NO_SYNC_CLKOUT10_11_CLOCK_XY_SYNC; + _lmk04816_regs.SYNC_EN_AUTO = lmk04816_regs_t::SYNC_EN_AUTO_SYNC_INT_GEN; + _lmk04816_regs.SYNC_POL_INV = lmk04816_regs_t::SYNC_POL_INV_SYNC_LOW; + _lmk04816_regs.SYNC_TYPE = lmk04816_regs_t::SYNC_TYPE_INPUT; + + // Register 12 + _lmk04816_regs.LD_MUX = lmk04816_regs_t::LD_MUX_BOTH; + + /* Input Clock Configurations */ + // Register 13 + _lmk04816_regs.EN_CLKin0 = lmk04816_regs_t::EN_CLKIN0_NO_VALID_USE; // This is not connected + _lmk04816_regs.EN_CLKin2 = lmk04816_regs_t::EN_CLKIN2_NO_VALID_USE; // Used only for CPRI + _lmk04816_regs.Status_CLKin1_MUX = lmk04816_regs_t::STATUS_CLKIN1_MUX_UWIRE_RB; + _lmk04816_regs.CLKin_Select_MODE = lmk04816_regs_t::CLKIN_SELECT_MODE_CLKIN1_MAN; + _lmk04816_regs.HOLDOVER_MUX = lmk04816_regs_t::HOLDOVER_MUX_PLL1_R; + // Register 14 + _lmk04816_regs.Status_CLKin1_TYPE = lmk04816_regs_t::STATUS_CLKIN1_TYPE_OUT_PUSH_PULL; + _lmk04816_regs.Status_CLKin0_TYPE = lmk04816_regs_t::STATUS_CLKIN0_TYPE_OUT_PUSH_PULL; + + // Register 26 + // PLL2_CP_GAIN_26 set above in individual cases + _lmk04816_regs.PLL2_CP_POL_26 = lmk04816_regs_t::PLL2_CP_POL_26_NEG_SLOPE; + _lmk04816_regs.EN_PLL2_REF_2X = lmk04816_regs_t::EN_PLL2_REF_2X_DOUBLED_FREQ_REF; + + // Register 27 + // PLL1_CP_GAIN_27 set in individual cases above + // PLL1_R_27 set in the individual cases above + + // Register 28 + // PLL1_N_28 and PLL2_R_28 are set in the individual cases above + + // Register 29 + _lmk04816_regs.PLL2_N_CAL_29 = _lmk04816_regs.PLL2_N_30; // N_CAL should always match N + _lmk04816_regs.OSCin_FREQ_29 = lmk04816_regs_t::OSCIN_FREQ_29_63_TO_127MHZ; + + // Register 30 + // PLL2_P_30 set in individual cases above + // PLL2_N_30 set in individual cases above + + /* Write the configuration values into the LMK */ + for (size_t i = 1; i <= 16; ++i) { + this->write_regs(i); + } + for (size_t i = 24; i <= 31; ++i) { + this->write_regs(i); + } + + this->sync_clocks(); +} + +UHD_INLINE bool doubles_are_equal(double a, double b) { + return (std::fabs(a - b) < std::numeric_limits<double>::epsilon()); +} + +const spi_iface::sptr _spiface; +const size_t _slaveno; +const size_t _hw_rev; +const double _master_clock_rate; +const double _system_ref_rate; +lmk04816_regs_t _lmk04816_regs; +}; + +x300_clock_ctrl::sptr x300_clock_ctrl::make(uhd::spi_iface::sptr spiface, + const size_t slaveno, + const size_t hw_rev, + const double master_clock_rate, + const double system_ref_rate) { + return sptr(new x300_clock_ctrl_impl(spiface, slaveno, hw_rev, + master_clock_rate, system_ref_rate)); +} diff --git a/host/lib/usrp/x300/x300_clock_ctrl.hpp b/host/lib/usrp/x300/x300_clock_ctrl.hpp new file mode 100644 index 000000000..0e3caf900 --- /dev/null +++ b/host/lib/usrp/x300/x300_clock_ctrl.hpp @@ -0,0 +1,83 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_X300_CLOCK_CTRL_HPP +#define INCLUDED_X300_CLOCK_CTRL_HPP + +#include <uhd/types/serial.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> + + +enum x300_clock_which_t +{ + X300_CLOCK_WHICH_ADC0, + X300_CLOCK_WHICH_ADC1, + X300_CLOCK_WHICH_DAC0, + X300_CLOCK_WHICH_DAC1, + X300_CLOCK_WHICH_DB0_RX, + X300_CLOCK_WHICH_DB0_TX, + X300_CLOCK_WHICH_DB1_RX, + X300_CLOCK_WHICH_DB1_TX, + X300_CLOCK_WHICH_TEST, +}; + +class x300_clock_ctrl : boost::noncopyable +{ +public: + + typedef boost::shared_ptr<x300_clock_ctrl> sptr; + + static sptr make(uhd::spi_iface::sptr spiface, + const size_t slaveno, + const size_t hw_rev, + const double master_clock_rate, + const double system_ref_rate); + + /*! Get the master clock rate of the device. + * \return the clock frequency in Hz + */ + virtual double get_master_clock_rate(void) = 0; + + /*! Get the system reference rate of the device. + * \return the clock frequency in Hz + */ + virtual double get_sysref_clock_rate(void) = 0; + + /*! Get the current reference output rate + * \return the clock frequency in Hz + */ + virtual double get_refout_clock_rate(void) = 0; + + /*! Set the clock rate on the given daughterboard clock. + * \param rate the new clock rate + * \throw exception when rate invalid + */ + virtual void set_dboard_rate(const x300_clock_which_t which, double rate) = 0; + + /*! Get a list of possible daughterboard clock rates. + * \return a list of clock rates in Hz + */ + virtual std::vector<double> get_dboard_rates(const x300_clock_which_t which) = 0; + + /*! Turn the reference output on/off + * \param true = on, false = off + */ + virtual void set_ref_out(const bool) = 0; +}; + +#endif /* INCLUDED_X300_CLOCK_CTRL_HPP */ diff --git a/host/lib/usrp/x300/x300_dac_ctrl.cpp b/host/lib/usrp/x300/x300_dac_ctrl.cpp new file mode 100644 index 000000000..5eae9cc48 --- /dev/null +++ b/host/lib/usrp/x300/x300_dac_ctrl.cpp @@ -0,0 +1,146 @@ +// +// Copyright 2010-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "x300_dac_ctrl.hpp" +#include "x300_regs.hpp" +#include <uhd/types/time_spec.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/safe_call.hpp> +#include <uhd/exception.hpp> +#include <boost/foreach.hpp> +#include <boost/thread/thread.hpp> //sleep + +using namespace uhd; + +#define write_ad9146_reg(addr, data) \ + _iface->write_spi(_slaveno, spi_config_t::EDGE_RISE, ((addr) << 8) | (data), 16) +#define read_ad9146_reg(addr) \ + (_iface->read_spi(_slaveno, spi_config_t::EDGE_RISE, ((addr) << 8) | (1 << 15), 16) & 0xff) + +/*! + * A X300 codec control specific to the ad9146 ic. + */ +class x300_dac_ctrl_impl : public x300_dac_ctrl +{ +public: + x300_dac_ctrl_impl(uhd::spi_iface::sptr iface, const size_t slaveno, const double refclk): + _iface(iface), _slaveno(slaveno) + { + write_ad9146_reg(0x00, 0x20); // Take DAC into reset. + write_ad9146_reg(0x00, 0x80); // Enable SPI reads and come out of reset + write_ad9146_reg(0x1e, 0x01); // Data path config - set for proper operation + + // Calculate N0 to be VCO friendly. + // Aim for VCO between 1 and 2GHz, assert otherwise. + // const int N1 = 4; + const int N1 = 4; + int N0_val, N0; + for (N0_val = 0; N0_val < 3; N0_val++) + { + N0 = (1 << N0_val); //1, 2, 4 + if ((refclk * N0 * N1) >= 1e9) break; + } + UHD_ASSERT_THROW((refclk * N0 * N1) >= 1e9); + UHD_ASSERT_THROW((refclk * N0 * N1) <= 2e9); + + /* Start PLL */ + //write_ad9146_reg(0x0C, 0xD1); // Narrow PLL loop filter, Midrange charge pump. + write_ad9146_reg(0x0D, 0xD1 | (N0_val << 2)); // N1=4, N2=16, N0 as calculated + //write_ad9146_reg(0x0D, 0x90 | (N0_val << 2)); // N1=2, N2=8, N0 as calculated + write_ad9146_reg(0x0A, 0xCF); // Auto init VCO band training as per datasheet + write_ad9146_reg(0x0A, 0xA0); // See above. + + // Verify PLL is Locked. 1 sec timeout. + // NOTE: Data sheet inconsistant about which pins give PLL lock status. FIXME! + const time_spec_t exit_time = time_spec_t::get_system_time() + time_spec_t(1.0); + while (true) + { + const size_t reg_e = read_ad9146_reg(0x0E); /* Expect bit 7 = 1 */ + if ((exit_time < time_spec_t::get_system_time()) && ((reg_e & (1 << 7)) == 0)) + throw uhd::runtime_error("x300_dac_ctrl: timeout waiting for DAC PLL to lock"); + else if ((reg_e & ((1 << 7) | (1 << 6))) != 0) break; + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + } + + /* Skew DCI signal to find stable data eye */ + //write_ad9146_reg(0x16, 0x04); //Disable delay in DCI + //write_ad9146_reg(0x16, 0x00); //165ps delay in DCI + //write_ad9146_reg(0x16, 0x01); //375ps delay in DCI + write_ad9146_reg(0x16, 0x02); //615ps delay in DCI + //write_ad9146_reg(0x16, 0x03); //720ps delay in DCI + + write_ad9146_reg(0x03, 0x00); // 2's comp, I first, byte wide interface + + //fpga wants I,Q in the sample word: + //first transaction goes into low bits + //second transaction goes into high bits + //therefore, we want Q to go first (bit 6 == 1) + write_ad9146_reg(0x03, (1 << 6)); //2s comp, i first, byte mode + + write_ad9146_reg(0x10, 0x48); // Disable SYNC mode. + write_ad9146_reg(0x17, 0x04); // FIFO write pointer offset + write_ad9146_reg(0x18, 0x02); // Request soft FIFO align + write_ad9146_reg(0x18, 0x00); // (See above) + write_ad9146_reg(0x1B, 0xE4); // Bypass: Modulator, InvSinc, IQ Bal + + /* Configure interpolation filters */ + write_ad9146_reg(0x1C, 0x00); // Configure HB1 + write_ad9146_reg(0x1D, 0x00); // Configure HB2 + + } + + + ~x300_dac_ctrl_impl(void) + { + UHD_SAFE_CALL + ( + write_ad9146_reg(0x1, 0xf); //total power down + write_ad9146_reg(0x2, 0xf); //total power down + ) + } + + void arm_dac_sync(void) + { + // + // Attempt to synchronize AD9146's + // + write_ad9146_reg(0x10, 0xCF); // Enable SYNC mode. Sync Averaging set to 128. + + const time_spec_t exit_time = time_spec_t::get_system_time() + time_spec_t(1.0); + while (true) + { + const size_t reg_12 = read_ad9146_reg(0x12); /* Expect bit 7 = 0, bit 6 = 1 */ + if ((exit_time < time_spec_t::get_system_time()) && (((reg_12 & (1 << 6)) == 0) || ((reg_12 & (1 << 7)) != 0))) + throw uhd::runtime_error("x300_dac_ctrl: timeout waiting for backend synchronization"); + else if (((reg_12 & (1 << 6)) != 0) && ((reg_12 & (1 << 7)) == 0)) break; + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + } + } + +private: + uhd::spi_iface::sptr _iface; + const size_t _slaveno; +}; + +/*********************************************************************** + * Public make function for the DAC control + **********************************************************************/ +x300_dac_ctrl::sptr x300_dac_ctrl::make(uhd::spi_iface::sptr iface, const size_t slaveno, const double clock_rate) +{ + return sptr(new x300_dac_ctrl_impl(iface, slaveno, clock_rate)); +} diff --git a/host/lib/usrp/x300/x300_dac_ctrl.hpp b/host/lib/usrp/x300/x300_dac_ctrl.hpp new file mode 100644 index 000000000..0db7e1e35 --- /dev/null +++ b/host/lib/usrp/x300/x300_dac_ctrl.hpp @@ -0,0 +1,42 @@ +// +// Copyright 2010-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_X300_DAC_CTRL_HPP +#define INCLUDED_X300_DAC_CTRL_HPP + +#include <uhd/types/serial.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> + +class x300_dac_ctrl : boost::noncopyable +{ +public: + typedef boost::shared_ptr<x300_dac_ctrl> sptr; + + /*! + * Make a codec control for the DAC. + * \param iface a pointer to the interface object + * \param spiface the interface to spi + * \return a new codec control object + */ + static sptr make(uhd::spi_iface::sptr iface, const size_t slaveno, const double clock_rate); + + // ! Arm the sync feature in DAC + virtual void arm_dac_sync(void) = 0; +}; + +#endif /* INCLUDED_X300_DAC_CTRL_HPP */ diff --git a/host/lib/usrp/x300/x300_dboard_iface.cpp b/host/lib/usrp/x300/x300_dboard_iface.cpp new file mode 100644 index 000000000..43da7ca08 --- /dev/null +++ b/host/lib/usrp/x300/x300_dboard_iface.cpp @@ -0,0 +1,333 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "x300_impl.hpp" +#include "x300_regs.hpp" +#include <uhd/usrp/dboard_iface.hpp> +#include <uhd/utils/safe_call.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/math/special_functions/round.hpp> +#include "ad7922_regs.hpp" //aux adc +#include "ad5623_regs.hpp" //aux dac + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + +class x300_dboard_iface : public dboard_iface +{ +public: + x300_dboard_iface(const x300_dboard_iface_config_t &config); + ~x300_dboard_iface(void); + + special_props_t get_special_props(void) + { + special_props_t props; + props.soft_clock_divider = false; + props.mangle_i2c_addrs = (_config.dboard_slot == 1); + return props; + } + + void write_aux_dac(unit_t, aux_dac_t, double); + double read_aux_adc(unit_t, aux_adc_t); + + void _set_pin_ctrl(unit_t, boost::uint16_t); + void _set_atr_reg(unit_t, atr_reg_t, boost::uint16_t); + void _set_gpio_ddr(unit_t, boost::uint16_t); + void _set_gpio_out(unit_t, boost::uint16_t); + + void set_gpio_debug(unit_t, int); + boost::uint16_t read_gpio(unit_t); + + void write_i2c(boost::uint16_t, const byte_vector_t &); + byte_vector_t read_i2c(boost::uint16_t, size_t); + + void set_clock_rate(unit_t, double); + double get_clock_rate(unit_t); + std::vector<double> get_clock_rates(unit_t); + void set_clock_enabled(unit_t, bool); + double get_codec_rate(unit_t); + + void write_spi( + unit_t unit, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits + ); + + boost::uint32_t read_write_spi( + unit_t unit, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits + ); + + const x300_dboard_iface_config_t _config; + uhd::dict<unit_t, ad5623_regs_t> _dac_regs; + uhd::dict<unit_t, double> _clock_rates; + void _write_aux_dac(unit_t); + +}; + +/*********************************************************************** + * Make Function + **********************************************************************/ +dboard_iface::sptr x300_make_dboard_iface(const x300_dboard_iface_config_t &config) +{ + return dboard_iface::sptr(new x300_dboard_iface(config)); +} + +/*********************************************************************** + * Structors + **********************************************************************/ +x300_dboard_iface::x300_dboard_iface(const x300_dboard_iface_config_t &config): + _config(config) +{ + //reset the aux dacs + _dac_regs[UNIT_RX] = ad5623_regs_t(); + _dac_regs[UNIT_TX] = ad5623_regs_t(); + BOOST_FOREACH(unit_t unit, _dac_regs.keys()) + { + _dac_regs[unit].data = 1; + _dac_regs[unit].addr = ad5623_regs_t::ADDR_ALL; + _dac_regs[unit].cmd = ad5623_regs_t::CMD_RESET; + this->_write_aux_dac(unit); + } + + this->set_clock_enabled(UNIT_RX, false); + this->set_clock_enabled(UNIT_TX, false); + + this->set_clock_rate(UNIT_RX, _config.clock->get_master_clock_rate()); + this->set_clock_rate(UNIT_TX, _config.clock->get_master_clock_rate()); + + + //some test code + /* + { + + this->write_aux_dac(UNIT_TX, AUX_DAC_A, .1); + this->write_aux_dac(UNIT_TX, AUX_DAC_B, 1); + this->write_aux_dac(UNIT_RX, AUX_DAC_A, 2); + this->write_aux_dac(UNIT_RX, AUX_DAC_B, 3); + while (1) + { + UHD_VAR(this->read_aux_adc(UNIT_TX, AUX_ADC_A)); + UHD_VAR(this->read_aux_adc(UNIT_TX, AUX_ADC_B)); + UHD_VAR(this->read_aux_adc(UNIT_RX, AUX_ADC_A)); + UHD_VAR(this->read_aux_adc(UNIT_RX, AUX_ADC_B)); + sleep(1); + } + } + */ + +} + +x300_dboard_iface::~x300_dboard_iface(void) +{ + UHD_SAFE_CALL + ( + this->set_clock_enabled(UNIT_RX, false); + this->set_clock_enabled(UNIT_TX, false); + ) +} + +/*********************************************************************** + * Clocks + **********************************************************************/ +void x300_dboard_iface::set_clock_rate(unit_t unit, double rate) +{ + _clock_rates[unit] = rate; //set to shadow + switch(unit) + { + case UNIT_RX: + _config.clock->set_dboard_rate(_config.which_rx_clk, rate); + return; + case UNIT_TX: + _config.clock->set_dboard_rate(_config.which_tx_clk, rate); + return; + } +} + +double x300_dboard_iface::get_clock_rate(unit_t unit) +{ + return _clock_rates[unit]; //get from shadow +} + +std::vector<double> x300_dboard_iface::get_clock_rates(unit_t unit) +{ + switch(unit) + { + case UNIT_RX: + return _config.clock->get_dboard_rates(_config.which_rx_clk); + case UNIT_TX: + return _config.clock->get_dboard_rates(_config.which_tx_clk); + default: + UHD_THROW_INVALID_CODE_PATH(); + } +} + +void x300_dboard_iface::set_clock_enabled(unit_t unit, bool enb) +{ + // TODO Variable DBoard clock control needs to be implemented for X300. +} + +double x300_dboard_iface::get_codec_rate(unit_t) +{ + return _config.clock->get_master_clock_rate(); +} + +/*********************************************************************** + * GPIO + **********************************************************************/ +void x300_dboard_iface::_set_pin_ctrl(unit_t unit, boost::uint16_t value) +{ + return _config.gpio->set_pin_ctrl(unit, value); +} + +void x300_dboard_iface::_set_gpio_ddr(unit_t unit, boost::uint16_t value) +{ + return _config.gpio->set_gpio_ddr(unit, value); +} + +void x300_dboard_iface::_set_gpio_out(unit_t unit, boost::uint16_t value) +{ + return _config.gpio->set_gpio_out(unit, value); +} + +boost::uint16_t x300_dboard_iface::read_gpio(unit_t unit) +{ + return _config.gpio->read_gpio(unit); +} + +void x300_dboard_iface::_set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value) +{ + return _config.gpio->set_atr_reg(unit, atr, value); +} + +void x300_dboard_iface::set_gpio_debug(unit_t, int) +{ + throw uhd::not_implemented_error("no set_gpio_debug implemented"); +} + +/*********************************************************************** + * SPI + **********************************************************************/ +#define toslaveno(unit) \ + (((unit) == dboard_iface::UNIT_TX)? _config.tx_spi_slaveno : _config.rx_spi_slaveno) + +void x300_dboard_iface::write_spi( + unit_t unit, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits +){ + _config.spi->write_spi(toslaveno(unit), config, data, num_bits); +} + +boost::uint32_t x300_dboard_iface::read_write_spi( + unit_t unit, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits +){ + return _config.spi->read_spi(toslaveno(unit), config, data, num_bits); +} + +/*********************************************************************** + * I2C + **********************************************************************/ +void x300_dboard_iface::write_i2c(boost::uint16_t addr, const byte_vector_t &bytes) +{ + return _config.i2c->write_i2c(addr, bytes); +} + +byte_vector_t x300_dboard_iface::read_i2c(boost::uint16_t addr, size_t num_bytes) +{ + return _config.i2c->read_i2c(addr, num_bytes); +} + +/*********************************************************************** + * Aux DAX/ADC + **********************************************************************/ +void x300_dboard_iface::_write_aux_dac(unit_t unit) +{ + static const uhd::dict<unit_t, int> unit_to_spi_dac = map_list_of + (UNIT_RX, DB_RX_LSDAC_SEN) + (UNIT_TX, DB_TX_LSDAC_SEN) + ; + _config.spi->write_spi( + unit_to_spi_dac[unit], spi_config_t::EDGE_FALL, + _dac_regs[unit].get_reg(), 24 + ); +} + +void x300_dboard_iface::write_aux_dac(unit_t unit, aux_dac_t which, double value) +{ + _dac_regs[unit].data = boost::math::iround(4095*value/3.3); + _dac_regs[unit].cmd = ad5623_regs_t::CMD_WR_UP_DAC_CHAN_N; + + typedef uhd::dict<aux_dac_t, ad5623_regs_t::addr_t> aux_dac_to_addr; + static const uhd::dict<unit_t, aux_dac_to_addr> unit_to_which_to_addr = map_list_of + (UNIT_RX, map_list_of + (AUX_DAC_A, ad5623_regs_t::ADDR_DAC_A) + (AUX_DAC_B, ad5623_regs_t::ADDR_DAC_B) + (AUX_DAC_C, ad5623_regs_t::ADDR_DAC_B) + (AUX_DAC_D, ad5623_regs_t::ADDR_DAC_A) + ) + (UNIT_TX, map_list_of + (AUX_DAC_A, ad5623_regs_t::ADDR_DAC_A) + (AUX_DAC_B, ad5623_regs_t::ADDR_DAC_B) + (AUX_DAC_C, ad5623_regs_t::ADDR_DAC_B) + (AUX_DAC_D, ad5623_regs_t::ADDR_DAC_A) + ) + ; + _dac_regs[unit].addr = unit_to_which_to_addr[unit][which]; + this->_write_aux_dac(unit); +} + +double x300_dboard_iface::read_aux_adc(unit_t unit, aux_adc_t which) +{ + static const uhd::dict<unit_t, int> unit_to_spi_adc = map_list_of + (UNIT_RX, DB_RX_LSADC_SEN) + (UNIT_TX, DB_TX_LSADC_SEN) + ; + + //setup spi config args + spi_config_t config; + config.mosi_edge = spi_config_t::EDGE_FALL; + config.miso_edge = spi_config_t::EDGE_RISE; + + //setup the spi registers + ad7922_regs_t ad7922_regs; + switch(which){ + case AUX_ADC_A: ad7922_regs.mod = 0; break; + case AUX_ADC_B: ad7922_regs.mod = 1; break; + } ad7922_regs.chn = ad7922_regs.mod; //normal mode: mod == chn + + //write and read spi + _config.spi->write_spi( + unit_to_spi_adc[unit], config, + ad7922_regs.get_reg(), 16 + ); + ad7922_regs.set_reg(boost::uint16_t(_config.spi->read_spi( + unit_to_spi_adc[unit], config, + ad7922_regs.get_reg(), 16 + ))); + + //convert to voltage and return + return 3.3*ad7922_regs.result/4095; +} diff --git a/host/lib/usrp/x300/x300_fw_common.h b/host/lib/usrp/x300/x300_fw_common.h new file mode 100644 index 000000000..c470e9bff --- /dev/null +++ b/host/lib/usrp/x300/x300_fw_common.h @@ -0,0 +1,125 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_X300_FW_COMMON_H +#define INCLUDED_X300_FW_COMMON_H + +#include <stdint.h> + +/*! + * Structs and constants for x300 communication. + * This header is shared by the firmware and host code. + * Therefore, this header may only contain valid C code. + */ +#ifdef __cplusplus +extern "C" { +#endif + +#define X300_FW_COMPAT_MAJOR 3 +#define X300_FW_COMPAT_MINOR 0 +#define X300_FPGA_COMPAT_MAJOR 3 + +//shared memory sections - in between the stack and the program space +#define X300_FW_SHMEM_BASE 0x6000 +#define X300_FW_SHMEM_COMPAT_NUM 0 +#define X300_FW_SHMEM_GPSDO_STATUS 1 +#define X300_FW_SHMEM_UART_RX_INDEX 2 +#define X300_FW_SHMEM_UART_TX_INDEX 3 +#define X300_FW_SHMEM_CLAIM_STATUS 5 +#define X300_FW_SHMEM_CLAIM_TIME 6 +#define X300_FW_SHMEM_CLAIM_SRC 7 +#define X300_FW_SHMEM_UART_RX_ADDR 8 +#define X300_FW_SHMEM_UART_TX_ADDR 9 +#define X300_FW_SHMEM_UART_WORDS32 10 +#define X300_FW_SHMEM_ROUTE_MAP_ADDR 11 +#define X300_FW_SHMEM_ROUTE_MAP_LEN 12 + +#define X300_FW_NUM_BYTES (1 << 15) //64k +#define X300_FW_COMMS_MTU (1 << 13) //8k +#define X300_FW_COMMS_UDP_PORT 49152 + +#define X300_VITA_UDP_PORT 49153 +#define X300_GPSDO_UDP_PORT 49156 +#define X300_FPGA_PROG_UDP_PORT 49157 +#define X300_MTU_DETECT_UDP_PORT 49158 + +#define X300_DEFAULT_MAC_ADDR_0 {0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff} +#define X300_DEFAULT_MAC_ADDR_1 {0x00, 0x50, 0xC2, 0x85, 0x3f, 0x33} + +#define X300_DEFAULT_GATEWAY (192 << 24 | 168 << 16 | 10 << 8 | 1 << 0) + +#define X300_DEFAULT_IP_ETH0_1G (192 << 24 | 168 << 16 | 10 << 8 | 2 << 0) +#define X300_DEFAULT_IP_ETH1_1G (192 << 24 | 168 << 16 | 20 << 8 | 2 << 0) +#define X300_DEFAULT_IP_ETH0_10G (192 << 24 | 168 << 16 | 30 << 8 | 2 << 0) +#define X300_DEFAULT_IP_ETH1_10G (192 << 24 | 168 << 16 | 40 << 8 | 2 << 0) + +#define X300_DEFAULT_NETMASK_ETH0_1G (255 << 24 | 255 << 16 | 255 << 8 | 0 << 0) +#define X300_DEFAULT_NETMASK_ETH1_1G (255 << 24 | 255 << 16 | 255 << 8 | 0 << 0) +#define X300_DEFAULT_NETMASK_ETH0_10G (255 << 24 | 255 << 16 | 255 << 8 | 0 << 0) +#define X300_DEFAULT_NETMASK_ETH1_10G (255 << 24 | 255 << 16 | 255 << 8 | 0 << 0) + +#define X300_FW_COMMS_FLAGS_ACK (1 << 0) +#define X300_FW_COMMS_FLAGS_ERROR (1 << 1) +#define X300_FW_COMMS_FLAGS_POKE32 (1 << 2) +#define X300_FW_COMMS_FLAGS_PEEK32 (1 << 3) + +#define X300_FPGA_PROG_FLAGS_ACK (1 << 0) +#define X300_FPGA_PROG_FLAGS_ERROR (1 << 1) +#define X300_FPGA_PROG_FLAGS_INIT (1 << 2) +#define X300_FPGA_PROG_FLAGS_CLEANUP (1 << 3) +#define X300_FPGA_PROG_FLAGS_ERASE (1 << 4) +#define X300_FPGA_PROG_FLAGS_VERIFY (1 << 5) +#define X300_FPGA_PROG_CONFIGURE (1 << 6) +#define X300_FPGA_PROG_CONFIG_STATUS (1 << 7) + +#define X300_MTU_DETECT_ECHO_REQUEST (1 << 0) +#define X300_MTU_DETECT_ECHO_REPLY (1 << 1) +#define X300_MTU_DETECT_ERROR (1 << 2) + +typedef struct +{ + uint32_t flags; + uint32_t sequence; + uint32_t addr; + uint32_t data; +} x300_fw_comms_t; + +typedef struct +{ + uint32_t flags; + uint32_t sector; + uint32_t index; + uint32_t size; + uint16_t data[128]; +} x300_fpga_prog_t; + +typedef struct +{ + uint32_t flags; +} x300_fpga_prog_flags_t; + +typedef struct +{ + uint32_t flags; + uint32_t size; +} x300_mtu_t; + +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDED_X300_FW_COMMON_H */ diff --git a/host/lib/usrp/x300/x300_fw_ctrl.cpp b/host/lib/usrp/x300/x300_fw_ctrl.cpp new file mode 100644 index 000000000..67c314d3f --- /dev/null +++ b/host/lib/usrp/x300/x300_fw_ctrl.cpp @@ -0,0 +1,300 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/types/wb_iface.hpp> +#include "x300_fw_common.h" +#include <uhd/transport/udp_simple.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/exception.hpp> +#include <boost/format.hpp> +#include <boost/thread/mutex.hpp> +#include <uhd/transport/nirio/status.h> +#include <uhd/transport/nirio/niriok_proxy.h> +#include "x300_regs.hpp" +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/thread/thread.hpp> + +using namespace uhd; +using namespace uhd::niusrprio; + +class x300_ctrl_iface : public wb_iface +{ +public: + enum {num_retries = 3}; + + void flush(void) + { + boost::mutex::scoped_lock lock(reg_access); + __flush(); + } + + void poke32(const wb_addr_type addr, const boost::uint32_t data) + { + for (size_t i = 1; i <= num_retries; i++) + { + boost::mutex::scoped_lock lock(reg_access); + try + { + return this->__poke32(addr, data); + } + catch(const std::exception &ex) + { + const std::string error_msg = str(boost::format( + "x300 fw communication failure #%u\n%s") % i % ex.what()); + UHD_MSG(error) << error_msg << std::endl; + if (i == num_retries) throw uhd::io_error(error_msg); + } + } + } + + boost::uint32_t peek32(const wb_addr_type addr) + { + for (size_t i = 1; i <= num_retries; i++) + { + boost::mutex::scoped_lock lock(reg_access); + try + { + boost::uint32_t data = this->__peek32(addr); + return data; + } + catch(const std::exception &ex) + { + const std::string error_msg = str(boost::format( + "x300 fw communication failure #%u\n%s") % i % ex.what()); + UHD_MSG(error) << error_msg << std::endl; + if (i == num_retries) throw uhd::io_error(error_msg); + } + } + return 0; + } + +protected: + virtual void __poke32(const wb_addr_type addr, const boost::uint32_t data) = 0; + virtual boost::uint32_t __peek32(const wb_addr_type addr) = 0; + virtual void __flush() = 0; + + boost::mutex reg_access; +}; + + +//----------------------------------------------------- +// Ethernet impl +//----------------------------------------------------- +class x300_ctrl_iface_enet : public x300_ctrl_iface +{ +public: + x300_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp): + udp(udp), seq(0) + { + try + { + this->peek32(0); + } + catch(...){} + } + +protected: + virtual void __poke32(const wb_addr_type addr, const boost::uint32_t data) + { + //load request struct + x300_fw_comms_t request = x300_fw_comms_t(); + request.flags = uhd::htonx<boost::uint32_t>(X300_FW_COMMS_FLAGS_ACK | X300_FW_COMMS_FLAGS_POKE32); + request.sequence = uhd::htonx<boost::uint32_t>(seq++); + request.addr = uhd::htonx(addr); + request.data = uhd::htonx(data); + + //send request + __flush(); + udp->send(boost::asio::buffer(&request, sizeof(request))); + + //recv reply + x300_fw_comms_t reply = x300_fw_comms_t(); + const size_t nbytes = udp->recv(boost::asio::buffer(&reply, sizeof(reply)), 1.0); + if (nbytes == 0) throw uhd::io_error("x300 fw poke32 - reply timed out"); + + //sanity checks + const size_t flags = uhd::ntohx<boost::uint32_t>(reply.flags); + UHD_ASSERT_THROW(nbytes == sizeof(reply)); + UHD_ASSERT_THROW(not (flags & X300_FW_COMMS_FLAGS_ERROR)); + UHD_ASSERT_THROW(flags & X300_FW_COMMS_FLAGS_POKE32); + UHD_ASSERT_THROW(flags & X300_FW_COMMS_FLAGS_ACK); + UHD_ASSERT_THROW(reply.sequence == request.sequence); + UHD_ASSERT_THROW(reply.addr == request.addr); + UHD_ASSERT_THROW(reply.data == request.data); + } + + virtual boost::uint32_t __peek32(const wb_addr_type addr) + { + //load request struct + x300_fw_comms_t request = x300_fw_comms_t(); + request.flags = uhd::htonx<boost::uint32_t>(X300_FW_COMMS_FLAGS_ACK | X300_FW_COMMS_FLAGS_PEEK32); + request.sequence = uhd::htonx<boost::uint32_t>(seq++); + request.addr = uhd::htonx(addr); + request.data = 0; + + //send request + __flush(); + udp->send(boost::asio::buffer(&request, sizeof(request))); + + //recv reply + x300_fw_comms_t reply = x300_fw_comms_t(); + const size_t nbytes = udp->recv(boost::asio::buffer(&reply, sizeof(reply)), 1.0); + if (nbytes == 0) throw uhd::io_error("x300 fw peek32 - reply timed out"); + + //sanity checks + const size_t flags = uhd::ntohx<boost::uint32_t>(reply.flags); + UHD_ASSERT_THROW(nbytes == sizeof(reply)); + UHD_ASSERT_THROW(not (flags & X300_FW_COMMS_FLAGS_ERROR)); + UHD_ASSERT_THROW(flags & X300_FW_COMMS_FLAGS_PEEK32); + UHD_ASSERT_THROW(flags & X300_FW_COMMS_FLAGS_ACK); + UHD_ASSERT_THROW(reply.sequence == request.sequence); + UHD_ASSERT_THROW(reply.addr == request.addr); + + //return result! + return uhd::ntohx<boost::uint32_t>(reply.data); + } + + virtual void __flush(void) + { + char buff[X300_FW_COMMS_MTU] = {}; + while (udp->recv(boost::asio::buffer(buff), 0.0)){} //flush + } + +private: + uhd::transport::udp_simple::sptr udp; + size_t seq; +}; + + +//----------------------------------------------------- +// PCIe impl +//----------------------------------------------------- +class x300_ctrl_iface_pcie : public x300_ctrl_iface +{ +public: + x300_ctrl_iface_pcie(niriok_proxy& drv_proxy): + _drv_proxy(drv_proxy) + { + nirio_status status = 0; + nirio_status_chain(_drv_proxy.set_attribute(ADDRESS_SPACE, BUS_INTERFACE), status); + + //Verify that the Ettus FPGA loaded in the device. This may not be true if the + //user is switching to UHD after using LabVIEW FPGA. + boost::uint32_t pcie_fpga_signature = 0; + _drv_proxy.peek(FPGA_PCIE_SIG_REG, pcie_fpga_signature); + if (pcie_fpga_signature != FPGA_X3xx_SIG_VALUE) + throw uhd::io_error("cannot create x300_ctrl_iface_pcie. incorrect/no fpga image"); + + //Also, poll on the ZPU_STATUS bit to ensure all the state machines in the FPGA are + //ready to accept register transaction requests. + boost::uint32_t reg_data = 0xffffffff; + boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time(); + boost::posix_time::time_duration elapsed; + + do { + boost::this_thread::sleep(boost::posix_time::microsec(500)); //Avoid flooding the bus + elapsed = boost::posix_time::microsec_clock::local_time() - start_time; + nirio_status_chain(_drv_proxy.peek(PCIE_ZPU_STATUS_REG(0), reg_data), status); + } while ( + nirio_status_not_fatal(status) && + (reg_data & PCIE_ZPU_STATUS_SUSPENDED) && + elapsed.total_milliseconds() < INIT_TIMEOUT_IN_MS); + + nirio_status_to_exception(status, "Could not initialize x300_ctrl_iface_pcie."); + + try + { + this->peek32(0); + } + catch(...){} + } + +protected: + virtual void __poke32(const wb_addr_type addr, const boost::uint32_t data) + { + nirio_status status = 0; + boost::uint32_t reg_data = 0xffffffff; + boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time(); + boost::posix_time::time_duration elapsed; + + nirio_status_chain(_drv_proxy.poke(PCIE_ZPU_DATA_REG(addr), data), status); + if (nirio_status_not_fatal(status)) { + do { + boost::this_thread::sleep(boost::posix_time::microsec(50)); //Avoid flooding the bus + elapsed = boost::posix_time::microsec_clock::local_time() - start_time; + nirio_status_chain(_drv_proxy.peek(PCIE_ZPU_STATUS_REG(addr), reg_data), status); + } while ( + nirio_status_not_fatal(status) && + ((reg_data & (PCIE_ZPU_STATUS_BUSY | PCIE_ZPU_STATUS_SUSPENDED)) != 0) && + elapsed.total_milliseconds() < READ_TIMEOUT_IN_MS); + } + + if (nirio_status_fatal(status)) + throw uhd::io_error("x300 fw poke32 - hardware IO error"); + if (elapsed.total_milliseconds() > READ_TIMEOUT_IN_MS) + throw uhd::io_error("x300 fw poke32 - operation timed out"); + } + + virtual boost::uint32_t __peek32(const wb_addr_type addr) + { + nirio_status status = 0; + boost::uint32_t reg_data = 0xffffffff; + boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time(); + boost::posix_time::time_duration elapsed; + + nirio_status_chain(_drv_proxy.poke(PCIE_ZPU_READ_REG(addr), PCIE_ZPU_READ_START), status); + if (nirio_status_not_fatal(status)) { + do { + boost::this_thread::sleep(boost::posix_time::microsec(50)); //Avoid flooding the bus + elapsed = boost::posix_time::microsec_clock::local_time() - start_time; + nirio_status_chain(_drv_proxy.peek(PCIE_ZPU_STATUS_REG(addr), reg_data), status); + } while ( + nirio_status_not_fatal(status) && + ((reg_data & (PCIE_ZPU_STATUS_BUSY | PCIE_ZPU_STATUS_SUSPENDED)) != 0) && + elapsed.total_milliseconds() < READ_TIMEOUT_IN_MS); + } + nirio_status_chain(_drv_proxy.peek(PCIE_ZPU_DATA_REG(addr), reg_data), status); + + if (nirio_status_fatal(status)) + throw uhd::io_error("x300 fw peek32 - hardware IO error"); + if (elapsed.total_milliseconds() > READ_TIMEOUT_IN_MS) + throw uhd::io_error("x300 fw peek32 - operation timed out"); + + return reg_data; + } + + virtual void __flush(void) + { + __peek32(0); + } + +private: + niriok_proxy& _drv_proxy; + static const uint32_t READ_TIMEOUT_IN_MS = 10; + static const uint32_t INIT_TIMEOUT_IN_MS = 5000; +}; + +wb_iface::sptr x300_make_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp) +{ + return wb_iface::sptr(new x300_ctrl_iface_enet(udp)); +} + +wb_iface::sptr x300_make_ctrl_iface_pcie(niriok_proxy& drv_proxy) +{ + return wb_iface::sptr(new x300_ctrl_iface_pcie(drv_proxy)); +} diff --git a/host/lib/usrp/x300/x300_fw_uart.cpp b/host/lib/usrp/x300/x300_fw_uart.cpp new file mode 100644 index 000000000..943b2d9fa --- /dev/null +++ b/host/lib/usrp/x300/x300_fw_uart.cpp @@ -0,0 +1,104 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "x300_impl.hpp" +#include <uhd/types/wb_iface.hpp> +#include "x300_regs.hpp" +#include <uhd/utils/msg.hpp> +#include <uhd/types/serial.hpp> +#include <uhd/exception.hpp> +#include <boost/format.hpp> +#include <boost/foreach.hpp> +#include <boost/thread/thread.hpp> + +using namespace uhd; + +struct x300_uart_iface : uart_iface +{ + x300_uart_iface(wb_iface::sptr iface): + rxoffset(0), txoffset(0), txword32(0), rxpool(0), txpool(0), poolsize(0) + { + _iface = iface; + rxoffset = _iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_RX_INDEX)); + txoffset = _iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_TX_INDEX)); + rxpool = _iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_RX_ADDR)); + txpool = _iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_TX_ADDR)); + poolsize = _iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_WORDS32)); + //this->write_uart("HELLO UART\n"); + //this->read_uart(0.1); + } + + void putchar(const char ch) + { + txoffset = (txoffset + 1) % (poolsize*4); + const int shift = ((txoffset%4) * 8); + if (shift == 0) txword32 = 0; + txword32 |= boost::uint32_t(ch) << shift; + _iface->poke32(SR_ADDR(txpool, txoffset/4), txword32); + _iface->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_TX_INDEX), txoffset); + } + + void write_uart(const std::string &buff) + { + BOOST_FOREACH(const char ch, buff) + { + if (ch == '\n') this->putchar('\r'); + this->putchar(ch); + } + } + + int getchar(void) + { + if (_iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_RX_INDEX)) != rxoffset) + { + const int shift = ((rxoffset%4) * 8); + const char ch = _iface->peek32(SR_ADDR(rxpool, rxoffset/4)) >> shift; + rxoffset = (rxoffset + 1) % (poolsize*4); + return ch; + } + return -1; + } + + std::string read_uart(double timeout) + { + const boost::system_time exit_time = boost::get_system_time() + boost::posix_time::microseconds(long(timeout*1e6)); + std::string buff; + while (true) + { + const int ch = this->getchar(); + if (ch == -1) + { + if (boost::get_system_time() > exit_time) break; + boost::this_thread::sleep(boost::posix_time::milliseconds(1)); + continue; + } + if (ch == '\r') continue; + buff += std::string(1, (char)ch); + if (ch == '\n') break; + } + //UHD_VAR(buff); + return buff; + } + + wb_iface::sptr _iface; + boost::uint32_t rxoffset, txoffset, txword32, rxpool, txpool, poolsize; +}; + +uart_iface::sptr x300_make_uart_iface(wb_iface::sptr iface) +{ + return uart_iface::sptr(new x300_uart_iface(iface)); +} diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp new file mode 100644 index 000000000..f62967018 --- /dev/null +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -0,0 +1,1481 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "x300_impl.hpp" +#include "x300_regs.hpp" +#include "x300_lvbitx.hpp" +#include "x310_lvbitx.hpp" +#include <boost/algorithm/string.hpp> +#include <boost/asio.hpp> +#include "apply_corrections.hpp" +#include <uhd/utils/static.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/images.hpp> +#include <uhd/utils/safe_call.hpp> +#include <uhd/usrp/subdev_spec.hpp> +#include <uhd/transport/if_addrs.hpp> +#include <boost/foreach.hpp> +#include <boost/bind.hpp> +#include <boost/functional/hash.hpp> +#include <boost/assign/list_of.hpp> +#include <fstream> +#include <uhd/transport/udp_zero_copy.hpp> +#include <uhd/transport/nirio_zero_copy.hpp> +#include <uhd/transport/nirio/niusrprio_session.h> +#include <uhd/utils/platform.hpp> + +#define NIUSRPRIO_DEFAULT_RPC_PORT "5444" + +#define X300_REV(x) (x - "A" + 1) + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; +using namespace uhd::niusrprio; +namespace asio = boost::asio; + +/*********************************************************************** + * Discovery over the udp and pcie transport + **********************************************************************/ +static std::string get_fpga_option(wb_iface::sptr zpu_ctrl) { + //1G = {0:1G, 1:1G} w/ DRAM, HG = {0:1G, 1:10G} w/ DRAM, XG = {0:10G, 1:10G} w/ DRAM + //HGS = {0:1G, 1:10G} w/ SRAM, XGS = {0:10G, 1:10G} w/ SRAM + + //In the default configuration, UHD does not support the HG and XG images so + //they are never autodetected. + bool eth0XG = (zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_ETH_TYPE0)) == 0x1); + bool eth1XG = (zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_ETH_TYPE1)) == 0x1); + return (eth0XG && eth1XG) ? "XGS" : (eth1XG ? "HGS" : "1G"); +} + +//@TODO: Refactor the find functions to collapse common code for ethernet and PCIe +static device_addrs_t x300_find_with_addr(const device_addr_t &hint) +{ + udp_simple::sptr comm = udp_simple::make_broadcast( + hint["addr"], BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT)); + + //load request struct + x300_fw_comms_t request = x300_fw_comms_t(); + request.flags = uhd::htonx<boost::uint32_t>(X300_FW_COMMS_FLAGS_ACK); + request.sequence = uhd::htonx<boost::uint32_t>(std::rand()); + + //send request + comm->send(asio::buffer(&request, sizeof(request))); + + //loop for replies until timeout + device_addrs_t addrs; + while (true) + { + char buff[X300_FW_COMMS_MTU] = {}; + const size_t nbytes = comm->recv(asio::buffer(buff), 0.050); + if (nbytes == 0) break; + const x300_fw_comms_t *reply = (const x300_fw_comms_t *)buff; + if (request.flags != reply->flags) break; + if (request.sequence != reply->sequence) break; + device_addr_t new_addr; + new_addr["type"] = "x300"; + new_addr["addr"] = comm->get_recv_addr(); + + //Attempt to read the name from the EEPROM and perform filtering. + //This operation can throw due to compatibility mismatch. + try + { + wb_iface::sptr zpu_ctrl = x300_make_ctrl_iface_enet(udp_simple::make_connected(new_addr["addr"], BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT))); + if (x300_impl::is_claimed(zpu_ctrl)) continue; //claimed by another process + new_addr["fpga"] = get_fpga_option(zpu_ctrl); + + i2c_core_100_wb32::sptr zpu_i2c = i2c_core_100_wb32::make(zpu_ctrl, I2C1_BASE); + i2c_iface::sptr eeprom16 = zpu_i2c->eeprom16(); + const mboard_eeprom_t mb_eeprom(*eeprom16, "X300"); + new_addr["name"] = mb_eeprom["name"]; + new_addr["serial"] = mb_eeprom["serial"]; + switch (x300_impl::get_mb_type_from_eeprom(mb_eeprom)) { + case x300_impl::USRP_X300_MB: + new_addr["product"] = "X300"; + break; + case x300_impl::USRP_X310_MB: + new_addr["product"] = "X310"; + break; + default: + break; + } + } + catch(const std::exception &) + { + //set these values as empty string so the device may still be found + //and the filter's below can still operate on the discovered device + new_addr["name"] = ""; + new_addr["serial"] = ""; + } + //filter the discovered device below by matching optional keys + if ( + (not hint.has_key("name") or hint["name"] == new_addr["name"]) and + (not hint.has_key("serial") or hint["serial"] == new_addr["serial"]) and + (not hint.has_key("product") or hint["product"] == new_addr["product"]) + ){ + addrs.push_back(new_addr); + } + } + + return addrs; +} + +static device_addrs_t x300_find_pcie(const device_addr_t &hint, bool explicit_query) +{ + std::string rpc_port_name(NIUSRPRIO_DEFAULT_RPC_PORT); + if (hint.has_key("niusrpriorpc_port")) { + rpc_port_name = hint["niusrpriorpc_port"]; + } + + device_addrs_t addrs; + niusrprio_session::device_info_vtr dev_info_vtr; + nirio_status status = niusrprio_session::enumerate(rpc_port_name, dev_info_vtr); + if (explicit_query) nirio_status_to_exception(status, "x300_find_pcie: Error enumerating NI-RIO devices."); + + BOOST_FOREACH(niusrprio_session::device_info &dev_info, dev_info_vtr) + { + device_addr_t new_addr; + new_addr["type"] = "x300"; + new_addr["resource"] = dev_info.resource_name; + std::string resource_d(dev_info.resource_name); + boost::to_upper(resource_d); + + switch (x300_impl::get_mb_type_from_pcie(resource_d, rpc_port_name)) { + case x300_impl::USRP_X300_MB: + new_addr["product"] = "X300"; + break; + case x300_impl::USRP_X310_MB: + new_addr["product"] = "X310"; + break; + default: + continue; + } + + niriok_proxy kernel_proxy; + kernel_proxy.open(dev_info.interface_path); + + //Attempt to read the name from the EEPROM and perform filtering. + //This operation can throw due to compatibility mismatch. + try + { + //This call could throw an exception if the user is switching to using UHD + //after LabVIEW FPGA. In that case, skip reading the name and serial and pick + //a default FPGA flavor. During make, a new image will be loaded and everything + //will be OK + wb_iface::sptr zpu_ctrl = x300_make_ctrl_iface_pcie(kernel_proxy); + if (x300_impl::is_claimed(zpu_ctrl)) continue; //claimed by another process + + //Attempt to autodetect the FPGA type + if (not hint.has_key("fpga")) { + new_addr["fpga"] = get_fpga_option(zpu_ctrl); + } + + i2c_core_100_wb32::sptr zpu_i2c = i2c_core_100_wb32::make(zpu_ctrl, I2C1_BASE); + i2c_iface::sptr eeprom16 = zpu_i2c->eeprom16(); + const mboard_eeprom_t mb_eeprom(*eeprom16, "X300"); + new_addr["name"] = mb_eeprom["name"]; + new_addr["serial"] = mb_eeprom["serial"]; + } + catch(const std::exception &) + { + //set these values as empty string so the device may still be found + //and the filter's below can still operate on the discovered device + if (not hint.has_key("fpga")) { + new_addr["fpga"] = "HGS"; + } + new_addr["name"] = ""; + new_addr["serial"] = ""; + } + kernel_proxy.close(); + + //filter the discovered device below by matching optional keys + std::string resource_i = hint.has_key("resource") ? hint["resource"] : ""; + boost::to_upper(resource_i); + + if ( + (not hint.has_key("resource") or resource_i == resource_d) and + (not hint.has_key("name") or hint["name"] == new_addr["name"]) and + (not hint.has_key("serial") or hint["serial"] == new_addr["serial"]) and + (not hint.has_key("product") or hint["product"] == new_addr["product"]) + ){ + addrs.push_back(new_addr); + } + } + return addrs; +} + +static device_addrs_t x300_find(const device_addr_t &hint_) +{ + //handle the multi-device discovery + device_addrs_t hints = separate_device_addr(hint_); + if (hints.size() > 1) + { + device_addrs_t found_devices; + std::string error_msg; + BOOST_FOREACH(const device_addr_t &hint_i, hints) + { + device_addrs_t found_devices_i = x300_find(hint_i); + if (found_devices_i.size() != 1) error_msg += str(boost::format( + "Could not resolve device hint \"%s\" to a single device." + ) % hint_i.to_string()); + else found_devices.push_back(found_devices_i[0]); + } + if (found_devices.empty()) return device_addrs_t(); + if (not error_msg.empty()) throw uhd::value_error(error_msg); + + return device_addrs_t(1, combine_device_addrs(found_devices)); + } + + //initialize the hint for a single device case + UHD_ASSERT_THROW(hints.size() <= 1); + hints.resize(1); //in case it was empty + device_addr_t hint = hints[0]; + device_addrs_t addrs; + if (hint.has_key("type") and hint["type"] != "x300") return addrs; + + + //use the address given + if (hint.has_key("addr")) + { + device_addrs_t reply_addrs; + try + { + reply_addrs = x300_find_with_addr(hint); + } + catch(const std::exception &ex) + { + UHD_MSG(error) << "X300 Network discovery error " << ex.what() << std::endl; + } + catch(...) + { + UHD_MSG(error) << "X300 Network discovery unknown error " << std::endl; + } + BOOST_FOREACH(const device_addr_t &reply_addr, reply_addrs) + { + device_addrs_t new_addrs = x300_find_with_addr(reply_addr); + addrs.insert(addrs.begin(), new_addrs.begin(), new_addrs.end()); + } + return addrs; + } + + if (!hint.has_key("resource")) + { + //otherwise, no address was specified, send a broadcast on each interface + BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()) + { + //avoid the loopback device + if (if_addrs.inet == asio::ip::address_v4::loopback().to_string()) continue; + + //create a new hint with this broadcast address + device_addr_t new_hint = hint; + new_hint["addr"] = if_addrs.bcast; + + //call discover with the new hint and append results + device_addrs_t new_addrs = x300_find(new_hint); + addrs.insert(addrs.begin(), new_addrs.begin(), new_addrs.end()); + } + } + + device_addrs_t pcie_addrs = x300_find_pcie(hint, hint.has_key("resource")); + if (not pcie_addrs.empty()) addrs.insert(addrs.end(), pcie_addrs.begin(), pcie_addrs.end()); + + return addrs; +} + +/*********************************************************************** + * Make + **********************************************************************/ +static device::sptr x300_make(const device_addr_t &device_addr) +{ + return device::sptr(new x300_impl(device_addr)); +} + +UHD_STATIC_BLOCK(register_x300_device) +{ + device::register_device(&x300_find, &x300_make); +} + +static void x300_load_fw(wb_iface::sptr fw_reg_ctrl, const std::string &file_name) +{ + UHD_MSG(status) << "Loading firmware " << file_name << std::flush; + + //load file into memory + std::ifstream fw_file(file_name.c_str()); + boost::uint32_t fw_file_buff[X300_FW_NUM_BYTES/sizeof(boost::uint32_t)]; + fw_file.read((char *)fw_file_buff, sizeof(fw_file_buff)); + fw_file.close(); + + //Poke the fw words into the WB boot loader + fw_reg_ctrl->poke32(SR_ADDR(BOOT_LDR_BASE, BL_ADDRESS), 0); + for (size_t i = 0; i < X300_FW_NUM_BYTES; i+=sizeof(boost::uint32_t)) + { + //@TODO: FIXME: Since x300_ctrl_iface acks each write and traps exceptions, the first try for the last word + // written will print an error because it triggers a FW reload and fails to reply. + fw_reg_ctrl->poke32(SR_ADDR(BOOT_LDR_BASE, BL_DATA), uhd::byteswap(fw_file_buff[i/sizeof(boost::uint32_t)])); + if ((i & 0x1fff) == 0) UHD_MSG(status) << "." << std::flush; + } + + UHD_MSG(status) << " done!" << std::endl; +} + +x300_impl::x300_impl(const uhd::device_addr_t &dev_addr) +{ + UHD_MSG(status) << "X300 initialization sequence..." << std::endl; + _async_md.reset(new async_md_type(1000/*messages deep*/)); + _tree = uhd::property_tree::make(); + _tree->create<std::string>("/name").set("X-Series Device"); + _sid_framer = 0; + + const device_addrs_t device_args = separate_device_addr(dev_addr); + _mb.resize(device_args.size()); + for (size_t i = 0; i < device_args.size(); i++) + { + this->setup_mb(i, device_args[i]); + } +} + +void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) +{ + const fs_path mb_path = "/mboards/"+boost::lexical_cast<std::string>(mb_i); + mboard_members_t &mb = _mb[mb_i]; + + mb.addr = dev_addr.has_key("resource") ? dev_addr["resource"] : dev_addr["addr"]; + mb.xport_path = dev_addr.has_key("resource") ? "nirio" : "eth"; + mb.if_pkt_is_big_endian = mb.xport_path != "nirio"; + + if (mb.xport_path == "nirio") + { + nirio_status status = 0; + + std::string rpc_port_name(NIUSRPRIO_DEFAULT_RPC_PORT); + if (dev_addr.has_key("niusrpriorpc_port")) { + rpc_port_name = dev_addr["niusrpriorpc_port"]; + } + UHD_MSG(status) << boost::format("Connecting to niusrpriorpc at localhost:%s...\n") % rpc_port_name; + + //Instantiate the correct lvbitx object + nifpga_lvbitx::sptr lvbitx; + switch (get_mb_type_from_pcie(dev_addr["resource"], rpc_port_name)) { + case USRP_X300_MB: + lvbitx.reset(new x300_lvbitx(dev_addr["fpga"])); + break; + case USRP_X310_MB: + lvbitx.reset(new x310_lvbitx(dev_addr["fpga"])); + break; + default: + nirio_status_to_exception(status, "Motherboard detection error. Please ensure that you \ + have a valid USRP X3x0, NI USRP-294xR or NI USRP-295xR device and that all the device \ + driver have been loaded."); + } + + //Load the lvbitx onto the device + UHD_MSG(status) << boost::format("Using LVBITX bitfile %s...\n") % lvbitx->get_bitfile_path(); + mb.rio_fpga_interface.reset(new niusrprio_session(dev_addr["resource"], rpc_port_name)); + nirio_status_chain(mb.rio_fpga_interface->open(lvbitx, dev_addr.has_key("download-fpga")), status); + nirio_status_to_exception(status, "x300_impl: Could not initialize RIO session."); + + //Tell the quirks object which FIFOs carry TX stream data + const uint32_t tx_data_fifos[2] = {X300_RADIO_DEST_PREFIX_TX, X300_RADIO_DEST_PREFIX_TX + 3}; + mb.rio_fpga_interface->get_kernel_proxy().get_rio_quirks().register_tx_streams(tx_data_fifos); + } + + BOOST_FOREACH(const std::string &key, dev_addr.keys()) + { + if (key.find("recv") != std::string::npos) mb.recv_args[key] = dev_addr[key]; + if (key.find("send") != std::string::npos) mb.send_args[key] = dev_addr[key]; + } + + const std::vector<std::string> DB_NAMES = boost::assign::list_of("A")("B"); + + //create basic communication + UHD_MSG(status) << "Setup basic communication..." << std::endl; + if (mb.xport_path == "nirio") { + mb.zpu_ctrl = x300_make_ctrl_iface_pcie(mb.rio_fpga_interface->get_kernel_proxy()); + } else { + mb.zpu_ctrl = x300_make_ctrl_iface_enet(udp_simple::make_connected(mb.addr, + BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT))); + } + + if (mb.xport_path == "eth") + { + mtu_result_t user_set; + user_set.recv_mtu = dev_addr.has_key("recv_frame_size") \ + ? boost::lexical_cast<size_t>(dev_addr["recv_frame_size"]) \ + : X300_ETH_DATA_FRAME_SIZE; + user_set.send_mtu = dev_addr.has_key("send_frame_size") \ + ? boost::lexical_cast<size_t>(dev_addr["send_frame_size"]) \ + : X300_ETH_DATA_FRAME_SIZE; + + // Detect the MTU on the path to the USRP + mtu_result_t result; + try { + result = determine_mtu(mb.addr, user_set); + } catch(std::exception &e) { + UHD_MSG(error) << e.what() << std::endl; + } + + #if defined UHD_PLATFORM_LINUX + const std::string mtu_tool("ip link"); + #elif defined UHD_PLATFORM_WIN32 + const std::string mtu_tool("netsh"); + #else + const std::string mtu_tool("ifconfig"); + #endif + + if(result.recv_mtu < user_set.recv_mtu) { + UHD_MSG(warning) + << boost::format("The receive path contains entities that do not support MTUs >= one recv frame's size (%lu).") + % user_set.recv_mtu << std::endl + << boost::format("Please verify your NIC's MTU setting using '%s' or set the recv_frame_size argument.") + % mtu_tool << std::endl; + } + + if(result.send_mtu < user_set.send_mtu) { + UHD_MSG(warning) + << boost::format("The send path contains entities that do not support MTUs >= one send frame's size (%lu).") + % user_set.send_mtu << std::endl + << boost::format("Please verify your NIC's MTU setting using '%s' or set the send_frame_size argument.") + % mtu_tool << std::endl; + } + } + + mb.claimer_task = uhd::task::make(boost::bind(&x300_impl::claimer_loop, this, mb.zpu_ctrl)); + + //extract the FW path for the X300 + //and live load fw over ethernet link + if (dev_addr.has_key("fw")) + { + const std::string x300_fw_image = find_image_path( + dev_addr.has_key("fw")? dev_addr["fw"] : X300_FW_FILE_NAME + ); + x300_load_fw(mb.zpu_ctrl, x300_fw_image); + } + + //check compat -- good place to do after conditional loading + this->check_fw_compat(mb_path, mb.zpu_ctrl); + this->check_fpga_compat(mb_path, mb.zpu_ctrl); + + //low speed perif access + mb.zpu_spi = spi_core_3000::make(mb.zpu_ctrl, SR_ADDR(SET0_BASE, ZPU_SR_SPI), + SR_ADDR(SET0_BASE, ZPU_RB_SPI)); + mb.zpu_i2c = i2c_core_100_wb32::make(mb.zpu_ctrl, I2C1_BASE); + mb.zpu_i2c->set_clock_rate(X300_BUS_CLOCK_RATE); + + //////////////////////////////////////////////////////////////////// + // print network routes mapping + //////////////////////////////////////////////////////////////////// + /* + const uint32_t routes_addr = mb.zpu_ctrl->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_ROUTE_MAP_ADDR)); + const uint32_t routes_len = mb.zpu_ctrl->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_ROUTE_MAP_LEN)); + UHD_VAR(routes_len); + for (size_t i = 0; i < routes_len; i+=1) + { + const uint32_t node_addr = mb.zpu_ctrl->peek32(SR_ADDR(routes_addr, i*2+0)); + const uint32_t nbor_addr = mb.zpu_ctrl->peek32(SR_ADDR(routes_addr, i*2+1)); + if (node_addr != 0 and nbor_addr != 0) + { + UHD_MSG(status) << boost::format("%u: %s -> %s") + % i + % asio::ip::address_v4(node_addr).to_string() + % asio::ip::address_v4(nbor_addr).to_string() + << std::endl; + } + } + */ + + //////////////////////////////////////////////////////////////////// + // setup the mboard eeprom + //////////////////////////////////////////////////////////////////// + UHD_MSG(status) << "Loading values from EEPROM..." << std::endl; + i2c_iface::sptr eeprom16 = mb.zpu_i2c->eeprom16(); + if (dev_addr.has_key("blank_eeprom")) + { + UHD_MSG(warning) << "Obliterating the motherboard EEPROM..." << std::endl; + eeprom16->write_eeprom(0x50, 0, byte_vector_t(256, 0xff)); + } + const mboard_eeprom_t mb_eeprom(*eeprom16, "X300"); + _tree->create<mboard_eeprom_t>(mb_path / "eeprom") + .set(mb_eeprom) + .subscribe(boost::bind(&x300_impl::set_mb_eeprom, this, mb.zpu_i2c, _1)); + + //////////////////////////////////////////////////////////////////// + // parse the product number + //////////////////////////////////////////////////////////////////// + std::string product_name = "X300?"; + switch (get_mb_type_from_eeprom(mb_eeprom)) { + case USRP_X300_MB: + product_name = "X300"; + break; + case USRP_X310_MB: + product_name = "X310"; + break; + default: + break; + } + _tree->create<std::string>(mb_path / "name").set(product_name); + _tree->create<std::string>(mb_path / "codename").set("Yetti"); + + //////////////////////////////////////////////////////////////////// + // determine routing based on address match + //////////////////////////////////////////////////////////////////// + mb.router_dst_here = X300_XB_DST_E0; //some default if eeprom not match + if (mb.xport_path == "nirio") { + mb.router_dst_here = X300_XB_DST_PCI; + } else { + if (mb.addr == mb_eeprom["ip-addr0"]) mb.router_dst_here = X300_XB_DST_E0; + else if (mb.addr == mb_eeprom["ip-addr1"]) mb.router_dst_here = X300_XB_DST_E1; + else if (mb.addr == mb_eeprom["ip-addr2"]) mb.router_dst_here = X300_XB_DST_E0; + else if (mb.addr == mb_eeprom["ip-addr3"]) mb.router_dst_here = X300_XB_DST_E1; + else if (mb.addr == boost::asio::ip::address_v4(boost::uint32_t(X300_DEFAULT_IP_ETH0_1G)).to_string()) mb.router_dst_here = X300_XB_DST_E0; + else if (mb.addr == boost::asio::ip::address_v4(boost::uint32_t(X300_DEFAULT_IP_ETH1_1G)).to_string()) mb.router_dst_here = X300_XB_DST_E1; + else if (mb.addr == boost::asio::ip::address_v4(boost::uint32_t(X300_DEFAULT_IP_ETH0_10G)).to_string()) mb.router_dst_here = X300_XB_DST_E0; + else if (mb.addr == boost::asio::ip::address_v4(boost::uint32_t(X300_DEFAULT_IP_ETH1_10G)).to_string()) mb.router_dst_here = X300_XB_DST_E1; + } + + //////////////////////////////////////////////////////////////////// + // read dboard eeproms + //////////////////////////////////////////////////////////////////// + for (size_t i = 0; i < 8; i++) + { + if (i == 0 or i == 2) continue; //not used + mb.db_eeproms[i].load(*mb.zpu_i2c, 0x50 | i); + } + + //////////////////////////////////////////////////////////////////// + // create clock control objects + //////////////////////////////////////////////////////////////////// + UHD_MSG(status) << "Setup RF frontend clocking..." << std::endl; + + // Init shadow and clock source; the device comes up with it's internal + // clock source before locking to something else (if requested). + mb.clock_control_regs__clock_source = 0; + mb.clock_control_regs__pps_select = 0; + mb.clock_control_regs__pps_out_enb = 0; + mb.clock_control_regs__tcxo_enb = 1; + mb.clock_control_regs__gpsdo_pwr = 1; + this->update_clock_source(mb, "internal"); + this->update_clock_control(mb); + + size_t hw_rev = 0; + if(mb_eeprom.has_key("revision") and not mb_eeprom["revision"].empty()) { + try { + hw_rev = boost::lexical_cast<size_t>(mb_eeprom["revision"]); + } catch(...) { + UHD_MSG(warning) << "Revision in EEPROM is invalid! Please reprogram your EEPROM." << std::endl; + } + } else { + UHD_MSG(warning) << "No revision detected MB EEPROM must be reprogrammed!" << std::endl; + } + + if(hw_rev == 0) { + UHD_MSG(warning) << "Defaulting to X300 RevD Clock Settings. This will result in non-optimal lock times." << std::endl; + hw_rev = X300_REV("D"); + } + + mb.clock = x300_clock_ctrl::make(mb.zpu_spi, + 1 /*slaveno*/, + hw_rev, + dev_addr.cast<double>("master_clock_rate", X300_DEFAULT_TICK_RATE), + dev_addr.cast<double>("system_ref_rate", X300_DEFAULT_SYSREF_RATE)); + + //////////////////////////////////////////////////////////////////// + // create clock properties + //////////////////////////////////////////////////////////////////// + _tree->create<double>(mb_path / "tick_rate") + .publish(boost::bind(&x300_clock_ctrl::get_master_clock_rate, mb.clock)); + + _tree->create<time_spec_t>(mb_path / "time" / "cmd"); + + UHD_MSG(status) << "Radio 1x clock:" << (mb.clock->get_master_clock_rate()/1e6) + << std::endl; + + //////////////////////////////////////////////////////////////////// + // Create the GPSDO control + //////////////////////////////////////////////////////////////////// + static const boost::uint32_t dont_look_for_gpsdo = 0x1234abcdul; + + //otherwise if not disabled, look for the internal GPSDO + if (mb.zpu_ctrl->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_GPSDO_STATUS)) != dont_look_for_gpsdo) + { + UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush; + try + { + mb.gps = gps_ctrl::make(x300_make_uart_iface(mb.zpu_ctrl)); + } + catch(std::exception &e) + { + UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl; + } + if (mb.gps and mb.gps->gps_detected()) + { + BOOST_FOREACH(const std::string &name, mb.gps->get_sensors()) + { + _tree->create<sensor_value_t>(mb_path / "sensors" / name) + .publish(boost::bind(&gps_ctrl::get_sensor, mb.gps, name)); + } + } + else + { + mb.zpu_ctrl->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_GPSDO_STATUS), dont_look_for_gpsdo); + } + } + + //////////////////////////////////////////////////////////////////// + //clear router? + //////////////////////////////////////////////////////////////////// + for (size_t i = 0; i < 512; i++) { + mb.zpu_ctrl->poke32(SR_ADDR(SETXB_BASE, i), 0); + } + + //////////////////////////////////////////////////////////////////// + // setup radios + //////////////////////////////////////////////////////////////////// + UHD_MSG(status) << "Initialize Radio control..." << std::endl; + this->setup_radio(mb_i, 0, DB_NAMES[0]); + this->setup_radio(mb_i, 1, DB_NAMES[1]); + + //////////////////////////////////////////////////////////////////// + // front panel gpio + //////////////////////////////////////////////////////////////////// + mb.fp_gpio = gpio_core_200::make(mb.radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO); + const std::vector<std::string> GPIO_ATTRS = boost::assign::list_of("CTRL")("DDR")("OUT")("ATR_0X")("ATR_RX")("ATR_TX")("ATR_XX"); + BOOST_FOREACH(const std::string &attr, GPIO_ATTRS) + { + _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr) + .set(0) + .subscribe(boost::bind(&x300_impl::set_fp_gpio, this, mb.fp_gpio, attr, _1)); + } + _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "READBACK") + .publish(boost::bind(&x300_impl::get_fp_gpio, this, mb.fp_gpio, "READBACK")); + + //////////////////////////////////////////////////////////////////// + // register the time keepers - only one can be the highlander + //////////////////////////////////////////////////////////////////// + _tree->create<time_spec_t>(mb_path / "time" / "now") + .publish(boost::bind(&time_core_3000::get_time_now, mb.radio_perifs[0].time64)) + .subscribe(boost::bind(&time_core_3000::set_time_now, mb.radio_perifs[0].time64, _1)) + .subscribe(boost::bind(&time_core_3000::set_time_now, mb.radio_perifs[1].time64, _1)); + _tree->create<time_spec_t>(mb_path / "time" / "pps") + .publish(boost::bind(&time_core_3000::get_time_last_pps, mb.radio_perifs[0].time64)) + .subscribe(boost::bind(&time_core_3000::set_time_next_pps, mb.radio_perifs[0].time64, _1)) + .subscribe(boost::bind(&time_core_3000::set_time_next_pps, mb.radio_perifs[1].time64, _1)); + + //////////////////////////////////////////////////////////////////// + // setup time sources and properties + //////////////////////////////////////////////////////////////////// + _tree->create<std::string>(mb_path / "time_source" / "value") + .subscribe(boost::bind(&x300_impl::update_time_source, this, boost::ref(mb), _1)); + static const std::vector<std::string> time_sources = boost::assign::list_of("internal")("external")("gpsdo"); + _tree->create<std::vector<std::string> >(mb_path / "time_source" / "options").set(time_sources); + + //setup the time output, default to ON + _tree->create<bool>(mb_path / "time_source" / "output") + .subscribe(boost::bind(&x300_impl::set_time_source_out, this, boost::ref(mb), _1)) + .set(true); + + //////////////////////////////////////////////////////////////////// + // setup clock sources and properties + //////////////////////////////////////////////////////////////////// + _tree->create<std::string>(mb_path / "clock_source" / "value") + .subscribe(boost::bind(&x300_impl::update_clock_source, this, boost::ref(mb), _1)); + + static const std::vector<std::string> clock_source_options = boost::assign::list_of("internal")("external")("gpsdo"); + _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options").set(clock_source_options); + + //setup external reference options. default to 10 MHz input reference + _tree->create<std::string>(mb_path / "clock_source" / "external"); + static const std::vector<double> external_freq_options = boost::assign::list_of(10e6)(30.72e6)(200e6); + _tree->create<std::vector<double> >(mb_path / "clock_source" / "external" / "freq" / "options") + .set(external_freq_options); + _tree->create<double>(mb_path / "clock_source" / "external" / "value") + .set(mb.clock->get_sysref_clock_rate()); + // FIXME the external clock source settings need to be more robust + + //setup the clock output, default to ON + _tree->create<bool>(mb_path / "clock_source" / "output") + .subscribe(boost::bind(&x300_clock_ctrl::set_ref_out, mb.clock, _1)); + + + //////////////////////////////////////////////////////////////////// + // create frontend mapping + //////////////////////////////////////////////////////////////////// + _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec") + .subscribe(boost::bind(&x300_impl::update_rx_subdev_spec, this, mb_i, _1)); + _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec") + .subscribe(boost::bind(&x300_impl::update_tx_subdev_spec, this, mb_i, _1)); + + //////////////////////////////////////////////////////////////////// + // and do the misc mboard sensors + //////////////////////////////////////////////////////////////////// + _tree->create<sensor_value_t>(mb_path / "sensors" / "ref_locked") + .publish(boost::bind(&x300_impl::get_ref_locked, this, mb.zpu_ctrl)); + + //////////////////////////////////////////////////////////////////// + // create clock properties + //////////////////////////////////////////////////////////////////// + _tree->access<double>(mb_path / "tick_rate") + .subscribe(boost::bind(&x300_impl::set_tick_rate, this, boost::ref(mb), _1)) + .subscribe(boost::bind(&x300_impl::update_tick_rate, this, boost::ref(mb), _1)) + .set(mb.clock->get_master_clock_rate()); + + //////////////////////////////////////////////////////////////////// + // do some post-init tasks + //////////////////////////////////////////////////////////////////// + subdev_spec_t rx_fe_spec, tx_fe_spec; + rx_fe_spec.push_back(subdev_spec_pair_t("A", + _tree->list(mb_path / "dboards" / "A" / "rx_frontends").at(0))); + rx_fe_spec.push_back(subdev_spec_pair_t("B", + _tree->list(mb_path / "dboards" / "B" / "rx_frontends").at(0))); + tx_fe_spec.push_back(subdev_spec_pair_t("A", + _tree->list(mb_path / "dboards" / "A" / "tx_frontends").at(0))); + tx_fe_spec.push_back(subdev_spec_pair_t("B", + _tree->list(mb_path / "dboards" / "B" / "tx_frontends").at(0))); + + _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(rx_fe_spec); + _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_fe_spec); + + //GPS installed: use external ref, time, and init time spec + if (mb.gps and mb.gps->gps_detected()) + { + UHD_MSG(status) << "Setting references to the internal GPSDO" << std::endl; + _tree->access<std::string>(mb_path / "time_source" / "value").set("gpsdo"); + _tree->access<std::string>(mb_path / "clock_source" / "value").set("gpsdo"); + UHD_MSG(status) << "Initializing time to the internal GPSDO" << std::endl; + const time_t tp = time_t(mb.gps->get_sensor("gps_time").to_int()+1); + _tree->access<time_spec_t>(mb_path / "time" / "pps").set(time_spec_t(tp)); + } + else + { + _tree->access<std::string>(mb_path / "time_source" / "value").set("external"); + _tree->access<std::string>(mb_path / "clock_source" / "value").set("external"); + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + if (this->get_ref_locked(mb.zpu_ctrl).to_bool()) + { + UHD_MSG(status) << "Setting references to external sources" << std::endl; + } + else + { + UHD_MSG(status) << "Setting references to internal sources" << std::endl; + _tree->access<std::string>(mb_path / "time_source" / "value").set("internal"); + _tree->access<std::string>(mb_path / "clock_source" / "value").set("internal"); + } + } +} + +x300_impl::~x300_impl(void) +{ + try + { + BOOST_FOREACH(mboard_members_t &mb, _mb) + { + mb.radio_perifs[0].ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 2)); //disable/reset ADC/DAC + mb.radio_perifs[1].ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 2)); //disable/reset ADC/DAC + + //kill the claimer task and unclaim the device + mb.claimer_task.reset(); + mb.zpu_ctrl->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_TIME), 0); + mb.zpu_ctrl->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_SRC), 0); + } + } + catch(...) + { + UHD_SAFE_CALL(throw;) + } +} + +static void check_adc(wb_iface::sptr iface, const boost::uint32_t val) +{ + boost::uint32_t adc_rb = iface->peek32(RB32_RX); + adc_rb ^= 0xfffc0000; //adapt for I inversion in FPGA + //UHD_MSG(status) << "adc_rb " << std::hex << adc_rb << " val " << std::hex << val << std::endl; + UHD_ASSERT_THROW(adc_rb == val); +} + +void x300_impl::setup_radio(const size_t mb_i, const size_t i, const std::string &db_name) +{ + const fs_path mb_path = "/mboards/"+boost::lexical_cast<std::string>(mb_i); + mboard_members_t &mb = _mb[mb_i]; + radio_perifs_t &perif = mb.radio_perifs[i]; + const size_t dspno = i; + + //////////////////////////////////////////////////////////////////// + // radio control + //////////////////////////////////////////////////////////////////// + uint8_t dest = (i == 0)? X300_XB_DST_R0 : X300_XB_DST_R1; + boost::uint32_t ctrl_sid; + both_xports_t xport = this->make_transport(mb_i, dest, X300_RADIO_DEST_PREFIX_CTRL, device_addr_t(), ctrl_sid); + perif.ctrl = radio_ctrl_core_3000::make(mb.if_pkt_is_big_endian, xport.recv, xport.send, ctrl_sid, db_name); + perif.ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 2)); //reset adc + dac + perif.ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 1) | (1 << 0)); //out of reset + dac enable + + this->register_loopback_self_test(perif.ctrl); + + perif.spi = spi_core_3000::make(perif.ctrl, TOREG(SR_SPI), RB32_SPI); + perif.adc = x300_adc_ctrl::make(perif.spi, DB_ADC_SEN); + perif.dac = x300_dac_ctrl::make(perif.spi, DB_DAC_SEN, mb.clock->get_master_clock_rate()); + perif.leds = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_LEDS)); + + _tree->access<time_spec_t>(mb_path / "time" / "cmd") + .subscribe(boost::bind(&radio_ctrl_core_3000::set_time, perif.ctrl, _1)); + _tree->access<double>(mb_path / "tick_rate") + .subscribe(boost::bind(&radio_ctrl_core_3000::set_tick_rate, perif.ctrl, _1)); + + //////////////////////////////////////////////////////////////// + // ADC self test + //////////////////////////////////////////////////////////////// + perif.adc->set_test_word("ones", "ones"); check_adc(perif.ctrl, 0xfffcfffc); + perif.adc->set_test_word("zeros", "zeros"); check_adc(perif.ctrl, 0x00000000); + perif.adc->set_test_word("ones", "zeros"); check_adc(perif.ctrl, 0xfffc0000); + perif.adc->set_test_word("zeros", "ones"); check_adc(perif.ctrl, 0x0000fffc); + for (size_t k = 0; k < 14; k++) + { + perif.adc->set_test_word("zeros", "custom", 1 << k); + check_adc(perif.ctrl, 1 << (k+2)); + } + for (size_t k = 0; k < 14; k++) + { + perif.adc->set_test_word("custom", "zeros", 1 << k); + check_adc(perif.ctrl, 1 << (k+18)); + } + perif.adc->set_test_word("normal", "normal"); + + //////////////////////////////////////////////////////////////// + // Sync DAC's for MIMO + //////////////////////////////////////////////////////////////// + UHD_MSG(status) << "Sync DAC's." << std::endl; + perif.dac->arm_dac_sync(); // Put DAC into data Sync mode + perif.ctrl->poke32(TOREG(SR_DACSYNC), 0x1); // Arm FRAMEP/N sync pulse + + + //////////////////////////////////////////////////////////////// + // create codec control objects + //////////////////////////////////////////////////////////////// + _tree->create<int>(mb_path / "rx_codecs" / db_name / "gains"); //phony property so this dir exists + _tree->create<int>(mb_path / "tx_codecs" / db_name / "gains"); //phony property so this dir exists + _tree->create<std::string>(mb_path / "rx_codecs" / db_name / "name").set("ads62p48"); + _tree->create<std::string>(mb_path / "tx_codecs" / db_name / "name").set("ad9146"); + + _tree->create<meta_range_t>(mb_path / "rx_codecs" / db_name / "gains" / "digital" / "range").set(meta_range_t(0, 6.0, 0.5)); + _tree->create<double>(mb_path / "rx_codecs" / db_name / "gains" / "digital" / "value") + .subscribe(boost::bind(&x300_adc_ctrl::set_gain, perif.adc, _1)).set(0); + + //////////////////////////////////////////////////////////////////// + // front end corrections + //////////////////////////////////////////////////////////////////// + perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, TOREG(SR_RX_FRONT)); + const fs_path rx_fe_path = mb_path / "rx_frontends" / db_name; + _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value") + .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, perif.rx_fe, _1)) + .set(std::complex<double>(0.0, 0.0)); + _tree->create<bool>(rx_fe_path / "dc_offset" / "enable") + .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, perif.rx_fe, _1)) + .set(true); + _tree->create<std::complex<double> >(rx_fe_path / "iq_balance" / "value") + .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, perif.rx_fe, _1)) + .set(std::complex<double>(0.0, 0.0)); + + perif.tx_fe = tx_frontend_core_200::make(perif.ctrl, TOREG(SR_TX_FRONT)); + const fs_path tx_fe_path = mb_path / "tx_frontends" / db_name; + _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value") + .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, perif.tx_fe, _1)) + .set(std::complex<double>(0.0, 0.0)); + _tree->create<std::complex<double> >(tx_fe_path / "iq_balance" / "value") + .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, perif.tx_fe, _1)) + .set(std::complex<double>(0.0, 0.0)); + + + + //////////////////////////////////////////////////////////////////// + // create rx dsp control objects + //////////////////////////////////////////////////////////////////// + perif.framer = rx_vita_core_3000::make(perif.ctrl, TOREG(SR_RX_CTRL)); + perif.ddc = rx_dsp_core_3000::make(perif.ctrl, TOREG(SR_RX_DSP)); + perif.ddc->set_link_rate(10e9/8); //whatever + _tree->access<double>(mb_path / "tick_rate") + .subscribe(boost::bind(&rx_vita_core_3000::set_tick_rate, perif.framer, _1)) + .subscribe(boost::bind(&rx_dsp_core_3000::set_tick_rate, perif.ddc, _1)); + const fs_path rx_dsp_path = mb_path / "rx_dsps" / str(boost::format("%u") % dspno); + _tree->create<meta_range_t>(rx_dsp_path / "rate" / "range") + .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, perif.ddc)); + _tree->create<double>(rx_dsp_path / "rate" / "value") + .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, perif.ddc, _1)) + .subscribe(boost::bind(&x300_impl::update_rx_samp_rate, this, boost::ref(mb), dspno, _1)) + .set(1e6); + _tree->create<double>(rx_dsp_path / "freq" / "value") + .coerce(boost::bind(&rx_dsp_core_3000::set_freq, perif.ddc, _1)) + .set(0.0); + _tree->create<meta_range_t>(rx_dsp_path / "freq" / "range") + .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, perif.ddc)); + _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd") + .subscribe(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1)); + + //////////////////////////////////////////////////////////////////// + // create tx dsp control objects + //////////////////////////////////////////////////////////////////// + perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL)); + perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP)); + perif.duc->set_link_rate(10e9/8); //whatever + _tree->access<double>(mb_path / "tick_rate") + .subscribe(boost::bind(&tx_vita_core_3000::set_tick_rate, perif.deframer, _1)) + .subscribe(boost::bind(&tx_dsp_core_3000::set_tick_rate, perif.duc, _1)); + const fs_path tx_dsp_path = mb_path / "tx_dsps" / str(boost::format("%u") % dspno); + _tree->create<meta_range_t>(tx_dsp_path / "rate" / "range") + .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, perif.duc)); + _tree->create<double>(tx_dsp_path / "rate" / "value") + .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, perif.duc, _1)) + .subscribe(boost::bind(&x300_impl::update_tx_samp_rate, this, boost::ref(mb), dspno, _1)) + .set(1e6); + _tree->create<double>(tx_dsp_path / "freq" / "value") + .coerce(boost::bind(&tx_dsp_core_3000::set_freq, perif.duc, _1)) + .set(0.0); + _tree->create<meta_range_t>(tx_dsp_path / "freq" / "range") + .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, perif.duc)); + + //////////////////////////////////////////////////////////////////// + // create time control objects + //////////////////////////////////////////////////////////////////// + time_core_3000::readback_bases_type time64_rb_bases; + time64_rb_bases.rb_now = RB64_TIME_NOW; + time64_rb_bases.rb_pps = RB64_TIME_PPS; + perif.time64 = time_core_3000::make(perif.ctrl, TOREG(SR_TIME), time64_rb_bases); + + //////////////////////////////////////////////////////////////////// + // create RF frontend interfacing + //////////////////////////////////////////////////////////////////// + const size_t j = (db_name == "B")? 0x2 : 0x0; + _tree->create<dboard_eeprom_t>(mb_path / "dboards" / db_name / "rx_eeprom") + .set(mb.db_eeproms[X300_DB0_RX_EEPROM | j]) + .subscribe(boost::bind(&x300_impl::set_db_eeprom, this, mb.zpu_i2c, (0x50 | X300_DB0_RX_EEPROM | j), _1)); + _tree->create<dboard_eeprom_t>(mb_path / "dboards" / db_name / "tx_eeprom") + .set(mb.db_eeproms[X300_DB0_TX_EEPROM | j]) + .subscribe(boost::bind(&x300_impl::set_db_eeprom, this, mb.zpu_i2c, (0x50 | X300_DB0_TX_EEPROM | j), _1)); + _tree->create<dboard_eeprom_t>(mb_path / "dboards" / db_name / "gdb_eeprom") + .set(mb.db_eeproms[X300_DB0_GDB_EEPROM | j]) + .subscribe(boost::bind(&x300_impl::set_db_eeprom, this, mb.zpu_i2c, (0x50 | X300_DB0_GDB_EEPROM | j), _1)); + + //create a new dboard interface + x300_dboard_iface_config_t db_config; + db_config.gpio = gpio_core_200::make(perif.ctrl, TOREG(SR_GPIO), RB32_GPIO); + db_config.spi = perif.spi; + db_config.rx_spi_slaveno = DB_RX_SEN; + db_config.tx_spi_slaveno = DB_TX_SEN; + db_config.i2c = mb.zpu_i2c; + db_config.clock = mb.clock; + db_config.which_rx_clk = (db_name == "A")? X300_CLOCK_WHICH_DB0_RX : X300_CLOCK_WHICH_DB1_RX; + db_config.which_tx_clk = (db_name == "A")? X300_CLOCK_WHICH_DB0_TX : X300_CLOCK_WHICH_DB1_TX; + db_config.dboard_slot = (db_name == "A")? 0 : 1; + _dboard_ifaces[db_name] = x300_make_dboard_iface(db_config); + + //create a new dboard manager + _tree->create<dboard_iface::sptr>(mb_path / "dboards" / db_name / "iface").set(_dboard_ifaces[db_name]); + _dboard_managers[db_name] = dboard_manager::make( + mb.db_eeproms[X300_DB0_RX_EEPROM | j].id, + mb.db_eeproms[X300_DB0_TX_EEPROM | j].id, + mb.db_eeproms[X300_DB0_GDB_EEPROM | j].id, + _dboard_ifaces[db_name], + _tree->subtree(mb_path / "dboards" / db_name) + ); + + //now that dboard is created -- register into rx antenna event + const std::string fe_name = _tree->list(mb_path / "dboards" / db_name / "rx_frontends").front(); + _tree->access<std::string>(mb_path / "dboards" / db_name / "rx_frontends" / fe_name / "antenna" / "value") + .subscribe(boost::bind(&x300_impl::update_atr_leds, this, mb.radio_perifs[i].leds, _1)); + this->update_atr_leds(mb.radio_perifs[i].leds, ""); //init anyway, even if never called + + //bind frontend corrections to the dboard freq props + const fs_path db_rx_fe_path = mb_path / "dboards" / db_name / "rx_frontends"; + BOOST_FOREACH(const std::string &name, _tree->list(db_rx_fe_path)) + { + _tree->access<double>(db_rx_fe_path / name / "freq" / "value") + .subscribe(boost::bind(&x300_impl::set_rx_fe_corrections, this, mb_path, db_name, _1)); + } +} + +void x300_impl::set_rx_fe_corrections(const uhd::fs_path &mb_path, const std::string &fe_name, const double lo_freq) +{ + apply_rx_fe_corrections(this->get_tree()->subtree(mb_path), fe_name, lo_freq); +} + +boost::uint32_t get_pcie_dma_channel(boost::uint8_t destination, boost::uint8_t prefix) +{ + static const boost::uint32_t RADIO_GRP_SIZE = 3; + static const boost::uint32_t RADIO0_GRP = 0; + static const boost::uint32_t RADIO1_GRP = 1; + + boost::uint32_t radio_grp = (destination == X300_XB_DST_R0) ? RADIO0_GRP : RADIO1_GRP; + return ((radio_grp * RADIO_GRP_SIZE) + prefix); +} + + +x300_impl::both_xports_t x300_impl::make_transport( + const size_t mb_index, + const uint8_t& destination, + const uint8_t& prefix, + const uhd::device_addr_t& args, + boost::uint32_t& sid +) +{ + mboard_members_t &mb = _mb[mb_index]; + const std::string& addr = mb.addr; + const std::string& xport_path = mb.xport_path; + both_xports_t xports; + + sid_config_t config; + config.router_addr_there = X300_DEVICE_THERE; + config.dst_prefix = prefix; + config.router_dst_there = destination; + config.router_dst_here = mb.router_dst_here; + sid = this->allocate_sid(mb, config); + + static const uhd::device_addr_t DEFAULT_XPORT_ARGS; + + const uhd::device_addr_t& xport_args = + (prefix != X300_RADIO_DEST_PREFIX_CTRL) ? args : DEFAULT_XPORT_ARGS; + + zero_copy_xport_params default_buff_args; + if (xport_path == "nirio") { + default_buff_args.send_frame_size = + (prefix == X300_RADIO_DEST_PREFIX_TX) ? X300_PCIE_DATA_FRAME_SIZE : X300_PCIE_MSG_FRAME_SIZE; + default_buff_args.recv_frame_size = + (prefix == X300_RADIO_DEST_PREFIX_RX) ? X300_PCIE_DATA_FRAME_SIZE : X300_PCIE_MSG_FRAME_SIZE; + default_buff_args.num_send_frames = + (prefix == X300_RADIO_DEST_PREFIX_TX) ? X300_PCIE_DATA_NUM_FRAMES : X300_PCIE_MSG_NUM_FRAMES; + default_buff_args.num_recv_frames = + (prefix == X300_RADIO_DEST_PREFIX_RX) ? X300_PCIE_DATA_NUM_FRAMES : X300_PCIE_MSG_NUM_FRAMES; + + xports.recv = nirio_zero_copy::make( + mb.rio_fpga_interface, get_pcie_dma_channel(destination, prefix), default_buff_args, xport_args); + xports.send = xports.recv; + + //For the nirio transport, buffer size is depends on the frame size and num frames + xports.recv_buff_size = xports.recv->get_num_recv_frames() * xports.recv->get_recv_frame_size(); + xports.send_buff_size = xports.send->get_num_send_frames() * xports.send->get_send_frame_size(); + } else { + + default_buff_args.send_frame_size = + (prefix == X300_RADIO_DEST_PREFIX_TX) ? X300_ETH_DATA_FRAME_SIZE : X300_ETH_MSG_FRAME_SIZE; + default_buff_args.recv_frame_size = + (prefix == X300_RADIO_DEST_PREFIX_RX) ? X300_ETH_DATA_FRAME_SIZE : X300_ETH_MSG_FRAME_SIZE; + default_buff_args.num_send_frames = + (prefix == X300_RADIO_DEST_PREFIX_TX) ? X300_ETH_DATA_NUM_FRAMES : X300_ETH_MSG_NUM_FRAMES; + default_buff_args.num_recv_frames = + (prefix == X300_RADIO_DEST_PREFIX_RX) ? X300_ETH_DATA_NUM_FRAMES : X300_ETH_MSG_NUM_FRAMES; + + //make a new transport - fpga has no idea how to talk to use on this yet + udp_zero_copy::buff_params buff_params; + xports.recv = udp_zero_copy::make(addr, BOOST_STRINGIZE(X300_VITA_UDP_PORT), default_buff_args, buff_params, xport_args); + xports.send = xports.recv; + + //For the UDP transport the buffer size if the size of the socket buffer in the kernel + xports.recv_buff_size = buff_params.recv_buff_size; + xports.send_buff_size = buff_params.send_buff_size; + } + + //always program the framer if this is a socket, even its caching was used + if (xport_path != "nirio") + { + //clear the ethernet dispatcher's udp port + //NOT clearing this, the dispatcher is now intelligent + //_zpu_ctrl->poke32(SR_ADDR(SET0_BASE, (ZPU_SR_ETHINT0+8+3)), 0); + + //send a mini packet with SID into the ZPU + //ZPU will reprogram the ethernet framer + UHD_LOG << "programming packet for new xport on " + << addr << std::hex << "sid 0x" << sid << std::dec << std::endl; + //YES, get a __send__ buffer from the __recv__ socket + //-- this is the only way to program the framer for recv: + managed_send_buffer::sptr buff = xports.recv->get_send_buff(); + buff->cast<boost::uint32_t *>()[0] = 0; //eth dispatch looks for != 0 + buff->cast<boost::uint32_t *>()[1] = uhd::htonx(sid); + buff->commit(8); + buff.reset(); + + //reprogram the ethernet dispatcher's udp port (should be safe to always set) + UHD_LOG << "reprogram the ethernet dispatcher's udp port" << std::endl; + mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, (ZPU_SR_ETHINT0+8+3)), X300_VITA_UDP_PORT); + mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, (ZPU_SR_ETHINT1+8+3)), X300_VITA_UDP_PORT); + + //Do a peek to an arbitrary address to guarantee that the + //ethernet framer has been programmed before we return. + mb.zpu_ctrl->peek32(0); + } + return xports; +} + + +boost::uint32_t x300_impl::allocate_sid(mboard_members_t &mb, const sid_config_t &config) +{ + const std::string &xport_path = mb.xport_path; + const boost::uint32_t stream = (config.dst_prefix | (config.router_dst_there << 2)) & 0xff; + + const boost::uint32_t sid = 0 + | (X300_DEVICE_HERE << 24) + | (_sid_framer << 16) + | (config.router_addr_there << 8) + | (stream << 0) + ; + UHD_LOG << std::hex + << " sid 0x" << sid + << " framer 0x" << _sid_framer + << " stream 0x" << stream + << " router_dst_there 0x" << int(config.router_dst_there) + << " router_addr_there 0x" << int(config.router_addr_there) + << std::dec << std::endl; + + // Program the X300 to recognise it's own local address. + mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_XB_LOCAL), config.router_addr_there); + // Program CAM entry for outgoing packets matching a X300 resource (for example a Radio) + // This type of packet does matches the XB_LOCAL address and is looked up in the upper half of the CAM + mb.zpu_ctrl->poke32(SR_ADDR(SETXB_BASE, 256 + (stream)), config.router_dst_there); + // Program CAM entry for returning packets to us (for example GR host via Eth0) + // This type of packet does not match the XB_LOCAL address and is looked up in the lower half of the CAM + mb.zpu_ctrl->poke32(SR_ADDR(SETXB_BASE, 0 + (X300_DEVICE_HERE)), config.router_dst_here); + + if (xport_path == "nirio") { + uint32_t router_config_word = ((_sid_framer & 0xff) << 16) | //Return SID + get_pcie_dma_channel(config.router_dst_there, config.dst_prefix); //Dest + mb.rio_fpga_interface->get_kernel_proxy().poke(PCIE_ROUTER_REG(0), router_config_word); + } + + UHD_LOG << std::hex + << "done router config for sid 0x" << sid + << std::dec << std::endl; + + //increment for next setup + _sid_framer++; + + return sid; +} + +void x300_impl::update_atr_leds(gpio_core_200_32wo::sptr leds, const std::string &rx_ant) +{ + const bool is_txrx = (rx_ant == "TX/RX"); + const int rx_led = (1 << 2); + const int txrx_led = (1 << 1); + const int tx_led = (1 << 0); + leds->set_atr_reg(dboard_iface::ATR_REG_IDLE, 0); + leds->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY, is_txrx? txrx_led : rx_led); + leds->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, tx_led); + leds->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, rx_led | tx_led); +} + +void x300_impl::set_tick_rate(mboard_members_t &mb, const double rate) +{ + BOOST_FOREACH(radio_perifs_t &perif, mb.radio_perifs) + perif.time64->set_tick_rate(rate); +} + +void x300_impl::register_loopback_self_test(wb_iface::sptr iface) +{ + bool test_fail = false; + UHD_MSG(status) << "Performing register loopback test... " << std::flush; + size_t hash = time(NULL); + for (size_t i = 0; i < 100; i++) + { + boost::hash_combine(hash, i); + iface->poke32(TOREG(SR_TEST), boost::uint32_t(hash)); + test_fail = iface->peek32(RB32_TEST) != boost::uint32_t(hash); + if (test_fail) break; //exit loop on any failure + } + UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl; +} + +void x300_impl::set_time_source_out(mboard_members_t &mb, const bool enb) +{ + mb.clock_control_regs__pps_out_enb = enb? 1 : 0; + this->update_clock_control(mb); +} + +void x300_impl::update_clock_control(mboard_members_t &mb) +{ + const size_t reg = mb.clock_control_regs__clock_source + | (mb.clock_control_regs__pps_select << 2) + | (mb.clock_control_regs__pps_out_enb << 3) + | (mb.clock_control_regs__tcxo_enb << 4) + | (mb.clock_control_regs__gpsdo_pwr << 5) + ; + mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_CLOCK_CTRL), reg); +} + +void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &source) +{ + mb.clock_control_regs__clock_source = 0; + if (source == "internal") { + mb.clock_control_regs__clock_source = 0x2; + + mb.clock_control_regs__tcxo_enb = (source == "internal")? 1 : 0; + } else if (source == "external") { + mb.clock_control_regs__clock_source = 0x0; + } else if (source == "gpsdo") { + mb.clock_control_regs__clock_source = 0x3; + } else { + throw uhd::key_error("update_clock_source: unknown source: " + source); + } + + this->update_clock_control(mb); +} + +void x300_impl::update_time_source(mboard_members_t &mb, const std::string &source) +{ + if (source == "internal") { + // no action needed + } else if (source == "external") { + mb.clock_control_regs__pps_select = (source == "external")? 1 : 0; + } else if (source == "gpsdo") { + // no action needed + } else { + throw uhd::key_error("update_time_source: unknown source: " + source); + } + + this->update_clock_control(mb); +} + +sensor_value_t x300_impl::get_ref_locked(wb_iface::sptr ctrl) +{ + const bool lock = (ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_CLK_STATUS)) & (1 << 2)) != 0; + return sensor_value_t("Ref", lock, "locked", "unlocked"); +} + +void x300_impl::set_db_eeprom(i2c_iface::sptr i2c, const size_t addr, const uhd::usrp::dboard_eeprom_t &db_eeprom) +{ + db_eeprom.store(*i2c, addr); +} + +void x300_impl::set_mb_eeprom(i2c_iface::sptr i2c, const mboard_eeprom_t &mb_eeprom) +{ + i2c_iface::sptr eeprom16 = i2c->eeprom16(); + mb_eeprom.commit(*eeprom16, "X300"); +} + +boost::uint32_t x300_impl::get_fp_gpio(gpio_core_200::sptr gpio, const std::string &) +{ + return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX)); +} + +void x300_impl::set_fp_gpio(gpio_core_200::sptr gpio, const std::string &attr, const boost::uint32_t value) +{ + if (attr == "CTRL") return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); + if (attr == "DDR") return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); + if (attr == "OUT") return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); + if (attr == "ATR_0X") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); + if (attr == "ATR_RX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); + if (attr == "ATR_TX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); + if (attr == "ATR_XX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); +} + +/*********************************************************************** + * claimer logic + **********************************************************************/ + +void x300_impl::claimer_loop(wb_iface::sptr iface) +{ + iface->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_TIME), time(NULL)); + iface->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_SRC), get_process_hash()); + boost::this_thread::sleep(boost::posix_time::milliseconds(1500)); //1.5 seconds +} + +bool x300_impl::is_claimed(wb_iface::sptr iface) +{ + //If timed out then device is definitely unclaimed + if (iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_STATUS)) == 0) + return false; + + //otherwise check claim src to determine if another thread with the same src has claimed the device + return iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_SRC)) != get_process_hash(); +} + +/*********************************************************************** + * MTU detection + **********************************************************************/ +x300_impl::mtu_result_t x300_impl::determine_mtu(const std::string &addr, const mtu_result_t &user_mtu) +{ + udp_simple::sptr udp = udp_simple::make_connected(addr, BOOST_STRINGIZE(X300_MTU_DETECT_UDP_PORT)); + + std::vector<boost::uint8_t> buffer(std::max(user_mtu.recv_mtu, user_mtu.send_mtu)); + x300_mtu_t *request = reinterpret_cast<x300_mtu_t *>(&buffer.front()); + static const double echo_timeout = 0.020; //20 ms + + //test holler - check if its supported in this fw version + request->flags = uhd::htonx<boost::uint32_t>(X300_MTU_DETECT_ECHO_REQUEST); + request->size = uhd::htonx<boost::uint32_t>(sizeof(x300_mtu_t)); + udp->send(boost::asio::buffer(buffer, sizeof(x300_mtu_t))); + udp->recv(boost::asio::buffer(buffer), echo_timeout); + if (!(uhd::ntohx<boost::uint32_t>(request->flags) & X300_MTU_DETECT_ECHO_REPLY)) + throw uhd::not_implemented_error("Holler protocol not implemented"); + + size_t min_recv_mtu = sizeof(x300_mtu_t); + size_t max_recv_mtu = user_mtu.recv_mtu; + size_t min_send_mtu = sizeof(x300_mtu_t); + size_t max_send_mtu = user_mtu.send_mtu; + + UHD_MSG(status) << "Determining receive MTU ... "; + while (min_recv_mtu < max_recv_mtu) + { + size_t test_mtu = (max_recv_mtu/2 + min_recv_mtu/2 + 3) & ~3; + + request->flags = uhd::htonx<boost::uint32_t>(X300_MTU_DETECT_ECHO_REQUEST); + request->size = uhd::htonx<boost::uint32_t>(test_mtu); + udp->send(boost::asio::buffer(buffer, sizeof(x300_mtu_t))); + + size_t len = udp->recv(boost::asio::buffer(buffer), echo_timeout); + + if (len >= test_mtu) + min_recv_mtu = test_mtu; + else + max_recv_mtu = test_mtu - 4; + + } + UHD_MSG(status) << min_recv_mtu << std::endl; + + UHD_MSG(status) << "Determining send MTU ... "; + while (min_send_mtu < max_send_mtu) + { + size_t test_mtu = (max_send_mtu/2 + min_send_mtu/2 + 3) & ~3; + + request->flags = uhd::htonx<boost::uint32_t>(X300_MTU_DETECT_ECHO_REQUEST); + request->size = uhd::htonx<boost::uint32_t>(sizeof(x300_mtu_t)); + udp->send(boost::asio::buffer(buffer, test_mtu)); + + size_t len = udp->recv(boost::asio::buffer(buffer), echo_timeout); + if (len >= sizeof(x300_mtu_t)) + len = uhd::ntohx<boost::uint32_t>(request->size); + + if (len >= test_mtu) + min_send_mtu = test_mtu; + else + max_send_mtu = test_mtu - 4; + } + UHD_MSG(status) << min_send_mtu << std::endl; + + mtu_result_t mtu; + mtu.recv_mtu = min_recv_mtu; + mtu.send_mtu = min_send_mtu; + return mtu; +} + +/*********************************************************************** + * compat checks + **********************************************************************/ + +void x300_impl::check_fw_compat(const fs_path &mb_path, wb_iface::sptr iface) +{ + boost::uint32_t compat_num = iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_COMPAT_NUM)); + boost::uint32_t compat_major = (compat_num >> 16); + boost::uint32_t compat_minor = (compat_num & 0xffff); + + if (compat_major != X300_FW_COMPAT_MAJOR) + { + throw uhd::runtime_error(str(boost::format( + "Expected firmware compatibility number 0x%x, but got 0x%x.%x:\n" + "The firmware build is not compatible with the host code build.\n" + "%s" + ) % int(X300_FW_COMPAT_MAJOR) % compat_major % compat_minor + % print_images_error())); + } + _tree->create<std::string>(mb_path / "fw_version").set(str(boost::format("%u.%u") + % compat_major % compat_minor)); +} + +void x300_impl::check_fpga_compat(const fs_path &mb_path, wb_iface::sptr iface) +{ + boost::uint32_t compat_num = iface->peek32(SR_ADDR(SET0_BASE, ZPU_RB_COMPAT_NUM)); + boost::uint32_t compat_major = (compat_num >> 16); + boost::uint32_t compat_minor = (compat_num & 0xffff); + + if (compat_major != X300_FPGA_COMPAT_MAJOR) + { + throw uhd::runtime_error(str(boost::format( + "Expected FPGA compatibility number 0x%x, but got 0x%x.%x:\n" + "The FPGA build is not compatible with the host code build.\n" + "%s" + ) % int(X300_FPGA_COMPAT_MAJOR) % compat_major % compat_minor + % print_images_error())); + } + _tree->create<std::string>(mb_path / "fpga_version").set(str(boost::format("%u.%u") + % compat_major % compat_minor)); +} + +x300_impl::x300_mboard_t x300_impl::get_mb_type_from_pcie(const std::string& resource, const std::string& rpc_port) +{ + x300_mboard_t mb_type = UNKNOWN; + + //Detect the PCIe product ID to distinguish between X300 and X310 + nirio_status status = NiRio_Status_Success; + boost::uint32_t pid; + niriok_proxy::sptr discovery_proxy = + niusrprio_session::create_kernel_proxy(resource, rpc_port); + if (discovery_proxy) { + nirio_status_chain(discovery_proxy->get_attribute(PRODUCT_NUMBER, pid), status); + discovery_proxy->close(); + if (nirio_status_not_fatal(status)) { + //The PCIe ID -> MB mapping may be different from the EEPROM -> MB mapping + switch (pid) { + case X300_USRP_PCIE_SSID: + mb_type = USRP_X300_MB; break; + case X310_USRP_PCIE_SSID: + case X310_2940R_PCIE_SSID: + case X310_2942R_PCIE_SSID: + case X310_2943R_PCIE_SSID: + case X310_2944R_PCIE_SSID: + case X310_2950R_PCIE_SSID: + case X310_2952R_PCIE_SSID: + case X310_2953R_PCIE_SSID: + case X310_2954R_PCIE_SSID: + mb_type = USRP_X310_MB; break; + default: + mb_type = UNKNOWN; break; + } + } + } + + return mb_type; +} + +x300_impl::x300_mboard_t x300_impl::get_mb_type_from_eeprom(const uhd::usrp::mboard_eeprom_t& mb_eeprom) +{ + x300_mboard_t mb_type = UNKNOWN; + if (not mb_eeprom["product"].empty()) + { + boost::uint16_t product_num = 0; + try { + product_num = boost::lexical_cast<boost::uint16_t>(mb_eeprom["product"]); + } catch (const boost::bad_lexical_cast &) { + product_num = 0; + } + + switch (product_num) { + //The PCIe ID -> MB mapping may be different from the EEPROM -> MB mapping + case X300_USRP_PCIE_SSID: + mb_type = USRP_X300_MB; break; + case X310_USRP_PCIE_SSID: + case X310_2940R_PCIE_SSID: + case X310_2942R_PCIE_SSID: + case X310_2943R_PCIE_SSID: + case X310_2944R_PCIE_SSID: + case X310_2950R_PCIE_SSID: + case X310_2952R_PCIE_SSID: + case X310_2953R_PCIE_SSID: + case X310_2954R_PCIE_SSID: + mb_type = USRP_X310_MB; break; + default: + UHD_MSG(warning) << "X300 unknown product code in EEPROM: " << product_num << std::endl; + mb_type = UNKNOWN; break; + } + } + return mb_type; +} + diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp new file mode 100644 index 000000000..37f8cc468 --- /dev/null +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -0,0 +1,306 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_X300_IMPL_HPP +#define INCLUDED_X300_IMPL_HPP + +#include <uhd/property_tree.hpp> +#include <uhd/device.hpp> +#include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/usrp/dboard_manager.hpp> +#include <uhd/usrp/dboard_eeprom.hpp> +#include <uhd/usrp/subdev_spec.hpp> +#include <uhd/types/sensors.hpp> +#include "x300_clock_ctrl.hpp" +#include "x300_fw_common.h" +#include <uhd/transport/udp_simple.hpp> //mtu +#include <uhd/utils/tasks.hpp> +#include "spi_core_3000.hpp" +#include "x300_adc_ctrl.hpp" +#include "x300_dac_ctrl.hpp" +#include "rx_vita_core_3000.hpp" +#include "tx_vita_core_3000.hpp" +#include "time_core_3000.hpp" +#include "rx_dsp_core_3000.hpp" +#include "tx_dsp_core_3000.hpp" +#include "i2c_core_100_wb32.hpp" +#include "radio_ctrl_core_3000.hpp" +#include "rx_frontend_core_200.hpp" +#include "tx_frontend_core_200.hpp" +#include "gpio_core_200.hpp" +#include <boost/weak_ptr.hpp> +#include <uhd/usrp/gps_ctrl.hpp> +#include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <uhd/transport/nirio/niusrprio_session.h> +#include <uhd/transport/vrt_if_packet.hpp> +#include "recv_packet_demuxer_3000.hpp" + +static const std::string X300_FW_FILE_NAME = "usrp_x300_fw.bin"; + +static const double X300_DEFAULT_TICK_RATE = 200e6; //Hz +static const double X300_BUS_CLOCK_RATE = 175e6; //Hz + +static const size_t X300_TX_HW_BUFF_SIZE = 0x90000; //576KiB +static const size_t X300_TX_FC_RESPONSE_FREQ = 8; //per flow-control window + +static const size_t X300_RX_SW_BUFF_SIZE_ETH = 0x2000000; //32MiB For an ~8k MTU any size >32MiB is just wasted buffer space +static const size_t X300_RX_SW_BUFF_SIZE_ETH_MACOS = 0x100000; //1Mib +static const double X300_RX_SW_BUFF_FULL_FACTOR = 0.90; //Buffer should ideally be 90% full. +static const size_t X300_RX_FC_REQUEST_FREQ = 32; //per flow-control window + +static const size_t X300_PCIE_DATA_FRAME_SIZE = 8192; //bytes +static const size_t X300_PCIE_DATA_NUM_FRAMES = 2048; +static const size_t X300_PCIE_MSG_FRAME_SIZE = 256; //bytes +static const size_t X300_PCIE_MSG_NUM_FRAMES = 32; + +static const size_t X300_ETH_DATA_FRAME_SIZE = 8000; //bytes +static const size_t X300_ETH_DATA_NUM_FRAMES = 32; +static const size_t X300_ETH_MSG_FRAME_SIZE = uhd::transport::udp_simple::mtu; //bytes +static const size_t X300_ETH_MSG_NUM_FRAMES = 32; +static const double X300_DEFAULT_SYSREF_RATE = 10e6; + +#define X300_RADIO_DEST_PREFIX_TX 0 +#define X300_RADIO_DEST_PREFIX_CTRL 1 +#define X300_RADIO_DEST_PREFIX_RX 2 + +#define X300_XB_DST_E0 0 +#define X300_XB_DST_E1 1 +#define X300_XB_DST_R0 2 +#define X300_XB_DST_R1 3 +#define X300_XB_DST_CE0 4 +#define X300_XB_DST_CE1 5 +#define X300_XB_DST_CE2 5 +#define X300_XB_DST_PCI 7 + +#define X300_DEVICE_THERE 2 +#define X300_DEVICE_HERE 0 + +//eeprom addrs for various boards +enum +{ + X300_DB0_RX_EEPROM = 0x5, + X300_DB0_TX_EEPROM = 0x4, + X300_DB0_GDB_EEPROM = 0x1, + X300_DB1_RX_EEPROM = 0x7, + X300_DB1_TX_EEPROM = 0x6, + X300_DB1_GDB_EEPROM = 0x3, +}; + +struct x300_dboard_iface_config_t +{ + gpio_core_200::sptr gpio; + spi_core_3000::sptr spi; + size_t rx_spi_slaveno; + size_t tx_spi_slaveno; + i2c_core_100_wb32::sptr i2c; + x300_clock_ctrl::sptr clock; + x300_clock_which_t which_rx_clk; + x300_clock_which_t which_tx_clk; + boost::uint8_t dboard_slot; +}; + +uhd::usrp::dboard_iface::sptr x300_make_dboard_iface(const x300_dboard_iface_config_t &); +uhd::uart_iface::sptr x300_make_uart_iface(uhd::wb_iface::sptr iface); + +uhd::wb_iface::sptr x300_make_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp); +uhd::wb_iface::sptr x300_make_ctrl_iface_pcie(uhd::niusrprio::niriok_proxy& drv_proxy); + +class x300_impl : public uhd::device +{ +public: + typedef uhd::transport::bounded_buffer<uhd::async_metadata_t> async_md_type; + + x300_impl(const uhd::device_addr_t &); + void setup_mb(const size_t which, const uhd::device_addr_t &); + ~x300_impl(void); + + //the io interface + uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &); + uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &); + + //support old async call + bool recv_async_msg(uhd::async_metadata_t &, double); + + // used by x300_find_with_addr to find X300 devices. + static bool is_claimed(uhd::wb_iface::sptr); + + enum x300_mboard_t { + USRP_X300_MB, USRP_X310_MB, UNKNOWN + }; + static x300_mboard_t get_mb_type_from_pcie(const std::string& resource, const std::string& rpc_port); + static x300_mboard_t get_mb_type_from_eeprom(const uhd::usrp::mboard_eeprom_t& mb_eeprom); + +private: + boost::shared_ptr<async_md_type> _async_md; + + //perifs in the radio core + struct radio_perifs_t + { + radio_ctrl_core_3000::sptr ctrl; + spi_core_3000::sptr spi; + x300_adc_ctrl::sptr adc; + x300_dac_ctrl::sptr dac; + time_core_3000::sptr time64; + rx_vita_core_3000::sptr framer; + rx_dsp_core_3000::sptr ddc; + tx_vita_core_3000::sptr deframer; + tx_dsp_core_3000::sptr duc; + gpio_core_200_32wo::sptr leds; + rx_frontend_core_200::sptr rx_fe; + tx_frontend_core_200::sptr tx_fe; + }; + + //overflow recovery impl + void handle_overflow(radio_perifs_t &perif, boost::weak_ptr<uhd::rx_streamer> streamer); + + //vector of member objects per motherboard + struct mboard_members_t + { + uhd::dict<size_t, boost::weak_ptr<uhd::rx_streamer> > rx_streamers; + uhd::dict<size_t, boost::weak_ptr<uhd::tx_streamer> > tx_streamers; + + uhd::task::sptr claimer_task; + std::string addr; + std::string xport_path; + int router_dst_here; + uhd::device_addr_t send_args; + uhd::device_addr_t recv_args; + bool if_pkt_is_big_endian; + uhd::niusrprio::niusrprio_session::sptr rio_fpga_interface; + + //perifs in the zpu + uhd::wb_iface::sptr zpu_ctrl; + spi_core_3000::sptr zpu_spi; + i2c_core_100_wb32::sptr zpu_i2c; + + //perifs in each radio + radio_perifs_t radio_perifs[2]; + uhd::usrp::dboard_eeprom_t db_eeproms[8]; + + //per mboard frontend mapping + uhd::usrp::subdev_spec_t rx_fe_map; + uhd::usrp::subdev_spec_t tx_fe_map; + + //other perifs on mboard + x300_clock_ctrl::sptr clock; + uhd::gps_ctrl::sptr gps; + gpio_core_200::sptr fp_gpio; + + //clock control register bits + int clock_control_regs__clock_source; + int clock_control_regs__pps_select; + int clock_control_regs__pps_out_enb; + int clock_control_regs__tcxo_enb; + int clock_control_regs__gpsdo_pwr; + }; + std::vector<mboard_members_t> _mb; + + //task for periodically reclaiming the device from others + void claimer_loop(uhd::wb_iface::sptr); + + boost::mutex _transport_setup_mutex; + + void register_loopback_self_test(uhd::wb_iface::sptr iface); + + void setup_radio(const size_t, const size_t which_radio, const std::string &db_name); + + size_t _sid_framer; + struct sid_config_t + { + boost::uint8_t router_addr_there; + boost::uint8_t dst_prefix; //2bits + boost::uint8_t router_dst_there; + boost::uint8_t router_dst_here; + }; + boost::uint32_t allocate_sid(mboard_members_t &mb, const sid_config_t &config); + + struct both_xports_t + { + uhd::transport::zero_copy_if::sptr recv; + uhd::transport::zero_copy_if::sptr send; + size_t recv_buff_size; + size_t send_buff_size; + }; + both_xports_t make_transport( + const size_t mb_index, + const uint8_t& destination, + const uint8_t& prefix, + const uhd::device_addr_t& args, + boost::uint32_t& sid); + + struct mtu_result_t + { + size_t recv_mtu; + size_t send_mtu; + }; + + mtu_result_t determine_mtu(const std::string &addr, const mtu_result_t &user_mtu); + + //////////////////////////////////////////////////////////////////// + // + //Caching for transport interface re-use -- like sharing a DMA. + //The cache is optionally used by make_transport by use-case. + //The cache maps an ID string to a transport-ish object. + //The ID string identifies a purpose for the transport. + // + //For recv, there is a demux cache, which maps a ID string + //to a recv demux object. When a demux is used, the underlying transport + //must never be used outside of the demux. Use demux->make_proxy(sid). + // + uhd::dict<std::string, uhd::usrp::recv_packet_demuxer_3000::sptr> _demux_cache; + // + //For send, there is a shared send xport, which maps an ID string + //to a transport capable of sending buffers. Send transports + //can be shared amongst multiple callers, unlike recv. + // + uhd::dict<std::string, uhd::transport::zero_copy_if::sptr> _send_cache; + // + //////////////////////////////////////////////////////////////////// + + uhd::dict<std::string, uhd::usrp::dboard_manager::sptr> _dboard_managers; + uhd::dict<std::string, uhd::usrp::dboard_iface::sptr> _dboard_ifaces; + + void set_rx_fe_corrections(const uhd::fs_path &mb_path, const std::string &fe_name, const double lo_freq); + + void update_rx_subdev_spec(const size_t, const uhd::usrp::subdev_spec_t &spec); + void update_tx_subdev_spec(const size_t, const uhd::usrp::subdev_spec_t &spec); + + void set_tick_rate(mboard_members_t &, const double); + void update_tick_rate(mboard_members_t &, const double); + void update_rx_samp_rate(mboard_members_t&, const size_t, const double); + void update_tx_samp_rate(mboard_members_t&, const size_t, const double); + + void update_clock_control(mboard_members_t&); + void set_time_source_out(mboard_members_t&, const bool); + void update_clock_source(mboard_members_t&, const std::string &); + void update_time_source(mboard_members_t&, const std::string &); + + uhd::sensor_value_t get_ref_locked(uhd::wb_iface::sptr); + + void set_db_eeprom(uhd::i2c_iface::sptr i2c, const size_t, const uhd::usrp::dboard_eeprom_t &); + void set_mb_eeprom(uhd::i2c_iface::sptr i2c, const uhd::usrp::mboard_eeprom_t &); + + void check_fw_compat(const uhd::fs_path &mb_path, uhd::wb_iface::sptr iface); + void check_fpga_compat(const uhd::fs_path &mb_path, uhd::wb_iface::sptr iface); + + void update_atr_leds(gpio_core_200_32wo::sptr, const std::string &ant); + boost::uint32_t get_fp_gpio(gpio_core_200::sptr, const std::string &); + void set_fp_gpio(gpio_core_200::sptr, const std::string &, const boost::uint32_t); +}; + +#endif /* INCLUDED_X300_IMPL_HPP */ diff --git a/host/lib/usrp/x300/x300_init.sh b/host/lib/usrp/x300/x300_init.sh new file mode 100644 index 000000000..96c346c12 --- /dev/null +++ b/host/lib/usrp/x300/x300_init.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +utils/usrp_burn_mb_eeprom --key=serial --val="x310rC12" +utils/usrp_burn_mb_eeprom --key=name --val="dillen" + +utils/usrp_burn_mb_eeprom --key=product --val="30410" +utils/usrp_burn_mb_eeprom --key=revision --val="3" + +mac0=$(python -c "import random; mac = [0x00, 0x16, 0x3e, random.randint(0x00, 0x7f),random.randint(0x00, 0xff),random.randint(0x00, 0xff)]; print ':'.join(map(lambda x: '%02x' % x, mac))") +mac1=$(python -c "import random; mac = [0x00, 0x16, 0x3e, random.randint(0x00, 0x7f),random.randint(0x00, 0xff),random.randint(0x00, 0xff)]; print ':'.join(map(lambda x: '%02x' % x, mac))") + +utils/usrp_burn_mb_eeprom --key=mac-addr0 --val="${mac0}" +utils/usrp_burn_mb_eeprom --key=mac-addr1 --val="${mac1}" + +utils/usrp_burn_mb_eeprom --key=gateway --val="192.168.10.1" + +utils/usrp_burn_mb_eeprom --key=subnet0 --val="255.255.255.0" +utils/usrp_burn_mb_eeprom --key=subnet1 --val="255.255.255.0" +utils/usrp_burn_mb_eeprom --key=subnet2 --val="255.255.255.0" +utils/usrp_burn_mb_eeprom --key=subnet3 --val="255.255.255.0" + +utils/usrp_burn_mb_eeprom --key=ip-addr0 --val="192.168.10.2" +utils/usrp_burn_mb_eeprom --key=ip-addr1 --val="192.168.10.3" +utils/usrp_burn_mb_eeprom --key=ip-addr2 --val="192.168.10.2" +utils/usrp_burn_mb_eeprom --key=ip-addr3 --val="192.168.10.3" diff --git a/host/lib/usrp/x300/x300_io_impl.cpp b/host/lib/usrp/x300/x300_io_impl.cpp new file mode 100644 index 000000000..00a31b8d6 --- /dev/null +++ b/host/lib/usrp/x300/x300_io_impl.cpp @@ -0,0 +1,652 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "x300_regs.hpp" +#include "x300_impl.hpp" +#include "validate_subdev_spec.hpp" +#include "../../transport/super_recv_packet_handler.hpp" +#include "../../transport/super_send_packet_handler.hpp" +#include <uhd/transport/nirio_zero_copy.hpp> +#include "async_packet_handler.hpp" +#include <uhd/transport/bounded_buffer.hpp> +#include <boost/bind.hpp> +#include <uhd/utils/tasks.hpp> +#include <uhd/utils/log.hpp> +#include <boost/foreach.hpp> +#include <boost/make_shared.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +/*********************************************************************** + * update streamer rates + **********************************************************************/ +void x300_impl::update_tick_rate(mboard_members_t &mb, const double rate) +{ + BOOST_FOREACH(const size_t &dspno, mb.rx_streamers.keys()) + { + boost::shared_ptr<sph::recv_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::recv_packet_streamer>(mb.rx_streamers[dspno].lock()); + if (my_streamer) my_streamer->set_tick_rate(rate); + } + BOOST_FOREACH(const size_t &dspno, mb.tx_streamers.keys()) + { + boost::shared_ptr<sph::send_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::send_packet_streamer>(mb.tx_streamers[dspno].lock()); + if (my_streamer) my_streamer->set_tick_rate(rate); + } +} + +void x300_impl::update_rx_samp_rate(mboard_members_t &mb, const size_t dspno, const double rate) +{ + if (not mb.rx_streamers.has_key(dspno)) return; + boost::shared_ptr<sph::recv_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::recv_packet_streamer>(mb.rx_streamers[dspno].lock()); + if (not my_streamer) return; + my_streamer->set_samp_rate(rate); + const double adj = mb.radio_perifs[dspno].ddc->get_scaling_adjustment(); + my_streamer->set_scale_factor(adj); +} + +void x300_impl::update_tx_samp_rate(mboard_members_t &mb, const size_t dspno, const double rate) +{ + if (not mb.tx_streamers.has_key(dspno)) return; + boost::shared_ptr<sph::send_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::send_packet_streamer>(mb.tx_streamers[dspno].lock()); + if (not my_streamer) return; + my_streamer->set_samp_rate(rate); + const double adj = mb.radio_perifs[dspno].duc->get_scaling_adjustment(); + my_streamer->set_scale_factor(adj); +} + +/*********************************************************************** + * Setup dboard muxing for IQ + **********************************************************************/ +void x300_impl::update_rx_subdev_spec(const size_t mb_i, const subdev_spec_t &spec) +{ + const std::string mb_name = boost::lexical_cast<std::string>(mb_i); + fs_path root = "/mboards/"+mb_name+"/dboards"; + + //sanity checking + validate_subdev_spec(_tree, spec, "rx", mb_name); + UHD_ASSERT_THROW(spec.size() <= 2); + if (spec.size() > 0) UHD_ASSERT_THROW(spec[0].db_name == "A"); + if (spec.size() > 1) UHD_ASSERT_THROW(spec[1].db_name == "B"); + + //setup mux for this spec + for (size_t i = 0; i < 2; i++) + { + //extract db name + const std::string db_name = (i == 0)? "A" : "B"; + if (i < spec.size()) UHD_ASSERT_THROW(spec[i].db_name == db_name); + + //extract fe name + std::string fe_name; + if (i < spec.size()) fe_name = spec[i].sd_name; + else fe_name = _tree->list(root / db_name / "rx_frontends").front(); + + //extract connection + const std::string conn = _tree->access<std::string>(root / db_name / "rx_frontends" / fe_name / "connection").get(); + + //swap condition + const bool fe_swapped = (conn == "QI" or conn == "Q"); + _mb[mb_i].radio_perifs[i].ddc->set_mux(conn, fe_swapped); + //see usrp/io_impl.cpp if multiple DSPs share the frontend: + _mb[mb_i].radio_perifs[i].rx_fe->set_mux(fe_swapped); + } + + _mb[mb_i].rx_fe_map = spec; +} + +void x300_impl::update_tx_subdev_spec(const size_t mb_i, const subdev_spec_t &spec) +{ + const std::string mb_name = boost::lexical_cast<std::string>(mb_i); + fs_path root = "/mboards/"+mb_name+"/dboards"; + + //sanity checking + validate_subdev_spec(_tree, spec, "tx", mb_name); + UHD_ASSERT_THROW(spec.size() <= 2); + if (spec.size() > 0) UHD_ASSERT_THROW(spec[0].db_name == "A"); + if (spec.size() > 1) UHD_ASSERT_THROW(spec[1].db_name == "B"); + + //set the mux for this spec + for (size_t i = 0; i < 2; i++) + { + //extract db name + const std::string db_name = (i == 0)? "A" : "B"; + if (i < spec.size()) UHD_ASSERT_THROW(spec[i].db_name == db_name); + + //extract fe name + std::string fe_name; + if (i < spec.size()) fe_name = spec[i].sd_name; + else fe_name = _tree->list(root / db_name / "tx_frontends").front(); + + //extract connection + const std::string conn = _tree->access<std::string>(root / db_name / "tx_frontends" / fe_name / "connection").get(); + + //swap condition + _mb[mb_i].radio_perifs[i].tx_fe->set_mux(conn); + + } + + _mb[mb_i].tx_fe_map = spec; +} + +/*********************************************************************** + * VITA stuff + **********************************************************************/ +static void x300_if_hdr_unpack_be( + const boost::uint32_t *packet_buff, + vrt::if_packet_info_t &if_packet_info +){ + if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; + return vrt::if_hdr_unpack_be(packet_buff, if_packet_info); +} + +static void x300_if_hdr_pack_be( + boost::uint32_t *packet_buff, + vrt::if_packet_info_t &if_packet_info +){ + if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; + return vrt::if_hdr_pack_be(packet_buff, if_packet_info); +} + +static void x300_if_hdr_unpack_le( + const boost::uint32_t *packet_buff, + vrt::if_packet_info_t &if_packet_info +){ + if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; + return vrt::if_hdr_unpack_le(packet_buff, if_packet_info); +} + +static void x300_if_hdr_pack_le( + boost::uint32_t *packet_buff, + vrt::if_packet_info_t &if_packet_info +){ + if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; + return vrt::if_hdr_pack_le(packet_buff, if_packet_info); +} + +/*********************************************************************** + * RX flow control handler + **********************************************************************/ +static size_t get_rx_flow_control_window(size_t frame_size, size_t sw_buff_size, const device_addr_t& rx_args) +{ + double fullness_factor = rx_args.cast<double>("recv_buff_fullness", X300_RX_SW_BUFF_FULL_FACTOR); + + if (fullness_factor < 0.01 || fullness_factor > 1) { + throw uhd::value_error("recv_buff_fullness must be between 0.01 and 1 inclusive (1% to 100%)"); + } + + size_t window_in_pkts = (static_cast<size_t>(sw_buff_size * fullness_factor) / frame_size); + if (window_in_pkts == 0) { + throw uhd::value_error("recv_buff_size must be larger than the recv_frame_size."); + } + return window_in_pkts; +} + +static void handle_rx_flowctrl(const boost::uint32_t sid, zero_copy_if::sptr xport, bool big_endian, boost::shared_ptr<boost::uint32_t> seq32_state, const size_t last_seq) +{ + managed_send_buffer::sptr buff = xport->get_send_buff(0.0); + if (not buff) + { + throw uhd::runtime_error("handle_rx_flowctrl timed out getting a send buffer"); + } + boost::uint32_t *pkt = buff->cast<boost::uint32_t *>(); + + //recover seq32 + boost::uint32_t &seq32 = *seq32_state; + const size_t seq12 = seq32 & 0xfff; + if (last_seq < seq12) seq32 += (1 << 12); + seq32 &= ~0xfff; + seq32 |= last_seq; + + //load packet info + vrt::if_packet_info_t packet_info; + packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_CONTEXT; + packet_info.num_payload_words32 = 2; + packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t); + packet_info.packet_count = seq32; + packet_info.sob = false; + packet_info.eob = false; + packet_info.sid = sid; + packet_info.has_sid = true; + packet_info.has_cid = false; + packet_info.has_tsi = false; + packet_info.has_tsf = false; + packet_info.has_tlr = false; + + //load header + if (big_endian) + x300_if_hdr_pack_be(pkt, packet_info); + else + x300_if_hdr_pack_le(pkt, packet_info); + + //load payload + pkt[packet_info.num_header_words32+0] = uhd::htonx<boost::uint32_t>(0); + pkt[packet_info.num_header_words32+1] = uhd::htonx<boost::uint32_t>(seq32); + + //send the buffer over the interface + buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32)); +} + + +/*********************************************************************** + * TX flow control handler + **********************************************************************/ +struct x300_tx_fc_guts_t +{ + x300_tx_fc_guts_t(void): + stream_channel(0), + device_channel(0), + last_seq_out(0), + last_seq_ack(0), + seq_queue(1){} + size_t stream_channel; + size_t device_channel; + size_t last_seq_out; + size_t last_seq_ack; + bounded_buffer<size_t> seq_queue; + boost::shared_ptr<x300_impl::async_md_type> async_queue; + boost::shared_ptr<x300_impl::async_md_type> old_async_queue; +}; + +static size_t get_tx_flow_control_window(size_t frame_size, const device_addr_t& tx_args) +{ + double hw_buff_size = tx_args.cast<double>("send_buff_size", X300_TX_HW_BUFF_SIZE); + size_t window_in_pkts = (static_cast<size_t>(hw_buff_size) / frame_size); + if (window_in_pkts == 0) { + throw uhd::value_error("send_buff_size must be larger than the send_frame_size."); + } + return window_in_pkts; +} + +static void handle_tx_async_msgs(boost::shared_ptr<x300_tx_fc_guts_t> guts, zero_copy_if::sptr xport, bool big_endian, x300_clock_ctrl::sptr clock) +{ + managed_recv_buffer::sptr buff = xport->get_recv_buff(); + if (not buff) return; + + //extract packet info + vrt::if_packet_info_t if_packet_info; + if_packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); + const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>(); + + //unpacking can fail + uint32_t (*endian_conv)(uint32_t) = uhd::ntohx; + try + { + if (big_endian) + { + x300_if_hdr_unpack_be(packet_buff, if_packet_info); + endian_conv = uhd::ntohx; + } + else + { + x300_if_hdr_unpack_le(packet_buff, if_packet_info); + endian_conv = uhd::wtohx; + } + } + catch(const std::exception &ex) + { + UHD_MSG(error) << "Error parsing async message packet: " << ex.what() << std::endl; + return; + } + + //catch the flow control packets and react + if (endian_conv(packet_buff[if_packet_info.num_header_words32+0]) == 0) + { + const size_t seq = endian_conv(packet_buff[if_packet_info.num_header_words32+1]); + guts->seq_queue.push_with_haste(seq); + return; + } + + //fill in the async metadata + async_metadata_t metadata; + load_metadata_from_buff( + endian_conv, metadata, if_packet_info, packet_buff, + clock->get_master_clock_rate(), guts->stream_channel); + guts->async_queue->push_with_pop_on_full(metadata); + metadata.channel = guts->device_channel; + guts->old_async_queue->push_with_pop_on_full(metadata); + standard_async_msg_prints(metadata); +} + +static managed_send_buffer::sptr get_tx_buff_with_flowctrl( + task::sptr /*holds ref*/, + boost::shared_ptr<x300_tx_fc_guts_t> guts, + zero_copy_if::sptr xport, + size_t fc_pkt_window, + const double timeout +){ + while (true) + { + const size_t delta = (guts->last_seq_out & 0xfff) - (guts->last_seq_ack & 0xfff); + if ((delta & 0xfff) <= fc_pkt_window) break; + + const bool ok = guts->seq_queue.pop_with_timed_wait(guts->last_seq_ack, timeout); + if (not ok) return managed_send_buffer::sptr(); //timeout waiting for flow control + } + + managed_send_buffer::sptr buff = xport->get_send_buff(timeout); + if (buff) guts->last_seq_out++; //update seq, this will actually be a send + return buff; +} + +/*********************************************************************** + * Async Data + **********************************************************************/ +bool x300_impl::recv_async_msg( + async_metadata_t &async_metadata, double timeout +){ + return _async_md->pop_with_timed_wait(async_metadata, timeout); +} + +/*********************************************************************** + * Receive streamer + **********************************************************************/ +rx_streamer::sptr x300_impl::get_rx_stream(const uhd::stream_args_t &args_) +{ + boost::mutex::scoped_lock lock(_transport_setup_mutex); + stream_args_t args = args_; + + //setup defaults for unspecified values + if (not args.otw_format.empty() and args.otw_format != "sc16") + { + throw uhd::value_error("x300_impl::get_rx_stream only supports otw_format sc16"); + } + args.otw_format = "sc16"; + args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + + boost::shared_ptr<sph::recv_packet_streamer> my_streamer; + for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) + { + const size_t chan = args.channels[stream_i]; + size_t mb_chan = chan, mb_index = 0; + BOOST_FOREACH(mboard_members_t &mb, _mb) + { + if (mb_chan < mb.rx_fe_map.size()) break; + else mb_chan -= mb.rx_fe_map.size(); + mb_index++; + } + mboard_members_t &mb = _mb[mb_index]; + radio_perifs_t &perif = mb.radio_perifs[mb_chan]; + + //setup the dsp transport hints (default to a large recv buff) + device_addr_t device_addr = mb.recv_args; + if (not device_addr.has_key("recv_buff_size")) + { + if (mb.xport_path != "nirio") { + //For the ethernet transport, the buffer has to be set before creating + //the transport because it is independent of the frame size and # frames + //For nirio, the buffer size is not configurable by the user + #if defined(UHD_PLATFORM_MACOS) || defined(UHD_PLATFORM_BSD) + //limit buffer resize on macos or it will error + device_addr["recv_buff_size"] = boost::lexical_cast<std::string>(X300_RX_SW_BUFF_SIZE_ETH_MACOS); + #elif defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_WIN32) + //set to half-a-second of buffering at max rate + device_addr["recv_buff_size"] = boost::lexical_cast<std::string>(X300_RX_SW_BUFF_SIZE_ETH); + #endif + } + } + + //allocate sid and create transport + uint8_t dest = (mb_chan == 0)? X300_XB_DST_R0 : X300_XB_DST_R1; + boost::uint32_t data_sid; + UHD_LOG << "creating rx stream " << device_addr.to_string() << std::endl; + both_xports_t xport = this->make_transport(mb_index, dest, X300_RADIO_DEST_PREFIX_RX, device_addr, data_sid); + UHD_LOG << boost::format("data_sid = 0x%08x, actual recv_buff_size = %d\n") % data_sid % xport.recv_buff_size << std::endl; + + //calculate packet size + static const size_t hdr_size = 0 + + vrt::num_vrl_words32*sizeof(boost::uint32_t) + + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) + + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer + - sizeof(vrt::if_packet_info_t().cid) //no class id ever used + - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used + ; + const size_t bpp = xport.recv->get_recv_frame_size() - hdr_size; + const size_t bpi = convert::get_bytes_per_item(args.otw_format); + const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi)); + + //make the new streamer given the samples per packet + if (not my_streamer) my_streamer = boost::make_shared<sph::recv_packet_streamer>(spp); + my_streamer->resize(args.channels.size()); + + //init some streamer stuff + std::string conv_endianness; + if (mb.if_pkt_is_big_endian) { + my_streamer->set_vrt_unpacker(&x300_if_hdr_unpack_be); + conv_endianness = "be"; + } else { + my_streamer->set_vrt_unpacker(&x300_if_hdr_unpack_le); + conv_endianness = "le"; + } + + //set the converter + uhd::convert::id_type id; + id.input_format = args.otw_format + "_item32_" + conv_endianness; + id.num_inputs = 1; + id.output_format = args.cpu_format; + id.num_outputs = 1; + my_streamer->set_converter(id); + + perif.framer->clear(); + perif.framer->set_nsamps_per_packet(spp); //seems to be a good place to set this + perif.framer->set_sid((data_sid << 16) | (data_sid >> 16)); + perif.framer->setup(args); + perif.ddc->setup(args); + + //flow control setup + const size_t fc_window = get_rx_flow_control_window(xport.recv->get_recv_frame_size(), xport.recv_buff_size, device_addr); + const size_t fc_handle_window = std::max<size_t>(1, fc_window / X300_RX_FC_REQUEST_FREQ); + + UHD_LOG << "RX Flow Control Window = " << fc_window << ", RX Flow Control Handler Window = " << fc_handle_window << std::endl; + + perif.framer->configure_flow_control(fc_window); + + boost::shared_ptr<boost::uint32_t> seq32(new boost::uint32_t(0)); + //Give the streamer a functor to get the recv_buffer + //bind requires a zero_copy_if::sptr to add a streamer->xport lifetime dependency + my_streamer->set_xport_chan_get_buff( + stream_i, + boost::bind(&zero_copy_if::get_recv_buff, xport.recv, _1), + true /*flush*/ + ); + //Give the streamer a functor to handle overflows + //bind requires a weak_ptr to break the a streamer->streamer circular dependency + //Using "this" is OK because we know that x300_impl will outlive the streamer + my_streamer->set_overflow_handler( + stream_i, + boost::bind(&x300_impl::handle_overflow, this, boost::ref(perif), boost::weak_ptr<uhd::rx_streamer>(my_streamer)) + ); + //Give the streamer a functor to send flow control messages + //handle_rx_flowctrl is static and has no lifetime issues + my_streamer->set_xport_handle_flowctrl( + stream_i, boost::bind(&handle_rx_flowctrl, data_sid, xport.send, mb.if_pkt_is_big_endian, seq32, _1), + fc_handle_window, + true/*init*/ + ); + //Give the streamer a functor issue stream cmd + //bind requires a rx_vita_core_3000::sptr to add a streamer->framer lifetime dependency + my_streamer->set_issue_stream_cmd( + stream_i, boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1) + ); + + //Store a weak pointer to prevent a streamer->x300_impl->streamer circular dependency + mb.rx_streamers[mb_chan] = boost::weak_ptr<sph::recv_packet_streamer>(my_streamer); + + //sets all tick and samp rates on this streamer + const fs_path mb_path = "/mboards/"+boost::lexical_cast<std::string>(mb_index); + _tree->access<double>(mb_path / "tick_rate").update(); + _tree->access<double>(mb_path / "rx_dsps" / boost::lexical_cast<std::string>(mb_chan) / "rate" / "value").update(); + } + + return my_streamer; +} + +void x300_impl::handle_overflow(x300_impl::radio_perifs_t &perif, boost::weak_ptr<uhd::rx_streamer> streamer) +{ + boost::shared_ptr<sph::recv_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::recv_packet_streamer>(streamer.lock()); + if (not my_streamer) return; //If the rx_streamer has expired then overflow handling makes no sense. + + if (my_streamer->get_num_channels() == 1) + { + perif.framer->handle_overflow(); + return; + } + + ///////////////////////////////////////////////////////////// + // MIMO overflow recovery time + ///////////////////////////////////////////////////////////// + //find out if we were in continuous mode before stopping + const bool in_continuous_streaming_mode = perif.framer->in_continuous_streaming_mode(); + //stop streaming + my_streamer->issue_stream_cmd(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + //flush transports + my_streamer->flush_all(0.001); + //restart streaming + if (in_continuous_streaming_mode) + { + stream_cmd_t stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + stream_cmd.stream_now = false; + stream_cmd.time_spec = perif.time64->get_time_now() + time_spec_t(0.01); + my_streamer->issue_stream_cmd(stream_cmd); + } +} + +/*********************************************************************** + * Transmit streamer + **********************************************************************/ +tx_streamer::sptr x300_impl::get_tx_stream(const uhd::stream_args_t &args_) +{ + boost::mutex::scoped_lock lock(_transport_setup_mutex); + stream_args_t args = args_; + + //setup defaults for unspecified values + if (not args.otw_format.empty() and args.otw_format != "sc16") + { + throw uhd::value_error("x300_impl::get_rx_stream only supports otw_format sc16"); + } + args.otw_format = "sc16"; + args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + + //shared async queue for all channels in streamer + boost::shared_ptr<async_md_type> async_md(new async_md_type(1000/*messages deep*/)); + + boost::shared_ptr<sph::send_packet_streamer> my_streamer; + for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) + { + const size_t chan = args.channels[stream_i]; + size_t mb_chan = chan, mb_index = 0; + BOOST_FOREACH(mboard_members_t &mb, _mb) + { + if (mb_chan < mb.tx_fe_map.size()) break; + else mb_chan -= mb.tx_fe_map.size(); + mb_index++; + } + mboard_members_t &mb = _mb[mb_index]; + radio_perifs_t &perif = mb.radio_perifs[mb_chan]; + + //setup the dsp transport hints (TODO) + device_addr_t device_addr = mb.send_args; + + //allocate sid and create transport + uint8_t dest = (mb_chan == 0)? X300_XB_DST_R0 : X300_XB_DST_R1; + boost::uint32_t data_sid; + UHD_LOG << "creating tx stream " << device_addr.to_string() << std::endl; + both_xports_t xport = this->make_transport(mb_index, dest, X300_RADIO_DEST_PREFIX_TX, device_addr, data_sid); + UHD_LOG << boost::format("data_sid = 0x%08x\n") % data_sid << std::endl; + + //calculate packet size + static const size_t hdr_size = 0 + + vrt::num_vrl_words32*sizeof(boost::uint32_t) + + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) + //+ sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer + - sizeof(vrt::if_packet_info_t().cid) //no class id ever used + - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used + ; + const size_t bpp = xport.send->get_send_frame_size() - hdr_size; + const size_t bpi = convert::get_bytes_per_item(args.otw_format); + const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi)); + + //make the new streamer given the samples per packet + if (not my_streamer) my_streamer = boost::make_shared<sph::send_packet_streamer>(spp); + my_streamer->resize(args.channels.size()); + + std::string conv_endianness; + if (mb.if_pkt_is_big_endian) { + my_streamer->set_vrt_packer(&x300_if_hdr_pack_be); + conv_endianness = "be"; + } else { + my_streamer->set_vrt_packer(&x300_if_hdr_pack_le); + conv_endianness = "le"; + } + + //set the converter + uhd::convert::id_type id; + id.input_format = args.cpu_format; + id.num_inputs = 1; + id.output_format = args.otw_format + "_item32_" + conv_endianness; + id.num_outputs = 1; + my_streamer->set_converter(id); + + perif.deframer->clear(); + perif.deframer->setup(args); + perif.duc->setup(args); + + //flow control setup + size_t fc_window = get_tx_flow_control_window(xport.send->get_send_frame_size(), device_addr); //In packets + const size_t fc_handle_window = std::max<size_t>(1, fc_window/X300_TX_FC_RESPONSE_FREQ); + + UHD_LOG << "TX Flow Control Window = " << fc_window << ", TX Flow Control Handler Window = " << fc_handle_window << std::endl; + + perif.deframer->configure_flow_control(0/*cycs off*/, fc_handle_window); + boost::shared_ptr<x300_tx_fc_guts_t> guts(new x300_tx_fc_guts_t()); + guts->stream_channel = stream_i; + guts->device_channel = chan; + guts->async_queue = async_md; + guts->old_async_queue = _async_md; + task::sptr task = task::make(boost::bind(&handle_tx_async_msgs, guts, xport.recv, mb.if_pkt_is_big_endian, mb.clock)); + + //Give the streamer a functor to get the send buffer + //get_tx_buff_with_flowctrl is static so bind has no lifetime issues + //xport.send (sptr) is required to add streamer->data-transport lifetime dependency + //task (sptr) is required to add a streamer->async-handler lifetime dependency + my_streamer->set_xport_chan_get_buff( + stream_i, + boost::bind(&get_tx_buff_with_flowctrl, task, guts, xport.send, fc_window, _1) + ); + //Give the streamer a functor handled received async messages + my_streamer->set_async_receiver( + boost::bind(&async_md_type::pop_with_timed_wait, async_md, _1, _2) + ); + my_streamer->set_xport_chan_sid(stream_i, true, data_sid); + my_streamer->set_enable_trailer(false); //TODO not implemented trailer support yet + + //Store a weak pointer to prevent a streamer->x300_impl->streamer circular dependency + mb.tx_streamers[mb_chan] = boost::weak_ptr<sph::send_packet_streamer>(my_streamer); + + //sets all tick and samp rates on this streamer + const fs_path mb_path = "/mboards/"+boost::lexical_cast<std::string>(mb_index); + _tree->access<double>(mb_path / "tick_rate").update(); + _tree->access<double>(mb_path / "tx_dsps" / boost::lexical_cast<std::string>(mb_chan) / "rate" / "value").update(); + } + + return my_streamer; +} diff --git a/host/lib/usrp/x300/x300_regs.hpp b/host/lib/usrp/x300/x300_regs.hpp new file mode 100644 index 000000000..e4ae76a38 --- /dev/null +++ b/host/lib/usrp/x300/x300_regs.hpp @@ -0,0 +1,152 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_X300_REGS_HPP +#define INCLUDED_X300_REGS_HPP + +#include <boost/cstdint.hpp> + +#define TOREG(x) ((x)*4) + +#define localparam static const int + +localparam SR_DACSYNC = 5; +localparam SR_LOOPBACK = 6; +localparam SR_TEST = 7; +localparam SR_SPI = 8; +localparam SR_GPIO = 16; +localparam SR_MISC_OUTS = 24; +localparam SR_READBACK = 32; +localparam SR_TX_CTRL = 64; +localparam SR_RX_CTRL = 96; +localparam SR_TIME = 128; +localparam SR_RX_DSP = 144; +localparam SR_TX_DSP = 184; +localparam SR_LEDS = 196; +localparam SR_FP_GPIO = 200; +localparam SR_RX_FRONT = 208; +localparam SR_TX_FRONT = 216; + +localparam RB32_GPIO = 0; +localparam RB32_SPI = 4; +localparam RB64_TIME_NOW = 8; +localparam RB64_TIME_PPS = 16; +localparam RB32_TEST = 24; +localparam RB32_RX = 28; +localparam RB32_FP_GPIO = 32; + +localparam BL_ADDRESS = 0; +localparam BL_DATA = 1; + +//wishbone settings map - relevant to host code +#define SET0_BASE 0xa000 +#define SETXB_BASE 0xb000 +#define BOOT_LDR_BASE 0xFA00 +#define I2C0_BASE 0xfe00 +#define I2C1_BASE 0xff00 +#define SR_ADDR(base, offset) ((base) + (offset)*4) + +localparam ZPU_SR_LEDS = 00; +localparam ZPU_SR_PHY_RST = 01; +localparam ZPU_SR_CLOCK_CTRL = 02; +localparam ZPU_SR_XB_LOCAL = 03; +localparam ZPU_SR_SPI = 32; +localparam ZPU_SR_ETHINT0 = 40; +localparam ZPU_SR_ETHINT1 = 56; + +localparam ZPU_RB_SPI = 2; +localparam ZPU_RB_CLK_STATUS = 3; +localparam ZPU_RB_COMPAT_NUM = 6; +localparam ZPU_RB_ETH_TYPE0 = 4; +localparam ZPU_RB_ETH_TYPE1 = 5; + +//spi slaves on radio +#define DB_DAC_SEN (1 << 7) +#define DB_ADC_SEN (1 << 6) +#define DB_RX_LSADC_SEN (1 << 5) +#define DB_RX_LSDAC_SEN (1 << 4) +#define DB_TX_LSADC_SEN (1 << 3) +#define DB_TX_LSDAC_SEN (1 << 2) +#define DB_RX_SEN (1 << 1) +#define DB_TX_SEN (1 << 0) + +//------------------------------------------------------------------- +// PCIe Registers +//------------------------------------------------------------------- + +static const uint32_t X300_PCIE_VID = 0x1093; +static const uint32_t X300_PCIE_PID = 0xC4C4; +static const uint32_t X300_USRP_PCIE_SSID = 0x7736; +static const uint32_t X310_USRP_PCIE_SSID = 0x76CA; +static const uint32_t X310_2940R_PCIE_SSID = 0x772B; +static const uint32_t X310_2942R_PCIE_SSID = 0x772C; +static const uint32_t X310_2943R_PCIE_SSID = 0x772D; +static const uint32_t X310_2944R_PCIE_SSID = 0x772E; +static const uint32_t X310_2950R_PCIE_SSID = 0x772F; +static const uint32_t X310_2952R_PCIE_SSID = 0x7730; +static const uint32_t X310_2953R_PCIE_SSID = 0x7731; +static const uint32_t X310_2954R_PCIE_SSID = 0x7732; + +static const uint32_t FPGA_X3xx_SIG_VALUE = 0x58333030; + +static const uint32_t PCIE_FPGA_ADDR_BASE = 0xC0000; +#define PCIE_FPGA_REG(X) (PCIE_FPGA_ADDR_BASE + X) + +static const uint32_t FPGA_PCIE_SIG_REG = PCIE_FPGA_REG(0x0000); +static const uint32_t FPGA_CNTR_LO_REG = PCIE_FPGA_REG(0x0004); +static const uint32_t FPGA_CNTR_HI_REG = PCIE_FPGA_REG(0x0008); +static const uint32_t FPGA_CNTR_FREQ_REG = PCIE_FPGA_REG(0x000C); +static const uint32_t FPGA_USR_SIG_REG_BASE = PCIE_FPGA_REG(0x0030); +static const uint32_t FPGA_USR_SIG_REG_SIZE = 16; + +static const uint32_t PCIE_TX_DMA_REG_BASE = PCIE_FPGA_REG(0x0200); +static const uint32_t PCIE_RX_DMA_REG_BASE = PCIE_FPGA_REG(0x0400); + +static const uint32_t DMA_REG_GRP_SIZE = 16; +static const uint32_t DMA_CTRL_STATUS_REG = 0x0; +static const uint32_t DMA_FRAME_SIZE_REG = 0x4; +static const uint32_t DMA_SAMPLE_COUNT_REG = 0x8; +static const uint32_t DMA_PKT_COUNT_REG = 0xC; + +#define PCIE_TX_DMA_REG(REG, CHAN) (PCIE_TX_DMA_REG_BASE + (CHAN*DMA_REG_GRP_SIZE) + REG) +#define PCIE_RX_DMA_REG(REG, CHAN) (PCIE_RX_DMA_REG_BASE + (CHAN*DMA_REG_GRP_SIZE) + REG) + +static const uint32_t DMA_CTRL_RESET = 1; +static const uint32_t DMA_CTRL_SW_BUF_U64 = (3 << 4); +static const uint32_t DMA_CTRL_SW_BUF_U32 = (2 << 4); +static const uint32_t DMA_CTRL_SW_BUF_U16 = (1 << 4); +static const uint32_t DMA_CTRL_SW_BUF_U8 = (0 << 4); +static const uint32_t DMA_STATUS_ERROR = 1; + +static const uint32_t PCIE_ROUTER_REG_BASE = PCIE_FPGA_REG(0x0500); +#define PCIE_ROUTER_REG(X) (PCIE_ROUTER_REG_BASE + X) + +static const uint32_t PCIE_ZPU_DATA_BASE = 0x30000; +static const uint32_t PCIE_ZPU_READ_BASE = 0x20000; //Trig and Status share the same base +static const uint32_t PCIE_ZPU_STATUS_BASE = 0x20000; + +#define PCIE_ZPU_DATA_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_DATA_BASE) + X) +#define PCIE_ZPU_READ_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_READ_BASE) + X) +#define PCIE_ZPU_STATUS_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_STATUS_BASE) + X) + +static const uint32_t PCIE_ZPU_READ_START = 0x0; +static const uint32_t PCIE_ZPU_READ_CLOBBER = 0x80000000; +static const uint32_t PCIE_ZPU_STATUS_BUSY = 0x1; +static const uint32_t PCIE_ZPU_STATUS_SUSPENDED = 0x80000000; + + +#endif /* INCLUDED_X300_REGS_HPP */ diff --git a/host/lib/utils/CMakeLists.txt b/host/lib/utils/CMakeLists.txt index a28e1f9ef..2252f3221 100644 --- a/host/lib/utils/CMakeLists.txt +++ b/host/lib/utils/CMakeLists.txt @@ -87,6 +87,7 @@ CHECK_CXX_SOURCE_COMPILES(" UNSET(CMAKE_REQUIRED_LIBRARIES) CHECK_CXX_SOURCE_COMPILES(" + #define WIN32_LEAN_AND_MEAN #include <windows.h> int main(){ LoadLibrary(0); @@ -136,6 +137,7 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/log.cpp ${CMAKE_CURRENT_SOURCE_DIR}/msg.cpp ${CMAKE_CURRENT_SOURCE_DIR}/paths.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform.cpp ${CMAKE_CURRENT_SOURCE_DIR}/static.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tasks.cpp ${CMAKE_CURRENT_SOURCE_DIR}/thread_priority.cpp diff --git a/host/lib/utils/platform.cpp b/host/lib/utils/platform.cpp new file mode 100644 index 000000000..e2f92039e --- /dev/null +++ b/host/lib/utils/platform.cpp @@ -0,0 +1,58 @@ +// +// Copyright 2010-2012 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/utils/platform.hpp> +#include <uhd/config.hpp> +#include <boost/functional/hash.hpp> +#ifdef UHD_PLATFORM_WIN32 +#include <Windows.h> +#else +#include <unistd.h> +#endif + +namespace uhd { + + boost::int32_t get_process_id() { +#ifdef UHD_PLATFORM_WIN32 + return boost::int32_t(GetCurrentProcessId()); +#else + return boost::int32_t(getpid()); +#endif + } + + boost::uint32_t get_host_id() { +#ifdef UHD_PLATFORM_WIN32 + //extract volume serial number + char szVolName[MAX_PATH+1], szFileSysName[MAX_PATH+1]; + DWORD dwSerialNumber, dwMaxComponentLen, dwFileSysFlags; + GetVolumeInformation("C:\\", szVolName, MAX_PATH, + &dwSerialNumber, &dwMaxComponentLen, + &dwFileSysFlags, szFileSysName, sizeof(szFileSysName)); + + return boost::uint32_t(dwSerialNumber); +#else + return boost::uint32_t(gethostid()); +#endif + } + + boost::uint32_t get_process_hash() { + size_t hash = 0; + boost::hash_combine(hash, uhd::get_process_id()); + boost::hash_combine(hash, uhd::get_host_id()); + return boost::uint32_t(hash); + } +} |