diff options
Diffstat (limited to 'host/lib/usrp/x300')
-rw-r--r-- | host/lib/usrp/x300/x300_fw_common.h | 2 | ||||
-rw-r--r-- | host/lib/usrp/x300/x300_impl.cpp | 66 | ||||
-rw-r--r-- | host/lib/usrp/x300/x300_impl.hpp | 16 | ||||
-rw-r--r-- | host/lib/usrp/x300/x300_io_impl.cpp | 59 | ||||
-rw-r--r-- | host/lib/usrp/x300/x300_regs.hpp | 10 |
5 files changed, 107 insertions, 46 deletions
diff --git a/host/lib/usrp/x300/x300_fw_common.h b/host/lib/usrp/x300/x300_fw_common.h index 632391644..0bbaee319 100644 --- a/host/lib/usrp/x300/x300_fw_common.h +++ b/host/lib/usrp/x300/x300_fw_common.h @@ -31,7 +31,7 @@ extern "C" { #define X300_FW_COMPAT_MAJOR 3 #define X300_FW_COMPAT_MINOR 0 -#define X300_FPGA_COMPAT_MAJOR 4 +#define X300_FPGA_COMPAT_MAJOR 6 //shared memory sections - in between the stack and the program space #define X300_FW_SHMEM_BASE 0x6000 diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index e492b2238..e931b7983 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -135,6 +135,12 @@ static device_addrs_t x300_find_with_addr(const device_addr_t &hint) return addrs; } +//We need a zpu xport registry to ensure synchronization between the static finder method +//and the instances of the x300_impl class. +typedef uhd::dict< std::string, boost::weak_ptr<wb_iface> > pcie_zpu_iface_registry_t; +UHD_SINGLETON_FCN(pcie_zpu_iface_registry_t, get_pcie_zpu_iface_registry) +static boost::mutex pcie_zpu_iface_registry_mutex; + static device_addrs_t x300_find_pcie(const device_addr_t &hint, bool explicit_query) { std::string rpc_port_name(NIUSRPRIO_DEFAULT_RPC_PORT); @@ -167,17 +173,30 @@ static device_addrs_t x300_find_pcie(const device_addr_t &hint, bool explicit_qu } 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 + //This block 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); + + wb_iface::sptr zpu_ctrl; + + //Hold on to the registry mutex as long as zpu_ctrl is alive + //to prevent any use by different threads while enumerating + boost::mutex::scoped_lock(pcie_zpu_iface_registry_mutex); + + if (get_pcie_zpu_iface_registry().has_key(resource_d)) { + zpu_ctrl = get_pcie_zpu_iface_registry()[resource_d].lock(); + } else { + kernel_proxy.open(dev_info.interface_path); + zpu_ctrl = x300_make_ctrl_iface_pcie(kernel_proxy); + //We don't put this zpu_ctrl in the registry because we need + //a persistent niriok_proxy associated with the object + } if (x300_impl::is_claimed(zpu_ctrl)) continue; //claimed by another process //Attempt to autodetect the FPGA type @@ -392,6 +411,8 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) //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); + + _tree->create<double>(mb_path / "link_max_rate").set(X300_MAX_RATE_PCIE); } BOOST_FOREACH(const std::string &key, dev_addr.keys()) @@ -456,12 +477,20 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) << "UHD will use the auto-detected max frame size for this connection." << std::endl; } + + _tree->create<double>(mb_path / "link_max_rate").set(X300_MAX_RATE_10GIGE); } //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()); + boost::mutex::scoped_lock(pcie_zpu_iface_registry_mutex); + if (get_pcie_zpu_iface_registry().has_key(mb.addr)) { + throw uhd::assertion_error("Someone else has a ZPU transport to the device open. Internal error!"); + } else { + mb.zpu_ctrl = x300_make_ctrl_iface_pcie(mb.rio_fpga_interface->get_kernel_proxy()); + get_pcie_zpu_iface_registry()[mb.addr] = boost::weak_ptr<wb_iface>(mb.zpu_ctrl); + } } else { mb.zpu_ctrl = x300_make_ctrl_iface_enet(udp_simple::make_connected(mb.addr, BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT))); @@ -830,8 +859,14 @@ x300_impl::~x300_impl(void) //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); + { //Critical section + boost::mutex::scoped_lock(pcie_zpu_iface_registry_mutex); + 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); + //If the process is killed, the entire registry will disappear so we + //don't need to worry about unclean shutdowns here. + get_pcie_zpu_iface_registry().pop(mb.addr); + } } } catch(...) @@ -1133,11 +1168,14 @@ x300_impl::both_xports_t x300_impl::make_transport( if (mb.loaded_fpga_image == "HGS") { if (mb.router_dst_here == X300_XB_DST_E0) { eth_data_rec_frame_size = X300_1GE_DATA_FRAME_MAX_SIZE; + _tree->access<double>("/mboards/"+boost::lexical_cast<std::string>(mb_index) / "link_max_rate").set(X300_MAX_RATE_1GIGE); } else if (mb.router_dst_here == X300_XB_DST_E1) { eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; + _tree->access<double>("/mboards/"+boost::lexical_cast<std::string>(mb_index) / "link_max_rate").set(X300_MAX_RATE_10GIGE); } } else if (mb.loaded_fpga_image == "XGS") { - eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; + eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; + _tree->access<double>("/mboards/"+boost::lexical_cast<std::string>(mb_index) / "link_max_rate").set(X300_MAX_RATE_10GIGE); } if (eth_data_rec_frame_size == 0) { @@ -1380,7 +1418,8 @@ void x300_impl::update_time_source(mboard_members_t &mb, const std::string &sour //check for valid pps if (!is_pps_present(mb.zpu_ctrl)) { - throw uhd::runtime_error((boost::format("The %d PPS was not detected. Please check the PPS source and try again.") % source).str()); + // TODO - Implement intelligent PPS detection + /* throw uhd::runtime_error((boost::format("The %d PPS was not detected. Please check the PPS source and try again.") % source).str()); */ } } @@ -1453,13 +1492,18 @@ void x300_impl::set_fp_gpio(gpio_core_200::sptr gpio, const std::string &attr, c 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 + { //Critical section + boost::mutex::scoped_lock(claimer_mutex); + 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(1000)); //1 second } bool x300_impl::is_claimed(wb_iface::sptr iface) { + boost::mutex::scoped_lock(claimer_mutex); + //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; diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index 259ea253d..90aed2fdb 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -53,7 +53,7 @@ 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 double X300_BUS_CLOCK_RATE = 166.666667e6; //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 @@ -76,6 +76,19 @@ static const size_t X300_ETH_MSG_NUM_FRAMES = 32; 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 @@ -139,6 +152,7 @@ public: 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 { diff --git a/host/lib/usrp/x300/x300_io_impl.cpp b/host/lib/usrp/x300/x300_io_impl.cpp index 85de34a54..9263c9b44 100644 --- a/host/lib/usrp/x300/x300_io_impl.cpp +++ b/host/lib/usrp/x300/x300_io_impl.cpp @@ -242,6 +242,8 @@ struct x300_tx_fc_guts_t boost::shared_ptr<x300_impl::async_md_type> old_async_queue; }; +#define X300_ASYNC_EVENT_CODE_FLOW_CTRL 0 + 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); @@ -283,23 +285,28 @@ static void handle_tx_async_msgs(boost::shared_ptr<x300_tx_fc_guts_t> guts, zero 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); + + //The FC response and the burst ack are two indicators that the radio + //consumed packets. Use them to update the FC metadata + if (metadata.event_code == X300_ASYNC_EVENT_CODE_FLOW_CTRL or + metadata.event_code == async_metadata_t::EVENT_CODE_BURST_ACK + ) { + const size_t seq = metadata.user_payload[0]; + guts->seq_queue.push_with_pop_on_full(seq); + } + + //FC responses don't propagate up to the user so filter them here + if (metadata.event_code != X300_ASYNC_EVENT_CODE_FLOW_CTRL) { + 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( @@ -319,7 +326,9 @@ static managed_send_buffer::sptr get_tx_buff_with_flowctrl( } managed_send_buffer::sptr buff = xport->get_send_buff(timeout); - if (buff) guts->last_seq_out++; //update seq, this will actually be a send + if (buff) { + guts->last_seq_out++; //update seq, this will actually be a send + } return buff; } @@ -399,15 +408,9 @@ rx_streamer::sptr x300_impl::get_rx_stream(const uhd::stream_args_t &args_) 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; // bytes per packet + // To calculate the max number of samples per packet, we assume the maximum header length + // to avoid fragmentation should the entire header be used. + const size_t bpp = xport.recv->get_recv_frame_size() - X300_RX_MAX_HDR_LEN; // bytes per packet const size_t bpi = convert::get_bytes_per_item(args.otw_format); // bytes per item const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi)); // samples per packet @@ -568,15 +571,9 @@ tx_streamer::sptr x300_impl::get_tx_stream(const uhd::stream_args_t &args_) 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; + // To calculate the max number of samples per packet, we assume the maximum header length + // to avoid fragmentation should the entire header be used. + const size_t bpp = xport.send->get_send_frame_size() - X300_TX_MAX_HDR_LEN; const size_t bpi = convert::get_bytes_per_item(args.otw_format); const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi)); diff --git a/host/lib/usrp/x300/x300_regs.hpp b/host/lib/usrp/x300/x300_regs.hpp index fb1786deb..cf1e33695 100644 --- a/host/lib/usrp/x300/x300_regs.hpp +++ b/host/lib/usrp/x300/x300_regs.hpp @@ -124,9 +124,12 @@ 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_STATUS_REG = PCIE_FPGA_REG(0x0020); 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 FPGA_STATUS_DMA_ACTIVE_MASK = 0x3F3F0000; + 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); @@ -139,12 +142,15 @@ 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_DISABLED = 0x00000000; +static const uint32_t DMA_CTRL_ENABLED = 0x00000002; +static const uint32_t DMA_CTRL_CLEAR_STB = 0x00000001; 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 DMA_STATUS_ERROR = 0x00000001; +static const uint32_t DMA_STATUS_BUSY = 0x00000002; static const uint32_t PCIE_ROUTER_REG_BASE = PCIE_FPGA_REG(0x0500); #define PCIE_ROUTER_REG(X) (PCIE_ROUTER_REG_BASE + X) |