/* -*- c++ -*- */
/*
- * Copyright 2008 Free Software Foundation, Inc.
+ * Copyright 2008,2009 Free Software Foundation, Inc.
*
* 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
#endif
static const int DEFAULT_RX_SCALE = 1024;
-static const int DEFAULT_TX_SCALE = 3000;
namespace usrp2 {
case OP_CONFIG_TX_REPLY_V2: return "OP_CONFIG_TX_REPLY_V2";
case OP_START_RX_STREAMING: return "OP_START_RX_STREAMING";
case OP_STOP_RX: return "OP_STOP_RX";
-#if 0
- case OP_WRITE_REG: return "OP_WRITE_REG";
- case OP_WRITE_REG_MASKED: return "OP_WRITE_REG_MASKED";
- case OP_READ_REG: return "OP_READ_REG";
- case OP_READ_REG_REPLY: return "OP_READ_REG_REPLY";
-#endif
+ case OP_CONFIG_MIMO: return "OP_CONFIG_MIMO";
+ case OP_DBOARD_INFO: return "OP_DBOARD_INFO";
+ case OP_DBOARD_INFO_REPLY: return "OP_DBOARD_INFO_REPLY";
+ case OP_SYNC_TO_PPS: return "OP_SYNC_TO_PPS";
+ case OP_PEEK: return "OP_PEEK";
+ case OP_PEEK_REPLY: return "OP_PEEK_REPLY";
+ case OP_SET_TX_LO_OFFSET: return "OP_SET_TX_LO_OFFSET";
+ case OP_SET_TX_LO_OFFSET_REPLY: return "OP_SET_TX_LO_OFFSET_REPLY";
+ case OP_SET_RX_LO_OFFSET: return "OP_SET_RX_LO_OFFSET";
+ case OP_SET_RX_LO_OFFSET_REPLY: return "OP_SET_RX_LO_OFFSET_REPLY";
+ case OP_SYNC_EVERY_PPS: return "OP_SYNC_EVERY_PPS";
+ case OP_SYNC_EVERY_PPS_REPLY: return "OP_SYNC_EVERY_PPS_REPLY";
+
default:
char buf[64];
snprintf(buf, sizeof(buf), "<unknown opcode: %d>", opcode);
md->word0 = u2p_word0(fh);
md->timestamp = u2p_timestamp(fh);
+ // FIXME when we've got more info
// md->start_of_burst = (md->word0 & XXX) != 0;
// md->end_of_burst = (md->word0 & XXX) != 0;
// md->rx_overrun = (md->word0 & XXX) != 0;
+ md->start_of_burst = 0;
+ md->end_of_burst = 0;
+ md->rx_overrun = 0;
*items = (uint32_t *)(&fh[1]);
size_t nbytes = payload_len_in_bytes - sizeof(u2_fixed_hdr_t);
}
- usrp2::impl::impl(const std::string &ifc, props *p)
- : d_eth_buf(new eth_buffer()), d_pf(0), d_bg_thread(0), d_bg_running(false),
- d_rx_decim(0), d_rx_seqno(-1), d_tx_seqno(0), d_next_rid(0),
+ usrp2::impl::impl(const std::string &ifc, props *p, size_t rx_bufsize)
+ : d_eth_buf(new eth_buffer(rx_bufsize)), d_interface_name(ifc), d_pf(0), d_bg_thread(0),
+ d_bg_running(false), d_rx_seqno(-1), d_tx_seqno(0), d_next_rid(0),
d_num_rx_frames(0), d_num_rx_missing(0), d_num_rx_overruns(0), d_num_rx_bytes(0),
d_num_enqueued(0), d_enqueued_mutex(), d_bg_pending_cond(&d_enqueued_mutex),
- d_channel_rings(NCHANS)
+ d_channel_rings(NCHANS), d_tx_interp(0), d_rx_decim(0), d_dont_enqueue(true)
{
if (!d_eth_buf->open(ifc, htons(U2_ETHERTYPE)))
throw std::runtime_error("Unable to register USRP2 protocol");
- d_pf = pktfilter::make_ethertype_inbound(U2_ETHERTYPE, d_eth_buf->mac());
+ d_addr = p->addr;
+
+ // Create a packet filter for U2_ETHERTYPE packets sourced from target USRP2
+ u2_mac_addr_t usrp_mac;
+ parse_mac_addr(d_addr, &usrp_mac);
+ d_pf = pktfilter::make_ethertype_inbound_target(U2_ETHERTYPE, (const unsigned char*)&(usrp_mac.addr));
if (!d_pf || !d_eth_buf->attach_pktfilter(d_pf))
throw std::runtime_error("Unable to attach packet filter.");
- d_addr = p->addr;
-
if (USRP2_IMPL_DEBUG)
std::cerr << "usrp2 constructor: using USRP2 at " << d_addr << std::endl;
d_bg_thread = new usrp2_thread(this);
d_bg_thread->start();
+ // In case the USRP2 was left streaming RX
+ // FIXME: only one channel right now
+ stop_rx_streaming(0);
+
+ if (!dboard_info()) // we're hosed
+ throw std::runtime_error("Unable to retrieve daughterboard info");
+
+ if (0){
+ int dbid;
+
+ tx_daughterboard_id(&dbid);
+ fprintf(stderr, "Tx dboard 0x%x\n", dbid);
+ fprintf(stderr, " freq_min = %g\n", tx_freq_min());
+ fprintf(stderr, " freq_max = %g\n", tx_freq_max());
+ fprintf(stderr, " gain_min = %g\n", tx_gain_min());
+ fprintf(stderr, " gain_max = %g\n", tx_gain_max());
+ fprintf(stderr, " gain_db_per_step = %g\n", tx_gain_db_per_step());
+
+ rx_daughterboard_id(&dbid);
+ fprintf(stderr, "Rx dboard 0x%x\n", dbid);
+ fprintf(stderr, " freq_min = %g\n", rx_freq_min());
+ fprintf(stderr, " freq_max = %g\n", rx_freq_max());
+ fprintf(stderr, " gain_min = %g\n", rx_gain_min());
+ fprintf(stderr, " gain_max = %g\n", rx_gain_max());
+ fprintf(stderr, " gain_db_per_step = %g\n", rx_gain_db_per_step());
+ }
+
+ // Ensure any custom values in hardware are cleared
+ if (!reset_db())
+ std::cerr << "usrp2::ctor reset_db failed\n";
+
+ // default gains to mid point
+ if (!set_tx_gain((tx_gain_min() + tx_gain_max()) / 2))
+ std::cerr << "usrp2::ctor set_tx_gain failed\n";
+
+ if (!set_rx_gain((rx_gain_min() + rx_gain_max()) / 2))
+ std::cerr << "usrp2::ctor set_rx_gain failed\n";
+
+ // default interp and decim
+ if (!set_tx_interp(12))
+ std::cerr << "usrp2::ctor set_tx_interp failed\n";
+
+ if (!set_rx_decim(12))
+ std::cerr << "usrp2::ctor set_rx_decim failed\n";
+
// set workable defaults for scaling
if (!set_rx_scale_iq(DEFAULT_RX_SCALE, DEFAULT_RX_SCALE))
std::cerr << "usrp2::ctor set_rx_scale_iq failed\n";
-
- if (!set_tx_scale_iq(DEFAULT_TX_SCALE, DEFAULT_TX_SCALE))
- std::cerr << "usrp2::ctor set_tx_scale_iq failed\n";
}
usrp2::impl::~impl()
cmd->eop.len = sizeof(cmd->eop);
}
+
+ bool
+ usrp2::impl::transmit_cmd(void *cmd, size_t len)
+ {
+ return d_eth_buf->tx_frame(cmd, len) == eth_buffer::EB_OK;
+ }
+
bool
- usrp2::impl::transmit_cmd(void *cmd, size_t len, pending_reply *p, double secs)
+ usrp2::impl::transmit_cmd_and_wait(void *cmd, size_t len, pending_reply *p, double secs)
{
- if (p)
- d_pending_replies[p->rid()] = p;
+ d_pending_replies[p->rid()] = p;
- // Transmit command
- if (d_eth_buf->tx_frame(cmd, len) != eth_buffer::EB_OK) {
+ if (!transmit_cmd(cmd, len)){
d_pending_replies[p->rid()] = 0;
return false;
}
- int res = 1;
- if (p)
- res = p->wait(secs);
-
+ int res = p->wait_for_completion(secs);
d_pending_replies[p->rid()] = 0;
return res == 1;
}
return handle_control_packet(base, len);
}
else { // data packets
+
+ if (d_dont_enqueue) // toss packet
+ return data_handler::RELEASE;
+
return handle_data_packet(base, len);
}
// Copy reply into caller's buffer
memcpy(rp->buffer(), p, std::min(oplen, buflen));
- rp->signal();
+ rp->notify_completion();
d_pending_replies[rid] = 0;
return data_handler::RELEASE;
}
// FIXME unaligned load!
unsigned int chan = u2p_chan(&pkt->hdrs.fixed);
- if (!d_channel_rings[chan]) {
- DEBUG_LOG("!");
- return data_handler::RELEASE; // discard packet, no channel handler
- }
+ {
+ omni_mutex_lock l(d_channel_rings_mutex);
- // Strip off ethernet header and transport header and enqueue the rest
-
- size_t offset = offsetof(u2_eth_samples_t, hdrs.fixed);
- if (d_channel_rings[chan]->enqueue(&pkt->hdrs.fixed, len-offset)) {
- inc_enqueued();
- DEBUG_LOG("+");
- return data_handler::KEEP; // channel ring runner will mark frame done
+ if (!d_channel_rings[chan]) {
+ DEBUG_LOG("!");
+ return data_handler::RELEASE; // discard packet, no channel handler
+ }
+
+ // Strip off ethernet header and transport header and enqueue the rest
+
+ size_t offset = offsetof(u2_eth_samples_t, hdrs.fixed);
+ if (d_channel_rings[chan]->enqueue(&pkt->hdrs.fixed, len-offset)) {
+ inc_enqueued();
+ DEBUG_LOG("+");
+ return data_handler::KEEP; // channel ring runner will mark frame done
+ }
+ else {
+ DEBUG_LOG("!");
+ return data_handler::RELEASE; // discard, no room in channel ring
+ }
+ return data_handler::RELEASE;
}
- else {
- DEBUG_LOG("!");
- return data_handler::RELEASE; // discard, no room in channel ring
- }
- return data_handler::RELEASE;
- }
-
-
- // ----------------------------------------------------------------
- // misc commands
- // ----------------------------------------------------------------
-
- bool
- usrp2::impl::burn_mac_addr(const std::string &new_addr)
- {
- op_burn_mac_addr_cmd cmd;
- op_generic_t reply;
-
- memset(&cmd, 0, sizeof(cmd));
- init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
- cmd.op.opcode = OP_BURN_MAC_ADDR;
- cmd.op.len = sizeof(cmd.op);
- cmd.op.rid = d_next_rid++;
- if (!parse_mac_addr(new_addr, &cmd.op.addr))
- return false;
-
- pending_reply p(cmd.op.rid, &reply, sizeof(reply));
- if (!transmit_cmd(&cmd, sizeof(cmd), &p, 4*DEF_CMD_TIMEOUT))
- return false;
-
- bool success = (ntohx(reply.ok) == 1);
- return success;
}
cmd.op.gain = htons(u2_double_to_fxpt_gain(gain));
pending_reply p(cmd.op.rid, &reply, sizeof(reply));
- if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+ if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
return false;
bool success = (ntohx(reply.ok) == 1);
return success;
}
+ bool
+ usrp2::impl::set_rx_lo_offset(double frequency)
+ {
+ op_freq_cmd cmd;
+ op_generic_t reply;
+
+ memset(&cmd, 0, sizeof(cmd));
+ init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+ cmd.op.opcode = OP_SET_RX_LO_OFFSET;
+ cmd.op.len = sizeof(cmd.op);
+ cmd.op.rid = d_next_rid++;
+
+ u2_fxpt_freq_t fxpt = u2_double_to_fxpt_freq(frequency);
+ cmd.op.freq_hi = htonl(u2_fxpt_freq_hi(fxpt));
+ cmd.op.freq_lo = htonl(u2_fxpt_freq_lo(fxpt));
+
+ cmd.eop.opcode = OP_EOP;
+ cmd.eop.len = sizeof(cmd.eop);
+
+ pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+ if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+ return false;
+
+ bool success = (ntohx(reply.ok) == 1);
+ return success;
+ }
+
bool
usrp2::impl::set_rx_center_freq(double frequency, tune_result *result)
{
cmd.op.freq_lo = htonl(u2_fxpt_freq_lo(fxpt));
pending_reply p(cmd.op.rid, &reply, sizeof(reply));
- if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+ if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
return false;
bool success = (ntohx(reply.ok) == 1);
cmd.op.decim = htonl(decimation_factor);
pending_reply p(cmd.op.rid, &reply, sizeof(reply));
- if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+ if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
return false;
bool success = (ntohx(reply.ok) == 1);
+ if (success)
+ d_rx_decim = decimation_factor;
return success;
}
cmd.op.scale_iq = htonl(((scale_i & 0xffff) << 16) | (scale_q & 0xffff));
pending_reply p(cmd.op.rid, &reply, sizeof(reply));
- if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+ if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
return false;
bool success = (ntohx(reply.ok) == 1);
return false;
}
- if (d_channel_rings[channel]) {
- std::cerr << "usrp2: channel " << channel
- << " already streaming" << std::endl;
- return false;
- }
-
- d_channel_rings[channel] = ring_sptr(new ring(d_eth_buf->max_frames()));
-
- if (items_per_frame == 0)
- items_per_frame = U2_MAX_SAMPLES; // minimize overhead
-
- op_start_rx_streaming_cmd cmd;
- op_generic_t reply;
-
- memset(&cmd, 0, sizeof(cmd));
- init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
- cmd.op.opcode = OP_START_RX_STREAMING;
- cmd.op.len = sizeof(cmd.op);
- cmd.op.rid = d_next_rid++;
- cmd.op.items_per_frame = htonl(items_per_frame);
- cmd.eop.opcode = OP_EOP;
- cmd.eop.len = sizeof(cmd.eop);
+ {
+ omni_mutex_lock l(d_channel_rings_mutex);
+ if (d_channel_rings[channel]) {
+ std::cerr << "usrp2: channel " << channel
+ << " already streaming" << std::endl;
+ return false;
+ }
+
+ if (items_per_frame == 0)
+ items_per_frame = U2_MAX_SAMPLES; // minimize overhead
+
+ op_start_rx_streaming_cmd cmd;
+ op_generic_t reply;
+
+ memset(&cmd, 0, sizeof(cmd));
+ init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+ cmd.op.opcode = OP_START_RX_STREAMING;
+ cmd.op.len = sizeof(cmd.op);
+ cmd.op.rid = d_next_rid++;
+ cmd.op.items_per_frame = htonl(items_per_frame);
+ cmd.eop.opcode = OP_EOP;
+ cmd.eop.len = sizeof(cmd.eop);
- pending_reply p(cmd.op.rid, &reply, sizeof(reply));
- if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
- return false;
+ d_dont_enqueue = false;
+ bool success = false;
+ pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+ success = transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT);
+ success = success && (ntohx(reply.ok) == 1);
+
+ if (success)
+ d_channel_rings[channel] = ring_sptr(new ring(d_eth_buf->max_frames()));
+ else
+ d_dont_enqueue = true;
- bool success = (ntohx(reply.ok) == 1);
- return success;
+ //fprintf(stderr, "usrp2::start_rx_streaming: success = %d\n", success);
+ return success;
+ }
}
bool
return false;
}
-#if 0 // don't be overzealous.
- if (!d_channel_rings[channel]) {
- std::cerr << "usrp2: channel " << channel
- << " not streaming" << std::endl;
- return false;
- }
-#endif
+ d_dont_enqueue = true; // no new samples
+ flush_rx_samples(channel); // dump any we may already have
op_stop_rx_cmd cmd;
op_generic_t reply;
- memset(&cmd, 0, sizeof(cmd));
- init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
- cmd.op.opcode = OP_STOP_RX;
- cmd.op.len = sizeof(cmd.op);
- cmd.op.rid = d_next_rid++;
- cmd.eop.opcode = OP_EOP;
- cmd.eop.len = sizeof(cmd.eop);
-
- pending_reply p(cmd.op.rid, &reply, sizeof(reply));
- if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
- return false;
+ {
+ omni_mutex_lock l(d_channel_rings_mutex);
- bool success = (ntohx(reply.ok) == 1);
- if (success)
+ memset(&cmd, 0, sizeof(cmd));
+ init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+ cmd.op.opcode = OP_STOP_RX;
+ cmd.op.len = sizeof(cmd.op);
+ cmd.op.rid = d_next_rid++;
+ cmd.eop.opcode = OP_EOP;
+ cmd.eop.len = sizeof(cmd.eop);
+
+ bool success = false;
+ pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+ success = transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT);
+ success = success && (ntohx(reply.ok) == 1);
d_channel_rings[channel].reset();
-
- return success;
+ //fprintf(stderr, "usrp2::stop_rx_streaming: success = %d\n", success);
+ return success;
+ }
}
-
bool
usrp2::impl::rx_samples(unsigned int channel, rx_sample_handler *handler)
return true;
}
+ bool
+ usrp2::impl::flush_rx_samples(unsigned int channel)
+ {
+ if (channel > MAX_CHAN) {
+ std::cerr << "usrp2: invalid channel (" << channel
+ << " )" << std::endl;
+ return false;
+ }
+
+ if (channel > 0) {
+ std::cerr << "usrp2: channel " << channel
+ << " not implemented" << std::endl;
+ return false;
+ }
+
+ ring_sptr rp = d_channel_rings[channel];
+ if (!rp){
+ return false;
+ }
+
+ // Iterate through frames and drop them
+ void *p;
+ size_t frame_len_in_bytes;
+ while (rp->dequeue(&p, &frame_len_in_bytes)) {
+ d_eth_buf->release_frame(p);
+ dec_enqueued();
+ }
+ return true;
+ }
+
// ----------------------------------------------------------------
// Transmit
// ----------------------------------------------------------------
cmd.op.gain = htons(u2_double_to_fxpt_gain(gain));
pending_reply p(cmd.op.rid, &reply, sizeof(reply));
- if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+ if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
return false;
bool success = (ntohx(reply.ok) == 1);
return success;
}
+ bool
+ usrp2::impl::set_tx_lo_offset(double frequency)
+ {
+ op_freq_cmd cmd;
+ op_generic_t reply;
+
+ memset(&cmd, 0, sizeof(cmd));
+ init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+ cmd.op.opcode = OP_SET_TX_LO_OFFSET;
+ cmd.op.len = sizeof(cmd.op);
+ cmd.op.rid = d_next_rid++;
+
+ u2_fxpt_freq_t fxpt = u2_double_to_fxpt_freq(frequency);
+ cmd.op.freq_hi = htonl(u2_fxpt_freq_hi(fxpt));
+ cmd.op.freq_lo = htonl(u2_fxpt_freq_lo(fxpt));
+
+ cmd.eop.opcode = OP_EOP;
+ cmd.eop.len = sizeof(cmd.eop);
+
+ pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+ if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+ return false;
+
+ bool success = (ntohx(reply.ok) == 1);
+ return success;
+ }
+
bool
usrp2::impl::set_tx_center_freq(double frequency, tune_result *result)
{
cmd.op.freq_lo = htonl(u2_fxpt_freq_lo(fxpt));
pending_reply p(cmd.op.rid, &reply, sizeof(reply));
- if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+ if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
return false;
bool success = (ntohx(reply.ok) == 1);
cmd.op.interp = htonl(interpolation_factor);
pending_reply p(cmd.op.rid, &reply, sizeof(reply));
- if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+ if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
return false;
bool success = (ntohx(reply.ok) == 1);
+ if (success) {
+ d_tx_interp = interpolation_factor;
+
+ // Auto-set TX scaling based on interpolation rate
+ int scale_i, scale_q;
+ default_tx_scale_iq(d_tx_interp, &scale_i, &scale_q);
+ return set_tx_scale_iq(scale_i, scale_q);
+ }
+
return success;
}
+ void
+ usrp2::impl::default_tx_scale_iq(int interpolation_factor, int *scale_i, int *scale_q)
+ {
+ // Calculate CIC interpolation (i.e., without halfband interpolators)
+ int i = interpolation_factor;
+ if (i > 128)
+ i = i >> 1;
+ if (i > 128)
+ i = i >> 1;
+
+ // Calculate dsp_core_tx gain absent scale multipliers
+ float gain = (1.65*i*i*i)/(4096*pow(2, ceil(log2(i*i*i))));
+
+ // Calculate closest multiplier constant to reverse gain
+ int scale = (int)rint(1.0/gain);
+ // fprintf(stderr, "if=%i i=%i gain=%f scale=%i\n", interpolation_factor, i, gain, scale);
+
+ // Both I and Q are identical in this case
+ if (scale_i)
+ *scale_i = scale;
+ if (scale_q)
+ *scale_q = scale;
+ }
+
bool
usrp2::impl::set_tx_scale_iq(int scale_i, int scale_q)
{
cmd.op.scale_iq = htonl(((scale_i & 0xffff) << 16) | (scale_q & 0xffff));
pending_reply p(cmd.op.rid, &reply, sizeof(reply));
- if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+ if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
return false;
bool success = (ntohx(reply.ok) == 1);
}
bool
- usrp2::impl::tx_complex_float(unsigned int channel,
- const std::complex<float> *samples,
- size_t nsamples,
- const tx_metadata *metadata)
+ usrp2::impl::tx_32fc(unsigned int channel,
+ const std::complex<float> *samples,
+ size_t nsamples,
+ const tx_metadata *metadata)
{
uint32_t items[nsamples];
- copy_host_complex_float_to_u2_complex_16(nsamples, samples, items);
+ copy_host_32fc_to_u2_16sc(nsamples, samples, items);
return tx_raw(channel, items, nsamples, metadata);
}
bool
- usrp2::impl::tx_complex_int16(unsigned int channel,
- const std::complex<int16_t> *samples,
- size_t nsamples,
- const tx_metadata *metadata)
+ usrp2::impl::tx_16sc(unsigned int channel,
+ const std::complex<int16_t> *samples,
+ size_t nsamples,
+ const tx_metadata *metadata)
{
#ifdef WORDS_BIGENDIAN
#else
uint32_t items[nsamples];
- copy_host_complex_16_to_u2_complex_16(nsamples, samples, items);
+ copy_host_16sc_to_u2_16sc(nsamples, samples, items);
return tx_raw(channel, items, nsamples, metadata);
#endif
if (nitems == 0)
return true;
- // FIXME there's the possibility that we send fewer than 9 items in a frame.
- // That would end up glitching the transmitter, since the ethernet will pad to
- // 64-bytes total (9 items). We really need some part of the stack to
- // carry the real length (thdr?).
+ // FIXME can't deal with nitems < U2_MIN_SAMPLES (will be fixed in VRT)
+ // FIXME need to check the MTU instead of assuming 1500 bytes
// fragment as necessary then fire away
init_etf_hdrs(&hdrs, d_addr, flags, channel, timestamp);
- size_t i = std::min((size_t) U2_MAX_SAMPLES, nitems - n);
+ // Avoid short packet by splitting last two packets if reqd
+ size_t i;
+ if ((nitems - n) > U2_MAX_SAMPLES && (nitems - n) < (U2_MAX_SAMPLES + U2_MIN_SAMPLES))
+ i = (nitems - n) / 2;
+ else
+ i = std::min((size_t) U2_MAX_SAMPLES, nitems - n);
eth_iovec iov[2];
iov[0].iov_base = &hdrs;
iov[1].iov_base = const_cast<uint32_t *>(&items[n]);
iov[1].iov_len = i * sizeof(uint32_t);
+ size_t total = iov[0].iov_len + iov[1].iov_len;
+ if (total < 64)
+ fprintf(stderr, "usrp2::tx_raw: FIXME: short packet: %zd items (%zd bytes)\n", i, total);
+
if (d_eth_buf->tx_framev(iov, 2) != eth_buffer::EB_OK){
return false;
}
return true;
}
+ // ----------------------------------------------------------------
+ // misc commands
+ // ----------------------------------------------------------------
+
+ bool
+ usrp2::impl::config_mimo(int flags)
+ {
+ op_config_mimo_cmd cmd;
+ op_generic_t reply;
+
+ memset(&cmd, 0, sizeof(cmd));
+ init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+ cmd.op.opcode = OP_CONFIG_MIMO;
+ cmd.op.len = sizeof(cmd.op);
+ cmd.op.rid = d_next_rid++;
+ cmd.op.flags = flags;
+ cmd.eop.opcode = OP_EOP;
+ cmd.eop.len = sizeof(cmd.eop);
+
+ pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+ if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+ return false;
+
+ return ntohx(reply.ok) == 1;
+ }
+
+ bool
+ usrp2::impl::fpga_master_clock_freq(long *freq)
+ {
+ *freq = 100000000L; // 100 MHz
+ return true;
+ }
+
+ bool
+ usrp2::impl::adc_rate(long *rate)
+ {
+ return fpga_master_clock_freq(rate);
+ }
+
+ bool
+ usrp2::impl::dac_rate(long *rate)
+ {
+ return fpga_master_clock_freq(rate);
+ }
+
+ bool
+ usrp2::impl::tx_daughterboard_id(int *dbid)
+ {
+ *dbid = d_tx_db_info.dbid;
+ return true;
+ }
+
+ bool
+ usrp2::impl::rx_daughterboard_id(int *dbid)
+ {
+ *dbid = d_rx_db_info.dbid;
+ return true;
+ }
+
+
+ // ----------------------------------------------------------------
+ // low-level commands
+ // ----------------------------------------------------------------
+
+ bool
+ usrp2::impl::burn_mac_addr(const std::string &new_addr)
+ {
+ op_burn_mac_addr_cmd cmd;
+ op_generic_t reply;
+
+ memset(&cmd, 0, sizeof(cmd));
+ init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+ cmd.op.opcode = OP_BURN_MAC_ADDR;
+ cmd.op.len = sizeof(cmd.op);
+ cmd.op.rid = d_next_rid++;
+ if (!parse_mac_addr(new_addr, &cmd.op.addr))
+ return false;
+
+ pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+ if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, 4*DEF_CMD_TIMEOUT))
+ return false;
+
+ bool success = (ntohx(reply.ok) == 1);
+ return success;
+ }
+
+ static void
+ fill_dboard_info(db_info *dst, const u2_db_info_t *src)
+ {
+ dst->dbid = ntohl(src->dbid);
+
+ dst->freq_min =
+ u2_fxpt_freq_to_double(u2_fxpt_freq_from_hilo(ntohl(src->freq_min_hi),
+ ntohl(src->freq_min_lo)));
+ dst->freq_max =
+ u2_fxpt_freq_to_double(u2_fxpt_freq_from_hilo(ntohl(src->freq_max_hi),
+ ntohl(src->freq_max_lo)));
+
+ dst->gain_min = u2_fxpt_gain_to_double(ntohs(src->gain_min));
+ dst->gain_max = u2_fxpt_gain_to_double(ntohs(src->gain_max));
+ dst->gain_step_size = u2_fxpt_gain_to_double(ntohs(src->gain_step_size));
+ }
+
+ bool
+ usrp2::impl::dboard_info()
+ {
+ op_dboard_info_cmd cmd;
+ op_dboard_info_reply_t reply;
+
+ memset(&cmd, 0, sizeof(cmd));
+ init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+ cmd.op.opcode = OP_DBOARD_INFO;
+ cmd.op.len = sizeof(cmd.op);
+ cmd.op.rid = d_next_rid++;
+ cmd.eop.opcode = OP_EOP;
+ cmd.eop.len = sizeof(cmd.eop);
+
+ pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+ if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+ return false;
+
+ bool success = (ntohx(reply.ok) == 1);
+ if (success){
+ fill_dboard_info(&d_tx_db_info, &reply.tx_db_info);
+ fill_dboard_info(&d_rx_db_info, &reply.rx_db_info);
+ }
+ return success;
+ }
+
+
+ bool
+ usrp2::impl::sync_to_pps()
+ {
+ op_generic_cmd cmd;
+ op_generic_t reply;
+
+ memset(&cmd, 0, sizeof(cmd));
+ init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+ cmd.op.opcode = OP_SYNC_TO_PPS;
+ cmd.op.len = sizeof(cmd.op);
+ cmd.op.rid = d_next_rid++;
+ cmd.eop.opcode = OP_EOP;
+ cmd.eop.len = sizeof(cmd.eop);
+
+ pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+ if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+ return false;
+
+ return ntohx(reply.ok) == 1;
+ }
+
+ bool
+ usrp2::impl::sync_every_pps(bool enable)
+ {
+ op_generic_cmd cmd;
+ op_generic_t reply;
+
+ memset(&cmd, 0, sizeof(cmd));
+ init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+ cmd.op.opcode = OP_SYNC_EVERY_PPS;
+ cmd.op.len = sizeof(cmd.op);
+ cmd.op.rid = d_next_rid++;
+ cmd.op.ok = enable ? 1 : 0;
+ cmd.eop.opcode = OP_EOP;
+ cmd.eop.len = sizeof(cmd.eop);
+
+ pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+ if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+ return false;
+
+ return ntohx(reply.ok) == 1;
+ }
+
+ std::vector<uint32_t>
+ usrp2::impl::peek32(uint32_t addr, uint32_t words)
+ {
+ std::vector<uint32_t> result; // zero sized on error return
+ // fprintf(stderr, "usrp2::peek: addr=%08X words=%u\n", addr, words);
+
+ if (addr % 4 != 0) {
+ fprintf(stderr, "usrp2::peek: addr (=%08X) must be 32-bit word aligned\n", addr);
+ return result;
+ }
+
+ if (words == 0)
+ return result;
+
+ op_peek_cmd cmd;
+ op_generic_t *reply;
+
+ int wlen = sizeof(uint32_t);
+ int rlen = sizeof(op_generic_t);
+ size_t bytes = words*wlen;
+
+ memset(&cmd, 0, sizeof(cmd));
+ init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+ cmd.op.opcode = OP_PEEK;
+ cmd.op.len = sizeof(cmd.op);
+ cmd.op.rid = d_next_rid++;
+ cmd.eop.opcode = OP_EOP;
+ cmd.eop.len = sizeof(cmd.eop);
+
+ cmd.op.addr = htonl(addr);
+ cmd.op.bytes = htonl(bytes);
+
+ reply = (op_generic_t *)malloc(rlen+bytes);
+ pending_reply p(cmd.op.rid, reply, rlen+bytes);
+ if (transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT)) {
+ uint32_t nwords = (reply->len-rlen)/sizeof(uint32_t);
+ uint32_t *data = (uint32_t *)(reply+rlen/wlen);
+ for (unsigned int i = 0; i < nwords; i++)
+ result.push_back(ntohl(data[i]));
+ }
+
+ free(reply);
+ return result;
+ }
+
+ bool
+ usrp2::impl::poke32(uint32_t addr, const std::vector<uint32_t> &data)
+ {
+ if (addr % 4 != 0) {
+ fprintf(stderr, "usrp2::poke32: addr (=%08X) must be 32-bit word aligned\n", addr);
+ return false;
+ }
+
+ int plen = sizeof(op_poke_cmd);
+ int wlen = sizeof(uint32_t);
+ int max_words = (MAX_SUBPKT_LEN-plen)/wlen;
+ int words = data.size();
+
+ if (words > max_words) {
+ fprintf(stderr, "usrp2::poke32: write size (=%u) exceeds maximum of %u words\n",
+ words, max_words);
+ return false;
+ }
+
+ //fprintf(stderr, "usrp2::poke32: addr=%08X words=%u\n", addr, words);
+
+ if (words == 0)
+ return true; // NOP
+
+ op_poke_cmd *cmd;
+ op_generic_t *eop;
+
+ // Allocate, clear, and initialize command packet
+ int bytes = words*wlen;
+ int l = plen+bytes+sizeof(*eop); // op_poke_cmd+data+eop
+ cmd = (op_poke_cmd *)malloc(l);
+ //fprintf(stderr, "cmd=%p l=%i\n", cmd, l);
+ memset(cmd, 0, l);
+ init_etf_hdrs(&cmd->h, d_addr, 0, CONTROL_CHAN, -1);
+ cmd->op.opcode = OP_POKE;
+ cmd->op.len = sizeof(cmd->op)+bytes;
+ cmd->op.rid = d_next_rid++;
+ cmd->op.addr = htonl(addr);
+
+ // Copy data from vector into packet space
+ uint32_t *dest = (uint32_t *)((uint8_t *)cmd+plen);
+ for (int i = 0; i < words; i++) {
+ //fprintf(stderr, "%03i@%p\n", i, dest);
+ *dest++ = htonl(data[i]);
+ }
+
+ // Write end-of-packet subpacket
+ eop = (op_generic_t *)dest;
+ eop->opcode = OP_EOP;
+ eop->len = sizeof(*eop);
+ //fprintf(stderr, "eop=%p len=%i\n", eop, eop->len);
+
+ // Send command to device and retrieve reply
+ bool ok = false;
+ op_generic_t reply;
+ pending_reply p(cmd->op.rid, &reply, sizeof(reply));
+ if (transmit_cmd_and_wait(cmd, l, &p, DEF_CMD_TIMEOUT))
+ ok = (ntohx(reply.ok) == 1);
+
+ free(cmd);
+ return ok;
+ }
+
+ bool
+ usrp2::impl::reset_db()
+ {
+ op_generic_cmd cmd;
+ op_generic_t reply;
+
+ memset(&cmd, 0, sizeof(cmd));
+ init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+ cmd.op.opcode = OP_RESET_DB;
+ cmd.op.len = sizeof(cmd.op);
+ cmd.op.rid = d_next_rid++;
+ cmd.eop.opcode = OP_EOP;
+ cmd.eop.len = sizeof(cmd.eop);
+
+ pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+ if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+ return false;
+
+ bool success = (ntohx(reply.ok) == 1);
+ return success;
+ }
+
+ bool usrp2::impl::set_gpio_ddr(int bank, uint16_t value, uint16_t mask)
+ {
+ if (bank != GPIO_TX_BANK && bank != GPIO_RX_BANK) {
+ fprintf(stderr, "set_gpio_ddr: bank must be one of GPIO_RX_BANK or GPIO_TX_BANK\n");
+ return false;
+ }
+
+ op_gpio_cmd cmd;
+ op_generic_t reply;
+
+ memset(&cmd, 0, sizeof(cmd));
+ init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+ cmd.op.opcode = OP_GPIO_SET_DDR;
+ cmd.op.len = sizeof(cmd.op);
+ cmd.op.rid = d_next_rid++;
+ cmd.op.bank = static_cast<uint8_t>(bank);
+ cmd.op.value = htons(value);
+ cmd.op.mask = htons(mask);
+ cmd.eop.opcode = OP_EOP;
+ cmd.eop.len = sizeof(cmd.eop);
+
+ pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+ if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+ return false;
+
+ bool success = (ntohx(reply.ok) == 1);
+ return success;
+ }
+
+ bool usrp2::impl::set_gpio_sels(int bank, std::string sels)
+ {
+ if (bank != GPIO_TX_BANK && bank != GPIO_RX_BANK) {
+ fprintf(stderr, "set_gpio_ddr: bank must be one of GPIO_RX_BANK or GPIO_TX_BANK\n");
+ return false;
+ }
+
+ if (sels.size() != 16) {
+ fprintf(stderr, "set_gpio_sels: sels must be exactly 16 bytes\n");
+ return false;
+ }
+
+ op_gpio_set_sels_cmd cmd;
+ op_generic_t reply;
+
+ memset(&cmd, 0, sizeof(cmd));
+ init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+ cmd.op.opcode = OP_GPIO_SET_SELS;
+ cmd.op.len = sizeof(cmd.op);
+ cmd.op.rid = d_next_rid++;
+ cmd.op.bank = static_cast<uint8_t>(bank);
+ memcpy(&cmd.op.sels, sels.c_str(), 16);
+ cmd.eop.opcode = OP_EOP;
+ cmd.eop.len = sizeof(cmd.eop);
+
+ pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+ if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+ return false;
+
+ bool success = (ntohx(reply.ok) == 1);
+ return success;
+ }
+
+ bool usrp2::impl::write_gpio(int bank, uint16_t value, uint16_t mask)
+ {
+ if (bank != GPIO_TX_BANK && bank != GPIO_RX_BANK) {
+ fprintf(stderr, "set_gpio_ddr: bank must be one of GPIO_RX_BANK or GPIO_TX_BANK\n");
+ return false;
+ }
+
+ op_gpio_cmd cmd;
+ op_generic_t reply;
+
+ memset(&cmd, 0, sizeof(cmd));
+ init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+ cmd.op.opcode = OP_GPIO_WRITE;
+ cmd.op.len = sizeof(cmd.op);
+ cmd.op.rid = d_next_rid++;
+ cmd.op.bank = static_cast<uint8_t>(bank);
+ cmd.op.value = htons(value);
+ cmd.op.mask = htons(mask);
+ cmd.eop.opcode = OP_EOP;
+ cmd.eop.len = sizeof(cmd.eop);
+
+ pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+ if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+ return false;
+
+ bool success = (ntohx(reply.ok) == 1);
+ return success;
+ }
+
+ bool usrp2::impl::read_gpio(int bank, uint16_t *value)
+ {
+ if (bank != GPIO_TX_BANK && bank != GPIO_RX_BANK) {
+ fprintf(stderr, "set_gpio_ddr: bank must be one of GPIO_RX_BANK or GPIO_TX_BANK\n");
+ return false;
+ }
+
+ op_gpio_cmd cmd;
+ op_gpio_read_reply_t reply;
+
+ memset(&cmd, 0, sizeof(cmd));
+ init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+ cmd.op.opcode = OP_GPIO_READ;
+ cmd.op.len = sizeof(cmd.op);
+ cmd.op.rid = d_next_rid++;
+ cmd.op.bank = static_cast<uint8_t>(bank);
+ cmd.op.value = 0; // not used
+ cmd.op.mask = 0; // not used
+ cmd.eop.opcode = OP_EOP;
+ cmd.eop.len = sizeof(cmd.eop);
+
+ pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+ if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+ return false;
+
+ bool success = (ntohx(reply.ok) == 1);
+ if (success && (value != NULL))
+ *value = ntohs(reply.value);
+
+ return success;
+ }
+
+ bool usrp2::impl::enable_gpio_streaming(int bank, int enable)
+ {
+ if (bank != GPIO_RX_BANK) {
+ fprintf(stderr, "enable_gpio_streaming: only RX streaming is currently implemented\n");
+ return false;
+ }
+
+ if ((enable & ~0x03) != 0) {
+ fprintf(stderr, "enable_gpio_streaming: invalid enable format\n");
+ return false;
+ }
+
+ op_gpio_cmd cmd;
+ op_generic_t reply;
+
+ memset(&cmd, 0, sizeof(cmd));
+ init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+ cmd.op.opcode = OP_GPIO_STREAM;
+ cmd.op.len = sizeof(cmd.op);
+ cmd.op.rid = d_next_rid++;
+ cmd.op.bank = static_cast<uint8_t>(bank);
+ cmd.op.value = htons((uint16_t)enable);
+ cmd.op.mask = 0; // not used
+ cmd.eop.opcode = OP_EOP;
+ cmd.eop.len = sizeof(cmd.eop);
+
+ pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+ if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+ return false;
+
+ bool success = (ntohx(reply.ok) == 1);
+ return success;
+ }
} // namespace usrp2