/*********************************************************************** * Structors **********************************************************************/ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ _tree = property_tree::make(); _type = device::USRP; _ignore_cal_file = device_addr.has_key("ignore-cal-file"); //read the eeprom so we can determine the hardware _dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE); const mboard_eeprom_t mb_eeprom(*_dev_i2c_iface, E100_EEPROM_MAP_KEY); //determine the model string for this device const std::string model = device_addr.get("model", mb_eeprom.get("model", "")); if (not model_to_fpga_file_name.has_key(model)) throw uhd::runtime_error(str(boost::format( "\n" " The specified model string \"%s\" is not recognized.\n" " Perhaps the EEPROM is uninitialized, missing, or damaged.\n" " Or, a monitor is pirating the I2C address of the EEPROM.\n" ) % model)); //extract the fpga path and compute hash const std::string default_fpga_file_name = model_to_fpga_file_name[model]; std::string e100_fpga_image; try{ e100_fpga_image = find_image_path(device_addr.get("fpga", default_fpga_file_name)); } catch(...){ UHD_MSG(error) << boost::format("Could not find FPGA image. %s\n") % print_utility_error("uhd_images_downloader.py"); throw; } e100_load_fpga(e100_fpga_image); //////////////////////////////////////////////////////////////////// // Setup the FPGA clock over AUX SPI //////////////////////////////////////////////////////////////////// bool dboard_clocks_diff = true; if (mb_eeprom.get("revision", "0") == "3") dboard_clocks_diff = false; else if (mb_eeprom.get("revision", "0") == "4") dboard_clocks_diff = true; else UHD_MSG(warning) << "Unknown E1XX revision number!\n" << "defaulting to differential dboard clocks to be safe.\n" << std::endl; const double master_clock_rate = device_addr.cast<double>("master_clock_rate", E100_DEFAULT_CLOCK_RATE); _aux_spi_iface = e100_ctrl::make_aux_spi_iface(); _clock_ctrl = e100_clock_ctrl::make(_aux_spi_iface, master_clock_rate, dboard_clocks_diff); //////////////////////////////////////////////////////////////////// // setup the main interface into fpga // - do this after aux spi, because we share gpio147 //////////////////////////////////////////////////////////////////// const std::string node = device_addr["node"]; _fpga_ctrl = e100_ctrl::make(node); //////////////////////////////////////////////////////////////////// // Initialize FPGA control communication //////////////////////////////////////////////////////////////////// fifo_ctrl_excelsior_config fifo_ctrl_config; fifo_ctrl_config.async_sid_base = E100_TX_ASYNC_SID; fifo_ctrl_config.num_async_chan = 1; fifo_ctrl_config.ctrl_sid_base = E100_CTRL_MSG_SID; fifo_ctrl_config.spi_base = TOREG(SR_SPI); fifo_ctrl_config.spi_rb = REG_RB_SPI; _fifo_ctrl = fifo_ctrl_excelsior::make(_fpga_ctrl, fifo_ctrl_config); //Perform wishbone readback tests, these tests also write the hash bool test_fail = false; UHD_MSG(status) << "Performing control readback test... " << std::flush; size_t hash = time(NULL); for (size_t i = 0; i < 100; i++){ boost::hash_combine(hash, i); _fifo_ctrl->poke32(TOREG(SR_MISC+0), boost::uint32_t(hash)); test_fail = _fifo_ctrl->peek32(REG_RB_CONFIG0) != boost::uint32_t(hash); if (test_fail) break; //exit loop on any failure } UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl; if (test_fail) UHD_MSG(error) << boost::format( "The FPGA is either clocked improperly\n" "or the FPGA build is not compatible.\n" "Subsequent errors may follow...\n" ); //check that the compatibility is correct this->check_fpga_compat(); //////////////////////////////////////////////////////////////////// // Create controller objects //////////////////////////////////////////////////////////////////// _fpga_i2c_ctrl = i2c_core_200::make(_fifo_ctrl, TOREG(SR_I2C), REG_RB_I2C); _data_transport = e100_make_mmap_zero_copy(_fpga_ctrl); //////////////////////////////////////////////////////////////////// // Initialize the properties tree //////////////////////////////////////////////////////////////////// _tree->create<std::string>("/name").set("E-Series Device"); const fs_path mb_path = "/mboards/0"; _tree->create<std::string>(mb_path / "name").set(model); _tree->create<std::string>(mb_path / "codename").set("Euwanee"); //////////////////////////////////////////////////////////////////// // setup the mboard eeprom //////////////////////////////////////////////////////////////////// _tree->create<mboard_eeprom_t>(mb_path / "eeprom") .set(mb_eeprom) .subscribe(boost::bind(&e100_impl::set_mb_eeprom, this, _1)); //////////////////////////////////////////////////////////////////// // create clock control objects //////////////////////////////////////////////////////////////////// //^^^ clock created up top, just reg props here... ^^^ _tree->create<double>(mb_path / "tick_rate") .publish(boost::bind(&e100_clock_ctrl::get_fpga_clock_rate, _clock_ctrl)) .subscribe(boost::bind(&fifo_ctrl_excelsior::set_tick_rate, _fifo_ctrl, _1)) .subscribe(boost::bind(&e100_impl::update_tick_rate, this, _1)); //subscribe the command time while we are at it _tree->create<time_spec_t>(mb_path / "time/cmd") .subscribe(boost::bind(&fifo_ctrl_excelsior::set_time, _fifo_ctrl, _1)); //////////////////////////////////////////////////////////////////// // create codec control objects //////////////////////////////////////////////////////////////////// _codec_ctrl = e100_codec_ctrl::make(_fifo_ctrl/*spi*/); const fs_path rx_codec_path = mb_path / "rx_codecs/A"; const fs_path tx_codec_path = mb_path / "tx_codecs/A"; _tree->create<std::string>(rx_codec_path / "name").set("ad9522"); _tree->create<meta_range_t>(rx_codec_path / "gains/pga/range").set(e100_codec_ctrl::rx_pga_gain_range); _tree->create<double>(rx_codec_path / "gains/pga/value") .coerce(boost::bind(&e100_impl::update_rx_codec_gain, this, _1)); _tree->create<std::string>(tx_codec_path / "name").set("ad9522"); _tree->create<meta_range_t>(tx_codec_path / "gains/pga/range").set(e100_codec_ctrl::tx_pga_gain_range); _tree->create<double>(tx_codec_path / "gains/pga/value") .subscribe(boost::bind(&e100_codec_ctrl::set_tx_pga_gain, _codec_ctrl, _1)) .publish(boost::bind(&e100_codec_ctrl::get_tx_pga_gain, _codec_ctrl)); //////////////////////////////////////////////////////////////////// // and do the misc mboard sensors //////////////////////////////////////////////////////////////////// _tree->create<sensor_value_t>(mb_path / "sensors/ref_locked") .publish(boost::bind(&e100_impl::get_ref_locked, this)); //////////////////////////////////////////////////////////////////// // Create the GPSDO control //////////////////////////////////////////////////////////////////// static const fs::path GPSDO_VOLATILE_PATH("/media/ram/e100_internal_gpsdo.cache"); if (not fs::exists(GPSDO_VOLATILE_PATH)) { UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush; try{ _gps = gps_ctrl::make(e100_ctrl::make_gps_uart_iface(E100_UART_DEV_NODE)); } catch(std::exception &e){ UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl; } if (_gps and _gps->gps_detected()) { BOOST_FOREACH(const std::string &name, _gps->get_sensors()) { _tree->create<sensor_value_t>(mb_path / "sensors" / name) .publish(boost::bind(&gps_ctrl::get_sensor, _gps, name)); } }
/*********************************************************************** * Structors **********************************************************************/ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ _tree = property_tree::make(); //setup the main interface into fpga const std::string node = device_addr["node"]; _fpga_ctrl = e100_ctrl::make(node); //read the eeprom so we can determine the hardware _dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE); const mboard_eeprom_t mb_eeprom(*_dev_i2c_iface, mboard_eeprom_t::MAP_E100); //determine the model string for this device const std::string model = device_addr.get("model", mb_eeprom.get("model", "")); if (not model_to_fpga_file_name.has_key(model)) throw uhd::runtime_error(str(boost::format( "\n" " The specified model string \"%s\" is not recognized.\n" " Perhaps the EEPROM is uninitialized, missing, or damaged.\n" " Or, a monitor is pirating the I2C address of the EEPROM.\n" ) % model)); //extract the fpga path and compute hash const std::string default_fpga_file_name = model_to_fpga_file_name[model]; const std::string e100_fpga_image = find_image_path(device_addr.get("fpga", default_fpga_file_name)); const boost::uint32_t file_hash = boost::uint32_t(hash_fpga_file(e100_fpga_image)); //When the hash does not match: // - close the device node // - load the fpga bin file // - re-open the device node if (_fpga_ctrl->peek32(E100_REG_RB_MISC_TEST32) != file_hash){ _fpga_ctrl.reset(); e100_load_fpga(e100_fpga_image); _fpga_ctrl = e100_ctrl::make(node); } //setup clock control here to ensure that the FPGA has a good clock before we continue bool dboard_clocks_diff = true; if (mb_eeprom.get("revision", "0") == "3") dboard_clocks_diff = false; else if (mb_eeprom.get("revision", "0") == "4") dboard_clocks_diff = true; else UHD_MSG(warning) << "Unknown E1XX revision number!\n" << "defaulting to differential dboard clocks to be safe.\n" << std::endl; const double master_clock_rate = device_addr.cast<double>("master_clock_rate", E100_DEFAULT_CLOCK_RATE); _aux_spi_iface = e100_ctrl::make_aux_spi_iface(); _clock_ctrl = e100_clock_ctrl::make(_aux_spi_iface, master_clock_rate, dboard_clocks_diff); //Perform wishbone readback tests, these tests also write the hash bool test_fail = false; UHD_MSG(status) << "Performing wishbone readback test... " << std::flush; for (size_t i = 0; i < 100; i++){ _fpga_ctrl->poke32(E100_REG_SR_MISC_TEST32, file_hash); test_fail = _fpga_ctrl->peek32(E100_REG_RB_MISC_TEST32) != file_hash; if (test_fail) break; //exit loop on any failure } UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl; if (test_fail) UHD_MSG(error) << boost::format( "The FPGA is either clocked improperly\n" "or the FPGA build is not compatible.\n" "Subsequent errors may follow...\n" ); //check that the compatibility is correct this->check_fpga_compat(); //////////////////////////////////////////////////////////////////// // Create controller objects //////////////////////////////////////////////////////////////////// _fpga_i2c_ctrl = i2c_core_100::make(_fpga_ctrl, E100_REG_SLAVE(3)); _fpga_spi_ctrl = spi_core_100::make(_fpga_ctrl, E100_REG_SLAVE(2)); _data_transport = e100_make_mmap_zero_copy(_fpga_ctrl); //////////////////////////////////////////////////////////////////// // Initialize the properties tree //////////////////////////////////////////////////////////////////// _tree->create<std::string>("/name").set("E-Series Device"); const fs_path mb_path = "/mboards/0"; _tree->create<std::string>(mb_path / "name").set(str(boost::format("%s (euewanee)") % model)); //////////////////////////////////////////////////////////////////// // setup the mboard eeprom //////////////////////////////////////////////////////////////////// _tree->create<mboard_eeprom_t>(mb_path / "eeprom") .set(mb_eeprom) .subscribe(boost::bind(&e100_impl::set_mb_eeprom, this, _1)); //////////////////////////////////////////////////////////////////// // create clock control objects //////////////////////////////////////////////////////////////////// //^^^ clock created up top, just reg props here... ^^^ _tree->create<double>(mb_path / "tick_rate") .publish(boost::bind(&e100_clock_ctrl::get_fpga_clock_rate, _clock_ctrl)) .subscribe(boost::bind(&e100_impl::update_tick_rate, this, _1)); //////////////////////////////////////////////////////////////////// // create codec control objects //////////////////////////////////////////////////////////////////// _codec_ctrl = e100_codec_ctrl::make(_fpga_spi_ctrl); const fs_path rx_codec_path = mb_path / "rx_codecs/A"; const fs_path tx_codec_path = mb_path / "tx_codecs/A"; _tree->create<std::string>(rx_codec_path / "name").set("ad9522"); _tree->create<meta_range_t>(rx_codec_path / "gains/pga/range").set(e100_codec_ctrl::rx_pga_gain_range); _tree->create<double>(rx_codec_path / "gains/pga/value") .coerce(boost::bind(&e100_impl::update_rx_codec_gain, this, _1)); _tree->create<std::string>(tx_codec_path / "name").set("ad9522"); _tree->create<meta_range_t>(tx_codec_path / "gains/pga/range").set(e100_codec_ctrl::tx_pga_gain_range); _tree->create<double>(tx_codec_path / "gains/pga/value") .subscribe(boost::bind(&e100_codec_ctrl::set_tx_pga_gain, _codec_ctrl, _1)) .publish(boost::bind(&e100_codec_ctrl::get_tx_pga_gain, _codec_ctrl)); //////////////////////////////////////////////////////////////////// // and do the misc mboard sensors //////////////////////////////////////////////////////////////////// _tree->create<sensor_value_t>(mb_path / "sensors/ref_locked") .publish(boost::bind(&e100_impl::get_ref_locked, this)); //////////////////////////////////////////////////////////////////// // Create the GPSDO control //////////////////////////////////////////////////////////////////// try{ _gps = gps_ctrl::make(e100_ctrl::make_gps_uart_iface(E100_UART_DEV_NODE)); } catch(std::exception &e){ UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl; } if (_gps.get() != NULL and _gps->gps_detected()){ BOOST_FOREACH(const std::string &name, _gps->get_sensors()){ _tree->create<sensor_value_t>(mb_path / "sensors" / name) .publish(boost::bind(&gps_ctrl::get_sensor, _gps, name)); } }
void rhodium_radio_ctrl_impl::set_rpc_client( uhd::rpc_client::sptr rpcc, const uhd::device_addr_t &block_args ) { _rpcc = rpcc; _block_args = block_args; // Get and verify the MCR before _init_peripherals, which will use this value // Note: MCR gets set during the init() call (prior to this), which takes // in arguments from the device args. So if block_args contains a // master_clock_rate key, then it should better be whatever the device is // configured to do. _master_clock_rate = _rpcc->request_with_token<double>(_rpc_prefix + "get_master_clock_rate"); if (block_args.cast<double>("master_clock_rate", _master_clock_rate) != _master_clock_rate) { throw uhd::runtime_error(str( boost::format("Master clock rate mismatch. Device returns %f MHz, " "but should have been %f MHz.") % (_master_clock_rate / 1e6) % (block_args.cast<double>( "master_clock_rate", _master_clock_rate) / 1e6) )); } UHD_LOG_DEBUG(unique_id(), "Master Clock Rate is: " << (_master_clock_rate / 1e6) << " MHz."); radio_ctrl_impl::set_rate(_master_clock_rate); UHD_LOG_TRACE(unique_id(), "Checking for existence of Rhodium DB in slot " << _radio_slot); const auto dboard_info = _rpcc->request<std::vector<std::map<std::string, std::string>>>("get_dboard_info"); // There is a bug that if only one DB is plugged into slot B the vector // will only have 1 element but not be correlated to slot B at all. // For now, we assume a 1 element array means the DB is in slot A. if (dboard_info.size() <= get_block_id().get_block_count()) { UHD_LOG_DEBUG(unique_id(), "No DB detected in slot " << _radio_slot); // Name and master clock rate are needed for RFNoC init, so set the // name now and let this function continue to set the MCR _tree->subtree(fs_path("dboards") / _radio_slot / "tx_frontends" / "0") ->create<std::string>("name").set("Unknown"); _tree->subtree(fs_path("dboards") / _radio_slot / "rx_frontends" / "0") ->create<std::string>("name").set("Unknown"); } else { UHD_LOG_DEBUG(unique_id(), "Rhodium DB detected in slot " << _radio_slot << ". Serial: " << dboard_info.at(get_block_id().get_block_count()).at("serial")); _init_defaults(); _init_peripherals(); _init_prop_tree(); } if (block_args.has_key("identify")) { const std::string identify_val = block_args.get("identify"); int identify_duration = std::atoi(identify_val.c_str()); if (identify_duration == 0) { identify_duration = DEFAULT_IDENTIFY_DURATION; } // TODO: Update this when LED control is added //UHD_LOG_INFO(unique_id(), // "Running LED identification process for " << identify_duration // << " seconds."); //_identify_with_leds(identify_duration); } }