Statistics
| Branch: | Tag: | Revision:

root / host / lib / usrp / usrp2 / usrp2_impl.cpp @ 4bcab9c5

History | View | Annotate | Download (32.1 KB)

1
//
2
// Copyright 2010-2011 Ettus Research LLC
3
//
4
// This program is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
//
17

    
18
#include "usrp2_impl.hpp"
19
#include "fw_common.h"
20
#include <uhd/utils/log.hpp>
21
#include <uhd/utils/msg.hpp>
22
#include <uhd/exception.hpp>
23
#include <uhd/transport/if_addrs.hpp>
24
#include <uhd/transport/udp_zero_copy.hpp>
25
#include <uhd/types/ranges.hpp>
26
#include <uhd/exception.hpp>
27
#include <uhd/utils/static.hpp>
28
#include <uhd/utils/byteswap.hpp>
29
#include <uhd/utils/safe_call.hpp>
30
#include <boost/format.hpp>
31
#include <boost/foreach.hpp>
32
#include <boost/lexical_cast.hpp>
33
#include <boost/bind.hpp>
34
#include <boost/assign/list_of.hpp>
35
#include <boost/asio/ip/address_v4.hpp>
36
#include <boost/asio.hpp> //used for htonl and ntohl
37

    
38
using namespace uhd;
39
using namespace uhd::usrp;
40
using namespace uhd::transport;
41
namespace asio = boost::asio;
42

    
43
/***********************************************************************
44
 * Discovery over the udp transport
45
 **********************************************************************/
46
static device_addrs_t usrp2_find(const device_addr_t &hint_){
47
    //handle the multi-device discovery
48
    device_addrs_t hints = separate_device_addr(hint_);
49
    if (hints.size() > 1){
50
        device_addrs_t found_devices;
51
        BOOST_FOREACH(const device_addr_t &hint_i, hints){
52
            device_addrs_t found_devices_i = usrp2_find(hint_i);
53
            if (found_devices_i.size() != 1) throw uhd::value_error(str(boost::format(
54
                "Could not resolve device hint \"%s\" to a single device."
55
            ) % hint_i.to_string()));
56
            found_devices.push_back(found_devices_i[0]);
57
        }
58
        return device_addrs_t(1, combine_device_addrs(found_devices));
59
    }
60

    
61
    //initialize the hint for a single device case
62
    UHD_ASSERT_THROW(hints.size() <= 1);
63
    hints.resize(1); //in case it was empty
64
    device_addr_t hint = hints[0];
65
    device_addrs_t usrp2_addrs;
66

    
67
    //return an empty list of addresses when type is set to non-usrp2
68
    if (hint.has_key("type") and hint["type"] != "usrp2") return usrp2_addrs;
69

    
70
    //if no address was specified, send a broadcast on each interface
71
    if (not hint.has_key("addr")){
72
        BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()){
73
            //avoid the loopback device
74
            if (if_addrs.inet == asio::ip::address_v4::loopback().to_string()) continue;
75

    
76
            //create a new hint with this broadcast address
77
            device_addr_t new_hint = hint;
78
            new_hint["addr"] = if_addrs.bcast;
79

    
80
            //call discover with the new hint and append results
81
            device_addrs_t new_usrp2_addrs = usrp2_find(new_hint);
82
            usrp2_addrs.insert(usrp2_addrs.begin(),
83
                new_usrp2_addrs.begin(), new_usrp2_addrs.end()
84
            );
85
        }
86
        return usrp2_addrs;
87
    }
88

    
89
    //create a udp transport to communicate
90
    std::string ctrl_port = boost::lexical_cast<std::string>(USRP2_UDP_CTRL_PORT);
91
    udp_simple::sptr udp_transport = udp_simple::make_broadcast(
92
        hint["addr"], ctrl_port
93
    );
94

    
95
    //send a hello control packet
96
    usrp2_ctrl_data_t ctrl_data_out = usrp2_ctrl_data_t();
97
    ctrl_data_out.proto_ver = uhd::htonx<boost::uint32_t>(USRP2_FW_COMPAT_NUM);
98
    ctrl_data_out.id = uhd::htonx<boost::uint32_t>(USRP2_CTRL_ID_WAZZUP_BRO);
99
    udp_transport->send(boost::asio::buffer(&ctrl_data_out, sizeof(ctrl_data_out)));
100

    
101
    //loop and recieve until the timeout
102
    boost::uint8_t usrp2_ctrl_data_in_mem[udp_simple::mtu]; //allocate max bytes for recv
103
    const usrp2_ctrl_data_t *ctrl_data_in = reinterpret_cast<const usrp2_ctrl_data_t *>(usrp2_ctrl_data_in_mem);
104
    while(true){
105
        size_t len = udp_transport->recv(asio::buffer(usrp2_ctrl_data_in_mem));
106
        if (len > offsetof(usrp2_ctrl_data_t, data) and ntohl(ctrl_data_in->id) == USRP2_CTRL_ID_WAZZUP_DUDE){
107

    
108
            //make a boost asio ipv4 with the raw addr in host byte order
109
            boost::asio::ip::address_v4 ip_addr(ntohl(ctrl_data_in->data.ip_addr));
110
            device_addr_t new_addr;
111
            new_addr["type"] = "usrp2";
112
            new_addr["addr"] = ip_addr.to_string();
113

    
114
            //Attempt to read the name from the EEPROM and perform filtering.
115
            //This operation can throw due to compatibility mismatch.
116
            try{
117
                usrp2_iface::sptr iface = usrp2_iface::make(udp_simple::make_connected(
118
                    new_addr["addr"], BOOST_STRINGIZE(USRP2_UDP_CTRL_PORT)
119
                ));
120
                if (iface->is_device_locked()) continue; //ignore locked devices
121
                mboard_eeprom_t mb_eeprom = iface->mb_eeprom;
122
                new_addr["name"] = mb_eeprom["name"];
123
                new_addr["serial"] = mb_eeprom["serial"];
124
            }
125
            catch(const std::exception &){
126
                //set these values as empty string so the device may still be found
127
                //and the filter's below can still operate on the discovered device
128
                new_addr["name"] = "";
129
                new_addr["serial"] = "";
130
            }
131

    
132
            //filter the discovered device below by matching optional keys
133
            if (
134
                (not hint.has_key("name")   or hint["name"]   == new_addr["name"]) and
135
                (not hint.has_key("serial") or hint["serial"] == new_addr["serial"])
136
            ){
137
                usrp2_addrs.push_back(new_addr);
138
            }
139

    
140
            //dont break here, it will exit the while loop
141
            //just continue on to the next loop iteration
142
        }
143
        if (len == 0) break; //timeout
144
    }
145

    
146
    return usrp2_addrs;
147
}
148

    
149
/***********************************************************************
150
 * Make
151
 **********************************************************************/
152
static device::sptr usrp2_make(const device_addr_t &device_addr){
153
    return device::sptr(new usrp2_impl(device_addr));
154
}
155

    
156
UHD_STATIC_BLOCK(register_usrp2_device){
157
    device::register_device(&usrp2_find, &usrp2_make);
158
}
159

    
160
/***********************************************************************
161
 * MTU Discovery
162
 **********************************************************************/
163
struct mtu_result_t{
164
    size_t recv_mtu, send_mtu;
165
};
166

    
167
static mtu_result_t determine_mtu(const std::string &addr, const mtu_result_t &user_mtu){
168
    udp_simple::sptr udp_sock = udp_simple::make_connected(
169
        addr, BOOST_STRINGIZE(USRP2_UDP_CTRL_PORT)
170
    );
171

    
172
    //The FPGA offers 4K buffers, and the user may manually request this.
173
    //However, multiple simultaneous receives (2DSP slave + 2DSP master),
174
    //require that buffering to be used internally, and this is a safe setting.
175
    std::vector<boost::uint8_t> buffer(std::max(user_mtu.recv_mtu, user_mtu.send_mtu));
176
    usrp2_ctrl_data_t *ctrl_data = reinterpret_cast<usrp2_ctrl_data_t *>(&buffer.front());
177
    static const double echo_timeout = 0.020; //20 ms
178

    
179
    //test holler - check if its supported in this fw version
180
    ctrl_data->id = htonl(USRP2_CTRL_ID_HOLLER_AT_ME_BRO);
181
    ctrl_data->proto_ver = htonl(USRP2_FW_COMPAT_NUM);
182
    ctrl_data->data.echo_args.len = htonl(sizeof(usrp2_ctrl_data_t));
183
    udp_sock->send(boost::asio::buffer(buffer, sizeof(usrp2_ctrl_data_t)));
184
    udp_sock->recv(boost::asio::buffer(buffer), echo_timeout);
185
    if (ntohl(ctrl_data->id) != USRP2_CTRL_ID_HOLLER_BACK_DUDE)
186
        throw uhd::not_implemented_error("holler protocol not implemented");
187

    
188
    size_t min_recv_mtu = sizeof(usrp2_ctrl_data_t), max_recv_mtu = user_mtu.recv_mtu;
189
    size_t min_send_mtu = sizeof(usrp2_ctrl_data_t), max_send_mtu = user_mtu.send_mtu;
190

    
191
    while (min_recv_mtu < max_recv_mtu){
192

    
193
        size_t test_mtu = (max_recv_mtu/2 + min_recv_mtu/2 + 3) & ~3;
194

    
195
        ctrl_data->id = htonl(USRP2_CTRL_ID_HOLLER_AT_ME_BRO);
196
        ctrl_data->proto_ver = htonl(USRP2_FW_COMPAT_NUM);
197
        ctrl_data->data.echo_args.len = htonl(test_mtu);
198
        udp_sock->send(boost::asio::buffer(buffer, sizeof(usrp2_ctrl_data_t)));
199

    
200
        size_t len = udp_sock->recv(boost::asio::buffer(buffer), echo_timeout);
201

    
202
        if (len >= test_mtu) min_recv_mtu = test_mtu;
203
        else                 max_recv_mtu = test_mtu - 4;
204

    
205
    }
206

    
207
    while (min_send_mtu < max_send_mtu){
208

    
209
        size_t test_mtu = (max_send_mtu/2 + min_send_mtu/2 + 3) & ~3;
210

    
211
        ctrl_data->id = htonl(USRP2_CTRL_ID_HOLLER_AT_ME_BRO);
212
        ctrl_data->proto_ver = htonl(USRP2_FW_COMPAT_NUM);
213
        ctrl_data->data.echo_args.len = htonl(sizeof(usrp2_ctrl_data_t));
214
        udp_sock->send(boost::asio::buffer(buffer, test_mtu));
215

    
216
        size_t len = udp_sock->recv(boost::asio::buffer(buffer), echo_timeout);
217
        if (len >= sizeof(usrp2_ctrl_data_t)) len = ntohl(ctrl_data->data.echo_args.len);
218

    
219
        if (len >= test_mtu) min_send_mtu = test_mtu;
220
        else                 max_send_mtu = test_mtu - 4;
221
    }
222

    
223
    mtu_result_t mtu;
224
    mtu.recv_mtu = min_recv_mtu;
225
    mtu.send_mtu = min_send_mtu;
226
    return mtu;
227
}
228

    
229
/***********************************************************************
230
 * Helpers
231
 **********************************************************************/
232
static void init_xport(zero_copy_if::sptr xport){
233
    //Send a small data packet so the usrp2 knows the udp source port.
234
    //This setup must happen before further initialization occurs
235
    //or the async update packets will cause ICMP destination unreachable.
236
    static const boost::uint32_t data[2] = {
237
        uhd::htonx(boost::uint32_t(0 /* don't care seq num */)),
238
        uhd::htonx(boost::uint32_t(USRP2_INVALID_VRT_HEADER))
239
    };
240

    
241
    transport::managed_send_buffer::sptr send_buff = xport->get_send_buff();
242
    std::memcpy(send_buff->cast<void*>(), &data, sizeof(data));
243
    send_buff->commit(sizeof(data));
244
}
245

    
246
/***********************************************************************
247
 * Structors
248
 **********************************************************************/
249
usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){
250
    UHD_MSG(status) << "Opening a USRP2/N-Series device..." << std::endl;
251
    device_addr_t device_addr = _device_addr;
252

    
253
    //setup the dsp transport hints (default to a large recv buff)
254
    if (not device_addr.has_key("recv_buff_size")){
255
        #if defined(UHD_PLATFORM_MACOS) || defined(UHD_PLATFORM_BSD)
256
            //limit buffer resize on macos or it will error
257
            device_addr["recv_buff_size"] = "1e6";
258
        #elif defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_WIN32)
259
            //set to half-a-second of buffering at max rate
260
            device_addr["recv_buff_size"] = "50e6";
261
        #endif
262
    }
263

    
264
    device_addrs_t device_args = separate_device_addr(device_addr);
265

    
266
    //extract the user's requested MTU size or default
267
    mtu_result_t user_mtu;
268
    user_mtu.recv_mtu = size_t(device_addr.cast<double>("recv_frame_size", udp_simple::mtu));
269
    user_mtu.send_mtu = size_t(device_addr.cast<double>("send_frame_size", udp_simple::mtu));
270

    
271
    try{
272
        //calculate the minimum send and recv mtu of all devices
273
        mtu_result_t mtu = determine_mtu(device_args[0]["addr"], user_mtu);
274
        for (size_t i = 1; i < device_args.size(); i++){
275
            mtu_result_t mtu_i = determine_mtu(device_args[i]["addr"], user_mtu);
276
            mtu.recv_mtu = std::min(mtu.recv_mtu, mtu_i.recv_mtu);
277
            mtu.send_mtu = std::min(mtu.send_mtu, mtu_i.send_mtu);
278
        }
279

    
280
        device_addr["recv_frame_size"] = boost::lexical_cast<std::string>(mtu.recv_mtu);
281
        device_addr["send_frame_size"] = boost::lexical_cast<std::string>(mtu.send_mtu);
282

    
283
        UHD_MSG(status) << boost::format("Current recv frame size: %d bytes") % mtu.recv_mtu << std::endl;
284
        UHD_MSG(status) << boost::format("Current send frame size: %d bytes") % mtu.send_mtu << std::endl;
285
    }
286
    catch(const uhd::not_implemented_error &){
287
        //just ignore this error, makes older fw work...
288
    }
289

    
290
    device_args = separate_device_addr(device_addr); //update args for new frame sizes
291

    
292
    ////////////////////////////////////////////////////////////////////
293
    // create controller objects and initialize the properties tree
294
    ////////////////////////////////////////////////////////////////////
295
    _tree = property_tree::make();
296
    _tree->create<std::string>("/name").set("USRP2 / N-Series Device");
297

    
298
    for (size_t mbi = 0; mbi < device_args.size(); mbi++){
299
        const device_addr_t device_args_i = device_args[mbi];
300
        const std::string mb = boost::lexical_cast<std::string>(mbi);
301
        const std::string addr = device_args_i["addr"];
302
        const property_tree::path_type mb_path = "/mboards/" + mb;
303

    
304
        ////////////////////////////////////////////////////////////////
305
        // construct transports for dsp and async errors
306
        ////////////////////////////////////////////////////////////////
307
        UHD_LOG << "Making transport for DSP0..." << std::endl;
308
        _mbc[mb].dsp_xports.push_back(udp_zero_copy::make(
309
            addr, BOOST_STRINGIZE(USRP2_UDP_DSP0_PORT), device_args_i
310
        ));
311
        init_xport(_mbc[mb].dsp_xports.back());
312

    
313
        UHD_LOG << "Making transport for DSP1..." << std::endl;
314
        _mbc[mb].dsp_xports.push_back(udp_zero_copy::make(
315
            addr, BOOST_STRINGIZE(USRP2_UDP_DSP1_PORT), device_args_i
316
        ));
317
        init_xport(_mbc[mb].dsp_xports.back());
318

    
319
        UHD_LOG << "Making transport for ERR0..." << std::endl;
320
        _mbc[mb].err_xports.push_back(udp_zero_copy::make(
321
            addr, BOOST_STRINGIZE(USRP2_UDP_ERR0_PORT), device_addr_t()
322
        ));
323
        init_xport(_mbc[mb].err_xports.back());
324

    
325
        ////////////////////////////////////////////////////////////////
326
        // create the iface that controls i2c, spi, uart, and wb
327
        ////////////////////////////////////////////////////////////////
328
        _mbc[mb].iface = usrp2_iface::make(udp_simple::make_connected(
329
            addr, BOOST_STRINGIZE(USRP2_UDP_CTRL_PORT)
330
        ));
331
        _tree->create<std::string>(mb_path / "name").set(_mbc[mb].iface->get_cname());
332

    
333
        ////////////////////////////////////////////////////////////////
334
        // setup the mboard eeprom
335
        ////////////////////////////////////////////////////////////////
336
        _tree->create<mboard_eeprom_t>(mb_path / "eeprom")
337
            .set(_mbc[mb].iface->mb_eeprom)
338
            .subscribe(boost::bind(&usrp2_impl::set_mb_eeprom, this, mb, _1));
339

    
340
        ////////////////////////////////////////////////////////////////
341
        // create clock control objects
342
        ////////////////////////////////////////////////////////////////
343
        _mbc[mb].clock = usrp2_clock_ctrl::make(_mbc[mb].iface);
344
        _tree->create<double>(mb_path / "tick_rate")
345
            .publish(boost::bind(&usrp2_clock_ctrl::get_master_clock_rate, _mbc[mb].clock))
346
            .subscribe(boost::bind(&usrp2_impl::update_tick_rate, this, _1));
347

    
348
        ////////////////////////////////////////////////////////////////
349
        // create codec control objects
350
        ////////////////////////////////////////////////////////////////
351
        const property_tree::path_type rx_codec_path = mb_path / "rx_codecs/A";
352
        const property_tree::path_type tx_codec_path = mb_path / "tx_codecs/A";
353
        _tree->create<int>(rx_codec_path / "gains"); //phony property so this dir exists
354
        _tree->create<int>(tx_codec_path / "gains"); //phony property so this dir exists
355
        _mbc[mb].codec = usrp2_codec_ctrl::make(_mbc[mb].iface);
356
        switch(_mbc[mb].iface->get_rev()){
357
        case usrp2_iface::USRP_N200:
358
        case usrp2_iface::USRP_N210:
359
        case usrp2_iface::USRP_N200_R4:
360
        case usrp2_iface::USRP_N210_R4:{
361
            _tree->create<std::string>(rx_codec_path / "name").set("ads62p44");
362
            _tree->create<meta_range_t>(rx_codec_path / "gains/digital/range").set(meta_range_t(0, 6.0, 0.5));
363
            _tree->create<double>(rx_codec_path / "gains/digital/value")
364
                .subscribe(boost::bind(&usrp2_codec_ctrl::set_rx_digital_gain, _mbc[mb].codec, _1)).set(0);
365
            _tree->create<meta_range_t>(rx_codec_path / "gains/fine/range").set(meta_range_t(0, 0.5, 0.05));
366
            _tree->create<double>(rx_codec_path / "gains/fine/value")
367
                .subscribe(boost::bind(&usrp2_codec_ctrl::set_rx_digital_fine_gain, _mbc[mb].codec, _1)).set(0);
368
        }break;
369

    
370
        case usrp2_iface::USRP2_REV3:
371
        case usrp2_iface::USRP2_REV4:
372
            _tree->create<std::string>(rx_codec_path / "name").set("ltc2284");
373
            break;
374

    
375
        case usrp2_iface::USRP_NXXX:
376
            _tree->create<std::string>(rx_codec_path / "name").set("??????");
377
            break;
378
        }
379
        _tree->create<std::string>(tx_codec_path / "name").set("ad9777");
380

    
381
        ////////////////////////////////////////////////////////////////
382
        // create gpsdo control objects
383
        ////////////////////////////////////////////////////////////////
384
        if (_mbc[mb].iface->mb_eeprom["gpsdo"] == "internal"){
385
            _mbc[mb].gps = gps_ctrl::make(
386
                _mbc[mb].iface->get_gps_write_fn(),
387
                _mbc[mb].iface->get_gps_read_fn()
388
            );
389
            BOOST_FOREACH(const std::string &name, _mbc[mb].gps->get_sensors()){
390
                _tree->create<sensor_value_t>(mb_path / "sensors" / name)
391
                    .publish(boost::bind(&gps_ctrl::get_sensor, _mbc[mb].gps, name));
392
            }
393
        }
394

    
395
        ////////////////////////////////////////////////////////////////
396
        // and do the misc mboard sensors
397
        ////////////////////////////////////////////////////////////////
398
        _tree->create<sensor_value_t>(mb_path / "sensors/mimo_locked")
399
            .publish(boost::bind(&usrp2_impl::get_mimo_locked, this, mb));
400
        _tree->create<sensor_value_t>(mb_path / "sensors/ref_locked")
401
            .publish(boost::bind(&usrp2_impl::get_ref_locked, this, mb));
402

    
403
        ////////////////////////////////////////////////////////////////
404
        // create frontend control objects
405
        ////////////////////////////////////////////////////////////////
406
        _mbc[mb].rx_fe = rx_frontend_core_200::make(
407
            _mbc[mb].iface, U2_REG_SR_ADDR(SR_RX_FRONT)
408
        );
409
        _mbc[mb].tx_fe = tx_frontend_core_200::make(
410
            _mbc[mb].iface, U2_REG_SR_ADDR(SR_TX_FRONT)
411
        );
412
        //TODO lots of properties to expose here for frontends
413
        _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec")
414
            .subscribe(boost::bind(&usrp2_impl::update_rx_subdev_spec, this, mb, _1));
415
        _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec")
416
            .subscribe(boost::bind(&usrp2_impl::update_tx_subdev_spec, this, mb, _1));
417

    
418
        ////////////////////////////////////////////////////////////////
419
        // create rx dsp control objects
420
        ////////////////////////////////////////////////////////////////
421
        _mbc[mb].rx_dsps.push_back(rx_dsp_core_200::make(
422
            _mbc[mb].iface, U2_REG_SR_ADDR(SR_RX_DSP0), U2_REG_SR_ADDR(SR_RX_CTRL0), USRP2_RX_SID_BASE + 0, true
423
        ));
424
        _mbc[mb].rx_dsps.push_back(rx_dsp_core_200::make(
425
            _mbc[mb].iface, U2_REG_SR_ADDR(SR_RX_DSP1), U2_REG_SR_ADDR(SR_RX_CTRL1), USRP2_RX_SID_BASE + 1, true
426
        ));
427
        for (size_t dspno = 0; dspno < _mbc[mb].rx_dsps.size(); dspno++){
428
            _tree->access<double>(mb_path / "tick_rate")
429
                .subscribe(boost::bind(&rx_dsp_core_200::set_tick_rate, _mbc[mb].rx_dsps[dspno], _1));
430
            //This is a hack/fix for the lingering packet problem.
431
            //The dsp core starts streaming briefly... now we flush
432
            _mbc[mb].dsp_xports[dspno]->get_recv_buff(0.01).get(); //recv with timeout for lingering
433
            _mbc[mb].dsp_xports[dspno]->get_recv_buff(0.01).get(); //recv with timeout for expected
434
            property_tree::path_type rx_dsp_path = mb_path / str(boost::format("rx_dsps/%u") % dspno);
435
            _tree->create<double>(rx_dsp_path / "rate/value")
436
                .coerce(boost::bind(&rx_dsp_core_200::set_host_rate, _mbc[mb].rx_dsps[dspno], _1))
437
                .subscribe(boost::bind(&usrp2_impl::update_rx_samp_rate, this, _1));
438
            _tree->create<double>(rx_dsp_path / "freq/value")
439
                .coerce(boost::bind(&rx_dsp_core_200::set_freq, _mbc[mb].rx_dsps[dspno], _1));
440
            _tree->create<meta_range_t>(rx_dsp_path / "freq/range")
441
                .publish(boost::bind(&rx_dsp_core_200::get_freq_range, _mbc[mb].rx_dsps[dspno]));
442
            _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd")
443
                .subscribe(boost::bind(&rx_dsp_core_200::issue_stream_command, _mbc[mb].rx_dsps[dspno], _1));
444
        }
445

    
446
        ////////////////////////////////////////////////////////////////
447
        // create tx dsp control objects
448
        ////////////////////////////////////////////////////////////////
449
        _mbc[mb].tx_dsp = tx_dsp_core_200::make(
450
            _mbc[mb].iface, U2_REG_SR_ADDR(SR_TX_DSP), U2_REG_SR_ADDR(SR_TX_CTRL), USRP2_TX_ASYNC_SID
451
        );
452
        _tree->access<double>(mb_path / "tick_rate")
453
            .subscribe(boost::bind(&tx_dsp_core_200::set_tick_rate, _mbc[mb].tx_dsp, _1));
454
        _tree->create<double>(mb_path / "tx_dsps/0/rate/value")
455
            .coerce(boost::bind(&tx_dsp_core_200::set_host_rate, _mbc[mb].tx_dsp, _1))
456
            .subscribe(boost::bind(&usrp2_impl::update_tx_samp_rate, this, _1));
457
        _tree->create<double>(mb_path / "tx_dsps/0/freq/value")
458
            .coerce(boost::bind(&usrp2_impl::set_tx_dsp_freq, this, mb, _1));
459
        _tree->create<meta_range_t>(mb_path / "tx_dsps/0/freq/range")
460
            .publish(boost::bind(&usrp2_impl::get_tx_dsp_freq_range, this, mb));
461

    
462
        //setup dsp flow control
463
        const double ups_per_sec = device_args_i.cast<double>("ups_per_sec", 20);
464
        const size_t send_frame_size = _mbc[mb].dsp_xports.front()->get_send_frame_size();
465
        const double ups_per_fifo = device_args_i.cast<double>("ups_per_fifo", 8.0);
466
        _mbc[mb].tx_dsp->set_updates(
467
            (ups_per_sec > 0.0)? size_t(100e6/*approx tick rate*//ups_per_sec) : 0,
468
            (ups_per_fifo > 0.0)? size_t(USRP2_SRAM_BYTES/ups_per_fifo/send_frame_size) : 0
469
        );
470

    
471
        ////////////////////////////////////////////////////////////////
472
        // create time control objects
473
        ////////////////////////////////////////////////////////////////
474
        time64_core_200::readback_bases_type time64_rb_bases;
475
        time64_rb_bases.rb_secs_now = U2_REG_TIME64_SECS_RB_IMM;
476
        time64_rb_bases.rb_ticks_now = U2_REG_TIME64_TICKS_RB_IMM;
477
        time64_rb_bases.rb_secs_pps = U2_REG_TIME64_SECS_RB_PPS;
478
        time64_rb_bases.rb_ticks_pps = U2_REG_TIME64_TICKS_RB_PPS;
479
        _mbc[mb].time64 = time64_core_200::make(
480
            _mbc[mb].iface, U2_REG_SR_ADDR(SR_TIME64), time64_rb_bases, mimo_clock_sync_delay_cycles
481
        );
482
        _tree->access<double>(mb_path / "tick_rate")
483
            .subscribe(boost::bind(&time64_core_200::set_tick_rate, _mbc[mb].time64, _1));
484
        _tree->create<time_spec_t>(mb_path / "time/now")
485
            .publish(boost::bind(&time64_core_200::get_time_now, _mbc[mb].time64))
486
            .subscribe(boost::bind(&time64_core_200::set_time_now, _mbc[mb].time64, _1));
487
        _tree->create<time_spec_t>(mb_path / "time/pps")
488
            .publish(boost::bind(&time64_core_200::get_time_last_pps, _mbc[mb].time64))
489
            .subscribe(boost::bind(&time64_core_200::set_time_next_pps, _mbc[mb].time64, _1));
490
        //setup time source props
491
        _tree->create<std::string>(mb_path / "time_source/value")
492
            .subscribe(boost::bind(&time64_core_200::set_time_source, _mbc[mb].time64, _1));
493
        _tree->create<std::vector<std::string> >(mb_path / "time_source/options")
494
            .publish(boost::bind(&time64_core_200::get_time_sources, _mbc[mb].time64));
495
        //setup reference source props
496
        _tree->create<std::string>(mb_path / "clock_source/value")
497
            .subscribe(boost::bind(&usrp2_impl::update_clock_source, this, mb, _1));
498
        static const std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("mimo");
499
        _tree->create<std::vector<std::string> >(mb_path / "clock_source/options").set(clock_sources);
500

    
501
        ////////////////////////////////////////////////////////////////
502
        // create dboard control objects
503
        ////////////////////////////////////////////////////////////////
504

    
505
        //read the dboard eeprom to extract the dboard ids
506
        dboard_eeprom_t rx_db_eeprom, tx_db_eeprom, gdb_eeprom;
507
        rx_db_eeprom.load(*_mbc[mb].iface, USRP2_I2C_ADDR_RX_DB);
508
        tx_db_eeprom.load(*_mbc[mb].iface, USRP2_I2C_ADDR_TX_DB);
509
        gdb_eeprom.load(*_mbc[mb].iface, USRP2_I2C_ADDR_TX_DB ^ 5);
510

    
511
        //create the properties and register subscribers
512
        _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/rx_eeprom")
513
            .set(rx_db_eeprom)
514
            .subscribe(boost::bind(&usrp2_impl::set_db_eeprom, this, mb, "rx", _1));
515
        _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/tx_eeprom")
516
            .set(tx_db_eeprom)
517
            .subscribe(boost::bind(&usrp2_impl::set_db_eeprom, this, mb, "tx", _1));
518
        _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/gdb_eeprom")
519
            .set(gdb_eeprom)
520
            .subscribe(boost::bind(&usrp2_impl::set_db_eeprom, this, mb, "gdb", _1));
521

    
522
        //create a new dboard interface and manager
523
        _mbc[mb].dboard_iface = make_usrp2_dboard_iface(_mbc[mb].iface, _mbc[mb].clock);
524
        _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_mbc[mb].dboard_iface);
525
        _mbc[mb].dboard_manager = dboard_manager::make(
526
            rx_db_eeprom.id,
527
            ((gdb_eeprom.id == dboard_id_t::none())? tx_db_eeprom : gdb_eeprom).id,
528
            _mbc[mb].dboard_iface
529
        );
530
        BOOST_FOREACH(const std::string &name, _mbc[mb].dboard_manager->get_rx_subdev_names()){
531
            dboard_manager::populate_prop_tree_from_subdev(
532
                _tree, mb_path / "dboards/A/rx_frontends" / name,
533
                _mbc[mb].dboard_manager->get_rx_subdev(name)
534
            );
535
        }
536
        BOOST_FOREACH(const std::string &name, _mbc[mb].dboard_manager->get_tx_subdev_names()){
537
            dboard_manager::populate_prop_tree_from_subdev(
538
                _tree, mb_path / "dboards/A/tx_frontends" / name,
539
                _mbc[mb].dboard_manager->get_tx_subdev(name)
540
            );
541
        }
542
    }
543

    
544
    //initialize io handling
545
    this->io_init();
546

    
547
    //do some post-init tasks
548
    BOOST_FOREACH(const std::string &mb, _mbc.keys()){
549
        property_tree::path_type root = "/mboards/" + mb;
550
        _tree->access<double>(root / "tick_rate").update();
551

    
552
        //and now that the tick rate is set, init the host rates to something
553
        BOOST_FOREACH(const std::string &name, _tree->list(root / "rx_dsps")){
554
            _tree->access<double>(root / "rx_dsps" / name / "rate" / "value").set(1e6);
555
        }
556
        BOOST_FOREACH(const std::string &name, _tree->list(root / "tx_dsps")){
557
            _tree->access<double>(root / "tx_dsps" / name / "rate" / "value").set(1e6);
558
        }
559

    
560
        _tree->access<subdev_spec_t>(root / "rx_subdev_spec").set(subdev_spec_t("A:"+_mbc[mb].dboard_manager->get_rx_subdev_names()[0]));
561
        _tree->access<subdev_spec_t>(root / "tx_subdev_spec").set(subdev_spec_t("A:"+_mbc[mb].dboard_manager->get_tx_subdev_names()[0]));
562
        _tree->access<std::string>(root / "clock_source/value").set("internal");
563
        _tree->access<std::string>(root / "time_source/value").set("none");
564

    
565
        //GPS installed: use external ref, time, and init time spec
566
        if (_mbc[mb].gps.get() != NULL){
567
            _tree->access<std::string>(root / "time_source/value").set("external");
568
            _tree->access<std::string>(root / "clock_source/value").set("external");
569
            _mbc[mb].time64->set_time_next_pps(time_spec_t(time_t(_mbc[mb].gps->get_sensor("gps_time").to_int()+1)));
570
        }
571
    }
572

    
573
}
574

    
575
usrp2_impl::~usrp2_impl(void){UHD_SAFE_CALL(
576
    BOOST_FOREACH(const std::string &mb, _mbc.keys()){
577
        _mbc[mb].tx_dsp->set_updates(0, 0);
578
    }
579
)}
580

    
581
void usrp2_impl::set_mb_eeprom(const std::string &mb, const uhd::usrp::mboard_eeprom_t &mb_eeprom){
582
    mb_eeprom.commit(*(_mbc[mb].iface), mboard_eeprom_t::MAP_N100);
583
}
584

    
585
void usrp2_impl::set_db_eeprom(const std::string &mb, const std::string &type, const uhd::usrp::dboard_eeprom_t &db_eeprom){
586
    if (type == "rx") db_eeprom.store(*_mbc[mb].iface, USRP2_I2C_ADDR_RX_DB);
587
    if (type == "tx") db_eeprom.store(*_mbc[mb].iface, USRP2_I2C_ADDR_TX_DB);
588
    if (type == "gdb") db_eeprom.store(*_mbc[mb].iface, USRP2_I2C_ADDR_TX_DB ^ 5);
589
}
590

    
591
sensor_value_t usrp2_impl::get_mimo_locked(const std::string &mb){
592
    const bool lock = (_mbc[mb].iface->peek32(U2_REG_IRQ_RB) & (1<<10)) != 0;
593
    return sensor_value_t("MIMO", lock, "locked", "unlocked");
594
}
595

    
596
sensor_value_t usrp2_impl::get_ref_locked(const std::string &mb){
597
    const bool lock = (_mbc[mb].iface->peek32(U2_REG_IRQ_RB) & (1<<11)) != 0;
598
    return sensor_value_t("Ref", lock, "locked", "unlocked");
599
}
600

    
601
#include <boost/math/special_functions/round.hpp>
602
#include <boost/math/special_functions/sign.hpp>
603

    
604
double usrp2_impl::set_tx_dsp_freq(const std::string &mb, const double freq_){
605
    double new_freq = freq_;
606
    const double tick_rate = _tree->access<double>("/mboards/"+mb+"/tick_rate").get();
607

    
608
    //calculate the DAC shift (multiples of rate)
609
    const int sign = boost::math::sign(new_freq);
610
    const int zone = std::min(boost::math::iround(new_freq/tick_rate), 2);
611
    const double dac_shift = sign*zone*tick_rate;
612
    new_freq -= dac_shift; //update FPGA DSP target freq
613

    
614
    //set the DAC shift (modulation mode)
615
    if (zone == 0) _mbc[mb].codec->set_tx_mod_mode(0); //no shift
616
    else _mbc[mb].codec->set_tx_mod_mode(sign*4/zone); //DAC interp = 4
617

    
618
    return _mbc[mb].tx_dsp->set_freq(new_freq) + dac_shift; //actual freq
619
}
620

    
621
meta_range_t usrp2_impl::get_tx_dsp_freq_range(const std::string &mb){
622
    const double tick_rate = _tree->access<double>("/mboards/"+mb+"/tick_rate").get();
623
    const meta_range_t dsp_range = _mbc[mb].tx_dsp->get_freq_range();
624
    return meta_range_t(dsp_range.start() - tick_rate*2, dsp_range.stop() + tick_rate*2, dsp_range.step());
625
}
626

    
627
void usrp2_impl::update_clock_source(const std::string &mb, const std::string &source){
628
    //clock source ref 10mhz
629
    switch(_mbc[mb].iface->get_rev()){
630
    case usrp2_iface::USRP_N200:
631
    case usrp2_iface::USRP_N210:
632
    case usrp2_iface::USRP_N200_R4:
633
    case usrp2_iface::USRP_N210_R4:
634
        if (source == "internal")       _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x12);
635
        else if (source == "external")  _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x1C);
636
        else if (source == "mimo")      _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x15);
637
        else throw uhd::value_error("unhandled clock configuration reference source: " + source);
638
        _mbc[mb].clock->enable_external_ref(true); //USRP2P has an internal 10MHz TCXO
639
        break;
640

    
641
    case usrp2_iface::USRP2_REV3:
642
    case usrp2_iface::USRP2_REV4:
643
        if (source == "internal")       _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x10);
644
        else if (source == "external")  _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x1C);
645
        else if (source == "mimo")      _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x15);
646
        else throw uhd::value_error("unhandled clock configuration reference source: " + source);
647
        _mbc[mb].clock->enable_external_ref(source != "internal");
648
        break;
649

    
650
    case usrp2_iface::USRP_NXXX: break;
651
    }
652

    
653
    //always drive the clock over serdes if not locking to it
654
    _mbc[mb].clock->enable_mimo_clock_out(source != "mimo");
655

    
656
    //set the mimo clock delay over the serdes
657
    if (source != "mimo"){
658
        switch(_mbc[mb].iface->get_rev()){
659
        case usrp2_iface::USRP_N200:
660
        case usrp2_iface::USRP_N210:
661
        case usrp2_iface::USRP_N200_R4:
662
        case usrp2_iface::USRP_N210_R4:
663
            _mbc[mb].clock->set_mimo_clock_delay(mimo_clock_delay_usrp_n2xx);
664
            break;
665

    
666
        case usrp2_iface::USRP2_REV4:
667
            _mbc[mb].clock->set_mimo_clock_delay(mimo_clock_delay_usrp2_rev4);
668
            break;
669

    
670
        default: break; //not handled
671
        }
672
    }
673
}