Statistics
| Branch: | Tag: | Revision:

root / host / lib / usrp / usrp2 / mboard_impl.cpp @ fa7d4a2a

History | View | Annotate | Download (13 KB)

1
//
2
// Copyright 2010 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
using namespace uhd;
31
using namespace uhd::usrp;
32
using namespace boost::posix_time;
33

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

    
58
    //contruct the interfaces to mboard perifs
59
    _clock_ctrl = usrp2_clock_ctrl::make(_iface);
60
    _codec_ctrl = usrp2_codec_ctrl::make(_iface);
61
    //_gps_ctrl = usrp2_gps_ctrl::make(_iface);
62

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

    
65
    //TODO move to dsp impl...
66
    //load the allowed decim/interp rates
67
    //_USRP2_RATES = range(4, 128+1, 1) + range(130, 256+1, 2) + range(260, 512+1, 4)
68
    _allowed_decim_and_interp_rates.clear();
69
    for (size_t i = 4; i <= 128; i+=1){
70
        _allowed_decim_and_interp_rates.push_back(i);
71
    }
72
    for (size_t i = 130; i <= 256; i+=2){
73
        _allowed_decim_and_interp_rates.push_back(i);
74
    }
75
    for (size_t i = 260; i <= 512; i+=4){
76
        _allowed_decim_and_interp_rates.push_back(i);
77
    }
78

    
79
    //setup the vrt rx registers
80
    _iface->poke32(_iface->regs.rx_ctrl_clear_overrun, 1); //reset
81
    _iface->poke32(_iface->regs.rx_ctrl_nsamps_per_pkt, recv_samps_per_packet);
82
    _iface->poke32(_iface->regs.rx_ctrl_nchannels, 1);
83
    _iface->poke32(_iface->regs.rx_ctrl_vrt_header, 0
84
        | (0x1 << 28) //if data with stream id
85
        | (0x1 << 26) //has trailer
86
        | (0x3 << 22) //integer time other
87
        | (0x1 << 20) //fractional time sample count
88
    );
89
    _iface->poke32(_iface->regs.rx_ctrl_vrt_stream_id, usrp2_impl::RECV_SID);
90
    _iface->poke32(_iface->regs.rx_ctrl_vrt_trailer, 0);
91
    _iface->poke32(_iface->regs.time64_tps, size_t(get_master_clock_freq()));
92

    
93
    //init the tx control registers
94
    _iface->poke32(_iface->regs.tx_ctrl_clear_state, 1); //reset
95
    _iface->poke32(_iface->regs.tx_ctrl_num_chan, 0);    //1 channel
96
    _iface->poke32(_iface->regs.tx_ctrl_report_sid, usrp2_impl::ASYNC_SID);
97
    _iface->poke32(_iface->regs.tx_ctrl_policy, U2_FLAG_TX_CTRL_POLICY_NEXT_PACKET);
98

    
99
    //setting the cycles per update (disabled by default)
100
    const double ups_per_sec = device_args.cast<double>("ups_per_sec", 0.0);
101
    if (ups_per_sec > 0.0){
102
        const size_t cycles_per_up = size_t(_clock_ctrl->get_master_clock_rate()/ups_per_sec);
103
        _iface->poke32(_iface->regs.tx_ctrl_cycles_per_up, U2_FLAG_TX_CTRL_UP_ENB | cycles_per_up);
104
    }
105

    
106
    //setting the packets per update (enabled by default)
107
    const double ups_per_fifo = device_args.cast<double>("ups_per_fifo", 8.0);
108
    if (ups_per_fifo > 0.0){
109
        const size_t packets_per_up = size_t(usrp2_impl::sram_bytes/ups_per_fifo/data_transport->get_send_frame_size());
110
        _iface->poke32(_iface->regs.tx_ctrl_packets_per_up, U2_FLAG_TX_CTRL_UP_ENB | packets_per_up);
111
    }
112

    
113
    //init the ddc
114
    init_ddc_config();
115

    
116
    //init the duc
117
    init_duc_config();
118

    
119
    //initialize the clock configuration
120
    init_clock_config();
121

    
122
    //init the codec before the dboard
123
    codec_init();
124

    
125
    //init the tx and rx dboards (do last)
126
    dboard_init();
127

    
128
    //set default subdev specs
129
    (*this)[MBOARD_PROP_RX_SUBDEV_SPEC] = subdev_spec_t();
130
    (*this)[MBOARD_PROP_TX_SUBDEV_SPEC] = subdev_spec_t();
131
}
132

    
133
usrp2_mboard_impl::~usrp2_mboard_impl(void){
134
    _iface->poke32(_iface->regs.tx_ctrl_cycles_per_up, 0);
135
    _iface->poke32(_iface->regs.tx_ctrl_packets_per_up, 0);
136
}
137

    
138
/***********************************************************************
139
 * Helper Methods
140
 **********************************************************************/
141
void usrp2_mboard_impl::init_clock_config(void){
142
    //setup the clock configuration settings
143
    _clock_config.ref_source = clock_config_t::REF_INT;
144
    _clock_config.pps_source = clock_config_t::PPS_SMA;
145
    _clock_config.pps_polarity = clock_config_t::PPS_NEG;
146

    
147
    //update the clock config (sends a control packet)
148
    update_clock_config();
149
}
150

    
151
void usrp2_mboard_impl::update_clock_config(void){
152
    boost::uint32_t pps_flags = 0;
153

    
154
    //translate pps source enums
155
    switch(_clock_config.pps_source){
156
    case clock_config_t::PPS_SMA:  pps_flags |= U2_FLAG_TIME64_PPS_SMA;  break;
157
    default: throw std::runtime_error("unhandled clock configuration pps source");
158
    }
159

    
160
    //translate pps polarity enums
161
    switch(_clock_config.pps_polarity){
162
    case clock_config_t::PPS_POS: pps_flags |= U2_FLAG_TIME64_PPS_POSEDGE; break;
163
    case clock_config_t::PPS_NEG: pps_flags |= U2_FLAG_TIME64_PPS_NEGEDGE; break;
164
    default: throw std::runtime_error("unhandled clock configuration pps polarity");
165
    }
166

    
167
    //set the pps flags
168
    _iface->poke32(_iface->regs.time64_flags, pps_flags);
169

    
170
    //clock source ref 10mhz
171
    switch(_iface->get_rev()){
172
    case usrp2_iface::USRP_N200:
173
    case usrp2_iface::USRP_N210:
174
        switch(_clock_config.ref_source){
175
        case clock_config_t::REF_INT : _iface->poke32(_iface->regs.misc_ctrl_clock, 0x12); break;
176
        case clock_config_t::REF_SMA : _iface->poke32(_iface->regs.misc_ctrl_clock, 0x1C); break;
177
        default: throw std::runtime_error("unhandled clock configuration reference source");
178
        }
179
        _clock_ctrl->enable_external_ref(true); //USRP2P has an internal 10MHz TCXO
180
        break;
181

    
182
    case usrp2_iface::USRP2_REV3:
183
    case usrp2_iface::USRP2_REV4:
184
        switch(_clock_config.ref_source){
185
        case clock_config_t::REF_INT : _iface->poke32(_iface->regs.misc_ctrl_clock, 0x10); break;
186
        case clock_config_t::REF_SMA : _iface->poke32(_iface->regs.misc_ctrl_clock, 0x1C); break;
187
        default: throw std::runtime_error("unhandled clock configuration reference source");
188
        }
189
        _clock_ctrl->enable_external_ref(_clock_config.ref_source != clock_config_t::REF_INT);
190
        break;
191

    
192
    case usrp2_iface::USRP_NXXX: break;
193
    }
194
}
195

    
196
void usrp2_mboard_impl::set_time_spec(const time_spec_t &time_spec, bool now){
197
    //set the ticks
198
    _iface->poke32(_iface->regs.time64_ticks, time_spec.get_tick_count(get_master_clock_freq()));
199

    
200
    //set the flags register
201
    boost::uint32_t imm_flags = (now)? U2_FLAG_TIME64_LATCH_NOW : U2_FLAG_TIME64_LATCH_NEXT_PPS;
202
    _iface->poke32(_iface->regs.time64_imm, imm_flags);
203

    
204
    //set the seconds (latches in all 3 registers)
205
    _iface->poke32(_iface->regs.time64_secs, boost::uint32_t(time_spec.get_full_secs()));
206
}
207

    
208
void usrp2_mboard_impl::handle_overflow(void){
209
    if (_continuous_streaming){ //re-issue the stream command if already continuous
210
        this->issue_ddc_stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
211
    }
212
}
213

    
214
void usrp2_mboard_impl::issue_ddc_stream_cmd(const stream_cmd_t &stream_cmd){
215
    _continuous_streaming = stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
216
    _iface->poke32(_iface->regs.rx_ctrl_stream_cmd, dsp_type1::calc_stream_cmd_word(stream_cmd));
217
    _iface->poke32(_iface->regs.rx_ctrl_time_secs,  boost::uint32_t(stream_cmd.time_spec.get_full_secs()));
218
    _iface->poke32(_iface->regs.rx_ctrl_time_ticks, stream_cmd.time_spec.get_tick_count(get_master_clock_freq()));
219
}
220

    
221
/***********************************************************************
222
 * MBoard Get Properties
223
 **********************************************************************/
224
static const std::string dboard_name = "0";
225

    
226
void usrp2_mboard_impl::get(const wax::obj &key_, wax::obj &val){
227
    named_prop_t key = named_prop_t::extract(key_);
228
    //handle the get request conditioned on the key
229
    switch(key.as<mboard_prop_t>()){
230
    case MBOARD_PROP_NAME:
231
        val = _iface->get_cname() + " mboard";
232
        return;
233

    
234
    case MBOARD_PROP_OTHERS:
235
        val = prop_names_t();
236
        return;
237

    
238
    case MBOARD_PROP_RX_DBOARD:
239
        UHD_ASSERT_THROW(key.name == dboard_name);
240
        val = _rx_dboard_proxy->get_link();
241
        return;
242

    
243
    case MBOARD_PROP_RX_DBOARD_NAMES:
244
        val = prop_names_t(1, dboard_name);
245
        return;
246

    
247
    case MBOARD_PROP_TX_DBOARD:
248
        UHD_ASSERT_THROW(key.name == dboard_name);
249
        val = _tx_dboard_proxy->get_link();
250
        return;
251

    
252
    case MBOARD_PROP_TX_DBOARD_NAMES:
253
        val = prop_names_t(1, dboard_name);
254
        return;
255

    
256
    case MBOARD_PROP_RX_DSP:
257
        UHD_ASSERT_THROW(key.name == "");
258
        val = _rx_dsp_proxy->get_link();
259
        return;
260

    
261
    case MBOARD_PROP_RX_DSP_NAMES:
262
        val = prop_names_t(1, "");
263
        return;
264

    
265
    case MBOARD_PROP_TX_DSP:
266
        UHD_ASSERT_THROW(key.name == "");
267
        val = _tx_dsp_proxy->get_link();
268
        return;
269

    
270
    case MBOARD_PROP_TX_DSP_NAMES:
271
        val = prop_names_t(1, "");
272
        return;
273

    
274
    case MBOARD_PROP_CLOCK_CONFIG:
275
        val = _clock_config;
276
        return;
277

    
278
    case MBOARD_PROP_TIME_NOW:{
279
            usrp2_iface::pair64 time64(
280
                _iface->peek64(_iface->regs.time64_secs_rb, _iface->regs.time64_ticks_rb)
281
            );
282
            val = time_spec_t(
283
                time64.first, time64.second, get_master_clock_freq()
284
            );
285
        }
286
        return;
287

    
288
    case MBOARD_PROP_RX_SUBDEV_SPEC:
289
        val = _rx_subdev_spec;
290
        return;
291

    
292
    case MBOARD_PROP_TX_SUBDEV_SPEC:
293
        val = _tx_subdev_spec;
294
        return;
295

    
296
    case MBOARD_PROP_EEPROM_MAP:
297
        val = _iface->mb_eeprom;
298
        return;
299

    
300
    default: UHD_THROW_PROP_GET_ERROR();
301
    }
302
}
303

    
304
/***********************************************************************
305
 * MBoard Set Properties
306
 **********************************************************************/
307
void usrp2_mboard_impl::set(const wax::obj &key, const wax::obj &val){
308
    //handle the set request conditioned on the key
309
    switch(key.as<mboard_prop_t>()){
310

    
311
    case MBOARD_PROP_CLOCK_CONFIG:
312
        _clock_config = val.as<clock_config_t>();
313
        update_clock_config();
314
        return;
315

    
316
    case MBOARD_PROP_TIME_NOW:
317
        set_time_spec(val.as<time_spec_t>(), true);
318
        return;
319

    
320
    case MBOARD_PROP_TIME_NEXT_PPS:
321
        set_time_spec(val.as<time_spec_t>(), false);
322
        return;
323

    
324
    case MBOARD_PROP_STREAM_CMD:
325
        issue_ddc_stream_cmd(val.as<stream_cmd_t>());
326
        return;
327

    
328
    case MBOARD_PROP_RX_SUBDEV_SPEC:
329
        _rx_subdev_spec = val.as<subdev_spec_t>();
330
        verify_rx_subdev_spec(_rx_subdev_spec, this->get_link());
331
        //sanity check
332
        UHD_ASSERT_THROW(_rx_subdev_spec.size() == 1);
333
        //set the mux
334
        _iface->poke32(_iface->regs.dsp_rx_mux, dsp_type1::calc_rx_mux_word(
335
            _dboard_manager->get_rx_subdev(_rx_subdev_spec.front().sd_name)[SUBDEV_PROP_CONNECTION].as<subdev_conn_t>()
336
        ));
337
        return;
338

    
339
    case MBOARD_PROP_TX_SUBDEV_SPEC:
340
        _tx_subdev_spec = val.as<subdev_spec_t>();
341
        verify_tx_subdev_spec(_tx_subdev_spec, this->get_link());
342
        //sanity check
343
        UHD_ASSERT_THROW(_tx_subdev_spec.size() == 1);
344
        //set the mux
345
        _iface->poke32(_iface->regs.dsp_tx_mux, dsp_type1::calc_tx_mux_word(
346
            _dboard_manager->get_tx_subdev(_tx_subdev_spec.front().sd_name)[SUBDEV_PROP_CONNECTION].as<subdev_conn_t>()
347
        ));
348
        return;
349

    
350
    case MBOARD_PROP_EEPROM_MAP:
351
        // Step1: commit the map, writing only those values set.
352
        // Step2: readback the entire eeprom map into the iface.
353
        val.as<mboard_eeprom_t>().commit(*_iface, mboard_eeprom_t::MAP_N100);
354
        _iface->mb_eeprom = mboard_eeprom_t(*_iface, mboard_eeprom_t::MAP_N100);
355
        return;
356

    
357
    default: UHD_THROW_PROP_SET_ERROR();
358
    }
359
}