Statistics
| Branch: | Tag: | Revision:

root / host / lib / usrp / usrp2 / mboard_impl.cpp @ 2b52aff1

History | View | Annotate | Download (15.2 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 "usrp2_regs.hpp"
20
#include <uhd/usrp/misc_utils.hpp>
21
#include <uhd/usrp/dsp_utils.hpp>
22
#include <uhd/usrp/mboard_props.hpp>
23
#include <uhd/utils/assert.hpp>
24
#include <uhd/utils/byteswap.hpp>
25
#include <uhd/utils/algorithm.hpp>
26
#include <boost/bind.hpp>
27
#include <iostream>
28
#include <boost/date_time/posix_time/posix_time.hpp>
29

    
30
static const double mimo_clock_delay_usrp2_rev4 = 4.18e-9;
31
static const double mimo_clock_delay_usrp_n2xx = 0; //TODO
32
static const int mimo_clock_sync_delay_cycles = 137;
33

    
34
using namespace uhd;
35
using namespace uhd::usrp;
36
using namespace boost::posix_time;
37

    
38
/***********************************************************************
39
 * Structors
40
 **********************************************************************/
41
usrp2_mboard_impl::usrp2_mboard_impl(
42
    size_t index,
43
    transport::udp_simple::sptr ctrl_transport,
44
    transport::zero_copy_if::sptr data_transport,
45
    transport::zero_copy_if::sptr err0_transport,
46
    const device_addr_t &device_args,
47
    size_t recv_samps_per_packet
48
):
49
    _index(index),
50
    _iface(usrp2_iface::make(ctrl_transport))
51
{
52
    //Send a small data packet so the usrp2 knows the udp source port.
53
    //This setup must happen before further initialization occurs
54
    //or the async update packets will cause ICMP destination unreachable.
55
    transport::managed_send_buffer::sptr send_buff;
56
    static const boost::uint32_t data[2] = {
57
        uhd::htonx(boost::uint32_t(0 /* don't care seq num */)),
58
        uhd::htonx(boost::uint32_t(USRP2_INVALID_VRT_HEADER))
59
    };
60
    send_buff = data_transport->get_send_buff();
61
    std::memcpy(send_buff->cast<void*>(), &data, sizeof(data));
62
    send_buff->commit(sizeof(data));
63
    send_buff = err0_transport->get_send_buff();
64
    std::memcpy(send_buff->cast<void*>(), &data, sizeof(data));
65
    send_buff->commit(sizeof(data));
66

    
67
    //contruct the interfaces to mboard perifs
68
    _clock_ctrl = usrp2_clock_ctrl::make(_iface);
69
    _codec_ctrl = usrp2_codec_ctrl::make(_iface);
70
    //_gps_ctrl = usrp2_gps_ctrl::make(_iface);
71

    
72
    //if(_gps_ctrl->gps_detected()) std::cout << "GPS time: " << _gps_ctrl->get_time() << std::endl;
73

    
74
    //TODO move to dsp impl...
75
    //load the allowed decim/interp rates
76
    //_USRP2_RATES = range(4, 128+1, 1) + range(130, 256+1, 2) + range(260, 512+1, 4)
77
    _allowed_decim_and_interp_rates.clear();
78
    for (size_t i = 4; i <= 128; i+=1){
79
        _allowed_decim_and_interp_rates.push_back(i);
80
    }
81
    for (size_t i = 130; i <= 256; i+=2){
82
        _allowed_decim_and_interp_rates.push_back(i);
83
    }
84
    for (size_t i = 260; i <= 512; i+=4){
85
        _allowed_decim_and_interp_rates.push_back(i);
86
    }
87

    
88
    //setup the vrt rx registers
89
    _iface->poke32(_iface->regs.rx_ctrl_clear_overrun, 1); //reset
90
    _iface->poke32(_iface->regs.rx_ctrl_nsamps_per_pkt, recv_samps_per_packet);
91
    _iface->poke32(_iface->regs.rx_ctrl_nchannels, 1);
92
    _iface->poke32(_iface->regs.rx_ctrl_vrt_header, 0
93
        | (0x1 << 28) //if data with stream id
94
        | (0x1 << 26) //has trailer
95
        | (0x3 << 22) //integer time other
96
        | (0x1 << 20) //fractional time sample count
97
    );
98
    _iface->poke32(_iface->regs.rx_ctrl_vrt_stream_id, usrp2_impl::RECV_SID);
99
    _iface->poke32(_iface->regs.rx_ctrl_vrt_trailer, 0);
100
    _iface->poke32(_iface->regs.time64_tps, size_t(get_master_clock_freq()));
101

    
102
    //init the tx control registers
103
    _iface->poke32(_iface->regs.tx_ctrl_clear_state, 1); //reset
104
    _iface->poke32(_iface->regs.tx_ctrl_num_chan, 0);    //1 channel
105
    _iface->poke32(_iface->regs.tx_ctrl_report_sid, usrp2_impl::ASYNC_SID);
106
    _iface->poke32(_iface->regs.tx_ctrl_policy, U2_FLAG_TX_CTRL_POLICY_NEXT_PACKET);
107

    
108
    //setting the cycles per update (disabled by default)
109
    const double ups_per_sec = device_args.cast<double>("ups_per_sec", 0.0);
110
    if (ups_per_sec > 0.0){
111
        const size_t cycles_per_up = size_t(_clock_ctrl->get_master_clock_rate()/ups_per_sec);
112
        _iface->poke32(_iface->regs.tx_ctrl_cycles_per_up, U2_FLAG_TX_CTRL_UP_ENB | cycles_per_up);
113
    }
114

    
115
    //setting the packets per update (enabled by default)
116
    const double ups_per_fifo = device_args.cast<double>("ups_per_fifo", 8.0);
117
    if (ups_per_fifo > 0.0){
118
        const size_t packets_per_up = size_t(usrp2_impl::sram_bytes/ups_per_fifo/data_transport->get_send_frame_size());
119
        _iface->poke32(_iface->regs.tx_ctrl_packets_per_up, U2_FLAG_TX_CTRL_UP_ENB | packets_per_up);
120
    }
121

    
122
    //init the ddc
123
    init_ddc_config();
124

    
125
    //init the duc
126
    init_duc_config();
127

    
128
    //initialize the clock configuration
129
    if (device_args.has_key("mimo_mode")){
130
        if (device_args["mimo_mode"] == "master"){
131
            _mimo_clocking_mode_is_master = true;
132
        }
133
        else if (device_args["mimo_mode"] == "slave"){
134
            _mimo_clocking_mode_is_master = false;
135
        }
136
        else throw std::runtime_error(
137
            "mimo_mode must be set to master or slave"
138
        );
139
    }
140
    else {
141
        _mimo_clocking_mode_is_master = (_iface->peek32(_iface->regs.status) & (1 << 8)) != 0;
142
    }
143
    std::cout << boost::format("mboard%d MIMO %s") % _index %
144
        (_mimo_clocking_mode_is_master?"master":"slave") << std::endl;
145

    
146
    //init the clock config
147
    _clock_config = clock_config_t::internal();
148
    update_clock_config();
149

    
150
    //init the codec before the dboard
151
    codec_init();
152

    
153
    //init the tx and rx dboards (do last)
154
    dboard_init();
155

    
156
    //set default subdev specs
157
    (*this)[MBOARD_PROP_RX_SUBDEV_SPEC] = subdev_spec_t();
158
    (*this)[MBOARD_PROP_TX_SUBDEV_SPEC] = subdev_spec_t();
159
}
160

    
161
usrp2_mboard_impl::~usrp2_mboard_impl(void){
162
    _iface->poke32(_iface->regs.tx_ctrl_cycles_per_up, 0);
163
    _iface->poke32(_iface->regs.tx_ctrl_packets_per_up, 0);
164
}
165

    
166
/***********************************************************************
167
 * Helper Methods
168
 **********************************************************************/
169
void usrp2_mboard_impl::update_clock_config(void){
170
    boost::uint32_t pps_flags = 0;
171

    
172
    //translate pps source enums
173
    switch(_clock_config.pps_source){
174
    case clock_config_t::PPS_SMA:  pps_flags |= U2_FLAG_TIME64_PPS_SMA;  break;
175
    default: throw std::runtime_error("unhandled clock configuration pps source");
176
    }
177

    
178
    //translate pps polarity enums
179
    switch(_clock_config.pps_polarity){
180
    case clock_config_t::PPS_POS: pps_flags |= U2_FLAG_TIME64_PPS_POSEDGE; break;
181
    case clock_config_t::PPS_NEG: pps_flags |= U2_FLAG_TIME64_PPS_NEGEDGE; break;
182
    default: throw std::runtime_error("unhandled clock configuration pps polarity");
183
    }
184

    
185
    //set the pps flags
186
    _iface->poke32(_iface->regs.time64_flags, pps_flags);
187

    
188
    //clock source ref 10mhz
189
    switch(_iface->get_rev()){
190
    case usrp2_iface::USRP_N200:
191
    case usrp2_iface::USRP_N210:
192
        switch(_clock_config.ref_source){
193
        case clock_config_t::REF_INT : _iface->poke32(_iface->regs.misc_ctrl_clock, 0x12); break;
194
        case clock_config_t::REF_SMA : _iface->poke32(_iface->regs.misc_ctrl_clock, 0x1C); break;
195
        default: throw std::runtime_error("unhandled clock configuration reference source");
196
        }
197
        _clock_ctrl->enable_external_ref(true); //USRP2P has an internal 10MHz TCXO
198
        break;
199

    
200
    case usrp2_iface::USRP2_REV3:
201
    case usrp2_iface::USRP2_REV4:
202
        switch(_clock_config.ref_source){
203
        case clock_config_t::REF_INT : _iface->poke32(_iface->regs.misc_ctrl_clock, 0x10); break;
204
        case clock_config_t::REF_SMA : _iface->poke32(_iface->regs.misc_ctrl_clock, 0x1C); break;
205
        default: throw std::runtime_error("unhandled clock configuration reference source");
206
        }
207
        _clock_ctrl->enable_external_ref(_clock_config.ref_source != clock_config_t::REF_INT);
208
        break;
209

    
210
    case usrp2_iface::USRP_NXXX: break;
211
    }
212

    
213
    //Handle the serdes clocking based on master/slave mode:
214
    //   - Masters always drive the clock over serdes.
215
    //   - Slaves always lock to this serdes clock.
216
    //   - Slaves lock their time over the serdes.
217
    if (_mimo_clocking_mode_is_master){
218
        _clock_ctrl->enable_mimo_clock_out(true);
219
        switch(_iface->get_rev()){
220
        case usrp2_iface::USRP_N200:
221
        case usrp2_iface::USRP_N210:
222
            _clock_ctrl->set_mimo_clock_delay(mimo_clock_delay_usrp_n2xx);
223
            break;
224

    
225
        case usrp2_iface::USRP2_REV4:
226
            _clock_ctrl->set_mimo_clock_delay(mimo_clock_delay_usrp2_rev4);
227
            break;
228

    
229
        default: break; //not handled
230
        }
231
        _iface->poke32(_iface->regs.time64_mimo_sync, 0);
232
    }
233
    else{
234
        _iface->poke32(_iface->regs.misc_ctrl_clock, 0x15);
235
        _clock_ctrl->enable_external_ref(true);
236
        _clock_ctrl->enable_mimo_clock_out(false);
237
        _iface->poke32(_iface->regs.time64_mimo_sync,
238
            (1 << 8) | (mimo_clock_sync_delay_cycles & 0xff)
239
        );
240
    }
241

    
242
}
243

    
244
void usrp2_mboard_impl::set_time_spec(const time_spec_t &time_spec, bool now){
245
    //set the ticks
246
    _iface->poke32(_iface->regs.time64_ticks, time_spec.get_tick_count(get_master_clock_freq()));
247

    
248
    //set the flags register
249
    boost::uint32_t imm_flags = (now)? U2_FLAG_TIME64_LATCH_NOW : U2_FLAG_TIME64_LATCH_NEXT_PPS;
250
    _iface->poke32(_iface->regs.time64_imm, imm_flags);
251

    
252
    //set the seconds (latches in all 3 registers)
253
    _iface->poke32(_iface->regs.time64_secs, boost::uint32_t(time_spec.get_full_secs()));
254
}
255

    
256
void usrp2_mboard_impl::handle_overflow(void){
257
    if (_continuous_streaming){ //re-issue the stream command if already continuous
258
        this->issue_ddc_stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
259
    }
260
}
261

    
262
void usrp2_mboard_impl::issue_ddc_stream_cmd(const stream_cmd_t &stream_cmd){
263
    _continuous_streaming = stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
264
    _iface->poke32(_iface->regs.rx_ctrl_stream_cmd, dsp_type1::calc_stream_cmd_word(stream_cmd));
265
    _iface->poke32(_iface->regs.rx_ctrl_time_secs,  boost::uint32_t(stream_cmd.time_spec.get_full_secs()));
266
    _iface->poke32(_iface->regs.rx_ctrl_time_ticks, stream_cmd.time_spec.get_tick_count(get_master_clock_freq()));
267
}
268

    
269
/***********************************************************************
270
 * MBoard Get Properties
271
 **********************************************************************/
272
static const std::string dboard_name = "0";
273

    
274
void usrp2_mboard_impl::get(const wax::obj &key_, wax::obj &val){
275
    named_prop_t key = named_prop_t::extract(key_);
276
    //handle the get request conditioned on the key
277
    switch(key.as<mboard_prop_t>()){
278
    case MBOARD_PROP_NAME:
279
        val = _iface->get_cname() + " mboard";
280
        return;
281

    
282
    case MBOARD_PROP_OTHERS:
283
        val = prop_names_t();
284
        return;
285

    
286
    case MBOARD_PROP_RX_DBOARD:
287
        UHD_ASSERT_THROW(key.name == dboard_name);
288
        val = _rx_dboard_proxy->get_link();
289
        return;
290

    
291
    case MBOARD_PROP_RX_DBOARD_NAMES:
292
        val = prop_names_t(1, dboard_name);
293
        return;
294

    
295
    case MBOARD_PROP_TX_DBOARD:
296
        UHD_ASSERT_THROW(key.name == dboard_name);
297
        val = _tx_dboard_proxy->get_link();
298
        return;
299

    
300
    case MBOARD_PROP_TX_DBOARD_NAMES:
301
        val = prop_names_t(1, dboard_name);
302
        return;
303

    
304
    case MBOARD_PROP_RX_DSP:
305
        UHD_ASSERT_THROW(key.name == "");
306
        val = _rx_dsp_proxy->get_link();
307
        return;
308

    
309
    case MBOARD_PROP_RX_DSP_NAMES:
310
        val = prop_names_t(1, "");
311
        return;
312

    
313
    case MBOARD_PROP_TX_DSP:
314
        UHD_ASSERT_THROW(key.name == "");
315
        val = _tx_dsp_proxy->get_link();
316
        return;
317

    
318
    case MBOARD_PROP_TX_DSP_NAMES:
319
        val = prop_names_t(1, "");
320
        return;
321

    
322
    case MBOARD_PROP_CLOCK_CONFIG:
323
        val = _clock_config;
324
        return;
325

    
326
    case MBOARD_PROP_TIME_NOW: while(true){
327
        uint32_t secs = _iface->peek32(_iface->regs.time64_secs_rb_imm);
328
        uint32_t ticks = _iface->peek32(_iface->regs.time64_ticks_rb_imm);
329
        if (secs != _iface->peek32(_iface->regs.time64_secs_rb_imm)) continue;
330
        val = time_spec_t(secs, ticks, get_master_clock_freq());
331
        return;
332
    }
333

    
334
    case MBOARD_PROP_TIME_PPS: while(true){
335
        uint32_t secs = _iface->peek32(_iface->regs.time64_secs_rb_pps);
336
        uint32_t ticks = _iface->peek32(_iface->regs.time64_ticks_rb_pps);
337
        if (secs != _iface->peek32(_iface->regs.time64_secs_rb_pps)) continue;
338
        val = time_spec_t(secs, ticks, get_master_clock_freq());
339
        return;
340
    }
341

    
342
    case MBOARD_PROP_RX_SUBDEV_SPEC:
343
        val = _rx_subdev_spec;
344
        return;
345

    
346
    case MBOARD_PROP_TX_SUBDEV_SPEC:
347
        val = _tx_subdev_spec;
348
        return;
349

    
350
    case MBOARD_PROP_EEPROM_MAP:
351
        val = _iface->mb_eeprom;
352
        return;
353

    
354
    default: UHD_THROW_PROP_GET_ERROR();
355
    }
356
}
357

    
358
/***********************************************************************
359
 * MBoard Set Properties
360
 **********************************************************************/
361
void usrp2_mboard_impl::set(const wax::obj &key, const wax::obj &val){
362
    //handle the set request conditioned on the key
363
    switch(key.as<mboard_prop_t>()){
364

    
365
    case MBOARD_PROP_CLOCK_CONFIG:
366
        _clock_config = val.as<clock_config_t>();
367
        update_clock_config();
368
        return;
369

    
370
    case MBOARD_PROP_TIME_NOW:
371
        set_time_spec(val.as<time_spec_t>(), true);
372
        return;
373

    
374
    case MBOARD_PROP_TIME_PPS:
375
        set_time_spec(val.as<time_spec_t>(), false);
376
        return;
377

    
378
    case MBOARD_PROP_STREAM_CMD:
379
        issue_ddc_stream_cmd(val.as<stream_cmd_t>());
380
        return;
381

    
382
    case MBOARD_PROP_RX_SUBDEV_SPEC:
383
        _rx_subdev_spec = val.as<subdev_spec_t>();
384
        verify_rx_subdev_spec(_rx_subdev_spec, this->get_link());
385
        //sanity check
386
        UHD_ASSERT_THROW(_rx_subdev_spec.size() == 1);
387
        //set the mux
388
        _iface->poke32(_iface->regs.dsp_rx_mux, dsp_type1::calc_rx_mux_word(
389
            _dboard_manager->get_rx_subdev(_rx_subdev_spec.front().sd_name)[SUBDEV_PROP_CONNECTION].as<subdev_conn_t>()
390
        ));
391
        return;
392

    
393
    case MBOARD_PROP_TX_SUBDEV_SPEC:
394
        _tx_subdev_spec = val.as<subdev_spec_t>();
395
        verify_tx_subdev_spec(_tx_subdev_spec, this->get_link());
396
        //sanity check
397
        UHD_ASSERT_THROW(_tx_subdev_spec.size() == 1);
398
        //set the mux
399
        _iface->poke32(_iface->regs.dsp_tx_mux, dsp_type1::calc_tx_mux_word(
400
            _dboard_manager->get_tx_subdev(_tx_subdev_spec.front().sd_name)[SUBDEV_PROP_CONNECTION].as<subdev_conn_t>()
401
        ));
402
        return;
403

    
404
    case MBOARD_PROP_EEPROM_MAP:
405
        // Step1: commit the map, writing only those values set.
406
        // Step2: readback the entire eeprom map into the iface.
407
        val.as<mboard_eeprom_t>().commit(*_iface, mboard_eeprom_t::MAP_N100);
408
        _iface->mb_eeprom = mboard_eeprom_t(*_iface, mboard_eeprom_t::MAP_N100);
409
        return;
410

    
411
    default: UHD_THROW_PROP_SET_ERROR();
412
    }
413
}