1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
|
//
// Copyright 2012-2015 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
#ifndef INCLUDED_B200_IMPL_HPP
#define INCLUDED_B200_IMPL_HPP
#include "b200_cores.hpp"
#include "b200_iface.hpp"
#include "b200_radio_ctrl_core.hpp"
#include "b200_uart.hpp"
#include <uhd/device.hpp>
#include <uhd/property_tree.hpp>
#include <uhd/transport/bounded_buffer.hpp>
#include <uhd/transport/usb_zero_copy.hpp>
#include <uhd/types/dict.hpp>
#include <uhd/types/sensors.hpp>
#include <uhd/types/stream_cmd.hpp>
#include <uhd/usrp/gps_ctrl.hpp>
#include <uhd/usrp/mboard_eeprom.hpp>
#include <uhd/usrp/subdev_spec.hpp>
#include <uhd/utils/pimpl.hpp>
#include <uhd/utils/tasks.hpp>
#include <uhdlib/usrp/common/ad9361_ctrl.hpp>
#include <uhdlib/usrp/common/ad936x_manager.hpp>
#include <uhdlib/usrp/common/adf4001_ctrl.hpp>
#include <uhdlib/usrp/common/pwr_cal_mgr.hpp>
#include <uhdlib/usrp/common/recv_packet_demuxer_3000.hpp>
#include <uhdlib/usrp/cores/gpio_atr_3000.hpp>
#include <uhdlib/usrp/cores/rx_dsp_core_3000.hpp>
#include <uhdlib/usrp/cores/rx_vita_core_3000.hpp>
#include <uhdlib/usrp/cores/time_core_3000.hpp>
#include <uhdlib/usrp/cores/tx_dsp_core_3000.hpp>
#include <uhdlib/usrp/cores/tx_vita_core_3000.hpp>
#include <uhdlib/usrp/cores/user_settings_core_3000.hpp>
#include <unordered_map>
#include <boost/assign.hpp>
#include <memory>
#include <mutex>
static const uint8_t B200_FW_COMPAT_NUM_MAJOR = 8;
static const uint8_t B200_FW_COMPAT_NUM_MINOR = 0;
static const uint16_t B200_FPGA_COMPAT_NUM = 16;
static const uint16_t B205_FPGA_COMPAT_NUM = 7;
static const double B200_BUS_CLOCK_RATE = 100e6;
static const uint32_t B200_GPSDO_ST_NONE = 0x83;
static constexpr double B200_MAX_RATE_USB2 = 53248000; // bytes/s
static constexpr double B200_MAX_RATE_USB3 = 500000000; // bytes/s
#define FLIP_SID(sid) (((sid) << 16) | ((sid) >> 16))
static const uint32_t B200_CTRL0_MSG_SID = 0x00000010;
static const uint32_t B200_RESP0_MSG_SID = FLIP_SID(B200_CTRL0_MSG_SID);
static const uint32_t B200_CTRL1_MSG_SID = 0x00000020;
static const uint32_t B200_RESP1_MSG_SID = FLIP_SID(B200_CTRL1_MSG_SID);
static const uint32_t B200_TX_DATA0_SID = 0x00000050;
static const uint32_t B200_TX_MSG0_SID = FLIP_SID(B200_TX_DATA0_SID);
static const uint32_t B200_TX_DATA1_SID = 0x00000060;
static const uint32_t B200_TX_MSG1_SID = FLIP_SID(B200_TX_DATA1_SID);
static const uint32_t B200_RX_DATA0_SID = 0x000000A0;
static const uint32_t B200_RX_DATA1_SID = 0x000000B0;
static const uint32_t B200_TX_GPS_UART_SID = 0x00000030;
static const uint32_t B200_RX_GPS_UART_SID = FLIP_SID(B200_TX_GPS_UART_SID);
static const uint32_t B200_LOCAL_CTRL_SID = 0x00000040;
static const uint32_t B200_LOCAL_RESP_SID = FLIP_SID(B200_LOCAL_CTRL_SID);
static const unsigned char B200_USB_CTRL_RECV_INTERFACE = 4;
static const unsigned char B200_USB_CTRL_RECV_ENDPOINT = 8;
static const unsigned char B200_USB_CTRL_SEND_INTERFACE = 3;
static const unsigned char B200_USB_CTRL_SEND_ENDPOINT = 4;
static const unsigned char B200_USB_DATA_RECV_INTERFACE = 2;
static const unsigned char B200_USB_DATA_RECV_ENDPOINT = 6;
static const unsigned char B200_USB_DATA_SEND_INTERFACE = 1;
static const unsigned char B200_USB_DATA_SEND_ENDPOINT = 2;
// Default recv_frame_size. Must not be a multiple of 512.
static const int B200_USB_DATA_DEFAULT_FRAME_SIZE = 8176;
// recv_frame_size values below this will be upped to this value
static const int B200_USB_DATA_MIN_RECV_FRAME_SIZE = 40;
static const int B200_USB_DATA_MAX_RECV_FRAME_SIZE = 16360;
/*
* VID/PID pairs for all B2xx products
*/
static std::vector<uhd::transport::usb_device_handle::vid_pid_pair_t> b200_vid_pid_pairs =
boost::assign::list_of(uhd::transport::usb_device_handle::vid_pid_pair_t(
B200_VENDOR_ID, B200_PRODUCT_ID))(
uhd::transport::usb_device_handle::vid_pid_pair_t(
B200_VENDOR_ID, B200MINI_PRODUCT_ID))(
uhd::transport::usb_device_handle::vid_pid_pair_t(
B200_VENDOR_ID, B205MINI_PRODUCT_ID))(
uhd::transport::usb_device_handle::vid_pid_pair_t(
B200_VENDOR_NI_ID, B200_PRODUCT_NI_ID))(
uhd::transport::usb_device_handle::vid_pid_pair_t(
B200_VENDOR_NI_ID, B210_PRODUCT_NI_ID));
b200_product_t get_b200_product(const uhd::transport::usb_device_handle::sptr& handle,
const uhd::usrp::mboard_eeprom_t& mb_eeprom);
std::vector<uhd::transport::usb_device_handle::sptr> get_b200_device_handles(
const uhd::device_addr_t& hint);
//! Implementation guts
class b200_impl : public uhd::device
{
public:
// structors
b200_impl(const uhd::device_addr_t&, uhd::transport::usb_device_handle::sptr& handle);
~b200_impl(void) override;
// the io interface
uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t& args) override;
uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t& args) override;
bool recv_async_msg(uhd::async_metadata_t&, double) override;
//! Check that the combination of stream args and tick rate are valid.
//
// Basically figures out the arguments for enforce_tick_rate_limits()
// and calls said method. If arguments are invalid, throws a
// uhd::value_error.
void check_streamer_args(const uhd::stream_args_t& args,
double tick_rate,
const std::string& direction = "");
static uhd::usrp::mboard_eeprom_t get_mb_eeprom(uhd::i2c_iface::sptr);
private:
b200_product_t _product;
size_t _revision;
bool _gpsdo_capable;
//! This flag is true if the FPGA has custom (user) registers and access to
// those needs to be enabled from software.
const bool _enable_user_regs;
// controllers
b200_iface::sptr _iface;
b200_radio_ctrl_core::sptr _local_ctrl;
uhd::usrp::ad9361_ctrl::sptr _codec_ctrl;
uhd::usrp::ad936x_manager::sptr _codec_mgr;
b200_local_spi_core::sptr _spi_iface;
std::shared_ptr<uhd::usrp::adf4001_ctrl> _adf4001_iface;
uhd::gps_ctrl::sptr _gps;
// transports
uhd::transport::zero_copy_if::sptr _data_transport;
uhd::transport::zero_copy_if::sptr _ctrl_transport;
uhd::usrp::recv_packet_demuxer_3000::sptr _demux;
std::weak_ptr<uhd::rx_streamer> _rx_streamer;
std::weak_ptr<uhd::tx_streamer> _tx_streamer;
std::mutex _transport_setup_mutex;
// async ctrl + msgs
uhd::msg_task::sptr _async_task;
typedef uhd::transport::bounded_buffer<uhd::async_metadata_t> async_md_type;
struct AsyncTaskData
{
std::shared_ptr<async_md_type> async_md;
std::weak_ptr<b200_radio_ctrl_core> local_ctrl;
std::weak_ptr<b200_radio_ctrl_core> radio_ctrl[2];
b200_uart::sptr gpsdo_uart;
};
std::shared_ptr<AsyncTaskData> _async_task_data;
boost::optional<uhd::msg_task::msg_type_t> handle_async_task(
uhd::transport::zero_copy_if::sptr, std::shared_ptr<AsyncTaskData>);
void register_loopback_self_test(uhd::wb_iface::sptr iface);
void set_mb_eeprom(const uhd::usrp::mboard_eeprom_t&);
void check_fw_compat(void);
void check_fpga_compat(void);
uhd::usrp::subdev_spec_t coerce_subdev_spec(const uhd::usrp::subdev_spec_t&);
void update_subdev_spec(const std::string& tx_rx, const uhd::usrp::subdev_spec_t&);
void update_time_source(const std::string&);
void set_time(const uhd::time_spec_t&);
void set_time_next_pps(const uhd::time_spec_t&);
void sync_times(void);
void update_clock_source(const std::string&);
void update_bandsel(const std::string& which, double freq);
void reset_codec(void);
void update_antenna_sel(const size_t which, const std::string& ant);
uhd::sensor_value_t get_ref_locked(void);
uhd::sensor_value_t get_fe_pll_locked(const bool is_tx);
// perifs in the radio core
struct radio_perifs_t
{
b200_radio_ctrl_core::sptr ctrl;
uhd::usrp::gpio_atr::gpio_atr_3000::sptr atr;
uhd::usrp::gpio_atr::gpio_atr_3000::sptr fp_gpio;
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;
std::weak_ptr<uhd::rx_streamer> rx_streamer;
std::weak_ptr<uhd::tx_streamer> tx_streamer;
user_settings_core_3000::sptr user_settings;
bool ant_rx2;
std::unordered_map<std::string, uhd::usrp::pwr_cal_mgr::sptr> pwr_mgr;
};
std::vector<radio_perifs_t> _radio_perifs;
// mapping of AD936x frontends (FE1 and FE2) to radio perif index (0 and 1)
// FE1 corresponds to the ports labeled "RF B" on the B200/B210
// FE2 corresponds to the ports labeled "RF A" on the B200/B210
// the mapping is product and revision specific
size_t _fe1;
size_t _fe2;
/*! \brief Setup the DSP chain for one radio front-end.
*
*/
void setup_radio(const size_t radio_index);
void handle_overflow(const size_t radio_index);
struct gpio_state
{
uint32_t tx_bandsel_a, tx_bandsel_b, rx_bandsel_a, rx_bandsel_b, rx_bandsel_c,
codec_arst, mimo, ref_sel, swap_atr;
gpio_state()
{
tx_bandsel_a = 0;
tx_bandsel_b = 0;
rx_bandsel_a = 0;
rx_bandsel_b = 0;
rx_bandsel_c = 0;
codec_arst = 0;
mimo = 0;
ref_sel = 0;
swap_atr = 0;
}
} _gpio_state;
enum time_source_t {
GPSDO = 0,
EXTERNAL = 1,
INTERNAL = 2,
NONE = 3,
UNKNOWN = 4
} _time_source;
bool _time_set_with_pps;
void update_gpio_state(void);
void update_enables(void);
void update_atrs(void);
double _tick_rate;
double get_tick_rate(void)
{
return _tick_rate;
}
double set_tick_rate(const double rate);
/*! \brief Choose a tick rate (master clock rate) that works well for the given
* sampling rate.
*
* This function will try and choose a master clock rate automatically.
* See the function definition for details on the algorithm.
*
* The chosen tick rate is the largest multiple of two that is smaler
* than the max tick rate.
* The base rate is either given explicitly, or is the lcm() of the tx
* and rx sampling rates. In that case, it reads the rates directly
* from the property tree. It also tries to guess the number of channels
* (for the max possible tick rate) by checking the available streamers.
* This value, too, can explicitly be given.
*
* \param rate If this is given, it will be used as a minimum rate, or
* argument to lcm().
* \param tree_dsp_path The sampling rate from this property tree path
* will be ignored.
* \param num_chans If given, specifies the number of channels.
*/
void set_auto_tick_rate(const double rate = 0,
const uhd::fs_path& tree_dsp_path = "",
size_t num_chans = 0);
void update_tick_rate(const double);
/*! Subscriber to the tick_rate property, updates DDCs after tick rate change.
*/
void update_rx_dsp_tick_rate(
const double, rx_dsp_core_3000::sptr, uhd::fs_path rx_dsp_path);
/*! Subscriber to the tick_rate property, updates DUCs after tick rate change.
*/
void update_tx_dsp_tick_rate(
const double, tx_dsp_core_3000::sptr, uhd::fs_path tx_dsp_path);
/*! Check if \p tick_rate works with \p chan_count channels.
*
* Throws a uhd::value_error if not.
*/
void enforce_tick_rate_limits(
size_t chan_count, double tick_rate, const std::string& direction = "");
void check_tick_rate_with_current_streamers(double rate);
/*! Return the max number of channels on active rx_streamer or tx_streamer objects
* associated with this device.
*
* \param direction Set to "TX" to only check tx_streamers, "RX" to only check
* rx_streamers. Any other value will check if \e any active
* streamers are available.
* \return Return the number of tx streamers (direction=="TX"), the number of rx
* streamers (direction=="RX") or the total number of streamers.
*/
size_t max_chan_count(const std::string& direction = "");
//! Coercer, attached to the "rate/value" property on the rx dsps.
double coerce_rx_samp_rate(rx_dsp_core_3000::sptr, size_t, const double);
void update_rx_samp_rate(const size_t, const double);
//! Coercer, attached to the "rate/value" property on the tx dsps.
double coerce_tx_samp_rate(tx_dsp_core_3000::sptr, size_t, const double);
void update_tx_samp_rate(const size_t, const double);
};
#endif /* INCLUDED_B200_IMPL_HPP */
|