aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/n230/n230_resource_manager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp/n230/n230_resource_manager.cpp')
-rw-r--r--host/lib/usrp/n230/n230_resource_manager.cpp569
1 files changed, 569 insertions, 0 deletions
diff --git a/host/lib/usrp/n230/n230_resource_manager.cpp b/host/lib/usrp/n230/n230_resource_manager.cpp
new file mode 100644
index 000000000..f13dd0b33
--- /dev/null
+++ b/host/lib/usrp/n230/n230_resource_manager.cpp
@@ -0,0 +1,569 @@
+//
+// 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 "n230_resource_manager.hpp"
+
+#include "usrp3_fw_ctrl_iface.hpp"
+#include <uhd/transport/if_addrs.hpp>
+#include <uhd/transport/udp_zero_copy.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/platform.hpp>
+#include <uhd/utils/paths.hpp>
+#include <boost/format.hpp>
+#include <boost/thread.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/functional/hash.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/make_shared.hpp>
+#include "n230_fw_defs.h"
+#include "n230_fw_host_iface.h"
+
+#define IF_DATA_I_MASK 0xFFF00000
+#define IF_DATA_Q_MASK 0x0000FFF0
+
+namespace uhd { namespace usrp { namespace n230 {
+
+//Constants
+static const uint8_t N230_HOST_SRC_ADDR_ETH0 = 0;
+static const uint8_t N230_HOST_SRC_ADDR_ETH1 = 1;
+static const uint8_t N230_HOST_DEST_ADDR = 2;
+
+static const uint8_t N230_ETH0_IFACE_ID = 0;
+static const uint8_t N230_ETH1_IFACE_ID = 1;
+
+class n230_ad9361_client_t : public ad9361_params {
+public:
+ ~n230_ad9361_client_t() {}
+ double get_band_edge(frequency_band_t band) {
+ switch (band) {
+ case AD9361_RX_BAND0: return 2.2e9;
+ case AD9361_RX_BAND1: return 4.0e9;
+ case AD9361_TX_BAND0: return 2.5e9;
+ default: return 0;
+ }
+ }
+ clocking_mode_t get_clocking_mode() {
+ return AD9361_XTAL_N_CLK_PATH;
+ }
+ digital_interface_mode_t get_digital_interface_mode() {
+ return AD9361_DDR_FDD_LVDS;
+ }
+ digital_interface_delays_t get_digital_interface_timing() {
+ digital_interface_delays_t delays;
+ delays.rx_clk_delay = 0;
+ delays.rx_data_delay = 0;
+ delays.tx_clk_delay = 0;
+ delays.tx_data_delay = 2;
+ return delays;
+ }
+};
+
+n230_resource_manager::n230_resource_manager(
+ const std::vector<std::string> ip_addrs,
+ const bool safe_mode
+) :
+ _safe_mode(safe_mode),
+ _last_host_enpoint(0)
+{
+ if (_safe_mode) UHD_MSG(warning) << "Initializing device in safe mode\n";
+ UHD_MSG(status) << "Setup basic communication...\n";
+
+ //Discover ethernet interfaces
+ bool dual_eth_expected = (ip_addrs.size() > 1);
+ BOOST_FOREACH(const std::string& addr, ip_addrs) {
+ n230_eth_conn_t conn_iface;
+ conn_iface.ip_addr = addr;
+
+ boost::uint32_t iface_id = 0xFFFFFFFF;
+ try {
+ iface_id = usrp3::usrp3_fw_ctrl_iface::get_iface_id(
+ conn_iface.ip_addr, BOOST_STRINGIZE(N230_FW_COMMS_UDP_PORT), N230_FW_PRODUCT_ID);
+ } catch (uhd::io_error&) {
+ throw uhd::io_error(str(boost::format(
+ "Could not communicate with the device over address %s") %
+ conn_iface.ip_addr));
+ }
+ switch (iface_id) {
+ case N230_ETH0_IFACE_ID: conn_iface.type = ETH0; break;
+ case N230_ETH1_IFACE_ID: conn_iface.type = ETH1; break;
+ default: {
+ if (dual_eth_expected) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not detect ethernet port number.");
+ } else {
+ //For backwards compatibility, if only one port is specified, assume that a detection
+ //failure means that the device does not support dual-ethernet behavior.
+ conn_iface.type = ETH0; break;
+ }
+ }
+ }
+ _eth_conns.push_back(conn_iface);
+ }
+ if (_eth_conns.size() < 1) {
+ throw uhd::runtime_error("N230 Initialization Error: No eth interfaces specified.)");
+ }
+
+ //Create firmware communication interface
+ _fw_ctrl = usrp3::usrp3_fw_ctrl_iface::make(
+ transport::udp_simple::make_connected(
+ _get_conn(PRI_ETH).ip_addr, BOOST_STRINGIZE(N230_FW_COMMS_UDP_PORT)), N230_FW_PRODUCT_ID);
+ if (_fw_ctrl.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create n230_ctrl_iface.)");
+ }
+ _check_fw_compat();
+
+ //Start the device claimer
+ _claimer_task = uhd::task::make(boost::bind(&n230_resource_manager::_claimer_loop, this));
+
+ //Create common settings interface
+ const sid_t core_sid = _generate_sid(CORE, _get_conn(PRI_ETH).type);
+
+ transport::udp_zero_copy::buff_params dummy_out_params;
+ transport::zero_copy_if::sptr core_xport =
+ _create_transport(_get_conn(PRI_ETH), core_sid, device_addr_t(), dummy_out_params);
+ if (core_xport.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create settings transport.)");
+ }
+ _core_ctrl = radio_ctrl_core_3000::make(
+ fpga::CVITA_BIG_ENDIAN, core_xport, core_xport, core_sid.get());
+ if (_core_ctrl.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create settings ctrl.)");
+ }
+ _check_fpga_compat();
+
+ UHD_MSG(status) << boost::format("Version signatures... Firmware:%s FPGA:%s...\n")
+ % _fw_version.get_hash_str() % _fpga_version.get_hash_str();
+
+ _core_radio_ctrl_reg.initialize(*_core_ctrl, true /*flush*/);
+ _core_misc_reg.initialize(*_core_ctrl, true /*flush*/);
+ _core_pps_sel_reg.initialize(*_core_ctrl, true /*flush*/);
+ _core_status_reg.initialize(*_core_ctrl);
+
+ //Create common SPI interface
+ _core_spi_ctrl = n230_core_spi_core::make(_core_ctrl);
+ if (_core_spi_ctrl.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create SPI ctrl.)");
+ }
+
+ //Create AD9361 interface
+ UHD_MSG(status) << "Initializing CODEC...\n";
+ _codec_ctrl = ad9361_ctrl::make_spi(
+ boost::make_shared<n230_ad9361_client_t>(), _core_spi_ctrl, fpga::AD9361_SPI_SLAVE_NUM);
+ if (_codec_ctrl.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create Catalina ctrl.)");
+ }
+ _codec_ctrl->set_clock_rate(fpga::CODEC_DEFAULT_CLK_RATE);
+ _codec_mgr = ad936x_manager::make(_codec_ctrl, fpga::NUM_RADIOS);
+ _codec_mgr->init_codec();
+
+ //Create AD4001 interface
+ _ref_pll_ctrl = boost::make_shared<n230_ref_pll_ctrl>(_core_spi_ctrl);
+ if (_ref_pll_ctrl.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create ADF4001 ctrl.)");
+ }
+
+ //Reset SERDES interface and synchronize to frame sync from AD9361
+ _reset_codec_digital_interface();
+
+ std::vector<time_core_3000::sptr> time_cores;
+ std::vector<gpio_atr::gpio_atr_3000::sptr> gpio_cores;
+ for (size_t i = 0; i < fpga::NUM_RADIOS; i++) {
+ _initialize_radio(i);
+ time_cores.push_back(_radios[i].time);
+ gpio_cores.push_back(_radios[i].gpio_atr);
+ }
+
+ //Create clock and PPS control interface
+ _clk_pps_ctrl = n230_clk_pps_ctrl::make(
+ _codec_ctrl, _ref_pll_ctrl, _core_misc_reg, _core_pps_sel_reg, _core_status_reg, time_cores);
+ if (_clk_pps_ctrl.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create clock and PPS ctrl.)");
+ }
+
+ //Create front-end control interface
+ _frontend_ctrl = n230_frontend_ctrl::make(_core_ctrl, _core_misc_reg, _codec_ctrl, gpio_cores);
+ if (_frontend_ctrl.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create front-end ctrl.)");
+ }
+
+ //Create miniSAS GPIO interfaces
+ _ms0_gpio = gpio_atr::gpio_atr_3000::make(
+ _core_ctrl, fpga::sr_addr(fpga::SR_CORE_MS0_GPIO), fpga::rb_addr(fpga::RB_CORE_MS0_GPIO));
+ _ms0_gpio->set_atr_mode(gpio_atr::MODE_GPIO,gpio_atr::gpio_atr_3000::MASK_SET_ALL);
+ _ms1_gpio = gpio_atr::gpio_atr_3000::make(
+ _core_ctrl, fpga::sr_addr(fpga::SR_CORE_MS1_GPIO), fpga::rb_addr(fpga::RB_CORE_MS1_GPIO));
+ _ms1_gpio->set_atr_mode(gpio_atr::MODE_GPIO,gpio_atr::gpio_atr_3000::MASK_SET_ALL);
+
+ //Create GPSDO interface
+ if (_core_status_reg.read(fpga::core_status_reg_t::GPSDO_STATUS) != fpga::GPSDO_ST_ABSENT) {
+ UHD_MSG(status) << "Detecting GPSDO.... " << std::flush;
+ try {
+ const sid_t gps_uart_sid = _generate_sid(GPS_UART, _get_conn(PRI_ETH).type);
+ transport::zero_copy_if::sptr gps_uart_xport =
+ _create_transport(_get_conn(PRI_ETH), gps_uart_sid, device_addr_t(), dummy_out_params);
+ _gps_uart = n230_uart::make(gps_uart_xport, uhd::htonx(gps_uart_sid.get()));
+ _gps_uart->set_baud_divider(fpga::BUS_CLK_RATE/fpga::GPSDO_UART_BAUDRATE);
+ _gps_uart->write_uart("\n"); //cause the baud and response to be setup
+ boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for a little propagation
+ _gps_ctrl = gps_ctrl::make(_gps_uart);
+ } catch(std::exception &e) {
+ UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl;
+ }
+ if (not is_gpsdo_present()) {
+ _core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_GPSDO_ST), fpga::GPSDO_ST_ABSENT);
+ }
+ }
+
+ //Perform data self-tests
+ _frontend_ctrl->set_stream_state(TXRX_STREAMING, TXRX_STREAMING);
+ for (size_t i = 0; i < fpga::NUM_RADIOS; i++) {
+ _frontend_ctrl->set_self_test_mode(LOOPBACK_RADIO);
+ bool radio_selftest_pass = _radio_data_loopback_self_test(_radios[i].ctrl);
+ if (!radio_selftest_pass) {
+ throw uhd::runtime_error("N230 Initialization Error: Data loopback test failed.)");
+ }
+
+ _frontend_ctrl->set_self_test_mode(LOOPBACK_CODEC);
+ bool codec_selftest_pass = _radio_data_loopback_self_test(_radios[i].ctrl);
+ if (!codec_selftest_pass) {
+ throw uhd::runtime_error("N230 Initialization Error: Codec loopback test failed.)");
+ }
+ }
+ _frontend_ctrl->set_self_test_mode(LOOPBACK_DISABLED);
+ _frontend_ctrl->set_stream_state(NONE_STREAMING, NONE_STREAMING);
+}
+
+n230_resource_manager::~n230_resource_manager()
+{
+ _claimer_task.reset();
+ { //Critical section
+ boost::mutex::scoped_lock(_claimer_mutex);
+ _fw_ctrl->poke32(N230_FW_HOST_SHMEM_OFFSET(claim_time), 0);
+ _fw_ctrl->poke32(N230_FW_HOST_SHMEM_OFFSET(claim_src), 0);
+ }
+}
+
+transport::zero_copy_if::sptr n230_resource_manager::create_transport(
+ n230_data_dir_t direction,
+ size_t radio_instance,
+ const device_addr_t &params,
+ sid_t& sid_pair,
+ transport::udp_zero_copy::buff_params& buff_out_params)
+{
+ const n230_eth_conn_t& conn = _get_conn((radio_instance==1)?SEC_ETH:PRI_ETH);
+ const sid_t temp_sid_pair =
+ _generate_sid(direction==RX_DATA?RADIO_RX_DATA:RADIO_TX_DATA, conn.type, radio_instance);
+ transport::zero_copy_if::sptr xport = _create_transport(conn, temp_sid_pair, params, buff_out_params);
+ if (xport.get() == NULL) {
+ throw uhd::runtime_error("N230 Create Data Transport: Could not create data transport.)");
+ } else {
+ sid_pair = temp_sid_pair;
+ }
+ return xport;
+}
+
+bool n230_resource_manager::is_device_claimed(uhd::usrp::usrp3::usrp3_fw_ctrl_iface::sptr fw_ctrl)
+{
+ boost::mutex::scoped_lock(_claimer_mutex);
+
+ //If timed out then device is definitely unclaimed
+ if (fw_ctrl->peek32(N230_FW_HOST_SHMEM_OFFSET(claim_status)) == 0)
+ return false;
+
+ //otherwise check claim src to determine if another thread with the same src has claimed the device
+ return fw_ctrl->peek32(N230_FW_HOST_SHMEM_OFFSET(claim_src)) != get_process_hash();
+}
+
+void n230_resource_manager::_claimer_loop()
+{
+ { //Critical section
+ boost::mutex::scoped_lock(_claimer_mutex);
+ _fw_ctrl->poke32(N230_FW_HOST_SHMEM_OFFSET(claim_time), time(NULL));
+ _fw_ctrl->poke32(N230_FW_HOST_SHMEM_OFFSET(claim_src), get_process_hash());
+ }
+ boost::this_thread::sleep(boost::posix_time::milliseconds(N230_CLAIMER_TIMEOUT_IN_MS / 2));
+}
+
+void n230_resource_manager::_initialize_radio(size_t instance)
+{
+ radio_resource_t& radio = _radios[instance];
+
+ //Create common settings interface
+ const sid_t ctrl_sid = _generate_sid(RADIO_CONTROL, _get_conn(PRI_ETH).type, instance);
+ transport::udp_zero_copy::buff_params buff_out_params;
+ transport::zero_copy_if::sptr ctrl_xport =
+ _create_transport(_get_conn(PRI_ETH), ctrl_sid, device_addr_t(), buff_out_params);
+ if (ctrl_xport.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create radio transport.)");
+ }
+ radio.ctrl = radio_ctrl_core_3000::make(
+ fpga::CVITA_BIG_ENDIAN, ctrl_xport, ctrl_xport, ctrl_sid.get());
+ if (radio.ctrl.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create radio ctrl.)");
+ }
+
+ //Perform register loopback test to verify the radio clock
+ bool reg_selftest_pass = _radio_register_loopback_self_test(radio.ctrl);
+ if (!reg_selftest_pass) {
+ throw uhd::runtime_error("N230 Initialization Error: Register loopback test failed.)");
+ }
+
+ //Write-only ATR interface
+ radio.gpio_atr = gpio_atr::gpio_atr_3000::make_write_only(radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_ATR));
+ radio.gpio_atr->set_atr_mode(gpio_atr::MODE_ATR,gpio_atr::gpio_atr_3000::MASK_SET_ALL);
+
+ //Core VITA time interface
+ time_core_3000::readback_bases_type time_bases;
+ time_bases.rb_now = fpga::rb_addr(fpga::RB_RADIO_TIME_NOW);
+ time_bases.rb_pps = fpga::rb_addr(fpga::RB_RADIO_TIME_PPS);
+ radio.time = time_core_3000::make(radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_TIME), time_bases);
+ if (radio.time.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create time core.)");
+ }
+
+ //RX DSP
+ radio.framer = rx_vita_core_3000::make(
+ radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_RX_CTRL));
+ radio.ddc = rx_dsp_core_3000::make(radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_RX_DSP), true /*old DDC?*/);
+ if (radio.framer.get() == NULL || radio.ddc.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create RX DSP interface.)");
+ }
+ radio.ddc->set_link_rate(fpga::N230_LINK_RATE_BPS);
+
+ //TX DSP
+ radio.deframer = tx_vita_core_3000::make(radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_TX_CTRL));
+ radio.duc = tx_dsp_core_3000::make(radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_TX_DSP));
+ if (radio.deframer.get() == NULL || radio.duc.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create RX DSP interface.)");
+ }
+ radio.duc->set_link_rate(fpga::N230_LINK_RATE_BPS);
+
+ //User settings
+ radio.user_settings = user_settings_core_3000::make(radio.ctrl,
+ fpga::sr_addr(fpga::SR_RADIO_USER_SR), fpga::rb_addr(fpga::SR_RADIO_USER_RB));
+ if (radio.user_settings.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create user settings bus.)");
+ }
+}
+
+boost::uint8_t xb_ep_to_sid(fpga::xb_endpoint_t ep) {
+ return static_cast<boost::uint8_t>(ep) << 4;
+}
+
+const sid_t n230_resource_manager::_generate_sid(const n230_endpoint_t type, const n230_eth_port_t xport, size_t instance)
+{
+ fpga::xb_endpoint_t xb_dest_ep;
+ boost::uint8_t sid_dest_ep = 0;
+ fpga::xb_endpoint_t xb_ret_ep = (xport == ETH1) ? fpga::N230_XB_DST_E1 : fpga::N230_XB_DST_E0;
+ boost::uint8_t sid_ret_addr = (xport == ETH1) ? N230_HOST_SRC_ADDR_ETH1 : N230_HOST_SRC_ADDR_ETH0;
+
+ if (type == CORE or type == GPS_UART) {
+ //Non-radio endpoints
+ xb_dest_ep = (type == CORE) ? fpga::N230_XB_DST_GCTRL : fpga::N230_XB_DST_UART;
+ sid_dest_ep = xb_ep_to_sid(xb_dest_ep);
+ } else {
+ //Radio endpoints
+ xb_dest_ep = (instance == 1) ? fpga::N230_XB_DST_R1 : fpga::N230_XB_DST_R0;
+ sid_dest_ep = xb_ep_to_sid(xb_dest_ep);
+ switch (type) {
+ case RADIO_TX_DATA:
+ sid_dest_ep |= fpga::RADIO_DATA_SUFFIX;
+ break;
+ case RADIO_RX_DATA:
+ sid_dest_ep |= fpga::RADIO_FC_SUFFIX;
+ break;
+ default:
+ sid_dest_ep |= fpga::RADIO_CTRL_SUFFIX;
+ break;
+ }
+ }
+
+ //Increment last host logical endpoint
+ sid_t sid(sid_ret_addr, ++_last_host_enpoint, N230_HOST_DEST_ADDR, sid_dest_ep);
+
+ //Program the crossbar addr
+ _fw_ctrl->poke32(fw::reg_addr(fw::WB_SBRB_BASE, fw::SR_ZPU_XB_LOCAL), sid.get_dst_addr());
+ // Program CAM entry for returning packets to us
+ // This type of packet does not match the XB_LOCAL address and is looked up in the lower half of the CAM
+ _fw_ctrl->poke32(fw::reg_addr(fw::WB_XB_SBRB_BASE, sid.get_src_addr()), static_cast<boost::uint32_t>(xb_ret_ep));
+ // Program CAM entry for outgoing packets matching a N230 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
+ _fw_ctrl->poke32(fw::reg_addr(fw::WB_XB_SBRB_BASE, 256 + sid.get_dst_endpoint()), static_cast<boost::uint32_t>(xb_dest_ep));
+
+ return sid;
+}
+
+transport::zero_copy_if::sptr n230_resource_manager::_create_transport(
+ const n230_eth_conn_t& eth_conn,
+ const sid_t& sid, const device_addr_t &buff_params,
+ transport::udp_zero_copy::buff_params& buff_params_out)
+{
+ transport::zero_copy_xport_params default_buff_args;
+ default_buff_args.recv_frame_size = transport::udp_simple::mtu;
+ default_buff_args.send_frame_size = transport::udp_simple::mtu;
+ default_buff_args.num_recv_frames = 32;
+ default_buff_args.num_send_frames = 32;
+
+ transport::zero_copy_if::sptr xport = transport::udp_zero_copy::make(
+ eth_conn.ip_addr, boost::lexical_cast<std::string>(fpga::CVITA_UDP_PORT),
+ default_buff_args, buff_params_out, buff_params);
+
+ if (xport.get()) {
+ _program_dispatcher(*xport, eth_conn.type, sid);
+ }
+ return xport;
+}
+
+void n230_resource_manager::_program_dispatcher(
+ transport::zero_copy_if& xport, const n230_eth_port_t port, const sid_t& sid)
+{
+ //Send a mini packet with SID into the ZPU
+ //ZPU will reprogram the ethernet framer
+ transport::managed_send_buffer::sptr buff = xport.get_send_buff();
+ buff->cast<boost::uint32_t *>()[0] = 0; //eth dispatch looks for != 0
+ buff->cast<boost::uint32_t *>()[1] = uhd::htonx(sid.get());
+ buff->commit(8);
+ buff.reset();
+
+ //reprogram the ethernet dispatcher's udp port (should be safe to always set)
+ uint32_t disp_base_offset =
+ ((port == ETH1) ? fw::SR_ZPU_ETHINT1 : fw::SR_ZPU_ETHINT0) + fw::SR_ZPU_ETHINT_DISPATCHER_BASE;
+ _fw_ctrl->poke32(fw::reg_addr(fw::WB_SBRB_BASE, disp_base_offset + fw::ETH_FRAMER_SRC_UDP_PORT), fpga::CVITA_UDP_PORT);
+
+ //Do a peek to an arbitrary address to guarantee that the
+ //ethernet framer has been programmed before we return.
+ _fw_ctrl->peek32(0);
+}
+
+void n230_resource_manager::_reset_codec_digital_interface()
+{
+ //Set timing registers
+ _core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_DATA_DELAY), fpga::CODEC_DATA_DELAY);
+ _core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_CLK_DELAY), fpga::CODEC_CLK_DELAY);
+
+ _core_radio_ctrl_reg.write(fpga::core_radio_ctrl_reg_t::CODEC_ARST, 1);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ _core_radio_ctrl_reg.write(fpga::core_radio_ctrl_reg_t::CODEC_ARST, 0);
+}
+
+bool n230_resource_manager::_radio_register_loopback_self_test(wb_iface::sptr iface)
+{
+ bool test_fail = false;
+ size_t hash = static_cast<size_t>(time(NULL));
+ for (size_t i = 0; i < 100; i++) {
+ boost::hash_combine(hash, i);
+ iface->poke32(fpga::sr_addr(fpga::SR_RADIO_TEST), boost::uint32_t(hash));
+ test_fail = iface->peek32(fpga::rb_addr(fpga::RB_RADIO_TEST)) != boost::uint32_t(hash);
+ if (test_fail) break; //exit loop on any failure
+ }
+ return !test_fail;
+}
+
+bool n230_resource_manager::_radio_data_loopback_self_test(wb_iface::sptr iface)
+{
+ bool test_fail = false;
+ size_t hash = size_t(time(NULL));
+ for (size_t i = 0; i < 100; i++) {
+ boost::hash_combine(hash, i);
+ const boost::uint32_t word32 = boost::uint32_t(hash) & (IF_DATA_I_MASK | IF_DATA_Q_MASK);
+ iface->poke32(fpga::sr_addr(fpga::SR_RADIO_CODEC_IDLE), word32);
+ iface->peek64(fpga::rb_addr(fpga::RB_RADIO_CODEC_DATA)); //block until request completes
+ boost::this_thread::sleep(boost::posix_time::microseconds(100)); //wait for loopback to propagate through codec
+ const boost::uint64_t rb_word64 = iface->peek64(fpga::rb_addr(fpga::RB_RADIO_CODEC_DATA));
+ const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32);
+ const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff);
+ test_fail = word32 != rb_tx or word32 != rb_rx;
+ if (test_fail)
+ UHD_MSG(fastpath) << boost::format("mismatch (exp:%x, got:%x and %x)... ") % word32 % rb_tx % rb_rx;
+ break; //exit loop on any failure
+ }
+
+ /* Zero out the idle data. */
+ iface->poke32(fpga::sr_addr(fpga::SR_RADIO_CODEC_IDLE), 0);
+ return !test_fail;
+}
+
+std::string n230_resource_manager::_get_fpga_upgrade_msg() {
+ std::string img_loader_path =
+ (fs::path(uhd::get_pkg_path()) / "bin" / "uhd_image_loader").string();
+
+ return str(boost::format(
+ "\nDownload the appropriate FPGA images for this version of UHD.\n"
+ "%s\n\n"
+ "Then burn a new image to the on-board flash storage of your\n"
+ "USRP N230 device using the image loader utility. Use this command:\n"
+ "\n \"%s\" --args=\"type=n230,addr=%s\"\n")
+ % print_utility_error("uhd_images_downloader.py")
+ % img_loader_path % _get_conn(PRI_ETH).ip_addr);
+
+}
+
+void n230_resource_manager::_check_fw_compat()
+{
+ boost::uint32_t compat_num = _fw_ctrl->peek32(N230_FW_HOST_SHMEM_OFFSET(fw_compat_num));
+ _fw_version.compat_major = compat_num >> 16;
+ _fw_version.compat_minor = compat_num;
+ _fw_version.version_hash = _fw_ctrl->peek32(N230_FW_HOST_SHMEM_OFFSET(fw_version_hash));
+
+ if (_fw_version.compat_major != N230_FW_COMPAT_NUM_MAJOR){
+ throw uhd::runtime_error(str(boost::format(
+ "Expected firmware compatibility number %d.x, but got %d.%d\n"
+ "The firmware build is not compatible with the host code build.\n"
+ "%s"
+ ) % static_cast<boost::uint32_t>(N230_FW_COMPAT_NUM_MAJOR)
+ % static_cast<boost::uint32_t>(_fw_version.compat_major)
+ % static_cast<boost::uint32_t>(_fw_version.compat_minor)
+ % _get_fpga_upgrade_msg()));
+ }
+}
+
+void n230_resource_manager::_check_fpga_compat()
+{
+ const boost::uint64_t compat = _core_ctrl->peek64(fpga::rb_addr(fpga::RB_CORE_SIGNATUE));
+ const boost::uint32_t signature = boost::uint32_t(compat >> 32);
+ const boost::uint16_t product_id = boost::uint8_t(compat >> 24);
+ _fpga_version.compat_major = static_cast<boost::uint8_t>(compat >> 16);
+ _fpga_version.compat_minor = static_cast<boost::uint16_t>(compat);
+
+ const boost::uint64_t version_hash = _core_ctrl->peek64(fpga::rb_addr(fpga::RB_CORE_VERSION_HASH));
+ _fpga_version.version_hash = boost::uint32_t(version_hash);
+
+ if (signature != 0x0ACE0BA5E || product_id != fpga::RB_N230_PRODUCT_ID)
+ throw uhd::runtime_error("Signature check failed. Please contact support.");
+
+ bool is_safe_image = (_fpga_version.compat_major > fpga::RB_N230_COMPAT_SAFE);
+
+ if (is_safe_image && !_safe_mode) {
+ throw uhd::runtime_error(
+ "The device appears to have the failsafe FPGA image loaded\n"
+ "This could have happened because the production FPGA image in the flash was either corrupt or non-existent\n"
+ "To remedy this error, please burn a valid FPGA image to the flash.\n"
+ "To continue using the failsafe image with UHD, create the UHD device with the \"safe_mode\" device arg.\n"
+ "Radio functionality/performance not guaranteed when operating in safe mode.\n");
+ } else if (_fpga_version.compat_major != fpga::RB_N230_COMPAT_MAJOR && !is_safe_image) {
+ throw uhd::runtime_error(str(boost::format(
+ "Expected FPGA compatibility number %d.x, but got %d.%d:\n"
+ "The FPGA build is not compatible with the host code build.\n"
+ "%s"
+ ) % static_cast<boost::uint32_t>(fpga::RB_N230_COMPAT_MAJOR)
+ % static_cast<boost::uint32_t>(_fpga_version.compat_major)
+ % static_cast<boost::uint32_t>(_fpga_version.compat_minor)
+ % _get_fpga_upgrade_msg()));
+ }
+}
+
+}}} //namespace