diff options
| author | Ben Hilburn <ben.hilburn@ettus.com> | 2014-02-04 11:04:07 -0800 | 
|---|---|---|
| committer | Ben Hilburn <ben.hilburn@ettus.com> | 2014-02-04 11:04:07 -0800 | 
| commit | 178ac3f1c9950d383c8f64b3df464c0f943c4a23 (patch) | |
| tree | 318ed621a7b59b7d34d4ce6e4a92f73f0bcef509 /host/lib | |
| parent | 2718ac110fa931cc29daf7cb3dc5ab6230ee02ab (diff) | |
| download | uhd-178ac3f1c9950d383c8f64b3df464c0f943c4a23.tar.gz uhd-178ac3f1c9950d383c8f64b3df464c0f943c4a23.tar.bz2 uhd-178ac3f1c9950d383c8f64b3df464c0f943c4a23.zip  | |
Merging USRP X300 and X310 support!!
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); +    } +}  | 
