// // Copyright 2013-2015 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 . // #ifndef INCLUDED_X300_IMPL_HPP #define INCLUDED_X300_IMPL_HPP #include #include #include #include #include #include #include #include "x300_clock_ctrl.hpp" #include "x300_fw_common.h" #include //mtu #include #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_atr_3000.hpp" #include "dma_fifo_core_3000.hpp" #include #include #include #include #include #include #include "recv_packet_demuxer_3000.hpp" #include "x300_regs.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 = 166.666667e6; //Hz static const size_t X300_TX_HW_BUFF_SIZE_SRAM = 520*1024; //512K SRAM buffer + 8K 2Clk FIFO static const size_t X300_TX_FC_RESPONSE_FREQ_SRAM = 8; //per flow-control window static const size_t X300_TX_HW_BUFF_SIZE_DRAM = 128*1024; static const size_t X300_TX_FC_RESPONSE_FREQ_DRAM = 32; static const boost::uint32_t X300_DRAM_FIFO_SIZE = 32*1024*1024; static const size_t X300_RX_SW_BUFF_SIZE_ETH = 0x2000000;//32MiB For an ~8k frame size 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 //The FIFO closest to the DMA controller is 1023 elements deep for RX and 1029 elements deep for TX //where an element is 8 bytes. For best throughput ensure that the data frame fits in these buffers. //Also ensure that the kernel has enough frames to hold buffered TX and RX data static const size_t X300_PCIE_RX_DATA_FRAME_SIZE = 8184; //bytes static const size_t X300_PCIE_TX_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 = 64; static const size_t X300_10GE_DATA_FRAME_MAX_SIZE = 8000; //bytes static const size_t X300_1GE_DATA_FRAME_MAX_SIZE = 1472; //bytes static const size_t X300_ETH_MSG_FRAME_SIZE = uhd::transport::udp_simple::mtu; //bytes static const size_t X300_ETH_MSG_NUM_FRAMES = 64; static const size_t X300_ETH_DATA_NUM_FRAMES = 32; static const double X300_DEFAULT_SYSREF_RATE = 10e6; static const size_t X300_TX_MAX_HDR_LEN = // bytes sizeof(boost::uint32_t) // Header + sizeof(uhd::transport::vrt::if_packet_info_t().sid) // SID + sizeof(uhd::transport::vrt::if_packet_info_t().tsf); // Timestamp static const size_t X300_RX_MAX_HDR_LEN = // bytes sizeof(boost::uint32_t) // Header + sizeof(uhd::transport::vrt::if_packet_info_t().sid) // SID + sizeof(uhd::transport::vrt::if_packet_info_t().tsf); // Timestamp static const size_t X300_MAX_RATE_PCIE = 800000000; // bytes/s static const size_t X300_MAX_RATE_10GIGE = 800000000; // bytes/s static const size_t X300_MAX_RATE_1GIGE = 100000000; // bytes/s #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 // Radio 0 -> Slot A #define X300_XB_DST_R1 3 // Radio 1 -> Slot B #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 { uhd::usrp::gpio_atr::db_gpio_atr_3000::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::timed_wb_iface::sptr cmd_time_ctrl; }; 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::sptr drv_proxy); uhd::device_addrs_t x300_find(const uhd::device_addr_t &hint_); class x300_impl : public uhd::device { public: typedef uhd::transport::bounded_buffer 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 boost::mutex claimer_mutex; //All claims and checks in this process are serialized 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; //perifs in the radio core struct radio_perifs_t { //Interfaces 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; uhd::usrp::gpio_atr::gpio_atr_3000::sptr leds; rx_frontend_core_200::sptr rx_fe; tx_frontend_core_200::sptr tx_fe; //Registers uhd::usrp::x300::radio_regmap_t::sptr regmap; }; //overflow recovery impl void handle_overflow(radio_perifs_t &perif, boost::weak_ptr streamer); //vector of member objects per motherboard struct mboard_members_t { uhd::dict > rx_streamers; uhd::dict > tx_streamers; bool initialization_done; 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 static const size_t NUM_RADIOS = 2; radio_perifs_t radio_perifs[NUM_RADIOS]; //!< This is hardcoded s.t. radio_perifs[0] points to slot A and [1] to B uhd::usrp::dboard_eeprom_t db_eeproms[8]; //! Return the index of a radio component, given a slot name. This means DSPs, radio_perifs size_t get_radio_index(const std::string &slot_name) { UHD_ASSERT_THROW(slot_name == "A" or slot_name == "B"); return slot_name == "A" ? 0 : 1; } bool has_dram_buff; dma_fifo_core_3000::sptr dram_buff_ctrl[NUM_RADIOS]; //other perifs on mboard x300_clock_ctrl::sptr clock; uhd::gps_ctrl::sptr gps; uhd::usrp::gpio_atr::gpio_atr_3000::sptr fp_gpio; uhd::usrp::x300::fw_regmap_t::sptr fw_regmap; //which FPGA image is loaded std::string loaded_fpga_image; size_t hw_rev; std::string current_refclk_src; uhd::soft_regmap_db_t::sptr regmap_db; }; std::vector _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 radio_loopback(uhd::wb_iface::sptr iface, const bool on); /*! \brief Initialize the radio component on a given slot. * * Call this function once per slot (A and B) and motherboard to initialize all the radio components. * This will: * - Reset and init DACs and ADCs * - Setup controls for DAC, ADC, SPI and LEDs * - Self test ADC * - Sync DACs (for MIMO) * - Initialize the property tree for control objects etc. (gain, rate...) * * \param mb_i Motherboard index * \param slot_name Slot name (A or B). */ void setup_radio(const size_t, const std::string &slot_name, const uhd::device_addr_t &dev_addr); 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 boost::uint8_t& destination, const boost::uint8_t& prefix, const uhd::device_addr_t& args, boost::uint32_t& sid); struct frame_size_t { size_t recv_frame_size; size_t send_frame_size; }; frame_size_t _max_frame_sizes; /*! * Automatically determine the maximum frame size available by sending a UDP packet * to the device and see which packet sizes actually work. This way, we can take * switches etc. into account which might live between the device and the host. */ frame_size_t determine_max_frame_size(const std::string &addr, const frame_size_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 _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 _send_cache; // //////////////////////////////////////////////////////////////////// uhd::dict _dboard_managers; uhd::dict _dboard_ifaces; void set_rx_fe_corrections(const uhd::fs_path &mb_path, const std::string &fe_name, const double lo_freq); void set_tx_fe_corrections(const uhd::fs_path &mb_path, const std::string &fe_name, const double lo_freq); bool _ignore_cal_file; /*! Update the IQ MUX settings for the radio peripheral according to given subdev spec. * * Also checks if the given subdev is valid for this device and updates the channel to DSP mapping. * * \param tx_rx "tx" or "rx", depending where you're setting the subdev spec * \param mb_i Mainboard index number. * \param spec Subdev spec */ void update_subdev_spec(const std::string &tx_rx, const size_t mb_i, 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 initialize_clock_control(mboard_members_t &mb); 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 &); void sync_times(mboard_members_t&, const uhd::time_spec_t&); uhd::sensor_value_t get_ref_locked(mboard_members_t& mb); bool wait_for_clk_locked(mboard_members_t& mb, boost::uint32_t which, double timeout); bool is_pps_present(mboard_members_t& mb); 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, const mboard_members_t &members); void update_atr_leds(uhd::usrp::gpio_atr::gpio_atr_3000::sptr, const std::string &ant); void self_cal_adc_capture_delay(mboard_members_t& mb, const size_t radio_i, bool print_status = false); double self_cal_adc_xfer_delay(mboard_members_t& mb, bool apply_delay = false); void self_test_adcs(mboard_members_t& mb, boost::uint32_t ramp_time_ms = 100); void extended_adc_test(mboard_members_t& mb, double duration_s); //**PRECONDITION** //This function assumes that all the VITA times in "radios" are synchronized //to a common reference. Currently, this function is called in get_tx_stream //which also has the same precondition. static void synchronize_dacs(const std::vector& mboards); }; #endif /* INCLUDED_X300_IMPL_HPP */