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
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
|
//
// Copyright 2013-2015 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/>.
//
#ifndef INCLUDED_X300_IMPL_HPP
#define INCLUDED_X300_IMPL_HPP
#include <uhd/property_tree.hpp>
#include <uhd/device.hpp>
#include <uhd/usrp/mboard_eeprom.hpp>
#include <uhd/usrp/dboard_manager.hpp>
#include <uhd/usrp/dboard_eeprom.hpp>
#include <uhd/usrp/subdev_spec.hpp>
#include <uhd/types/sensors.hpp>
#include "x300_clock_ctrl.hpp"
#include "x300_fw_common.h"
#include <uhd/transport/udp_simple.hpp> //mtu
#include <uhd/utils/tasks.hpp>
#include "spi_core_3000.hpp"
#include "x300_adc_ctrl.hpp"
#include "x300_dac_ctrl.hpp"
#include "rx_vita_core_3000.hpp"
#include "tx_vita_core_3000.hpp"
#include "time_core_3000.hpp"
#include "rx_dsp_core_3000.hpp"
#include "tx_dsp_core_3000.hpp"
#include "i2c_core_100_wb32.hpp"
#include "radio_ctrl_core_3000.hpp"
#include "rx_frontend_core_200.hpp"
#include "tx_frontend_core_200.hpp"
#include "gpio_core_200.hpp"
#include <boost/weak_ptr.hpp>
#include <uhd/usrp/gps_ctrl.hpp>
#include <uhd/usrp/mboard_eeprom.hpp>
#include <uhd/transport/bounded_buffer.hpp>
#include <uhd/transport/nirio/niusrprio_session.h>
#include <uhd/transport/vrt_if_packet.hpp>
#include "recv_packet_demuxer_3000.hpp"
#include "x300_regs.hpp"
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 = 166.666667e6; //Hz
static const size_t X300_TX_HW_BUFF_SIZE = 520*1024; //512K SRAM buffer + 8K 2Clk FIFO
static const size_t X300_TX_FC_RESPONSE_FREQ = 8; //per flow-control window
static const size_t X300_RX_SW_BUFF_SIZE_ETH = 0x2000000;//32MiB For an ~8k frame size any size >32MiB is just wasted buffer space
static const size_t X300_RX_SW_BUFF_SIZE_ETH_MACOS = 0x100000; //1Mib
static const double X300_RX_SW_BUFF_FULL_FACTOR = 0.90; //Buffer should ideally be 90% full.
static const size_t X300_RX_FC_REQUEST_FREQ = 32; //per flow-control window
//The FIFO closest to the DMA controller is 1023 elements deep for RX and 1029 elements deep for TX
//where an element is 8 bytes. For best throughput ensure that the data frame fits in these buffers.
//Also ensure that the kernel has enough frames to hold buffered TX and RX data
static const size_t X300_PCIE_RX_DATA_FRAME_SIZE = 8184; //bytes
static const size_t X300_PCIE_TX_DATA_FRAME_SIZE = 8192; //bytes
static const size_t X300_PCIE_DATA_NUM_FRAMES = 2048;
static const size_t X300_PCIE_MSG_FRAME_SIZE = 256; //bytes
static const size_t X300_PCIE_MSG_NUM_FRAMES = 64;
static const size_t X300_10GE_DATA_FRAME_MAX_SIZE = 8000; //bytes
static const size_t X300_1GE_DATA_FRAME_MAX_SIZE = 1472; //bytes
static const size_t X300_ETH_MSG_FRAME_SIZE = uhd::transport::udp_simple::mtu; //bytes
static const size_t X300_ETH_MSG_NUM_FRAMES = 64;
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
#define X300_XB_DST_E0 0
#define X300_XB_DST_E1 1
#define X300_XB_DST_R0 2 // Radio 0 -> Slot A
#define X300_XB_DST_R1 3 // Radio 1 -> Slot B
#define X300_XB_DST_CE0 4
#define X300_XB_DST_CE1 5
#define X300_XB_DST_CE2 5
#define X300_XB_DST_PCI 7
#define X300_DEVICE_THERE 2
#define X300_DEVICE_HERE 0
//eeprom addrs for various boards
enum
{
X300_DB0_RX_EEPROM = 0x5,
X300_DB0_TX_EEPROM = 0x4,
X300_DB0_GDB_EEPROM = 0x1,
X300_DB1_RX_EEPROM = 0x7,
X300_DB1_TX_EEPROM = 0x6,
X300_DB1_GDB_EEPROM = 0x3,
};
struct x300_dboard_iface_config_t
{
gpio_core_200::sptr gpio;
spi_core_3000::sptr spi;
size_t rx_spi_slaveno;
size_t tx_spi_slaveno;
i2c_core_100_wb32::sptr i2c;
x300_clock_ctrl::sptr clock;
x300_clock_which_t which_rx_clk;
x300_clock_which_t which_tx_clk;
boost::uint8_t dboard_slot;
uhd::timed_wb_iface::sptr cmd_time_ctrl;
};
uhd::usrp::dboard_iface::sptr x300_make_dboard_iface(const x300_dboard_iface_config_t &);
uhd::uart_iface::sptr x300_make_uart_iface(uhd::wb_iface::sptr iface);
uhd::wb_iface::sptr x300_make_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp);
uhd::wb_iface::sptr x300_make_ctrl_iface_pcie(uhd::niusrprio::niriok_proxy::sptr drv_proxy);
uhd::device_addrs_t x300_find(const uhd::device_addr_t &hint_);
class x300_impl : public uhd::device
{
public:
typedef uhd::transport::bounded_buffer<uhd::async_metadata_t> async_md_type;
x300_impl(const uhd::device_addr_t &);
void setup_mb(const size_t which, const uhd::device_addr_t &);
~x300_impl(void);
//the io interface
uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &);
uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &);
//support old async call
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 {
USRP_X300_MB, USRP_X310_MB, UNKNOWN
};
static x300_mboard_t get_mb_type_from_pcie(const std::string& resource, const std::string& rpc_port);
static x300_mboard_t get_mb_type_from_eeprom(const uhd::usrp::mboard_eeprom_t& mb_eeprom);
private:
boost::shared_ptr<async_md_type> _async_md;
//perifs in the radio core
struct radio_perifs_t
{
//Interfaces
radio_ctrl_core_3000::sptr ctrl;
spi_core_3000::sptr spi;
x300_adc_ctrl::sptr adc;
x300_dac_ctrl::sptr dac;
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;
gpio_core_200_32wo::sptr leds;
rx_frontend_core_200::sptr rx_fe;
tx_frontend_core_200::sptr tx_fe;
//Registers
uhd::usrp::x300::radio_regmap_t::sptr regmap;
};
//overflow recovery impl
void handle_overflow(radio_perifs_t &perif, boost::weak_ptr<uhd::rx_streamer> streamer);
//vector of member objects per motherboard
struct mboard_members_t
{
uhd::dict<size_t, boost::weak_ptr<uhd::rx_streamer> > rx_streamers;
uhd::dict<size_t, boost::weak_ptr<uhd::tx_streamer> > tx_streamers;
bool initialization_done;
uhd::task::sptr claimer_task;
std::string addr;
std::string xport_path;
int router_dst_here;
uhd::device_addr_t send_args;
uhd::device_addr_t recv_args;
bool if_pkt_is_big_endian;
uhd::niusrprio::niusrprio_session::sptr rio_fpga_interface;
//perifs in the zpu
uhd::wb_iface::sptr zpu_ctrl;
spi_core_3000::sptr zpu_spi;
i2c_core_100_wb32::sptr zpu_i2c;
//perifs in each radio
static const size_t NUM_RADIOS = 2;
radio_perifs_t radio_perifs[NUM_RADIOS]; //!< This is hardcoded s.t. radio_perifs[0] points to slot A and [1] to B
uhd::usrp::dboard_eeprom_t db_eeproms[8];
//! Return the index of a radio component, given a slot name. This means DSPs, radio_perifs
size_t get_radio_index(const std::string &slot_name) {
UHD_ASSERT_THROW(slot_name == "A" or slot_name == "B");
return slot_name == "A" ? 0 : 1;
}
//other perifs on mboard
x300_clock_ctrl::sptr clock;
uhd::gps_ctrl::sptr gps;
gpio_core_200::sptr fp_gpio;
uhd::usrp::x300::fw_regmap_t::sptr fw_regmap;
//which FPGA image is loaded
std::string loaded_fpga_image;
size_t hw_rev;
std::string current_refclk_src;
uhd::soft_regmap_db_t::sptr regmap_db;
};
std::vector<mboard_members_t> _mb;
//task for periodically reclaiming the device from others
void claimer_loop(uhd::wb_iface::sptr);
boost::mutex _transport_setup_mutex;
void register_loopback_self_test(uhd::wb_iface::sptr iface);
void radio_loopback(uhd::wb_iface::sptr iface, const bool on);
/*! \brief Initialize the radio component on a given slot.
*
* Call this function once per slot (A and B) and motherboard to initialize all the radio components.
* This will:
* - Reset and init DACs and ADCs
* - Setup controls for DAC, ADC, SPI and LEDs
* - Self test ADC
* - Sync DACs (for MIMO)
* - Initialize the property tree for control objects etc. (gain, rate...)
*
* \param mb_i Motherboard index
* \param slot_name Slot name (A or B).
*/
void setup_radio(const size_t, const std::string &slot_name, const uhd::device_addr_t &dev_addr);
size_t _sid_framer;
struct sid_config_t
{
boost::uint8_t router_addr_there;
boost::uint8_t dst_prefix; //2bits
boost::uint8_t router_dst_there;
boost::uint8_t router_dst_here;
};
boost::uint32_t allocate_sid(mboard_members_t &mb, const sid_config_t &config);
struct both_xports_t
{
uhd::transport::zero_copy_if::sptr recv;
uhd::transport::zero_copy_if::sptr send;
size_t recv_buff_size;
size_t send_buff_size;
};
both_xports_t make_transport(
const size_t mb_index,
const boost::uint8_t& destination,
const boost::uint8_t& prefix,
const uhd::device_addr_t& args,
boost::uint32_t& sid);
struct frame_size_t
{
size_t recv_frame_size;
size_t send_frame_size;
};
frame_size_t _max_frame_sizes;
/*!
* Automatically determine the maximum frame size available by sending a UDP packet
* to the device and see which packet sizes actually work. This way, we can take
* switches etc. into account which might live between the device and the host.
*/
frame_size_t determine_max_frame_size(const std::string &addr, const frame_size_t &user_mtu);
////////////////////////////////////////////////////////////////////
//
//Caching for transport interface re-use -- like sharing a DMA.
//The cache is optionally used by make_transport by use-case.
//The cache maps an ID string to a transport-ish object.
//The ID string identifies a purpose for the transport.
//
//For recv, there is a demux cache, which maps a ID string
//to a recv demux object. When a demux is used, the underlying transport
//must never be used outside of the demux. Use demux->make_proxy(sid).
//
uhd::dict<std::string, uhd::usrp::recv_packet_demuxer_3000::sptr> _demux_cache;
//
//For send, there is a shared send xport, which maps an ID string
//to a transport capable of sending buffers. Send transports
//can be shared amongst multiple callers, unlike recv.
//
uhd::dict<std::string, uhd::transport::zero_copy_if::sptr> _send_cache;
//
////////////////////////////////////////////////////////////////////
uhd::dict<std::string, uhd::usrp::dboard_manager::sptr> _dboard_managers;
uhd::dict<std::string, uhd::usrp::dboard_iface::sptr> _dboard_ifaces;
void set_rx_fe_corrections(const uhd::fs_path &mb_path, const std::string &fe_name, const double lo_freq);
void set_tx_fe_corrections(const uhd::fs_path &mb_path, const std::string &fe_name, const double lo_freq);
bool _ignore_cal_file;
/*! Update the IQ MUX settings for the radio peripheral according to given subdev spec.
*
* Also checks if the given subdev is valid for this device and updates the channel to DSP mapping.
*
* \param tx_rx "tx" or "rx", depending where you're setting the subdev spec
* \param mb_i Mainboard index number.
* \param spec Subdev spec
*/
void update_subdev_spec(const std::string &tx_rx, const size_t mb_i, const uhd::usrp::subdev_spec_t &spec);
void set_tick_rate(mboard_members_t &, const double);
void update_tick_rate(mboard_members_t &, const double);
void update_rx_samp_rate(mboard_members_t&, const size_t, const double);
void update_tx_samp_rate(mboard_members_t&, const size_t, const double);
void update_clock_control(mboard_members_t&);
void initialize_clock_control(mboard_members_t &mb);
void set_time_source_out(mboard_members_t&, const bool);
void update_clock_source(mboard_members_t&, const std::string &);
void update_time_source(mboard_members_t&, const std::string &);
void sync_times(mboard_members_t&, const uhd::time_spec_t&);
uhd::sensor_value_t get_ref_locked(mboard_members_t& mb);
bool wait_for_clk_locked(mboard_members_t& mb, boost::uint32_t which, double timeout);
bool is_pps_present(mboard_members_t& mb);
void set_db_eeprom(uhd::i2c_iface::sptr i2c, const size_t, const uhd::usrp::dboard_eeprom_t &);
void set_mb_eeprom(uhd::i2c_iface::sptr i2c, const uhd::usrp::mboard_eeprom_t &);
void check_fw_compat(const uhd::fs_path &mb_path, uhd::wb_iface::sptr iface);
void check_fpga_compat(const uhd::fs_path &mb_path, const mboard_members_t &members);
void update_atr_leds(gpio_core_200_32wo::sptr, const std::string &ant);
boost::uint32_t get_fp_gpio(gpio_core_200::sptr);
void set_fp_gpio(gpio_core_200::sptr, const gpio_attr_t, const boost::uint32_t);
void self_cal_adc_capture_delay(mboard_members_t& mb, const size_t radio_i, bool print_status = false);
double self_cal_adc_xfer_delay(mboard_members_t& mb, bool apply_delay = false);
void self_test_adcs(mboard_members_t& mb, boost::uint32_t ramp_time_ms = 100);
void extended_adc_test(mboard_members_t& mb, double duration_s);
//**PRECONDITION**
//This function assumes that all the VITA times in "radios" are synchronized
//to a common reference. Currently, this function is called in get_tx_stream
//which also has the same precondition.
static void synchronize_dacs(const std::vector<radio_perifs_t*>& mboards);
};
#endif /* INCLUDED_X300_IMPL_HPP */
|