diff options
Diffstat (limited to 'host/lib/usrp/x300/x300_impl.cpp')
-rw-r--r-- | host/lib/usrp/x300/x300_impl.cpp | 524 |
1 files changed, 49 insertions, 475 deletions
diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index 223b112ec..d1d7b43f7 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -10,19 +10,16 @@ #include "x300_claim.hpp" #include "x300_eth_mgr.hpp" #include "x300_mb_eeprom.hpp" +#include "x300_mb_controller.hpp" #include "x300_mb_eeprom_iface.hpp" #include "x300_mboard_type.hpp" #include "x300_pcie_mgr.hpp" #include <uhd/transport/if_addrs.hpp> -#include <uhd/types/sid.hpp> -#include <uhd/usrp/subdev_spec.hpp> #include <uhd/utils/log.hpp> -#include <uhd/utils/math.hpp> #include <uhd/utils/paths.hpp> #include <uhd/utils/safe_call.hpp> #include <uhd/utils/static.hpp> -#include <boost/algorithm/string.hpp> -#include <boost/make_shared.hpp> +#include <uhdlib/rfnoc/device_id.hpp> #include <chrono> #include <fstream> #include <thread> @@ -35,6 +32,17 @@ using namespace uhd::rfnoc; using namespace uhd::usrp::x300; namespace asio = boost::asio; +namespace uhd { namespace usrp { namespace x300 { + +void init_prop_tree( + const size_t mb_idx, uhd::rfnoc::x300_mb_controller* mbc, property_tree::sptr pt); + +}}} // namespace uhd::usrp::x300 + + +const uhd::rfnoc::chdr::chdr_packet_factory x300_impl::_pkt_factory( + CHDR_W_64, ENDIANNESS_BIG); + /*********************************************************************** * Discovery over the udp and pcie transport @@ -168,10 +176,10 @@ static void x300_load_fw(wb_iface::sptr fw_reg_ctrl, const std::string& file_nam UHD_LOGGER_INFO("X300") << "Firmware loaded!"; } -x300_impl::x300_impl(const uhd::device_addr_t& dev_addr) : device3_impl(), _sid_framer(0) +x300_impl::x300_impl(const uhd::device_addr_t& dev_addr) + : rfnoc_device() { UHD_LOGGER_INFO("X300") << "X300 initialization sequence..."; - _tree->create<std::string>("/name").set("X-Series Device"); const device_addrs_t device_args = separate_device_addr(dev_addr); _mb.resize(device_args.size()); @@ -216,6 +224,10 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t& dev_addr) mb.send_args[key] = dev_addr[key]; } + mb.device_id = allocate_device_id(); + UHD_LOG_DEBUG( + "X300", "Motherboard " << mb_i << " has remote device ID: " << mb.device_id); + UHD_LOGGER_DEBUG("X300") << "Setting up basic communication..."; if (mb.xport_path == xport_path_t::NIRIO) { mb.conn_mgr = std::make_shared<pcie_manager>(mb.args, _tree, mb_path); @@ -243,9 +255,6 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t& dev_addr) this->check_fpga_compat(mb_path, mb); this->check_fw_compat(mb_path, mb); - mb.fw_regmap = boost::make_shared<fw_regmap_t>(); - mb.fw_regmap->initialize(*mb.zpu_ctrl.get(), true); - // store which FPGA image is loaded mb.loaded_fpga_image = get_fpga_option(mb.zpu_ctrl); @@ -256,28 +265,6 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t& dev_addr) mb.zpu_i2c->set_clock_rate(x300::BUS_CLOCK_RATE / 2); //////////////////////////////////////////////////////////////////// - // 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_LOGGER_INFO("X300") << boost::format("%u: %s -> %s") - % i - % asio::ip::address_v4(node_addr).to_string() - % asio::ip::address_v4(nbor_addr).to_string(); - } - } - */ - - //////////////////////////////////////////////////////////////////// // setup the mboard eeprom //////////////////////////////////////////////////////////////////// UHD_LOGGER_DEBUG("X300") << "Loading values from EEPROM..."; @@ -322,8 +309,6 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t& dev_addr) "reprogramming."); } } - _tree->create<std::string>(mb_path / "name").set(product_name); - _tree->create<std::string>(mb_path / "codename").set("Yetti"); //////////////////////////////////////////////////////////////////// // discover interfaces, frame sizes, and link rates @@ -344,190 +329,56 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t& dev_addr) // create clock control objects //////////////////////////////////////////////////////////////////// UHD_LOGGER_DEBUG("X300") << "Setting up RF frontend clocking..."; - - // Initialize clock control registers. NOTE: This does not configure the LMK yet. + // Initialize clock control registers. + // NOTE: This does not configure the LMK yet. mb.clock = x300_clock_ctrl::make(mb.zpu_spi, 1 /*slaveno*/, mb.hw_rev, mb.args.get_master_clock_rate(), mb.args.get_dboard_clock_rate(), mb.args.get_system_ref_rate()); - mb.fw_regmap->ref_freq_reg.write( - fw_regmap_t::ref_freq_reg_t::REF_FREQ, uint32_t(mb.args.get_system_ref_rate())); - - // Initialize clock source to use internal reference and generate - // a valid radio clock. This may change after configuration is done. - // This will configure the LMK and wait for lock - update_clock_source(mb, mb.args.get_clock_source()); //////////////////////////////////////////////////////////////////// - // create clock properties + // create motherboard controller //////////////////////////////////////////////////////////////////// - _tree->create<double>(mb_path / "master_clock_rate").set_publisher([&mb]() { - return mb.clock->get_master_clock_rate(); - }); + // Now we have all the peripherals, create the MB controller. It will also + // initialize the clock source, and the time source. + auto mb_ctrl = std::make_shared<x300_mb_controller>( + mb.hw_rev, product_name, mb.zpu_i2c, mb.zpu_ctrl, mb.clock, mb_eeprom, mb.args); + register_mb_controller(mb_i, mb_ctrl); + // Clock should be up now! UHD_LOGGER_INFO("X300") << "Radio 1x clock: " << (mb.clock->get_master_clock_rate() / 1e6) << " MHz"; //////////////////////////////////////////////////////////////////// - // Create the GPSDO control - //////////////////////////////////////////////////////////////////// - static constexpr 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_LOG_DEBUG("X300", "Detecting internal GPSDO...."); - try { - // gps_ctrl will print its own log statements if a GPSDO was found - mb.gps = gps_ctrl::make(x300_make_uart_iface(mb.zpu_ctrl)); - } catch (std::exception& e) { - UHD_LOGGER_ERROR("X300") - << "An error occurred making GPSDO control: " << e.what(); - } - if (mb.gps and mb.gps->gps_detected()) { - for (const std::string& name : mb.gps->get_sensors()) { - _tree->create<sensor_value_t>(mb_path / "sensors" / name) - .set_publisher([&mb, name]() { return mb.gps->get_sensor(name); }); - } - } else { - mb.zpu_ctrl->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_GPSDO_STATUS), - dont_look_for_gpsdo); - } - } - - //////////////////////////////////////////////////////////////////// - // setup time sources and properties - //////////////////////////////////////////////////////////////////// - _tree->create<std::string>(mb_path / "time_source" / "value") - .set(mb.args.get_time_source()) - .add_coerced_subscriber([this, &mb](const std::string& time_source) { - this->update_time_source(mb, time_source); - }); - _tree->create<std::vector<std::string>>(mb_path / "time_source" / "options") - .set(TIME_SOURCE_OPTIONS); - - // setup the time output, default to ON - _tree->create<bool>(mb_path / "time_source" / "output") - .add_coerced_subscriber([this, &mb](const bool time_output) { - this->set_time_source_out(mb, time_output); - }) - .set(true); - - //////////////////////////////////////////////////////////////////// - // setup clock sources and properties + // setup properties //////////////////////////////////////////////////////////////////// - _tree->create<std::string>(mb_path / "clock_source" / "value") - .set(mb.args.get_clock_source()) - .add_coerced_subscriber([this, &mb](const std::string& clock_source) { - this->update_clock_source(mb, clock_source); - }); - _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"); - _tree - ->create<std::vector<double>>( - mb_path / "clock_source" / "external" / "freq" / "options") - .set(x300::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") - .add_coerced_subscriber( - [&mb](const bool clock_output) { mb.clock->set_ref_out(clock_output); }); - - // Initialize tick rate (must be done before setting time) - // Note: The master tick rate can't be changed at runtime! - const double master_clock_rate = mb.clock->get_master_clock_rate(); - _tree->create<double>(mb_path / "tick_rate") - .set_coercer([master_clock_rate](const double rate) { - // The contract of multi_usrp::set_master_clock_rate() is to coerce - // and not throw, so we'll follow that behaviour here. - if (!uhd::math::frequencies_are_equal(rate, master_clock_rate)) { - UHD_LOGGER_WARNING("X300") - << "Cannot update master clock rate! X300 Series does not " - "allow changing the clock rate during runtime."; - } - return master_clock_rate; - }) - .add_coerced_subscriber([this](const double) { this->update_tx_streamers(); }) - .add_coerced_subscriber([this](const double) { this->update_rx_streamers(); }) - .set(master_clock_rate); + init_prop_tree(mb_i, mb_ctrl.get(), _tree); //////////////////////////////////////////////////////////////////// - // and do the misc mboard sensors + // RFNoC Stuff //////////////////////////////////////////////////////////////////// - _tree->create<sensor_value_t>(mb_path / "sensors" / "ref_locked") - .set_publisher([this, &mb]() { return this->get_ref_locked(mb); }); - - //////////////// RFNOC ///////////////// - const size_t n_rfnoc_blocks = mb.zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_NUM_CE)); - enumerate_rfnoc_blocks(mb_i, - n_rfnoc_blocks, - x300::XB_DST_PCI + 1, /* base port */ - uhd::sid_t(x300::SRC_ADDR0, 0, x300::DST_ADDR + mb_i, 0), - dev_addr); - //////////////// RFNOC ///////////////// - - // If we have a radio, we must configure its codec control: - const std::string radio_blockid_hint = str(boost::format("%d/Radio") % mb_i); - std::vector<rfnoc::block_id_t> radio_ids = - find_blocks<rfnoc::x300_radio_ctrl_impl>(radio_blockid_hint); - if (not radio_ids.empty()) { - if (radio_ids.size() > 2) { - UHD_LOGGER_WARNING("X300") - << "Too many Radio Blocks found. Using only the first two."; - radio_ids.resize(2); - } - - for (const rfnoc::block_id_t& id : radio_ids) { - rfnoc::x300_radio_ctrl_impl::sptr radio( - get_block_ctrl<rfnoc::x300_radio_ctrl_impl>(id)); - mb.radios.push_back(radio); - radio->setup_radio(mb.zpu_i2c, - mb.clock, - mb.args.get_ignore_cal_file(), - mb.args.get_self_cal_adc_delay()); - } - - //////////////////////////////////////////////////////////////////// - // ADC test and cal - //////////////////////////////////////////////////////////////////// - if (mb.args.get_self_cal_adc_delay()) { - rfnoc::x300_radio_ctrl_impl::self_cal_adc_xfer_delay(mb.radios, - mb.clock, - [this, &mb](const double timeout) { - return this->wait_for_clk_locked( - mb, fw_regmap_t::clk_status_reg_t::LMK_LOCK, timeout); - }, - true /* Apply ADC delay */); - } - if (mb.args.get_ext_adc_self_test()) { - rfnoc::x300_radio_ctrl_impl::extended_adc_test( - mb.radios, mb.args.get_ext_adc_self_test_duration()); - } else { - for (size_t i = 0; i < mb.radios.size(); i++) { - mb.radios.at(i)->self_test_adc(); - } + // Set the remote device ID + mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_XB_LOCAL), mb.device_id); + // Configure the CHDR port number in the dispatcher + 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); + // Peek to finish transaction + mb.zpu_ctrl->peek32(0); + + { // Need to lock access to _mb_ifaces, so we can run setup_mb() in + // parallel + std::lock_guard<std::mutex> l(_mb_iface_mutex); + _mb_ifaces.insert({mb_i, + x300_mb_iface(mb.conn_mgr, mb.clock->get_master_clock_rate(), mb.device_id)}); + UHD_LOG_DEBUG("X300", "Motherboard " << mb_i << " has local device IDs: "); + for (const auto local_dev_id : _mb_ifaces.at(mb_i).get_local_device_ids()) { + UHD_LOG_DEBUG("X300", "* " << local_dev_id); } + } // End of locked section - //////////////////////////////////////////////////////////////////// - // Synchronize times (dboard initialization can desynchronize them) - //////////////////////////////////////////////////////////////////// - if (radio_ids.size() == 2) { - this->sync_times(mb, mb.radios[0]->get_time_now()); - } - - } else { - UHD_LOGGER_INFO("X300") << "No Radio Block found. Assuming radio-less operation."; - } /* end of radio block(s) initialization */ - - mb.initialization_done = true; + mb_ctrl->set_initialization_done(); } x300_impl::~x300_impl(void) @@ -548,283 +399,6 @@ x300_impl::~x300_impl(void) } } -uhd::both_xports_t x300_impl::make_transport(const uhd::sid_t& address, - const xport_type_t xport_type, - const uhd::device_addr_t& args) -{ - const size_t mb_index = address.get_dst_addr() - x300::DST_ADDR; - mboard_members_t& mb = _mb[mb_index]; - both_xports_t xports; - - // Calculate MTU based on MTU in args and device limitations - const size_t send_mtu = args.cast<size_t>("mtu", - get_mtu(mb_index, uhd::TX_DIRECTION)); - const size_t recv_mtu = args.cast<size_t>("mtu", - get_mtu(mb_index, uhd::RX_DIRECTION)); - - if (mb.xport_path == xport_path_t::NIRIO) { - xports.send_sid = - this->allocate_sid(mb, address, x300::SRC_ADDR0, x300::XB_DST_PCI); - xports.recv_sid = xports.send_sid.reversed(); - return std::dynamic_pointer_cast<pcie_manager>(mb.conn_mgr) - ->make_transport(xports, xport_type, args, send_mtu, recv_mtu); - } else if (mb.xport_path == xport_path_t::ETH) { - xports = std::dynamic_pointer_cast<eth_manager>(mb.conn_mgr) - ->make_transport(xports, - xport_type, - args, - send_mtu, - recv_mtu, - [this, &mb, address]( - const uint32_t src_addr, const uint32_t src_dst) { - return this->allocate_sid(mb, address, src_addr, src_dst); - }); - - // reprogram the ethernet dispatcher's udp port (should be safe to always set) - UHD_LOGGER_TRACE("X300") - << "reprogram the ethernet dispatcher's udp port to " << X300_VITA_UDP_PORT; - 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; - } - UHD_THROW_INVALID_CODE_PATH(); -} - - -uhd::sid_t x300_impl::allocate_sid(mboard_members_t& mb, - const uhd::sid_t& address, - const uint32_t src_addr, - const uint32_t src_dst) -{ - uhd::sid_t sid = address; - sid.set_src_addr(src_addr); - sid.set_src_endpoint(_sid_framer++); // increment for next setup - - // TODO Move all of this setup_mb() - // Program the X300 to recognise it's own local address. - mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_XB_LOCAL), address.get_dst_addr()); - // Program CAM entry for outgoing packets matching a X300 resource (for example a - // Radio) This type of packet 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 + address.get_dst_endpoint()), - address.get_dst_xbarport()); - // 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 + src_addr), src_dst); - - UHD_LOGGER_TRACE("X300") << "done router config for sid " << sid; - - return sid; -} - -/*********************************************************************** - * clock and time control logic - **********************************************************************/ -void x300_impl::set_time_source_out(mboard_members_t& mb, const bool enb) -{ - mb.fw_regmap->clock_ctrl_reg.write( - fw_regmap_t::clk_ctrl_reg_t::PPS_OUT_EN, enb ? 1 : 0); -} - -void x300_impl::update_clock_source(mboard_members_t& mb, const std::string& source) -{ - // Optimize for the case when the current source is internal and we are trying - // to set it to internal. This is the only case where we are guaranteed that - // the clock has not gone away so we can skip setting the MUX and reseting the LMK. - const bool reconfigure_clks = (mb.current_refclk_src != "internal") - or (source != "internal"); - if (reconfigure_clks) { - // Update the clock MUX on the motherboard to select the requested source - if (source == "internal") { - mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::CLK_SOURCE, - fw_regmap_t::clk_ctrl_reg_t::SRC_INTERNAL); - mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::TCXO_EN, 1); - } else if (source == "external") { - mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::CLK_SOURCE, - fw_regmap_t::clk_ctrl_reg_t::SRC_EXTERNAL); - mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::TCXO_EN, 0); - } else if (source == "gpsdo") { - mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::CLK_SOURCE, - fw_regmap_t::clk_ctrl_reg_t::SRC_GPSDO); - mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::TCXO_EN, 0); - } else { - throw uhd::key_error("update_clock_source: unknown source: " + source); - } - mb.fw_regmap->clock_ctrl_reg.flush(); - - // Reset the LMK to make sure it re-locks to the new reference - mb.clock->reset_clocks(); - } - - // Wait for the LMK to lock (always, as a sanity check that the clock is useable) - //* Currently the LMK can take as long as 30 seconds to lock to a reference but we - // don't - //* want to wait that long during initialization. - // TODO: Need to verify timeout and settings to make sure lock can be achieved in - // < 1.0 seconds - double timeout = mb.initialization_done ? 30.0 : 1.0; - - // The programming code in x300_clock_ctrl is not compatible with revs <= 4 and may - // lead to locking issues. So, disable the ref-locked check for older (unsupported) - // boards. - if (mb.hw_rev > 4) { - if (not wait_for_clk_locked( - mb, fw_regmap_t::clk_status_reg_t::LMK_LOCK, timeout)) { - // failed to lock on reference - if (mb.initialization_done) { - throw uhd::runtime_error( - (boost::format("Reference Clock PLL failed to lock to %s source.") - % source) - .str()); - } else { - // TODO: Re-enable this warning when we figure out a reliable lock time - // UHD_LOGGER_WARNING("X300") << "Reference clock failed to lock to " + - // source + " during device initialization. " << - // "Check for the lock before operation or ignore this warning if using - // another clock source." ; - } - } - } - - if (reconfigure_clks) { - // Reset the radio clock PLL in the FPGA - mb.zpu_ctrl->poke32( - SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), ZPU_SR_SW_RST_RADIO_CLK_PLL); - mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), 0); - - // Wait for radio clock PLL to lock - if (not wait_for_clk_locked( - mb, fw_regmap_t::clk_status_reg_t::RADIO_CLK_LOCK, 0.01)) { - throw uhd::runtime_error( - (boost::format("Reference Clock PLL in FPGA failed to lock to %s source.") - % source) - .str()); - } - - // Reset the IDELAYCTRL used to calibrate the data interface delays - mb.zpu_ctrl->poke32( - SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), ZPU_SR_SW_RST_ADC_IDELAYCTRL); - mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), 0); - - // Wait for the ADC IDELAYCTRL to be ready - if (not wait_for_clk_locked( - mb, fw_regmap_t::clk_status_reg_t::IDELAYCTRL_LOCK, 0.01)) { - throw uhd::runtime_error( - (boost::format( - "ADC Calibration Clock in FPGA failed to lock to %s source.") - % source) - .str()); - } - - // Reset ADCs and DACs - for (rfnoc::x300_radio_ctrl_impl::sptr r : mb.radios) { - r->reset_codec(); - } - } - - // Update cache value - mb.current_refclk_src = source; -} - -void x300_impl::update_time_source(mboard_members_t& mb, const std::string& source) -{ - if (source == "internal") { - mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_SELECT, - fw_regmap_t::clk_ctrl_reg_t::SRC_INTERNAL); - } else if (source == "external") { - mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_SELECT, - fw_regmap_t::clk_ctrl_reg_t::SRC_EXTERNAL); - } else if (source == "gpsdo") { - mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_SELECT, - fw_regmap_t::clk_ctrl_reg_t::SRC_GPSDO); - } else { - throw uhd::key_error("update_time_source: unknown source: " + source); - } - - /* TODO - Implement intelligent PPS detection - //check for valid pps - if (!is_pps_present(mb)) { - throw uhd::runtime_error((boost::format("The %d PPS was not detected. Please - check the PPS source and try again.") % source).str()); - } - */ -} - -void x300_impl::sync_times(mboard_members_t& mb, const uhd::time_spec_t& t) -{ - std::vector<rfnoc::block_id_t> radio_ids = - find_blocks<rfnoc::x300_radio_ctrl_impl>("Radio"); - for (const rfnoc::block_id_t& id : radio_ids) { - get_block_ctrl<rfnoc::x300_radio_ctrl_impl>(id)->set_time_sync(t); - } - - mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::TIME_SYNC, 0); - mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::TIME_SYNC, 1); - mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::TIME_SYNC, 0); -} - -bool x300_impl::wait_for_clk_locked(mboard_members_t& mb, uint32_t which, double timeout) -{ - const auto timeout_time = std::chrono::steady_clock::now() - + std::chrono::milliseconds(int64_t(timeout * 1000)); - do { - if (mb.fw_regmap->clock_status_reg.read(which) == 1) { - return true; - } - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } while (std::chrono::steady_clock::now() < timeout_time); - - // Check one last time - return (mb.fw_regmap->clock_status_reg.read(which) == 1); -} - -sensor_value_t x300_impl::get_ref_locked(mboard_members_t& mb) -{ - mb.fw_regmap->clock_status_reg.refresh(); - const bool lock = - (mb.fw_regmap->clock_status_reg.get(fw_regmap_t::clk_status_reg_t::LMK_LOCK) == 1) - && (mb.fw_regmap->clock_status_reg.get( - fw_regmap_t::clk_status_reg_t::RADIO_CLK_LOCK) - == 1) - && (mb.fw_regmap->clock_status_reg.get( - fw_regmap_t::clk_status_reg_t::IDELAYCTRL_LOCK) - == 1); - return sensor_value_t("Ref", lock, "locked", "unlocked"); -} - -bool x300_impl::is_pps_present(mboard_members_t& mb) -{ - // The ZPU_RB_CLK_STATUS_PPS_DETECT bit toggles with each rising edge of the PPS. - // We monitor it for up to 1.5 seconds looking for it to toggle. - uint32_t pps_detect = - mb.fw_regmap->clock_status_reg.read(fw_regmap_t::clk_status_reg_t::PPS_DETECT); - for (int i = 0; i < 15; i++) { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - if (pps_detect - != mb.fw_regmap->clock_status_reg.read( - fw_regmap_t::clk_status_reg_t::PPS_DETECT)) - return true; - } - return false; -} - -/*********************************************************************** - * Frame size detection - **********************************************************************/ -size_t x300_impl::get_mtu(const size_t mb_index, const uhd::direction_t dir) -{ - auto& mb = _mb.at(mb_index); - return mb.conn_mgr->get_mtu(dir); -} - /*********************************************************************** * compat checks **********************************************************************/ |