aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/x300/x300_impl.hpp
blob: df6af5521481ee90288e7fdecb2a22e3972c2cef (plain)
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
//
// Copyright 2013-2016 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: GPL-3.0-or-later
//

#ifndef INCLUDED_X300_IMPL_HPP
#define INCLUDED_X300_IMPL_HPP

#include "x300_radio_ctrl_impl.hpp"
#include "x300_clock_ctrl.hpp"
#include "x300_fw_common.h"
#include "x300_regs.hpp"

#include "../device3/device3_impl.hpp"
#include <uhd/property_tree.hpp>
#include <uhd/usrp/mboard_eeprom.hpp>
#include <uhd/usrp/subdev_spec.hpp>
#include <uhd/types/sensors.hpp>
#include <uhd/transport/udp_simple.hpp> //mtu
#include <uhd/usrp/gps_ctrl.hpp>
#include <uhd/transport/nirio/niusrprio_session.h>
#include <uhd/transport/vrt_if_packet.hpp>
#include <uhd/transport/muxed_zero_copy_if.hpp>
///////////// RFNOC /////////////////////
#include <uhd/rfnoc/block_ctrl.hpp>
///////////// RFNOC /////////////////////

#include <uhdlib/usrp/cores/i2c_core_100_wb32.hpp>
#include <uhdlib/usrp/common/recv_packet_demuxer_3000.hpp>
#include <boost/dynamic_bitset.hpp>
#include <boost/weak_ptr.hpp>
#include <atomic>

static const std::string X300_FW_FILE_NAME  = "usrp_x300_fw.bin";
static const std::string X300_DEFAULT_CLOCK_SOURCE  = "internal";

static const double X300_DEFAULT_TICK_RATE          = 200e6;   //Hz
static const double X300_DEFAULT_DBOARD_CLK_RATE    = 50e6;    //Hz
static const double X300_BUS_CLOCK_RATE             = 187.5e6; //Hz

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

//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. The buffers (number of frames * frame size) must be aligned to the
//memory page size.  For the control, we are getting lucky because 64 frames * 256 bytes each aligns
//with the typical page size of 4096 bytes.  Since most page sizes are 4096 bytes or some multiple of
//that, keep the number of frames * frame size aligned to it.
static const size_t X300_PCIE_RX_DATA_FRAME_SIZE        = 4096;     //bytes
static const size_t X300_PCIE_RX_DATA_NUM_FRAMES        = 4096;
static const size_t X300_PCIE_TX_DATA_FRAME_SIZE        = 4096;     //bytes
static const size_t X300_PCIE_TX_DATA_NUM_FRAMES	    = 4096;
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_PCIE_MAX_CHANNELS              = 6;
static const size_t X300_PCIE_MAX_MUXED_CTRL_XPORTS     = 32;
static const size_t X300_PCIE_MAX_MUXED_ASYNC_XPORTS    = 4;

static const size_t X300_10GE_DATA_FRAME_MAX_SIZE   = 8000;     // CHDR packet size in bytes
static const size_t X300_1GE_DATA_FRAME_MAX_SIZE    = 1472;     // CHDR packet size in bytes
static const size_t X300_ETH_MSG_FRAME_SIZE         = uhd::transport::udp_simple::mtu;  //bytes
// MTU throttling for ethernet/TX (see above):
static const size_t X300_ETH_DATA_FRAME_MAX_TX_SIZE = 8000;

static const double X300_THREAD_BUFFER_TIMEOUT      = 0.1;   // Time in seconds

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;

// Limit the number of initialization threads
static const size_t X300_MAX_INIT_THREADS           = 10;

static const size_t X300_MAX_RATE_PCIE              = 800000000; // bytes/s
static const size_t X300_MAX_RATE_10GIGE            = (size_t)(  // bytes/s
        10e9 / 8 *                                               // wire speed multiplied by percentage of packets that is sample data
        ( float(X300_10GE_DATA_FRAME_MAX_SIZE - uhd::usrp::DEVICE3_TX_MAX_HDR_LEN) /
          float(X300_10GE_DATA_FRAME_MAX_SIZE + 8 /* UDP header */ + 20 /* Ethernet header length */ )));
static const size_t X300_MAX_RATE_1GIGE            = (size_t)(  // bytes/s
        10e9 / 8 *                                               // wire speed multiplied by percentage of packets that is sample data
        ( float(X300_1GE_DATA_FRAME_MAX_SIZE - uhd::usrp::DEVICE3_TX_MAX_HDR_LEN) /
          float(X300_1GE_DATA_FRAME_MAX_SIZE + 8 /* UDP header */ + 20 /* Ethernet header length */ )));

#define X300_RADIO_DEST_PREFIX_TX 0

#define X300_XB_DST_E0  0
#define X300_XB_DST_E1  1
#define X300_XB_DST_PCI 2
#define X300_XB_DST_R0  3 // Radio 0 -> Slot A
#define X300_XB_DST_R1  4 // Radio 1 -> Slot B
#define X300_XB_DST_CE0 5

#define X300_SRC_ADDR0  0
#define X300_SRC_ADDR1  1
#define X300_DST_ADDR   2

// Ethernet ports
enum x300_eth_iface_t
{
    X300_IFACE_NONE = 0,
    X300_IFACE_ETH0 = 1,
    X300_IFACE_ETH1 = 2,
};

struct x300_eth_conn_t
{
    std::string addr;
    x300_eth_iface_t type;
};


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, bool enable_errors = true);
uhd::wb_iface::sptr x300_make_ctrl_iface_pcie(uhd::niusrprio::niriok_proxy::sptr drv_proxy, bool enable_errors = true);

uhd::device_addrs_t x300_find(const uhd::device_addr_t &hint_);

class x300_impl : public uhd::usrp::device3_impl
{
public:

    x300_impl(const uhd::device_addr_t &);
    void setup_mb(const size_t which, const uhd::device_addr_t &);
    ~x300_impl(void);

    // device claim functions
    enum claim_status_t {UNCLAIMED, CLAIMED_BY_US, CLAIMED_BY_OTHER};
    static claim_status_t claim_status(uhd::wb_iface::sptr iface);
    static void claim(uhd::wb_iface::sptr iface);
    static bool try_to_claim(uhd::wb_iface::sptr iface, long timeout = 2000);
    static void release(uhd::wb_iface::sptr iface);

    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);

    //! Read out the on-board EEPROM, convert to dict, and return
    static uhd::usrp::mboard_eeprom_t get_mb_eeprom(uhd::i2c_iface::sptr i2c);

protected:
    void subdev_to_blockid(
            const uhd::usrp::subdev_spec_pair_t &spec, const size_t mb_i,
            uhd::rfnoc::block_id_t &block_id, uhd::device_addr_t &block_args
    );
    uhd::usrp::subdev_spec_pair_t blockid_to_subdev(
            const uhd::rfnoc::block_id_t &blockid, const uhd::device_addr_t &block_args
    );

private:

    //vector of member objects per motherboard
    struct mboard_members_t
    {
        bool initialization_done;
        uhd::task::sptr claimer_task;
        std::string xport_path;

        std::vector<x300_eth_conn_t> eth_conns;
        size_t next_src_addr;
        size_t next_tx_src_addr;
        size_t next_rx_src_addr;

        // Discover the ethernet connections per motherboard
        void discover_eth(const uhd::usrp::mboard_eeprom_t mb_eeprom,
                          const std::vector<std::string> &ip_addrs);

        // Get the primary ethernet connection
        inline const x300_eth_conn_t& get_pri_eth() const
        {
            return eth_conns[0];
        }

        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;

        //other perifs on mboard
        x300_clock_ctrl::sptr clock;
        uhd::gps_ctrl::sptr gps;

        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;

        std::vector<uhd::rfnoc::x300_radio_ctrl_impl::sptr> radios;

        // PCIe specific components:

        //! Maps SID -> DMA channel
        std::map<uint32_t, uint32_t> _dma_chan_pool;
        //! Control transport for one PCIe connection
        uhd::transport::muxed_zero_copy_if::sptr ctrl_dma_xport;
        //! Async message transport
        uhd::transport::muxed_zero_copy_if::sptr async_msg_dma_xport;

        /*! Allocate or return a previously allocated PCIe channel pair
         *
         * Note the SID is always the transmit SID (i.e. from host to device).
         */
        uint32_t allocate_pcie_dma_chan(const uhd::sid_t &tx_sid, const xport_type_t xport_type);
    };
    std::vector<mboard_members_t> _mb;

    //task for periodically reclaiming the device from others
    void claimer_loop(uhd::wb_iface::sptr);

    std::atomic<size_t> _sid_framer;

    uhd::sid_t allocate_sid(
        mboard_members_t &mb,
        const uhd::sid_t &address,
        const uint32_t src_addr,
        const uint32_t src_dst);
    uhd::both_xports_t make_transport(
        const uhd::sid_t &address,
        const xport_type_t xport_type,
        const uhd::device_addr_t& args
    );

    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;

    bool _ignore_cal_file;

    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, uint32_t which, double timeout);
    bool is_pps_present(mboard_members_t& mb);

    //! Write the contents of an EEPROM dict to the on-board EEPROM
    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);

    /// More IO stuff
    uhd::device_addr_t get_tx_hints(size_t mb_index);
    uhd::device_addr_t get_rx_hints(size_t mb_index);

    void post_streamer_hooks(uhd::direction_t dir);
};

#endif /* INCLUDED_X300_IMPL_HPP */
// vim: sw=4 expandtab: