From 397521fcdbe88d4d797bf9a5bcd2cdb097003ab3 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Sun, 12 Jun 2011 19:54:09 -0700 Subject: usrp2: super packet handler support squashed --- host/lib/usrp/usrp2/dsp_impl.cpp | 8 +- host/lib/usrp/usrp2/io_impl.cpp | 286 ++++++++++--------------------------- host/lib/usrp/usrp2/usrp2_impl.hpp | 1 - 3 files changed, 77 insertions(+), 218 deletions(-) (limited to 'host/lib/usrp') diff --git a/host/lib/usrp/usrp2/dsp_impl.cpp b/host/lib/usrp/usrp2/dsp_impl.cpp index 292659f36..03cdeae42 100644 --- a/host/lib/usrp/usrp2/dsp_impl.cpp +++ b/host/lib/usrp/usrp2/dsp_impl.cpp @@ -118,12 +118,6 @@ void usrp2_mboard_impl::issue_ddc_stream_cmd(const stream_cmd_t &stream_cmd, siz _iface->poke32(U2_REG_RX_CTRL_TIME_TICKS(which_dsp), stream_cmd.time_spec.get_tick_count(get_master_clock_freq())); } -void usrp2_mboard_impl::handle_overflow(size_t which_dsp){ - if (_dsp_impl->continuous_streaming[which_dsp]){ //re-issue the stream command if already continuous - this->issue_ddc_stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS, which_dsp); - } -} - /*********************************************************************** * DDC Properties **********************************************************************/ @@ -186,6 +180,7 @@ void usrp2_mboard_impl::ddc_set(const wax::obj &key_, const wax::obj &val, size_ dsp_type1::calc_iq_scale_word(default_rx_scale_iq, default_rx_scale_iq) ); } + _device.update_xport_channel_mapping(); //rate changed -> update return; default: UHD_THROW_PROP_SET_ERROR(); @@ -259,6 +254,7 @@ void usrp2_mboard_impl::duc_set(const wax::obj &key_, const wax::obj &val, size_ //set the scaling _iface->poke32(U2_REG_DSP_TX_SCALE_IQ, dsp_type1::calc_iq_scale_word(_dsp_impl->duc_interp[which_dsp])); } + _device.update_xport_channel_mapping(); //rate changed -> update return; default: UHD_THROW_PROP_SET_ERROR(); diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index 33f249599..ffe9a88e7 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -15,18 +15,21 @@ // along with this program. If not, see . // -#include "../../transport/vrt_packet_handler.hpp" +#include "../../transport/super_recv_packet_handler.hpp" +#include "../../transport/super_send_packet_handler.hpp" #include "usrp2_impl.hpp" #include "usrp2_regs.hpp" #include #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -79,15 +82,22 @@ public: _ready_fcn = boost::bind(&flow_control_monitor::ready, this); } + /*! + * Gets the current sequence number to go out. + * Increments the sequence for the next call + * \return the sequence to be sent to the dsp + */ + UHD_INLINE seq_type get_curr_seq_out(void){ + return _last_seq_out++; + } + /*! * Check the flow control condition. - * \param seq the sequence to go out * \param timeout the timeout in seconds * \return false on timeout */ - UHD_INLINE bool check_fc_condition(seq_type seq, double timeout){ - boost::unique_lock lock(_fc_mutex); - _last_seq_out = seq; + UHD_INLINE bool check_fc_condition(double timeout){ + boost::mutex::scoped_lock lock(_fc_mutex); if (this->ready()) return true; boost::this_thread::disable_interruption di; //disable because the wait can throw return _fc_cond.timed_wait(lock, to_time_dur(timeout), _ready_fcn); @@ -98,7 +108,7 @@ public: * \param seq the last sequence number to be ACK'd */ UHD_INLINE void update_fc_condition(seq_type seq){ - boost::unique_lock lock(_fc_mutex); + boost::mutex::scoped_lock lock(_fc_mutex); _last_seq_ack = seq; lock.unlock(); _fc_cond.notify_one(); @@ -115,23 +125,6 @@ private: boost::function _ready_fcn; }; -/*********************************************************************** - * Alignment indexes class: keeps track of indexes - **********************************************************************/ -class alignment_indexes{ -public: - alignment_indexes(void){_indexes = 0;} - void reset(size_t len){_indexes = (1 << len) - 1;} - size_t front(void){ //TODO replace with look-up table - size_t index = 0; - while ((_indexes & (1 << index)) == 0) index++; - return index; - } - void remove(size_t index){_indexes &= ~(1 << index);} - bool empty(void){return _indexes == 0;} -private: size_t _indexes; -}; - /*********************************************************************** * io impl details (internal to this file) * - pirate crew @@ -143,24 +136,13 @@ struct usrp2_impl::io_impl{ io_impl(std::vector &dsp_xports): dsp_xports(dsp_xports), //the assumption is that all data transports should be identical - get_recv_buffs_fcn(boost::bind(&usrp2_impl::io_impl::get_recv_buffs, this, _1)), - get_send_buffs_fcn(boost::bind(&usrp2_impl::io_impl::get_send_buffs, this, _1)), async_msg_fifo(100/*messages deep*/) { for (size_t i = 0; i < dsp_xports.size(); i++){ fc_mons.push_back(flow_control_monitor::sptr(new flow_control_monitor( usrp2_impl::sram_bytes/dsp_xports.front()->get_send_frame_size() - )));; + ))); } - - //init empty packet infos - vrt::if_packet_info_t packet_info = vrt::if_packet_info_t(); - packet_info.packet_count = 0xf; - packet_info.has_tsi = true; - packet_info.tsi = 0; - packet_info.has_tsf = true; - packet_info.tsf = 0; - prev_infos.resize(dsp_xports.size(), packet_info); } ~io_impl(void){ @@ -169,38 +151,27 @@ struct usrp2_impl::io_impl{ recv_pirate_crew.join_all(); } - bool get_send_buffs(vrt_packet_handler::managed_send_buffs_t &buffs){ - UHD_ASSERT_THROW(send_map.size() == buffs.size()); + managed_send_buffer::sptr get_send_buff(size_t chan, double timeout){ + const size_t index = send_map[chan]; + flow_control_monitor &fc_mon = *fc_mons[index]; - //calculate the flow control word - const boost::uint32_t fc_word32 = packet_handler_send_state.next_packet_seq; + //wait on flow control w/ timeout + if (not fc_mon.check_fc_condition(timeout)) return managed_send_buffer::sptr(); - //grab a managed buffer for each index - for (size_t i = 0; i < buffs.size(); i++){ - if (not fc_mons[send_map[i]]->check_fc_condition(fc_word32, send_timeout)) return false; - buffs[i] = dsp_xports[send_map[i]]->get_send_buff(send_timeout); - if (not buffs[i].get()) return false; - buffs[i]->cast()[0] = uhd::htonx(fc_word32); - } - return true; - } + //get a buffer from the transport w/ timeout + managed_send_buffer::sptr buff = dsp_xports[index]->get_send_buff(timeout); + + //write the flow control word into the buffer + if (buff.get()) buff->cast()[0] = uhd::htonx(fc_mon.get_curr_seq_out()); - alignment_indexes indexes_to_do; //used in alignment logic - time_spec_t expected_time; //used in alignment logic - bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs); + return buff; + } std::vector &dsp_xports; //mappings from channel index to dsp xport std::vector send_map, recv_map; - //timeouts set on calls to recv/send (passed into get buffs methods) - double recv_timeout, send_timeout; - - //bound callbacks for get buffs (bound once here, not in fast-path) - vrt_packet_handler::get_recv_buffs_t get_recv_buffs_fcn; - vrt_packet_handler::get_send_buffs_t get_send_buffs_fcn; - //previous state for each buffer std::vector prev_infos; @@ -208,8 +179,8 @@ struct usrp2_impl::io_impl{ std::vector fc_mons; //state management for the vrt packet handler code - vrt_packet_handler::recv_state packet_handler_recv_state; - vrt_packet_handler::send_state packet_handler_send_state; + sph::recv_packet_handler recv_handler; + sph::send_packet_handler send_handler; //methods and variables for the pirate crew void recv_pirate_loop(boost::barrier &, usrp2_mboard_impl::sptr, zero_copy_if::sptr, size_t); @@ -258,7 +229,7 @@ void usrp2_impl::io_impl::recv_pirate_loop( metadata.time_spec = time_spec_t( time_t(if_packet_info.tsi), size_t(if_packet_info.tsf), mboard->get_master_clock_freq() ); - metadata.event_code = vrt_packet_handler::get_context_code(vrt_hdr, if_packet_info); + metadata.event_code = async_metadata_t::event_code_t(sph::get_context_code(vrt_hdr, if_packet_info)); //catch the flow control packets and react if (metadata.event_code == 0){ @@ -327,8 +298,39 @@ void usrp2_impl::update_xport_channel_mapping(void){ } - _io_impl->packet_handler_recv_state = vrt_packet_handler::recv_state(_io_impl->recv_map.size()); - _io_impl->packet_handler_send_state = vrt_packet_handler::send_state(_io_impl->send_map.size()); + //set all of the relevant properties on the handler + boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); + _io_impl->recv_handler.resize(_io_impl->recv_map.size()); + _io_impl->recv_handler.set_vrt_unpacker(&vrt::if_hdr_unpack_be); + _io_impl->recv_handler.set_tick_rate(_mboards.front()->get_master_clock_freq()); + //TODO temporarily use the first dsp rate until we support non-homo rates + const std::string rx_dsp_name = _mboards.at(0)->get_link()[MBOARD_PROP_RX_DSP_NAMES].as().at(0); + const double rx_host_rate = _mboards.at(0)->get_link()[named_prop_t(MBOARD_PROP_RX_DSP, rx_dsp_name)][DSP_PROP_HOST_RATE].as(); + _io_impl->recv_handler.set_samp_rate(rx_host_rate); + for (size_t chan = 0; chan < _io_impl->recv_handler.size(); chan++){ + _io_impl->recv_handler.set_xport_chan_get_buff(chan, boost::bind( + &uhd::transport::zero_copy_if::get_recv_buff, + _io_impl->dsp_xports[_io_impl->recv_map[chan]], _1 + )); + } + _io_impl->recv_handler.set_converter(_rx_otw_type); + + //set all of the relevant properties on the handler + boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); + _io_impl->send_handler.resize(_io_impl->send_map.size()); + _io_impl->send_handler.set_vrt_packer(&vrt::if_hdr_pack_be, vrt_send_header_offset_words32); + _io_impl->send_handler.set_tick_rate(_mboards.front()->get_master_clock_freq()); + //TODO temporarily use the first dsp rate until we support non-homo rates + const std::string tx_dsp_name = _mboards.at(0)->get_link()[MBOARD_PROP_TX_DSP_NAMES].as().at(0); + const double tx_host_rate = _mboards.at(0)->get_link()[named_prop_t(MBOARD_PROP_TX_DSP, tx_dsp_name)][DSP_PROP_HOST_RATE].as(); + _io_impl->send_handler.set_samp_rate(tx_host_rate); + for (size_t chan = 0; chan < _io_impl->send_handler.size(); chan++){ + _io_impl->send_handler.set_xport_chan_get_buff(chan, boost::bind( + &usrp2_impl::io_impl::get_send_buff, _io_impl.get(), chan, _1 + )); + } + _io_impl->send_handler.set_converter(_tx_otw_type); + _io_impl->send_handler.set_max_samples_per_packet(get_max_send_samps_per_packet()); } /*********************************************************************** @@ -355,143 +357,17 @@ size_t usrp2_impl::get_max_send_samps_per_packet(void) const{ } size_t usrp2_impl::send( - const send_buffs_type &buffs, size_t num_samps, + const send_buffs_type &buffs, size_t nsamps_per_buff, const tx_metadata_t &metadata, const io_type_t &io_type, send_mode_t send_mode, double timeout ){ - _io_impl->send_timeout = timeout; - return vrt_packet_handler::send( - _io_impl->packet_handler_send_state, //last state of the send handler - buffs, num_samps, //buffer to fill - metadata, send_mode, //samples metadata - io_type, _tx_otw_type, //input and output types to convert - _mboards.front()->get_master_clock_freq(), //master clock tick rate - uhd::transport::vrt::if_hdr_pack_be, - _io_impl->get_send_buffs_fcn, - get_max_send_samps_per_packet(), - vrt_send_header_offset_words32 - ); -} - -/*********************************************************************** - * Alignment logic on receive - **********************************************************************/ -static UHD_INLINE time_spec_t extract_time_spec( - const vrt::if_packet_info_t &packet_info -){ - return time_spec_t( //assumes has_tsi and has_tsf are true - time_t(packet_info.tsi), size_t(packet_info.tsf), - 100e6 //tick rate does not have to be correct for comparison purposes + return _io_impl->send_handler.send( + buffs, nsamps_per_buff, + metadata, io_type, + send_mode, timeout ); } -static UHD_INLINE void extract_packet_info( - managed_recv_buffer::sptr &buff, - vrt::if_packet_info_t &prev_info, - time_spec_t &time, bool &clear, bool &msg -){ - //extract packet info - vrt::if_packet_info_t next_info; - next_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); - vrt::if_hdr_unpack_be(buff->cast(), next_info); - - //handle the packet count / sequence number - if ((prev_info.packet_count+1)%16 != next_info.packet_count){ - UHD_MSG(fastpath) << "O"; //report overflow (drops in the kernel) - } - - time = extract_time_spec(next_info); - clear = extract_time_spec(prev_info) > time; - msg = next_info.packet_type != vrt::if_packet_info_t::PACKET_TYPE_DATA; - prev_info = next_info; -} - -static UHD_INLINE bool handle_msg_packet( - vrt_packet_handler::managed_recv_buffs_t &buffs, size_t index -){ - for (size_t i = 0; i < buffs.size(); i++){ - if (i == index) continue; - buffs[i].reset(); //set NULL - } - return true; -} - -UHD_INLINE bool usrp2_impl::io_impl::get_recv_buffs( - vrt_packet_handler::managed_recv_buffs_t &buffs -){ - if (buffs.size() == 1){ - buffs[0] = dsp_xports[recv_map[0]]->get_recv_buff(recv_timeout); - if (buffs[0].get() == NULL) return false; - bool clear, msg; time_spec_t time; //unused variables - //call extract_packet_info to handle printing the overflows - extract_packet_info(buffs[0], this->prev_infos[recv_map[0]], time, clear, msg); - return true; - } - //-------------------- begin alignment logic ---------------------// - UHD_ASSERT_THROW(recv_map.size() == buffs.size()); - boost::system_time exit_time = boost::get_system_time() + to_time_dur(recv_timeout); - managed_recv_buffer::sptr buff_tmp; - bool clear, msg; - size_t index; - - //If we did not enter this routine with an empty indexes set, - //jump to after the clear so we can preserve the previous state. - //This saves buffers from being lost when using non-blocking recv. - if (not indexes_to_do.empty()) goto skip_pop_initial; - - //respond to a clear by starting from scratch - got_clear: - indexes_to_do.reset(buffs.size()); - clear = false; - - //do an initial pop to load an initial sequence id - index = indexes_to_do.front(); - buff_tmp = dsp_xports[recv_map[index]]->get_recv_buff(from_time_dur(exit_time - boost::get_system_time())); - if (buff_tmp.get() == NULL) return false; - extract_packet_info(buff_tmp, this->prev_infos[recv_map[index]], expected_time, clear, msg); - if (clear) goto got_clear; - buffs[index] = buff_tmp; - if (msg) return handle_msg_packet(buffs, index); - indexes_to_do.remove(index); - skip_pop_initial: - - //get an aligned set of elements from the buffers: - while(not indexes_to_do.empty()){ - - //pop an element off for this index - index = indexes_to_do.front(); - buff_tmp = dsp_xports[recv_map[index]]->get_recv_buff(from_time_dur(exit_time - boost::get_system_time())); - if (buff_tmp.get() == NULL) return false; - time_spec_t this_time; - extract_packet_info(buff_tmp, this->prev_infos[recv_map[index]], this_time, clear, msg); - if (clear) goto got_clear; - buffs[index] = buff_tmp; - if (msg) return handle_msg_packet(buffs, index); - - //if the sequence id matches: - // remove this index from the list and continue - if (this_time == expected_time){ - indexes_to_do.remove(index); - } - - //if the sequence id is newer: - // use the new expected time for comparison - // add all other indexes back into the list - else if (this_time > expected_time){ - expected_time = this_time; - indexes_to_do.reset(buffs.size()); - indexes_to_do.remove(index); - } - - //if the sequence id is older: - // continue with the same index to try again - //else if (this_time < expected_time)... - - } - return true; - //-------------------- end alignment logic -----------------------// -} - /*********************************************************************** * Receive Data **********************************************************************/ @@ -505,26 +381,14 @@ size_t usrp2_impl::get_max_recv_samps_per_packet(void) const{ return bpp/_rx_otw_type.get_sample_size(); } -void usrp2_impl::handle_overflow(size_t chan){ - UHD_MSG(fastpath) << "O"; - ldiv_t indexes = ldiv(chan, usrp2_mboard_impl::NUM_RX_DSPS); - _mboards.at(indexes.quot)->handle_overflow(indexes.rem); -} - size_t usrp2_impl::recv( - const recv_buffs_type &buffs, size_t num_samps, + const recv_buffs_type &buffs, size_t nsamps_per_buff, rx_metadata_t &metadata, const io_type_t &io_type, recv_mode_t recv_mode, double timeout ){ - _io_impl->recv_timeout = timeout; - return vrt_packet_handler::recv( - _io_impl->packet_handler_recv_state, //last state of the recv handler - buffs, num_samps, //buffer to fill - metadata, recv_mode, //samples metadata - io_type, _rx_otw_type, //input and output types to convert - _mboards.front()->get_master_clock_freq(), //master clock tick rate - uhd::transport::vrt::if_hdr_unpack_be, - _io_impl->get_recv_buffs_fcn, - boost::bind(&usrp2_impl::handle_overflow, this, _1) + return _io_impl->recv_handler.recv( + buffs, nsamps_per_buff, + metadata, io_type, + recv_mode, timeout ); } diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index ccaf0c9a8..4d19863b1 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -220,7 +220,6 @@ private: uhd::otw_type_t _rx_otw_type, _tx_otw_type; UHD_PIMPL_DECL(io_impl) _io_impl; void io_init(void); - void handle_overflow(size_t); }; #endif /* INCLUDED_USRP2_IMPL_HPP */ -- cgit v1.2.3 From 89fa3291548b2b2c413c9613977db032b5dc048b Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Sun, 12 Jun 2011 19:54:42 -0700 Subject: usrp1: super packet handler support squashed --- host/lib/usrp/usrp1/dsp_impl.cpp | 5 +- host/lib/usrp/usrp1/io_impl.cpp | 180 +++++++++++++++++------------------- host/lib/usrp/usrp1/mboard_impl.cpp | 2 + host/lib/usrp/usrp1/usrp1_impl.cpp | 6 +- host/lib/usrp/usrp1/usrp1_impl.hpp | 3 + 5 files changed, 98 insertions(+), 98 deletions(-) (limited to 'host/lib/usrp') diff --git a/host/lib/usrp/usrp1/dsp_impl.cpp b/host/lib/usrp/usrp1/dsp_impl.cpp index 66b11b989..0bddc49f0 100644 --- a/host/lib/usrp/usrp1/dsp_impl.cpp +++ b/host/lib/usrp/usrp1/dsp_impl.cpp @@ -114,6 +114,7 @@ void usrp1_impl::rx_dsp_set(const wax::obj &key_, const wax::obj &val, size_t wh _iface->poke32(FR_DECIM_RATE, _rx_dsp_decim/2 - 1); this->restore_rx(s); } + this->update_xport_channel_mapping(); //rate changed -> update return; case DSP_PROP_STREAM_CMD: @@ -211,8 +212,10 @@ void usrp1_impl::tx_dsp_set(const wax::obj &key_, const wax::obj &val, size_t wh bool s = this->disable_tx(); _iface->poke32(FR_INTERP_RATE, _tx_dsp_interp/2 - 1); this->restore_tx(s); - return; } + this->update_xport_channel_mapping(); //rate changed -> update + return; + default: UHD_THROW_PROP_SET_ERROR(); } diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp index 8ac2696eb..90ed17cd8 100644 --- a/host/lib/usrp/usrp1/io_impl.cpp +++ b/host/lib/usrp/usrp1/io_impl.cpp @@ -15,12 +15,15 @@ // along with this program. If not, see . // -#include "../../transport/vrt_packet_handler.hpp" +#define SRPH_DONT_CHECK_SEQUENCE +#include "../../transport/super_recv_packet_handler.hpp" +#include "../../transport/super_send_packet_handler.hpp" #include "usrp_commands.h" #include "usrp1_impl.hpp" #include #include #include +#include #include #include #include @@ -87,14 +90,40 @@ private: commit_cb_type _commit_cb; }; +/*********************************************************************** + * BS VRT packer/unpacker functions (since samples don't have headers) + **********************************************************************/ +static void usrp1_bs_vrt_packer( + boost::uint32_t *, + vrt::if_packet_info_t &if_packet_info +){ + if_packet_info.num_header_words32 = 0; + if_packet_info.num_packet_words32 = if_packet_info.num_payload_words32; +} + +static void usrp1_bs_vrt_unpacker( + const boost::uint32_t *, + vrt::if_packet_info_t &if_packet_info +){ + if_packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA; + if_packet_info.num_payload_words32 = if_packet_info.num_packet_words32; + if_packet_info.num_header_words32 = 0; + if_packet_info.packet_count = 0; + if_packet_info.sob = false; + if_packet_info.eob = false; + if_packet_info.has_sid = false; + if_packet_info.has_cid = false; + if_packet_info.has_tsi = false; + if_packet_info.has_tsf = false; + if_packet_info.has_tlr = false; +} + /*********************************************************************** * IO Implementation Details **********************************************************************/ struct usrp1_impl::io_impl{ io_impl(zero_copy_if::sptr data_transport): data_transport(data_transport), - get_recv_buffs_fcn(boost::bind(&usrp1_impl::io_impl::get_recv_buffs, this, _1)), - get_send_buffs_fcn(boost::bind(&usrp1_impl::io_impl::get_send_buffs, this, _1)), underflow_poll_samp_count(0), overflow_poll_samp_count(0), curr_buff(offset_send_buffer(data_transport->get_send_buff())), @@ -109,16 +138,9 @@ struct usrp1_impl::io_impl{ zero_copy_if::sptr data_transport; - //timeouts set on calls to recv/send (passed into get buffs methods) - double recv_timeout, send_timeout; - - //bound callbacks for get buffs (bound once here, not in fast-path) - vrt_packet_handler::get_recv_buffs_t get_recv_buffs_fcn; - vrt_packet_handler::get_send_buffs_t get_send_buffs_fcn; - //state management for the vrt packet handler code - vrt_packet_handler::recv_state packet_handler_recv_state; - vrt_packet_handler::send_state packet_handler_send_state; + sph::recv_packet_handler recv_handler; + sph::send_packet_handler send_handler; //state management for overflow and underflow size_t underflow_poll_samp_count; @@ -132,11 +154,13 @@ struct usrp1_impl::io_impl{ offset_managed_send_buffer omsb; void commit_send_buff(offset_send_buffer&, offset_send_buffer&, size_t); void flush_send_buff(void); - bool get_send_buffs(vrt_packet_handler::managed_send_buffs_t &); - bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs){ - UHD_ASSERT_THROW(buffs.size() == 1); - buffs[0] = data_transport->get_recv_buff(recv_timeout); - return buffs[0].get() != NULL; + managed_send_buffer::sptr get_send_buff(double timeout){ + //try to get a new managed buffer with timeout + offset_send_buffer next_buff(data_transport->get_send_buff(timeout)); + if (not next_buff.buff.get()) return managed_send_buffer::sptr(); /* propagate timeout here */ + + //make a new managed buffer with the offset buffs + return omsb.get_new(curr_buff, next_buff); } }; @@ -185,32 +209,13 @@ void usrp1_impl::io_impl::flush_send_buff(void){ if (bytes_to_pad == 0) bytes_to_pad = alignment_padding; //get the buffer, clear, and commit (really current buffer) - vrt_packet_handler::managed_send_buffs_t buffs(1); - if (this->get_send_buffs(buffs)){ - std::memset(buffs[0]->cast(), 0, bytes_to_pad); - buffs[0]->commit(bytes_to_pad); + managed_send_buffer::sptr buff = this->get_send_buff(.1); + if (buff.get() != NULL){ + std::memset(buff->cast(), 0, bytes_to_pad); + buff->commit(bytes_to_pad); } } -/*! - * Get a managed send buffer with the alignment padding: - * Always grab the next send buffer so we can timeout here. - */ -bool usrp1_impl::io_impl::get_send_buffs( - vrt_packet_handler::managed_send_buffs_t &buffs -){ - UHD_ASSERT_THROW(buffs.size() == 1); - - //try to get a new managed buffer with timeout - offset_send_buffer next_buff(data_transport->get_send_buff(send_timeout)); - if (not next_buff.buff.get()) return false; /* propagate timeout here */ - - //make a new managed buffer with the offset buffs - buffs[0] = omsb.get_new(curr_buff, next_buff); - - return true; -} - /*********************************************************************** * Initialize internals within this file **********************************************************************/ @@ -232,6 +237,34 @@ void usrp1_impl::io_init(void){ this->enable_tx(true); //always enabled rx_stream_on_off(false); _io_impl->flush_send_buff(); + + //update mapping here since it didnt b4 when io init not called first + update_xport_channel_mapping(); +} + +void usrp1_impl::update_xport_channel_mapping(void){ + if (_io_impl.get() == NULL) return; //not inited yet + + //set all of the relevant properties on the handler + boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); + _io_impl->recv_handler.set_vrt_unpacker(&usrp1_bs_vrt_unpacker); + _io_impl->recv_handler.set_tick_rate(_clock_ctrl->get_master_clock_freq()); + _io_impl->recv_handler.set_samp_rate(_rx_dsp_proxies[_rx_dsp_proxies.keys().at(0)]->get_link()[DSP_PROP_HOST_RATE].as()); + _io_impl->recv_handler.set_xport_chan_get_buff(0, boost::bind( + &uhd::transport::zero_copy_if::get_recv_buff, _io_impl->data_transport, _1 + )); + _io_impl->recv_handler.set_converter(_rx_otw_type, _rx_subdev_spec.size()); + + //set all of the relevant properties on the handler + boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); + _io_impl->send_handler.set_vrt_packer(&usrp1_bs_vrt_packer); + _io_impl->send_handler.set_tick_rate(_clock_ctrl->get_master_clock_freq()); + _io_impl->send_handler.set_samp_rate(_tx_dsp_proxies[_tx_dsp_proxies.keys().at(0)]->get_link()[DSP_PROP_HOST_RATE].as()); + _io_impl->send_handler.set_xport_chan_get_buff(0, boost::bind( + &usrp1_impl::io_impl::get_send_buff, _io_impl.get(), _1 + )); + _io_impl->send_handler.set_converter(_tx_otw_type, _tx_subdev_spec.size()); + _io_impl->send_handler.set_max_samples_per_packet(get_max_send_samps_per_packet()); } void usrp1_impl::rx_stream_on_off(bool enb){ @@ -245,14 +278,6 @@ void usrp1_impl::rx_stream_on_off(bool enb){ /*********************************************************************** * Data send + helper functions **********************************************************************/ -static void usrp1_bs_vrt_packer( - boost::uint32_t *, - vrt::if_packet_info_t &if_packet_info -){ - if_packet_info.num_header_words32 = 0; - if_packet_info.num_packet_words32 = if_packet_info.num_payload_words32; -} - size_t usrp1_impl::get_max_send_samps_per_packet(void) const { return (_data_transport->get_send_frame_size() - alignment_padding) / _tx_otw_type.get_sample_size() @@ -261,29 +286,21 @@ size_t usrp1_impl::get_max_send_samps_per_packet(void) const { } size_t usrp1_impl::send( - const send_buffs_type &buffs, size_t num_samps, + const send_buffs_type &buffs, size_t nsamps_per_buff, const tx_metadata_t &metadata, const io_type_t &io_type, send_mode_t send_mode, double timeout ){ - if (_soft_time_ctrl->send_pre(metadata, timeout)) return num_samps; - - _io_impl->send_timeout = timeout; - size_t num_samps_sent = vrt_packet_handler::send( - _io_impl->packet_handler_send_state, //last state of the send handler - buffs, num_samps, //buffer to fill - metadata, send_mode, //samples metadata - io_type, _tx_otw_type, //input and output types to convert - _clock_ctrl->get_master_clock_freq(), //master clock tick rate - &usrp1_bs_vrt_packer, - _io_impl->get_send_buffs_fcn, - get_max_send_samps_per_packet(), - 0, //vrt header offset - _tx_subdev_spec.size() //num channels + if (_soft_time_ctrl->send_pre(metadata, timeout)) return 0; + + size_t num_samps_sent = _io_impl->send_handler.send( + buffs, nsamps_per_buff, + metadata, io_type, + send_mode, timeout ); //handle eob flag (commit the buffer, disable the DACs) //check num samps sent to avoid flush on incomplete/timeout - if (metadata.end_of_burst and num_samps_sent == num_samps){ + if (metadata.end_of_burst and num_samps_sent == nsamps_per_buff){ _io_impl->flush_send_buff(); } @@ -306,23 +323,6 @@ size_t usrp1_impl::send( /*********************************************************************** * Data recv + helper functions **********************************************************************/ -static void usrp1_bs_vrt_unpacker( - const boost::uint32_t *, - vrt::if_packet_info_t &if_packet_info -){ - if_packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA; - if_packet_info.num_payload_words32 = if_packet_info.num_packet_words32; - if_packet_info.num_header_words32 = 0; - if_packet_info.packet_count = 0; - if_packet_info.sob = false; - if_packet_info.eob = false; - if_packet_info.has_sid = false; - if_packet_info.has_cid = false; - if_packet_info.has_tsi = false; - if_packet_info.has_tsf = false; - if_packet_info.has_tlr = false; -} - size_t usrp1_impl::get_max_recv_samps_per_packet(void) const { return _data_transport->get_recv_frame_size() / _rx_otw_type.get_sample_size() @@ -331,22 +331,14 @@ size_t usrp1_impl::get_max_recv_samps_per_packet(void) const { } size_t usrp1_impl::recv( - const recv_buffs_type &buffs, size_t num_samps, + const recv_buffs_type &buffs, size_t nsamps_per_buff, rx_metadata_t &metadata, const io_type_t &io_type, recv_mode_t recv_mode, double timeout ){ - _io_impl->recv_timeout = timeout; - size_t num_samps_recvd = vrt_packet_handler::recv( - _io_impl->packet_handler_recv_state, //last state of the recv handler - buffs, num_samps, //buffer to fill - metadata, recv_mode, //samples metadata - io_type, _rx_otw_type, //input and output types to convert - _clock_ctrl->get_master_clock_freq(), //master clock tick rate - &usrp1_bs_vrt_unpacker, - _io_impl->get_recv_buffs_fcn, - &vrt_packet_handler::handle_overflow_nop, - 0, //vrt header offset - _rx_subdev_spec.size() //num channels + size_t num_samps_recvd = _io_impl->recv_handler.recv( + buffs, nsamps_per_buff, + metadata, io_type, + recv_mode, timeout ); _soft_time_ctrl->recv_post(metadata, num_samps_recvd); diff --git a/host/lib/usrp/usrp1/mboard_impl.cpp b/host/lib/usrp/usrp1/mboard_impl.cpp index f699c8e12..87911b312 100644 --- a/host/lib/usrp/usrp1/mboard_impl.cpp +++ b/host/lib/usrp/usrp1/mboard_impl.cpp @@ -352,6 +352,7 @@ void usrp1_impl::mboard_set(const wax::obj &key, const wax::obj &val) bool s = this->disable_rx(); _iface->poke32(FR_RX_MUX, calc_rx_mux(_rx_subdev_spec, _mboard_proxy->get_link())); this->restore_rx(s); + this->update_xport_channel_mapping(); }return; case MBOARD_PROP_TX_SUBDEV_SPEC:{ @@ -367,6 +368,7 @@ void usrp1_impl::mboard_set(const wax::obj &key, const wax::obj &val) bool s = this->disable_tx(); _iface->poke32(FR_TX_MUX, calc_tx_mux(_tx_subdev_spec, _mboard_proxy->get_link())); this->restore_tx(s); + this->update_xport_channel_mapping(); }return; case MBOARD_PROP_EEPROM_MAP: diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index 37955d628..a3d502038 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -199,12 +199,12 @@ usrp1_impl::usrp1_impl(uhd::transport::usb_zero_copy::sptr data_transport, //initialize the dsps tx_dsp_init(); - //initialize the send/recv - io_init(); - //init the subdev specs this->mboard_set(MBOARD_PROP_RX_SUBDEV_SPEC, subdev_spec_t()); this->mboard_set(MBOARD_PROP_TX_SUBDEV_SPEC, subdev_spec_t()); + + //initialize the send/recv + io_init(); } usrp1_impl::~usrp1_impl(void){UHD_SAFE_CALL( diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp index fbae2a3b9..bea1dbe3b 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.hpp +++ b/host/lib/usrp/usrp1/usrp1_impl.hpp @@ -114,6 +114,9 @@ private: const uhd::usrp::dboard_id_t &rx_dboard_id ); + //!call when the channel mapping is changed + void update_xport_channel_mapping(void); + //soft time control emulation uhd::usrp::soft_time_ctrl::sptr _soft_time_ctrl; -- cgit v1.2.3 From bafe93a272129b4974b2f8386a3a33c793c59a9b Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Sun, 12 Jun 2011 19:54:54 -0700 Subject: usrp_e100: super packet handler support squashed --- host/lib/usrp/usrp_e100/dsp_impl.cpp | 2 + host/lib/usrp/usrp_e100/io_impl.cpp | 110 ++++++++++++++++------------- host/lib/usrp/usrp_e100/usrp_e100_impl.hpp | 1 + 3 files changed, 65 insertions(+), 48 deletions(-) (limited to 'host/lib/usrp') diff --git a/host/lib/usrp/usrp_e100/dsp_impl.cpp b/host/lib/usrp/usrp_e100/dsp_impl.cpp index 8d084f066..93034b5dc 100644 --- a/host/lib/usrp/usrp_e100/dsp_impl.cpp +++ b/host/lib/usrp/usrp_e100/dsp_impl.cpp @@ -104,6 +104,7 @@ void usrp_e100_impl::rx_ddc_set(const wax::obj &key_, const wax::obj &val){ dsp_type1::calc_iq_scale_word(default_rx_scale_iq, default_rx_scale_iq) ); } + this->update_xport_channel_mapping(); //rate changed -> update return; default: UHD_THROW_PROP_SET_ERROR(); @@ -181,6 +182,7 @@ void usrp_e100_impl::tx_duc_set(const wax::obj &key_, const wax::obj &val){ //set the scaling _iface->poke32(UE_REG_DSP_TX_SCALE_IQ, dsp_type1::calc_iq_scale_word(_duc_interp)); } + this->update_xport_channel_mapping(); //rate changed -> update return; default: UHD_THROW_PROP_SET_ERROR(); diff --git a/host/lib/usrp/usrp_e100/io_impl.cpp b/host/lib/usrp/usrp_e100/io_impl.cpp index aa6e7c485..91b129276 100644 --- a/host/lib/usrp/usrp_e100/io_impl.cpp +++ b/host/lib/usrp/usrp_e100/io_impl.cpp @@ -15,14 +15,16 @@ // along with this program. If not, see . // +#include "../../transport/super_recv_packet_handler.hpp" +#include "../../transport/super_send_packet_handler.hpp" #include "usrp_e100_impl.hpp" #include "usrp_e100_regs.hpp" #include #include #include +#include #include #include -#include "../../transport/vrt_packet_handler.hpp" #include #include #include @@ -51,8 +53,6 @@ static const int underflow_flags = async_metadata_t::EVENT_CODE_UNDERFLOW | asyn struct usrp_e100_impl::io_impl{ io_impl(zero_copy_if::sptr &xport): data_xport(xport), - get_recv_buffs_fcn(boost::bind(&usrp_e100_impl::io_impl::get_recv_buffs, this, _1)), - get_send_buffs_fcn(boost::bind(&usrp_e100_impl::io_impl::get_send_buffs, this, _1)), recv_pirate_booty(data_xport->get_num_recv_frames()), async_msg_fifo(100/*messages deep*/) { @@ -65,16 +65,11 @@ struct usrp_e100_impl::io_impl{ recv_pirate_crew.join_all(); } - bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs){ - UHD_ASSERT_THROW(buffs.size() == 1); + managed_recv_buffer::sptr get_recv_buff(double timeout){ boost::this_thread::disable_interruption di; //disable because the wait can throw - return recv_pirate_booty.pop_with_timed_wait(buffs.front(), recv_timeout); - } - - bool get_send_buffs(vrt_packet_handler::managed_send_buffs_t &buffs){ - UHD_ASSERT_THROW(buffs.size() == 1); - buffs[0] = data_xport->get_send_buff(send_timeout); - return buffs[0].get() != NULL; + managed_recv_buffer::sptr buff; + recv_pirate_booty.pop_with_timed_wait(buff, timeout); + return buff; //ASSUME buff == NULL when pop times-out } //The data transport is listed first so that it is deconstructed last, @@ -82,16 +77,9 @@ struct usrp_e100_impl::io_impl{ //This comment is invalid because its now a reference and not stored here. zero_copy_if::sptr &data_xport; - //bound callbacks for get buffs (bound once here, not in fast-path) - vrt_packet_handler::get_recv_buffs_t get_recv_buffs_fcn; - vrt_packet_handler::get_send_buffs_t get_send_buffs_fcn; - - //timeouts set on calls to recv/send (passed into get buffs methods) - double recv_timeout, send_timeout; - //state management for the vrt packet handler code - vrt_packet_handler::recv_state packet_handler_recv_state; - vrt_packet_handler::send_state packet_handler_send_state; + sph::recv_packet_handler recv_handler; + sph::send_packet_handler send_handler; bool continuous_streaming; //a pirate's life is the life for me! @@ -133,16 +121,18 @@ void usrp_e100_impl::io_impl::recv_pirate_loop( vrt::if_packet_info_t if_packet_info; if_packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); const boost::uint32_t *vrt_hdr = buff->cast(); - vrt::if_hdr_unpack_le(vrt_hdr, if_packet_info); //handle an rx data packet or inline message - if (if_packet_info.sid == rx_data_inline_sid){ + if (uhd::ntohx(vrt_hdr[1]) == rx_data_inline_sid){ //ASSUME has_sid if (fp_recv_debug) UHD_LOGV(always) << "this is rx_data_inline_sid\n"; //same number of frames as the data transport -> always immediate recv_pirate_booty.push_with_wait(buff); continue; } + //unpack the vrt header and process below... + vrt::if_hdr_unpack_le(vrt_hdr, if_packet_info); + //handle a tx async report message if (if_packet_info.sid == tx_async_report_sid and if_packet_info.packet_type != vrt::if_packet_info_t::PACKET_TYPE_DATA){ if (fp_recv_debug) UHD_LOGV(always) << "this is tx_async_report_sid\n"; @@ -154,7 +144,7 @@ void usrp_e100_impl::io_impl::recv_pirate_loop( metadata.time_spec = time_spec_t( time_t(if_packet_info.tsi), size_t(if_packet_info.tsf), clock_ctrl->get_fpga_clock_rate() ); - metadata.event_code = vrt_packet_handler::get_context_code(vrt_hdr, if_packet_info); + metadata.event_code = async_metadata_t::event_code_t(sph::get_context_code(vrt_hdr, if_packet_info)); //print the famous U, and push the metadata into the message queue if (metadata.event_code & underflow_flags) UHD_MSG(fastpath) << "U"; @@ -162,6 +152,7 @@ void usrp_e100_impl::io_impl::recv_pirate_loop( continue; } + //TODO replace this below with a UHD_MSG(error) if (fp_recv_debug) UHD_LOGV(always) << "this is unknown packet\n"; }catch(const std::exception &e){ @@ -213,6 +204,42 @@ void usrp_e100_impl::io_init(void){ boost::ref(spawn_barrier), _clock_ctrl )); spawn_barrier.wait(); + //update mapping here since it didnt b4 when io init not called first + update_xport_channel_mapping(); +} + +void usrp_e100_impl::update_xport_channel_mapping(void){ + if (_io_impl.get() == NULL) return; //not inited yet + + //set all of the relevant properties on the handler + boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); + _io_impl->recv_handler.resize(_rx_subdev_spec.size()); + _io_impl->recv_handler.set_vrt_unpacker(&vrt::if_hdr_unpack_le); + _io_impl->recv_handler.set_tick_rate(_clock_ctrl->get_fpga_clock_rate()); + _io_impl->recv_handler.set_samp_rate(_rx_ddc_proxy->get_link()[DSP_PROP_HOST_RATE].as()); + for (size_t chan = 0; chan < _io_impl->recv_handler.size(); chan++){ + _io_impl->recv_handler.set_xport_chan_get_buff(chan, boost::bind( + &usrp_e100_impl::io_impl::get_recv_buff, _io_impl.get(), _1 + )); + _io_impl->recv_handler.set_overflow_handler(chan, boost::bind( + &usrp_e100_impl::handle_overrun, this, chan + )); + } + _io_impl->recv_handler.set_converter(_recv_otw_type); + + //set all of the relevant properties on the handler + boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); + _io_impl->send_handler.resize(_tx_subdev_spec.size()); + _io_impl->send_handler.set_vrt_packer(&vrt::if_hdr_pack_le); + _io_impl->send_handler.set_tick_rate(_clock_ctrl->get_fpga_clock_rate()); + _io_impl->send_handler.set_samp_rate(_tx_duc_proxy->get_link()[DSP_PROP_HOST_RATE].as()); + for (size_t chan = 0; chan < _io_impl->send_handler.size(); chan++){ + _io_impl->send_handler.set_xport_chan_get_buff(chan, boost::bind( + &uhd::transport::zero_copy_if::get_send_buff, _io_impl->data_xport, _1 + )); + } + _io_impl->send_handler.set_converter(_send_otw_type); + _io_impl->send_handler.set_max_samples_per_packet(get_max_send_samps_per_packet()); } void usrp_e100_impl::issue_stream_cmd(const stream_cmd_t &stream_cmd){ @@ -222,8 +249,7 @@ void usrp_e100_impl::issue_stream_cmd(const stream_cmd_t &stream_cmd){ _iface->poke32(UE_REG_CTRL_RX_TIME_TICKS, stream_cmd.time_spec.get_tick_count(_clock_ctrl->get_fpga_clock_rate())); } -void usrp_e100_impl::handle_overrun(size_t){ - UHD_MSG(fastpath) << "O"; //the famous OOOOOOOOOOO +void usrp_e100_impl::handle_overrun(size_t /*chan*/){ if (_io_impl->continuous_streaming){ this->issue_stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS); } @@ -242,20 +268,14 @@ size_t usrp_e100_impl::get_max_send_samps_per_packet(void) const{ } size_t usrp_e100_impl::send( - const send_buffs_type &buffs, size_t num_samps, + const send_buffs_type &buffs, size_t nsamps_per_buff, const tx_metadata_t &metadata, const io_type_t &io_type, send_mode_t send_mode, double timeout ){ - _io_impl->send_timeout = timeout; - return vrt_packet_handler::send( - _io_impl->packet_handler_send_state, //last state of the send handler - buffs, num_samps, //buffer to fill - metadata, send_mode, //samples metadata - io_type, _send_otw_type, //input and output types to convert - _clock_ctrl->get_fpga_clock_rate(), //master clock tick rate - uhd::transport::vrt::if_hdr_pack_le, - _io_impl->get_send_buffs_fcn, - get_max_send_samps_per_packet() + return _io_impl->send_handler.send( + buffs, nsamps_per_buff, + metadata, io_type, + send_mode, timeout ); } @@ -273,20 +293,14 @@ size_t usrp_e100_impl::get_max_recv_samps_per_packet(void) const{ } size_t usrp_e100_impl::recv( - const recv_buffs_type &buffs, size_t num_samps, + const recv_buffs_type &buffs, size_t nsamps_per_buff, rx_metadata_t &metadata, const io_type_t &io_type, recv_mode_t recv_mode, double timeout ){ - _io_impl->recv_timeout = timeout; - return vrt_packet_handler::recv( - _io_impl->packet_handler_recv_state, //last state of the recv handler - buffs, num_samps, //buffer to fill - metadata, recv_mode, //samples metadata - io_type, _recv_otw_type, //input and output types to convert - _clock_ctrl->get_fpga_clock_rate(), //master clock tick rate - uhd::transport::vrt::if_hdr_unpack_le, - _io_impl->get_recv_buffs_fcn, - boost::bind(&usrp_e100_impl::handle_overrun, this, _1) + return _io_impl->recv_handler.recv( + buffs, nsamps_per_buff, + metadata, io_type, + recv_mode, timeout ); } diff --git a/host/lib/usrp/usrp_e100/usrp_e100_impl.hpp b/host/lib/usrp/usrp_e100/usrp_e100_impl.hpp index 318a75191..1c17863fb 100644 --- a/host/lib/usrp/usrp_e100/usrp_e100_impl.hpp +++ b/host/lib/usrp/usrp_e100/usrp_e100_impl.hpp @@ -104,6 +104,7 @@ private: void io_init(void); void issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd); void handle_overrun(size_t); + void update_xport_channel_mapping(void); //configuration shadows uhd::clock_config_t _clock_config; -- cgit v1.2.3 From d04fe45f552daa98be18995674ee34fa24f9652f Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Mon, 13 Jun 2011 08:43:07 -0700 Subject: uhd: update packet handlers when the clock rate is changed --- host/lib/usrp/usrp1/mboard_impl.cpp | 1 + host/lib/usrp/usrp2/mboard_impl.cpp | 1 + host/lib/usrp/usrp_e100/mboard_impl.cpp | 1 + 3 files changed, 3 insertions(+) (limited to 'host/lib/usrp') diff --git a/host/lib/usrp/usrp1/mboard_impl.cpp b/host/lib/usrp/usrp1/mboard_impl.cpp index 87911b312..a265a5089 100644 --- a/host/lib/usrp/usrp1/mboard_impl.cpp +++ b/host/lib/usrp/usrp1/mboard_impl.cpp @@ -389,6 +389,7 @@ void usrp1_impl::mboard_set(const wax::obj &key, const wax::obj &val) << "See the application notes for USRP1 for further instructions.\n" ; _clock_ctrl->set_master_clock_freq(val.as()); + this->update_xport_channel_mapping(); return; case MBOARD_PROP_CLOCK_CONFIG:{ diff --git a/host/lib/usrp/usrp2/mboard_impl.cpp b/host/lib/usrp/usrp2/mboard_impl.cpp index 4a28ea9c4..6bf412a3e 100644 --- a/host/lib/usrp/usrp2/mboard_impl.cpp +++ b/host/lib/usrp/usrp2/mboard_impl.cpp @@ -466,6 +466,7 @@ void usrp2_mboard_impl::set(const wax::obj &key, const wax::obj &val){ case MBOARD_PROP_CLOCK_RATE: UHD_ASSERT_THROW(val.as() == this->get_master_clock_freq()); + _device.update_xport_channel_mapping(); return; default: UHD_THROW_PROP_SET_ERROR(); diff --git a/host/lib/usrp/usrp_e100/mboard_impl.cpp b/host/lib/usrp/usrp_e100/mboard_impl.cpp index 483286e47..f4b8d79f6 100644 --- a/host/lib/usrp/usrp_e100/mboard_impl.cpp +++ b/host/lib/usrp/usrp_e100/mboard_impl.cpp @@ -214,6 +214,7 @@ void usrp_e100_impl::mboard_set(const wax::obj &key, const wax::obj &val){ << "See the application notes for USRP-E1XX for further instructions.\n" ; _clock_ctrl->set_fpga_clock_rate(val.as()); + this->update_xport_channel_mapping(); return; default: UHD_THROW_PROP_SET_ERROR(); -- cgit v1.2.3 From 8173943ea945e685babe461c682622f1db05a413 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Mon, 13 Jun 2011 16:15:50 -0700 Subject: usrp_e100: init the subdev specs before calling io_init --- host/lib/usrp/usrp_e100/usrp_e100_impl.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'host/lib/usrp') diff --git a/host/lib/usrp/usrp_e100/usrp_e100_impl.cpp b/host/lib/usrp/usrp_e100/usrp_e100_impl.cpp index e9e9b6e20..01a49c129 100644 --- a/host/lib/usrp/usrp_e100/usrp_e100_impl.cpp +++ b/host/lib/usrp/usrp_e100/usrp_e100_impl.cpp @@ -171,12 +171,13 @@ usrp_e100_impl::usrp_e100_impl( //init the codec properties codec_init(); - //init the io send/recv - io_init(); - //set default subdev specs this->mboard_set(MBOARD_PROP_RX_SUBDEV_SPEC, subdev_spec_t()); this->mboard_set(MBOARD_PROP_TX_SUBDEV_SPEC, subdev_spec_t()); + + //init the io send/recv + io_init(); + } usrp_e100_impl::~usrp_e100_impl(void){ -- cgit v1.2.3 From 5dfc458188538a32f95c05f860587d2c542e14c1 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Tue, 14 Jun 2011 17:25:22 -0700 Subject: b100: added b100 host (squashed) --- host/docs/CMakeLists.txt | 1 + host/docs/index.rst | 1 + host/docs/usrp_b1xx.rst | 76 ++++ host/include/uhd/transport/usb_zero_copy.hpp | 21 +- host/lib/transport/CMakeLists.txt | 1 + host/lib/transport/libusb1_zero_copy.cpp | 33 +- host/lib/transport/usb_zero_copy_wrapper.cpp | 202 +++++++++++ host/lib/usrp/CMakeLists.txt | 1 + host/lib/usrp/dboard/db_basic_and_lf.cpp | 2 + host/lib/usrp/fx2/fx2_ctrl.hpp | 2 +- host/lib/usrp/usrp_b100/CMakeLists.txt | 47 +++ host/lib/usrp/usrp_b100/b100_ctrl.cpp | 245 +++++++++++++ host/lib/usrp/usrp_b100/b100_ctrl.hpp | 69 ++++ host/lib/usrp/usrp_b100/b100_iface.cpp | 336 +++++++++++++++++ host/lib/usrp/usrp_b100/b100_iface.hpp | 73 ++++ host/lib/usrp/usrp_b100/b100_impl.cpp | 277 ++++++++++++++ host/lib/usrp/usrp_b100/b100_impl.hpp | 204 +++++++++++ host/lib/usrp/usrp_b100/b100_regs.hpp | 264 ++++++++++++++ host/lib/usrp/usrp_b100/clock_ctrl.cpp | 525 +++++++++++++++++++++++++++ host/lib/usrp/usrp_b100/clock_ctrl.hpp | 118 ++++++ host/lib/usrp/usrp_b100/codec_ctrl.cpp | 283 +++++++++++++++ host/lib/usrp/usrp_b100/codec_ctrl.hpp | 90 +++++ host/lib/usrp/usrp_b100/codec_impl.cpp | 149 ++++++++ host/lib/usrp/usrp_b100/ctrl_packet.hpp | 75 ++++ host/lib/usrp/usrp_b100/dboard_iface.cpp | 298 +++++++++++++++ host/lib/usrp/usrp_b100/dboard_impl.cpp | 185 ++++++++++ host/lib/usrp/usrp_b100/dsp_impl.cpp | 189 ++++++++++ host/lib/usrp/usrp_b100/io_impl.cpp | 210 +++++++++++ host/lib/usrp/usrp_b100/mboard_impl.cpp | 246 +++++++++++++ host/lib/usrp/usrp_e100/usrp_e100_impl.cpp | 2 +- host/utils/usrp1p_gpif_loopback.cpp | 111 ++++++ host/utils/usrp1p_poketest.cpp | 134 +++++++ 32 files changed, 4456 insertions(+), 14 deletions(-) create mode 100644 host/docs/usrp_b1xx.rst create mode 100644 host/lib/transport/usb_zero_copy_wrapper.cpp create mode 100644 host/lib/usrp/usrp_b100/CMakeLists.txt create mode 100644 host/lib/usrp/usrp_b100/b100_ctrl.cpp create mode 100644 host/lib/usrp/usrp_b100/b100_ctrl.hpp create mode 100644 host/lib/usrp/usrp_b100/b100_iface.cpp create mode 100644 host/lib/usrp/usrp_b100/b100_iface.hpp create mode 100644 host/lib/usrp/usrp_b100/b100_impl.cpp create mode 100644 host/lib/usrp/usrp_b100/b100_impl.hpp create mode 100644 host/lib/usrp/usrp_b100/b100_regs.hpp create mode 100644 host/lib/usrp/usrp_b100/clock_ctrl.cpp create mode 100644 host/lib/usrp/usrp_b100/clock_ctrl.hpp create mode 100644 host/lib/usrp/usrp_b100/codec_ctrl.cpp create mode 100644 host/lib/usrp/usrp_b100/codec_ctrl.hpp create mode 100644 host/lib/usrp/usrp_b100/codec_impl.cpp create mode 100644 host/lib/usrp/usrp_b100/ctrl_packet.hpp create mode 100644 host/lib/usrp/usrp_b100/dboard_iface.cpp create mode 100644 host/lib/usrp/usrp_b100/dboard_impl.cpp create mode 100644 host/lib/usrp/usrp_b100/dsp_impl.cpp create mode 100644 host/lib/usrp/usrp_b100/io_impl.cpp create mode 100644 host/lib/usrp/usrp_b100/mboard_impl.cpp create mode 100644 host/utils/usrp1p_gpif_loopback.cpp create mode 100644 host/utils/usrp1p_poketest.cpp (limited to 'host/lib/usrp') diff --git a/host/docs/CMakeLists.txt b/host/docs/CMakeLists.txt index 5926e062e..88afb9c98 100644 --- a/host/docs/CMakeLists.txt +++ b/host/docs/CMakeLists.txt @@ -30,6 +30,7 @@ SET(manual_sources transport.rst usrp1.rst usrp2.rst + usrp_b1xx.rst usrp_e1xx.rst ) diff --git a/host/docs/index.rst b/host/docs/index.rst index 40fe64599..b4ebdad6c 100644 --- a/host/docs/index.rst +++ b/host/docs/index.rst @@ -24,6 +24,7 @@ Application Notes * `USRP1 Application Notes <./usrp1.html>`_ * `USRP2 Application Notes <./usrp2.html>`_ * `USRP-N2XX Series Application Notes <./usrp2.html>`_ +* `USRP-B1XX Series Application Notes <./usrp_b1xx.html>`_ * `USRP-E1XX Series Application Notes <./usrp_e1xx.html>`_ * `Daughterboard Application Notes <./dboards.html>`_ * `Transport Application Notes <./transport.html>`_ diff --git a/host/docs/usrp_b1xx.rst b/host/docs/usrp_b1xx.rst new file mode 100644 index 000000000..2f7385070 --- /dev/null +++ b/host/docs/usrp_b1xx.rst @@ -0,0 +1,76 @@ +======================================================================== +UHD - USRP-B1XX Series Application Notes +======================================================================== + +.. contents:: Table of Contents + +------------------------------------------------------------------------ +Specify a non-standard image +------------------------------------------------------------------------ +The UHD will automatically select the USRP B-Series images from the installed images package. +The image selection can be overridden with the "fpga" and "fw" device address parameters. + +Example device address string representations to specify non-standard images: + +:: + + fpga=usrp_b100_fpga_firmware.bin + + -- OR -- + + fw=usrp_b100_fw_firmware.ihx + +------------------------------------------------------------------------ +Changing the master clock rate +------------------------------------------------------------------------ +The master clock rate of the USRP embedded feeds both the FPGA DSP and the codec chip. +Hundreds of rates between 32MHz and 64MHz are available. +A few notable rates are: + +* 64MHz - maximum rate of the codec chip +* 61.44MHz - good for UMTS/WCDMA applications +* 52Mhz - good for GSM applications + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Set 61.44MHz - uses external VCXO +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +To use the 61.44MHz clock rate, the USRP embedded will require two jumpers to be moved. + +* J16 is a two pin header, remove the jumper (or leave it on pin1 only) +* J15 is a three pin header, move the jumper to (pin1, pin2) + +**Note:** See instructions below to communicate the desired clock rate into the UHD. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Set other rates - uses internal VCO +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +To use other clock rates, the jumpers will need to be in the default position. + +* J16 is a two pin header, move the jumper to (pin1, pin2) +* J15 is a three pin header, move the jumper to (pin2, pin3) + +To communicate the desired clock rate into the UHD, +specify the a special device address argument, +where the key is "master_clock_rate" and the value is a rate in Hz. +Example: +:: + + uhd_usrp_probe --args="master_clock_rate=52e6" + +------------------------------------------------------------------------ +OS specific notes +------------------------------------------------------------------------ + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Linux - setup udev +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +On Linux, udev handles USB plug and unplug events. +The following commands create a udev rule for the B100 +so that non-root users may access the device: + +:: + + echo 'ACTION=="add", BUS=="usb", SYSFS{idVendor}=="2500", SYSFS{idProduct}=="0001", MODE:="0666"' > tmpfile + sudo chown root.root tmpfile + sudo mv tmpfile /etc/udev/rules.d/10-usrp_b100.rules + sudo udevadm control --reload-rules diff --git a/host/include/uhd/transport/usb_zero_copy.hpp b/host/include/uhd/transport/usb_zero_copy.hpp index b39171fba..dc344ad8b 100644 --- a/host/include/uhd/transport/usb_zero_copy.hpp +++ b/host/include/uhd/transport/usb_zero_copy.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010-2011 Ettus Research LLC // // 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 @@ -49,6 +49,7 @@ public: * \param recv_endpoint an integer specifiying an IN endpoint number * \param send_endpoint an integer specifiying an OUT endpoint number * \param hints optional parameters to pass to the underlying transport + * \return a new zero copy usb object */ static sptr make( usb_device_handle::sptr handle, @@ -56,6 +57,24 @@ public: size_t send_endpoint, const device_addr_t &hints = device_addr_t() ); + + /*! + * Make a wrapper around a zero copy implementation. + * The wrapper performs the following functions: + * - Pad commits to the frame boundary + * - Extract multiple packets on recv + * + * When enable multiple receive packets is set to true, + * the implementation inspects the vita length on transfers, + * and may split a single transfer into multiple managed buffers. + * + * \param usb_zc a usb zero copy interface object + * \param usb_frame_boundary bytes per frame + * \return a new zero copy wrapper object + */ + static sptr make_wrapper( + sptr usb_zc, size_t usb_frame_boundary = 512 + ); }; }} //namespace diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index 439c3d230..b1821956c 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -98,4 +98,5 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/if_addrs.cpp ${CMAKE_CURRENT_SOURCE_DIR}/udp_simple.cpp ${CMAKE_CURRENT_SOURCE_DIR}/udp_zero_copy.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/usb_zero_copy_wrapper.cpp ) diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp index 28bea978b..19a7a3742 100644 --- a/host/lib/transport/libusb1_zero_copy.cpp +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -44,15 +44,24 @@ static const size_t DEFAULT_XFER_SIZE = 32*512; //bytes * to ensure that they are compiled with the same calling convention as libusb. */ -//! helper function: handles all async callbacks -static void LIBUSB_CALL libusb_async_cb(libusb_transfer *lut){ +//! helper function: handles all rx async callbacks +static void LIBUSB_CALL libusb_async_rx_cb(libusb_transfer *lut){ + if(lut->actual_length == 0) { + UHD_ASSERT_THROW(libusb_submit_transfer(lut) == 0); //get out until you find some real data + return; + } + (*static_cast *>(lut->user_data))(); +} + +//! helper function: handles all tx async callbacks +static void LIBUSB_CALL libusb_async_tx_cb(libusb_transfer *lut) { (*static_cast *>(lut->user_data))(); } //! callback to free transfer upon cancellation static void LIBUSB_CALL cancel_transfer_cb(libusb_transfer *lut){ - if (lut->status == LIBUSB_TRANSFER_CANCELLED) libusb_free_transfer(lut); - else UHD_LOGV(rarely) << "libusb cancel_transfer unexpected status " << lut->status << std::endl; + if (lut->status == LIBUSB_TRANSFER_CANCELLED || lut->status == LIBUSB_TRANSFER_TIMED_OUT) libusb_free_transfer(lut); + else UHD_MSG(error) << "libusb cancel_transfer unexpected status " << lut->status << std::endl; } /*********************************************************************** @@ -97,7 +106,7 @@ public: void commit(size_t len){ if (_expired) return; _lut->length = len; - if(len == 0) libusb_async_cb(_lut); + if(len == 0) libusb_async_tx_cb(_lut); else UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0); _expired = true; } @@ -157,9 +166,9 @@ public: (recv_endpoint & 0x7f) | 0x80, // endpoint static_cast(_recv_buffer_pool->at(i)), // buffer this->get_recv_frame_size(), // length - libusb_transfer_cb_fn(&libusb_async_cb), // callback + libusb_transfer_cb_fn(&libusb_async_rx_cb), // callback static_cast(&_callbacks.back()), // user_data - 0 // timeout + 0 // timeout (ms) ); _all_luts.push_back(lut); @@ -183,13 +192,13 @@ public: (send_endpoint & 0x7f) | 0x00, // endpoint static_cast(_send_buffer_pool->at(i)), // buffer this->get_send_frame_size(), // length - libusb_transfer_cb_fn(&libusb_async_cb), // callback + libusb_transfer_cb_fn(&libusb_async_tx_cb), // callback static_cast(&_callbacks.back()), // user_data 0 // timeout ); _all_luts.push_back(lut); - libusb_async_cb(lut); + libusb_async_tx_cb(lut); } //spawn the event handler threads @@ -206,7 +215,9 @@ public: BOOST_FOREACH(libusb_transfer *lut, _all_luts){ lut->callback = libusb_transfer_cb_fn(&cancel_transfer_cb); libusb_cancel_transfer(lut); - while(lut->status != LIBUSB_TRANSFER_CANCELLED && lut->status != LIBUSB_TRANSFER_COMPLETED) { + while(lut->status != LIBUSB_TRANSFER_CANCELLED + && lut->status != LIBUSB_TRANSFER_COMPLETED + && lut->status != LIBUSB_TRANSFER_TIMED_OUT) { boost::this_thread::sleep(boost::posix_time::milliseconds(10)); } } diff --git a/host/lib/transport/usb_zero_copy_wrapper.cpp b/host/lib/transport/usb_zero_copy_wrapper.cpp new file mode 100644 index 000000000..227c4b392 --- /dev/null +++ b/host/lib/transport/usb_zero_copy_wrapper.cpp @@ -0,0 +1,202 @@ +// +// Copyright 2011 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include +#include +#include +#include +#include +#include + +using namespace uhd::transport; +bool debug = true; + +static inline size_t next_boundary(size_t length, size_t boundary){ + //pad to the boundary, assumes boundary is a power of 2 + return (length + (boundary-1)) & ~(boundary-1); +} + +/*********************************************************************** + * USB zero copy wrapper - managed receive buffer + **********************************************************************/ +class usb_zero_copy_wrapper_mrb : public managed_recv_buffer{ +public: + usb_zero_copy_wrapper_mrb(bounded_buffer &queue): + _queue(queue){/*NOP*/} + + void release(void){ + if (_mrb.get() == NULL) return; + _mrb->release(); + _queue.push_with_haste(this); + _mrb.reset(); + } + + sptr get_new(managed_recv_buffer::sptr mrb, const void *mem, size_t len){ + _mrb = mrb; + _mem = mem; + _len = len; + return make_managed_buffer(this); + } + +private: + const void *get_buff(void) const{return _mem;} + size_t get_size(void) const{return _len;} + + bounded_buffer &_queue; + const void *_mem; + size_t _len; + managed_recv_buffer::sptr _mrb; +}; + +/*********************************************************************** + * USB zero copy wrapper - managed send buffer + **********************************************************************/ +class usb_zero_copy_wrapper_msb : public managed_send_buffer{ +public: + usb_zero_copy_wrapper_msb(bounded_buffer &queue, size_t boundary): + _queue(queue), _boundary(boundary){/*NOP*/} + + void commit(size_t len){ + if (_msb.get() == NULL) return; + _msb->commit(next_boundary(len, _boundary)); + _queue.push_with_haste(this); + _msb.reset(); + } + + sptr get_new(managed_send_buffer::sptr msb){ + _msb = msb; + return make_managed_buffer(this); + } + +private: + void *get_buff(void) const{return _msb->cast();} + size_t get_size(void) const{return _msb->size();} + + bounded_buffer &_queue; + size_t _boundary; + managed_send_buffer::sptr _msb; +}; + +/*********************************************************************** + * USB zero copy wrapper implementation + **********************************************************************/ +class usb_zero_copy_wrapper : public usb_zero_copy{ +public: + usb_zero_copy_wrapper( + sptr usb_zc, size_t usb_frame_boundary + ): + _internal_zc(usb_zc), + _usb_frame_boundary(usb_frame_boundary), + _available_recv_buffs(this->get_num_recv_frames()), + _available_send_buffs(this->get_num_send_frames()), + _mrb_pool(this->get_num_recv_frames(), usb_zero_copy_wrapper_mrb(_available_recv_buffs)), + _msb_pool(this->get_num_send_frames(), usb_zero_copy_wrapper_msb(_available_send_buffs, usb_frame_boundary)) + { + BOOST_FOREACH(usb_zero_copy_wrapper_mrb &mrb, _mrb_pool){ + _available_recv_buffs.push_with_haste(&mrb); + } + + BOOST_FOREACH(usb_zero_copy_wrapper_msb &msb, _msb_pool){ + _available_send_buffs.push_with_haste(&msb); + } + } + + managed_recv_buffer::sptr get_recv_buff(double timeout){ + //attempt to get a managed recv buffer + if (not _last_recv_buff.get()){ + _last_recv_buff = _internal_zc->get_recv_buff(timeout); + _last_recv_offset = 0; + } + + //attempt to get a wrapper for a managed recv buffer + usb_zero_copy_wrapper_mrb *wmrb = NULL; + if (_last_recv_buff.get() and _available_recv_buffs.pop_with_timed_wait(wmrb, timeout)){ + //extract this packet's memory address and length in bytes + const char *mem = _last_recv_buff->cast() + _last_recv_offset; + const boost::uint32_t *mem32 = reinterpret_cast(mem); + size_t len = (mem32[0] & 0xffff)*sizeof(boost::uint32_t); //length in bytes (from VRT header) + + managed_recv_buffer::sptr recv_buff; //the buffer to be returned to the user + + recv_buff = wmrb->get_new(_last_recv_buff, mem, len); + _last_recv_offset = next_boundary(_last_recv_offset + len, _usb_frame_boundary); + + //check if this receive buffer has been exhausted + if (_last_recv_offset >= _last_recv_buff->size()) { + _last_recv_buff.reset(); + } + + return recv_buff; + } + + //otherwise return a null sptr for failure + return managed_recv_buffer::sptr(); + } + + size_t get_num_recv_frames(void) const{ + return _internal_zc->get_num_recv_frames(); + } + + size_t get_recv_frame_size(void) const{ + return _internal_zc->get_recv_frame_size(); + } + + managed_send_buffer::sptr get_send_buff(double timeout){ + managed_send_buffer::sptr send_buff = _internal_zc->get_send_buff(timeout); + + //attempt to get a wrapper for a managed send buffer + usb_zero_copy_wrapper_msb *wmsb = NULL; + if (send_buff.get() and _available_send_buffs.pop_with_haste(wmsb)){ + return wmsb->get_new(send_buff); + } + + //otherwise return a null sptr for failure + return managed_send_buffer::sptr(); + } + + size_t get_num_send_frames(void) const{ + return _internal_zc->get_num_send_frames(); + } + + size_t get_send_frame_size(void) const{ + return _internal_zc->get_send_frame_size(); + } + +private: + sptr _internal_zc; + size_t _usb_frame_boundary; + bounded_buffer _available_recv_buffs; + bounded_buffer _available_send_buffs; + std::vector _mrb_pool; + std::vector _msb_pool; + + //buffer to store partially-received VRT packets in + buffer_pool::sptr _fragment_mem; + + //state for last recv buffer to create multiple managed buffers + managed_recv_buffer::sptr _last_recv_buff; + size_t _last_recv_offset; +}; + +/*********************************************************************** + * USB zero copy wrapper factory function + **********************************************************************/ +usb_zero_copy::sptr usb_zero_copy::make_wrapper( + sptr usb_zc, size_t usb_frame_boundary +){ + return sptr(new usb_zero_copy_wrapper(usb_zc, usb_frame_boundary)); +} diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt index 281910735..a30a45977 100644 --- a/host/lib/usrp/CMakeLists.txt +++ b/host/lib/usrp/CMakeLists.txt @@ -37,4 +37,5 @@ INCLUDE_SUBDIRECTORY(dboard) INCLUDE_SUBDIRECTORY(fx2) INCLUDE_SUBDIRECTORY(usrp1) INCLUDE_SUBDIRECTORY(usrp2) +INCLUDE_SUBDIRECTORY(usrp_b100) INCLUDE_SUBDIRECTORY(usrp_e100) diff --git a/host/lib/usrp/dboard/db_basic_and_lf.cpp b/host/lib/usrp/dboard/db_basic_and_lf.cpp index 6f8de9a7b..566e24d97 100644 --- a/host/lib/usrp/dboard/db_basic_and_lf.cpp +++ b/host/lib/usrp/dboard/db_basic_and_lf.cpp @@ -111,6 +111,7 @@ basic_rx::basic_rx(ctor_args_t args, double max_freq) : rx_dboard_base(args){ this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0x0000); this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0xFFFF); this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0x0000); + this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); } basic_rx::~basic_rx(void){ @@ -216,6 +217,7 @@ void basic_rx::rx_set(const wax::obj &key_, const wax::obj &val){ **********************************************************************/ basic_tx::basic_tx(ctor_args_t args, double max_freq) : tx_dboard_base(args){ _max_freq = max_freq; + this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); } basic_tx::~basic_tx(void){ diff --git a/host/lib/usrp/fx2/fx2_ctrl.hpp b/host/lib/usrp/fx2/fx2_ctrl.hpp index 98c98bb8c..37fa09605 100644 --- a/host/lib/usrp/fx2/fx2_ctrl.hpp +++ b/host/lib/usrp/fx2/fx2_ctrl.hpp @@ -57,7 +57,7 @@ public: * \param filename name of EEPROM image */ virtual void usrp_load_eeprom(std::string filestring) = 0; - + /*! * Submit an IN transfer * \param request device specific request diff --git a/host/lib/usrp/usrp_b100/CMakeLists.txt b/host/lib/usrp/usrp_b100/CMakeLists.txt new file mode 100644 index 000000000..f83a10935 --- /dev/null +++ b/host/lib/usrp/usrp_b100/CMakeLists.txt @@ -0,0 +1,47 @@ +# +# Copyright 2010-2011 Ettus Research LLC +# +# 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 +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## + +######################################################################## +# Conditionally configure the B100 support +######################################################################## +LIBUHD_REGISTER_COMPONENT("B100" ENABLE_B100 ON "ENABLE_LIBUHD;ENABLE_USB" OFF) + +IF(ENABLE_B100) + LIBUHD_APPEND_SOURCES( + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/clock_ctrl.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/clock_ctrl.hpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/codec_ctrl.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/codec_ctrl.hpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/codec_impl.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/ctrl_packet.hpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/dboard_impl.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/dboard_iface.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/dsp_impl.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/io_impl.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/mboard_impl.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/b100_ctrl.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/b100_ctrl.hpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/b100_iface.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/b100_iface.hpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/b100_impl.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/b100_impl.hpp + ) +ENDIF(ENABLE_B100) diff --git a/host/lib/usrp/usrp_b100/b100_ctrl.cpp b/host/lib/usrp/usrp_b100/b100_ctrl.cpp new file mode 100644 index 000000000..4d4520e1e --- /dev/null +++ b/host/lib/usrp/usrp_b100/b100_ctrl.cpp @@ -0,0 +1,245 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "../../transport/super_recv_packet_handler.hpp" +#include "b100_ctrl.hpp" +#include "b100_impl.hpp" +#include +#include +#include +#include +#include +#include +#include +#include "ctrl_packet.hpp" +#include +#include + +using namespace uhd::transport; +using namespace uhd; + +bool b100_ctrl_debug = false; + +class b100_ctrl_impl : public b100_ctrl { +public: + b100_ctrl_impl(uhd::transport::usb_zero_copy::sptr ctrl_transport) : + sync_ctrl_fifo(2), + async_msg_fifo(100), + _ctrl_transport(ctrl_transport), + _seq(0) + { + boost::barrier spawn_barrier(2); + viking_marauders.create_thread(boost::bind(&b100_ctrl_impl::viking_marauder_loop, this, boost::ref(spawn_barrier))); + spawn_barrier.wait(); + } + + int write(boost::uint32_t addr, const ctrl_data_t &data); + ctrl_data_t read(boost::uint32_t addr, size_t len); + + ~b100_ctrl_impl(void) { + bbl_out_marauding = false; + viking_marauders.interrupt_all(); + viking_marauders.join_all(); + } + + bool get_ctrl_data(ctrl_data_t &pkt_data, double timeout); + bool recv_async_msg(uhd::async_metadata_t &async_metadata, double timeout); + +private: + int send_pkt(boost::uint16_t *cmd); + + //änd hërë wë gö ä-Vïkïng för äsynchronous control packets + void viking_marauder_loop(boost::barrier &); + bounded_buffer sync_ctrl_fifo; + bounded_buffer async_msg_fifo; + boost::thread_group viking_marauders; + bool bbl_out_marauding; + + uhd::transport::usb_zero_copy::sptr _ctrl_transport; + boost::uint8_t _seq; +}; + +/*********************************************************************** + * helper functions for packing/unpacking control packets + **********************************************************************/ +void pack_ctrl_pkt(boost::uint16_t *pkt_buff, + const ctrl_pkt_t &pkt){ + //first two bits are OP + //next six bits are CALLBACKS + //next 8 bits are SEQUENCE + //next 16 bits are LENGTH (16-bit word) + //next 32 bits are ADDRESS (16-bit word LSW) + //then DATA (28 16-bit words) + pkt_buff[0] = (boost::uint16_t(pkt.pkt_meta.op) << 14) | (boost::uint16_t(pkt.pkt_meta.callbacks) << 8) | pkt.pkt_meta.seq; + pkt_buff[1] = pkt.pkt_meta.len; + pkt_buff[2] = (pkt.pkt_meta.addr & 0x00000FFF); + pkt_buff[3] = 0x0000; //address high bits always 0 on this device + + for(size_t i = 0; i < pkt.data.size(); i++) { + pkt_buff[4+i] = pkt.data[i]; + } +} + +void unpack_ctrl_pkt(const boost::uint16_t *pkt_buff, + ctrl_pkt_t &pkt){ + pkt.pkt_meta.seq = pkt_buff[0] & 0xFF; + pkt.pkt_meta.op = CTRL_PKT_OP_READ; //really this is useless + pkt.pkt_meta.len = pkt_buff[1]; + pkt.pkt_meta.callbacks = 0; //callbacks aren't implemented yet + pkt.pkt_meta.addr = pkt_buff[2] | boost::uint32_t(pkt_buff[3] << 16); + + //let's check this so we don't go pushing 64K of crap onto the pkt + if(pkt.pkt_meta.len > CTRL_PACKET_DATA_LENGTH) { + throw uhd::runtime_error("Received control packet too long"); + } + + for(int i = 4; i < 4+pkt.pkt_meta.len; i++) pkt.data.push_back(pkt_buff[i]); +} + +int b100_ctrl_impl::send_pkt(boost::uint16_t *cmd) { + managed_send_buffer::sptr sbuf = _ctrl_transport->get_send_buff(); + if(!sbuf.get()) { + throw uhd::runtime_error("Control channel send error"); + } + + //FIXME there's a better way to do this + for(size_t i = 0; i < (CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)); i++) { + sbuf->cast()[i] = cmd[i]; + } + sbuf->commit(CTRL_PACKET_LENGTH); //fixed size transaction + return 0; +} + +int b100_ctrl_impl::write(boost::uint32_t addr, const ctrl_data_t &data) { + UHD_ASSERT_THROW(data.size() <= (CTRL_PACKET_DATA_LENGTH / sizeof(boost::uint16_t))); + ctrl_pkt_t pkt; + pkt.data = data; + pkt.pkt_meta.op = CTRL_PKT_OP_WRITE; + pkt.pkt_meta.callbacks = 0; + pkt.pkt_meta.seq = _seq++; + pkt.pkt_meta.len = pkt.data.size(); + pkt.pkt_meta.addr = addr; + boost::uint16_t pkt_buff[CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)]; + + pack_ctrl_pkt(pkt_buff, pkt); + size_t result = send_pkt(pkt_buff); + return result; +} + +ctrl_data_t b100_ctrl_impl::read(boost::uint32_t addr, size_t len) { + UHD_ASSERT_THROW(len <= (CTRL_PACKET_DATA_LENGTH / sizeof(boost::uint16_t))); + + ctrl_pkt_t pkt; + pkt.pkt_meta.op = CTRL_PKT_OP_READ; + pkt.pkt_meta.callbacks = 0; + pkt.pkt_meta.seq = _seq++; + pkt.pkt_meta.len = len; + pkt.pkt_meta.addr = addr; + boost::uint16_t pkt_buff[CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)]; + + pack_ctrl_pkt(pkt_buff, pkt); + send_pkt(pkt_buff); + + //loop around waiting for the response to appear + while(!get_ctrl_data(pkt.data, 0.05)); + + return pkt.data; +} + +/*********************************************************************** + * Viking marauders go pillaging for asynchronous control packets in the + * control response endpoint. Sync packets go in sync_ctrl_fifo, + * async TX error messages go in async_msg_fifo. sync_ctrl_fifo should + * never have more than 1 message in it, since it's expected that we'll + * wait for a control operation to finish before starting another one. + **********************************************************************/ +void b100_ctrl_impl::viking_marauder_loop(boost::barrier &spawn_barrier) { + bbl_out_marauding = true; + spawn_barrier.wait(); + set_thread_priority_safe(); + + while(bbl_out_marauding){ + managed_recv_buffer::sptr rbuf = _ctrl_transport->get_recv_buff(); + if(!rbuf.get()) continue; //that's ok, there are plenty of villages to pillage! + const boost::uint16_t *pkt_buf = rbuf->cast(); + + if(pkt_buf[0] >> 8 == CTRL_PACKET_HEADER_MAGIC) { + //so it's got a control packet header, let's parse it. + ctrl_pkt_t pkt; + unpack_ctrl_pkt(pkt_buf, pkt); + + if(pkt.pkt_meta.seq != boost::uint8_t(_seq - 1)) { + throw uhd::runtime_error("Sequence error on control channel"); + } + if(pkt.pkt_meta.len > (CTRL_PACKET_LENGTH - CTRL_PACKET_HEADER_LENGTH)) { + throw uhd::runtime_error("Control channel packet length too long"); + } + + //push it onto the queue + sync_ctrl_fifo.push_with_wait(pkt.data); + } else { //it's an async status pkt + //extract the vrt header packet info + vrt::if_packet_info_t if_packet_info; + if_packet_info.num_packet_words32 = rbuf->size()/sizeof(boost::uint32_t); + const boost::uint32_t *vrt_hdr = rbuf->cast(); + vrt::if_hdr_unpack_le(vrt_hdr, if_packet_info); + + if( if_packet_info.sid == 0 + and if_packet_info.packet_type != vrt::if_packet_info_t::PACKET_TYPE_DATA){ + //fill in the async metadata + async_metadata_t metadata; + metadata.channel = 0; + metadata.has_time_spec = if_packet_info.has_tsi and if_packet_info.has_tsf; + metadata.time_spec = time_spec_t( + time_t(if_packet_info.tsi), size_t(if_packet_info.tsf), 64e6 //FIXME get from clock_ctrl + ); + metadata.event_code = async_metadata_t::event_code_t(sph::get_context_code(vrt_hdr, if_packet_info)); + //print the famous U, and push the metadata into the message queue + if (metadata.event_code & + ( async_metadata_t::EVENT_CODE_UNDERFLOW + | async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET) ) + UHD_MSG(fastpath) << "U"; + + if (metadata.event_code & + ( async_metadata_t::EVENT_CODE_SEQ_ERROR + | async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST) ) + UHD_MSG(fastpath) << "S"; + + async_msg_fifo.push_with_pop_on_full(metadata); + continue; + } + throw uhd::runtime_error("Control: unknown async response"); + } + } +} + +bool b100_ctrl_impl::get_ctrl_data(ctrl_data_t &pkt_data, double timeout){ + boost::this_thread::disable_interruption di; //disable because the wait can throw + return sync_ctrl_fifo.pop_with_timed_wait(pkt_data, timeout); +} + +bool b100_ctrl_impl::recv_async_msg(uhd::async_metadata_t &async_metadata, double timeout) { + boost::this_thread::disable_interruption di; //disable because the wait can throw + return async_msg_fifo.pop_with_timed_wait(async_metadata, timeout); +} + +/*********************************************************************** + * Public make function for b100_ctrl interface + **********************************************************************/ +b100_ctrl::sptr b100_ctrl::make(uhd::transport::usb_zero_copy::sptr ctrl_transport){ + return sptr(new b100_ctrl_impl(ctrl_transport)); +} diff --git a/host/lib/usrp/usrp_b100/b100_ctrl.hpp b/host/lib/usrp/usrp_b100/b100_ctrl.hpp new file mode 100644 index 000000000..ae706dbb4 --- /dev/null +++ b/host/lib/usrp/usrp_b100/b100_ctrl.hpp @@ -0,0 +1,69 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INCLUDED_B100_CTRL_HPP +#define INCLUDED_B100_CTRL_HPP + +#include +#include +#include +#include +#include +#include +#include "ctrl_packet.hpp" +#include + +class b100_ctrl : boost::noncopyable{ +public: + typedef boost::shared_ptr sptr; + + /*! + * Make a USRP control object from a data transport + * \param ctrl_transport a USB data transport + * \return a new b100 control object + */ + static sptr make(uhd::transport::usb_zero_copy::sptr ctrl_transport); + + /*! + * Write a byte vector to an FPGA register + * \param addr the FPGA register address + * \param bytes the data to write + * \return 0 on success, error code on failure + */ + virtual int write(boost::uint32_t addr, const ctrl_data_t &data) = 0; + + /*! + * Read a byte vector from an FPGA register (blocking read) + * \param addr the FPGA register address + * \param len the length of the read + * \return a vector of bytes from the register(s) in question + */ + virtual ctrl_data_t read(boost::uint32_t addr, size_t len) = 0; + + /*! + * Get a sync ctrl packet (blocking) + * \param the packet data buffer + * \param the timeout value + * \return true if it got something + */ + virtual bool get_ctrl_data(ctrl_data_t &pkt_data, double timeout) = 0; + + virtual bool recv_async_msg(uhd::async_metadata_t &async_metadata, double timeout) = 0; + +}; + +#endif /* INCLUDED_B100_CTRL_HPP */ diff --git a/host/lib/usrp/usrp_b100/b100_iface.cpp b/host/lib/usrp/usrp_b100/b100_iface.cpp new file mode 100644 index 000000000..17ea2e6ad --- /dev/null +++ b/host/lib/usrp/usrp_b100/b100_iface.cpp @@ -0,0 +1,336 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "b100_iface.hpp" +#include "usrp_commands.h" +#include +#include +#include +#include +#include +#include +#include + +//FOR TESTING ONLY +#include "b100_regs.hpp" +#include +#include "usrp_i2c_addr.h" + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +/*********************************************************************** + * Constants + **********************************************************************/ +static const bool iface_debug = true; +static const boost::uint16_t USRP_B_FW_COMPAT_NUM = 0x02; +static const boost::uint16_t USRP_B_FPGA_COMPAT_NUM = 0x03; + +/*********************************************************************** + * I2C + FX2 implementation wrapper + **********************************************************************/ +class b100_i2c_fx2_iface : public i2c_iface{ +public: + b100_i2c_fx2_iface(uhd::usrp::fx2_ctrl::sptr fx2_ctrl){ + _fx2_ctrl = fx2_ctrl; + } + + void write_i2c(boost::uint8_t addr, const byte_vector_t &bytes) + { + UHD_ASSERT_THROW(bytes.size() < max_i2c_data_bytes); + + unsigned char buff[max_i2c_data_bytes]; + std::copy(bytes.begin(), bytes.end(), buff); + + int ret = _fx2_ctrl->usrp_i2c_write(addr & 0xff, + buff, + bytes.size()); + + if (iface_debug && (ret < 0)) + uhd::runtime_error("USRP: failed i2c write"); + } + + byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes) + { + UHD_ASSERT_THROW(num_bytes < max_i2c_data_bytes); + + unsigned char buff[max_i2c_data_bytes]; + int ret = _fx2_ctrl->usrp_i2c_read(addr & 0xff, + buff, + num_bytes); + + if (iface_debug && ((ret < 0) || (unsigned)ret < (num_bytes))) + uhd::runtime_error("USRP: failed i2c read"); + + byte_vector_t out_bytes; + for (size_t i = 0; i < num_bytes; i++) + out_bytes.push_back(buff[i]); + + return out_bytes; + } + +private: + static const size_t max_i2c_data_bytes = 64; + uhd::usrp::fx2_ctrl::sptr _fx2_ctrl; +}; + +/*********************************************************************** + * USRP-E100 interface implementation + **********************************************************************/ +class b100_iface_impl : public b100_iface{ +public: + /******************************************************************* + * Structors + ******************************************************************/ + b100_iface_impl(uhd::usrp::fx2_ctrl::sptr fx2_ctrl, + b100_ctrl::sptr fpga_ctrl) : + _fx2_i2c_iface(fx2_ctrl), + _fx2_ctrl(fx2_ctrl), + _fpga_ctrl(fpga_ctrl) + { + this->check_fw_compat(); + if (fpga_ctrl.get() != NULL){ + enable_gpif(1); + i2c_init(); + this->check_fpga_compat(); + } + mb_eeprom = mboard_eeprom_t(get_fx2_i2c_iface(), mboard_eeprom_t::MAP_B000); + } + + void check_fw_compat(void){ + unsigned char data[4]; //useless data buffer + const boost::uint16_t fw_compat_num = _fx2_ctrl->usrp_control_read( + VRQ_FW_COMPAT, 0, 0, data, sizeof(data) + ); + if (fw_compat_num != USRP_B_FW_COMPAT_NUM){ + throw uhd::runtime_error(str(boost::format( + "Expected firmware compatibility number 0x%x, but got 0x%x:\n" + "The firmware build is not compatible with the host code build." + ) % USRP_B_FW_COMPAT_NUM % fw_compat_num)); + } + } + + void check_fpga_compat(void){ + const boost::uint16_t fpga_compat_num = this->peek16(B100_REG_MISC_COMPAT); + if (fpga_compat_num != USRP_B_FPGA_COMPAT_NUM){ + throw uhd::runtime_error(str(boost::format( + "Expected FPGA compatibility number 0x%x, but got 0x%x:\n" + "The FPGA build is not compatible with the host code build." + ) % USRP_B_FPGA_COMPAT_NUM % fpga_compat_num)); + } + } + + ~b100_iface_impl(void) + { + /* NOP */ + } + + /******************************************************************* + * Peek and Poke + ******************************************************************/ + + void poke(boost::uint32_t addr, const ctrl_data_t &data) { + boost::mutex::scoped_lock lock(_ctrl_mutex); + _fpga_ctrl->write(addr, data); + } + + ctrl_data_t peek(boost::uint32_t addr, size_t len) { + boost::mutex::scoped_lock lock(_ctrl_mutex); + return _fpga_ctrl->read(addr, len); + } + + void poke16(boost::uint32_t addr, boost::uint16_t value) + { + ctrl_data_t words(1); + words[0] = value; + poke(addr, words); + } + + void poke32(boost::uint32_t addr, boost::uint32_t value) + { + //just a subset of poke() to maintain compatibility + ctrl_data_t words(2); + words[0] = value & 0x0000FFFF; + words[1] = value >> 16; + poke(addr, words); + } + + boost::uint32_t peek32(boost::uint32_t addr) + { + ctrl_data_t words = peek(addr, 2); + return boost::uint32_t((boost::uint32_t(words[1]) << 16) | words[0]); + } + + boost::uint16_t peek16(boost::uint32_t addr) + { + ctrl_data_t words = peek(addr, 1); + return boost::uint16_t(words[0]); + } + + /******************************************************************* + * I2C + ******************************************************************/ + static const boost::uint32_t i2c_datarate = 400000; + static const boost::uint32_t wishbone_clk = 64000000; //FIXME should go somewhere else + + void i2c_init(void) { + //init I2C FPGA interface. + poke16(B100_REG_I2C_CTRL, 0x0000); + //set prescalers to operate at 400kHz: WB_CLK is 64MHz... + boost::uint16_t prescaler = wishbone_clk / (i2c_datarate*5) - 1; + poke16(B100_REG_I2C_PRESCALER_LO, prescaler & 0xFF); + poke16(B100_REG_I2C_PRESCALER_HI, (prescaler >> 8) & 0xFF); + poke16(B100_REG_I2C_CTRL, I2C_CTRL_EN); //enable I2C core + } + + static const size_t max_i2c_data_bytes = 64; + + void i2c_wait_for_xfer(void) + { + while(this->peek16(B100_REG_I2C_CMD_STATUS) & I2C_ST_TIP) + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + } + + bool wait_chk_ack(void) { + i2c_wait_for_xfer(); + return (this->peek16(B100_REG_I2C_CMD_STATUS) & I2C_ST_RXACK) == 0; + } + + void write_i2c(boost::uint8_t addr, const byte_vector_t &bytes) + { + poke16(B100_REG_I2C_DATA, (addr << 1) | 0); //addr and read bit (0) + poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_WR | I2C_CMD_START | (bytes.size() == 0 ? I2C_CMD_STOP : 0)); + + //wait for previous transfer to complete + if(!wait_chk_ack()) { + poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_STOP); + return; + } + + for(size_t i = 0; i < bytes.size(); i++) { + poke16(B100_REG_I2C_DATA, bytes[i]); + poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_WR | ((i == (bytes.size() - 1)) ? I2C_CMD_STOP : 0)); + if(!wait_chk_ack()) { + poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_STOP); + return; + } + } + } + + byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes) + { + byte_vector_t bytes; + if(num_bytes == 0) return bytes; + + while (peek16(B100_REG_I2C_CMD_STATUS) & I2C_ST_BUSY); + + poke16(B100_REG_I2C_DATA, (addr << 1) | 1); //addr and read bit (1) + poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_WR | I2C_CMD_START); + //wait for previous transfer to complete + if(!wait_chk_ack()) { + poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_STOP); + } + + for(; num_bytes > 0; num_bytes--) { + poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_RD | ((num_bytes == 1) ? (I2C_CMD_STOP | I2C_CMD_NACK) : 0)); + i2c_wait_for_xfer(); + boost::uint8_t readback = peek16(B100_REG_I2C_DATA) & 0xFF; + bytes.push_back(readback); + } + return bytes; + } + + i2c_iface &get_fx2_i2c_iface(void){ + return _fx2_i2c_iface; + } + + /******************************************************************* + * SPI interface + * Eventually this will be replaced with a control-channel system + * to let the firmware do the actual write/readback cycles. + * This keeps the bandwidth on the control channel down. + ******************************************************************/ + + void spi_wait(void) { + while(peek32(B100_REG_SPI_CTRL) & SPI_CTRL_GO_BSY); + } + + boost::uint32_t transact_spi(int which_slave, + const spi_config_t &config, + boost::uint32_t bits, + size_t num_bits, + bool readback) + { + UHD_ASSERT_THROW((num_bits <= 32) && !(num_bits % 8)); + + int edge_flags = ((config.miso_edge==spi_config_t::EDGE_FALL) ? SPI_CTRL_RXNEG : 0) | + ((config.mosi_edge==spi_config_t::EDGE_FALL) ? 0 : SPI_CTRL_TXNEG) + ; + + boost::uint16_t ctrl = SPI_CTRL_ASS | (SPI_CTRL_CHAR_LEN_MASK & num_bits) | edge_flags; + + poke16(B100_REG_SPI_DIV, 0x0001); // = fpga_clk / 4 + poke32(B100_REG_SPI_SS, which_slave & 0xFFFF); + poke32(B100_REG_SPI_TXRX0, bits); + poke16(B100_REG_SPI_CTRL, ctrl); + + poke16(B100_REG_SPI_CTRL, ctrl | SPI_CTRL_GO_BSY); + if(readback) { + spi_wait(); + return peek32(B100_REG_SPI_TXRX0); + } + else { + return 0; + } + } + + void reset_gpif(boost::uint16_t ep) { + _fx2_ctrl->usrp_control_write(VRQ_RESET_GPIF, ep, ep, 0, 0); + } + + void enable_gpif(bool en) { + _fx2_ctrl->usrp_control_write(VRQ_ENABLE_GPIF, en ? 1 : 0, 0, 0, 0); + } + + void clear_fpga_fifo(void) { + _fx2_ctrl->usrp_control_write(VRQ_CLEAR_FPGA_FIFO, 0, 0, 0, 0); + } + + void write_uart(boost::uint8_t, const std::string &) { + throw uhd::not_implemented_error("Unhandled command write_uart()"); + } + + std::string read_uart(boost::uint8_t) { + throw uhd::not_implemented_error("Unhandled command read_uart()"); + } + +private: + b100_i2c_fx2_iface _fx2_i2c_iface; + uhd::usrp::fx2_ctrl::sptr _fx2_ctrl; + b100_ctrl::sptr _fpga_ctrl; + boost::mutex _ctrl_mutex; +}; + +/*********************************************************************** + * Public Make Function + **********************************************************************/ +b100_iface::sptr b100_iface::make(uhd::usrp::fx2_ctrl::sptr fx2_ctrl, + b100_ctrl::sptr fpga_ctrl) +{ + return b100_iface::sptr(new b100_iface_impl(fx2_ctrl, fpga_ctrl)); +} diff --git a/host/lib/usrp/usrp_b100/b100_iface.hpp b/host/lib/usrp/usrp_b100/b100_iface.hpp new file mode 100644 index 000000000..4da43681d --- /dev/null +++ b/host/lib/usrp/usrp_b100/b100_iface.hpp @@ -0,0 +1,73 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INCLUDED_B100_IFACE_HPP +#define INCLUDED_B100_IFACE_HPP + +#include +#include +#include +#include +#include +#include "fx2_ctrl.hpp" +#include "b100_ctrl.hpp" + +/*! + * The usrp1 interface class: + * Provides a set of functions to implementation layer. + * Including spi, peek, poke, control... + */ +class b100_iface : boost::noncopyable, public uhd::usrp::mboard_iface{ +public: + typedef boost::shared_ptr sptr; + + /*! + * Make a new b100 interface with the control transport. + * \param fx2_ctrl the usrp control object + * \param fpga_ctrl the FPGA interface control object + * \return a new usrp1 interface object + */ + static sptr make(uhd::usrp::fx2_ctrl::sptr fx2_ctrl, + b100_ctrl::sptr fpga_ctrl = b100_ctrl::sptr() + ); + + /*! + * Reset the GPIF interface on the FX2 + * \param which endpoint to reset + * \return + */ + virtual void reset_gpif(boost::uint16_t ep) = 0; + + /*! + * Clear the GPIF FIFOs on the FPGA + * \return + */ + virtual void clear_fpga_fifo(void) = 0; + + /*! + * Enable/disable the GPIF interfaces on the FX2 + * \return + */ + virtual void enable_gpif(bool en) = 0; + + //! Get access to the FX2 I2C interface + virtual uhd::i2c_iface &get_fx2_i2c_iface(void) = 0; + + uhd::usrp::mboard_eeprom_t mb_eeprom; +}; + +#endif /* INCLUDED_USRP1_IFACE_HPP */ diff --git a/host/lib/usrp/usrp_b100/b100_impl.cpp b/host/lib/usrp/usrp_b100/b100_impl.cpp new file mode 100644 index 000000000..066831a92 --- /dev/null +++ b/host/lib/usrp/usrp_b100/b100_impl.cpp @@ -0,0 +1,277 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "b100_impl.hpp" +#include "b100_ctrl.hpp" +#include "fpga_regs_standard.h" +#include "usrp_spi_defs.h" +#include +#include "ctrl_packet.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "b100_regs.hpp" + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +const boost::uint16_t B100_VENDOR_ID = 0x2500; +const boost::uint16_t B100_PRODUCT_ID = 0x0001; +const boost::uint16_t FX2_VENDOR_ID = 0x04b4; +const boost::uint16_t FX2_PRODUCT_ID = 0x8613; + +/*********************************************************************** + * Discovery + **********************************************************************/ +static device_addrs_t b100_find(const device_addr_t &hint) +{ + device_addrs_t b100_addrs; + + //return an empty list of addresses when type is set to non-b100 + if (hint.has_key("type") and hint["type"] != "b100") return b100_addrs; + + //extract the firmware path for the b100 + std::string b100_fw_image; + try{ + b100_fw_image = find_image_path( + hint.has_key("fw")? hint["fw"] : "usrp_b100_fw_c2.ihx" + ); + } + catch(...){ + UHD_MSG(warning) << boost::format( + "Could not locate B100 firmware.\n" + "Please install the images package.\n" + ); + return b100_addrs; + } + + boost::uint16_t vid = hint.has_key("uninit") ? FX2_VENDOR_ID : B100_VENDOR_ID; + boost::uint16_t pid = hint.has_key("uninit") ? FX2_PRODUCT_ID : B100_PRODUCT_ID; + + // Important note: + // The get device list calls are nested inside the for loop. + // This allows the usb guts to decontruct when not in use, + // so that re-enumeration after fw load can occur successfully. + // This requirement is a courtesy of libusb1.0 on windows. + + //find the usrps and load firmware + BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { + try { + fx2_ctrl::make(usb_control::make(handle))->usrp_load_firmware(b100_fw_image); + } catch (...) { + UHD_MSG(status) << "Interface claimed, ignoring device" << std::endl; + } + } + + //get descriptors again with serial number, but using the initialized VID/PID now since we have firmware + vid = B100_VENDOR_ID; + pid = B100_PRODUCT_ID; + + BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { + device_addr_t new_addr; + new_addr["type"] = "b100"; + + //Attempt to read the name from the EEPROM and perform filtering. + //This operation can throw due to compatibility mismatch. + try{ + usb_control::sptr control = usb_control::make(handle); + b100_iface::sptr iface = b100_iface::make(fx2_ctrl::make(control)); + new_addr["name"] = iface->mb_eeprom["name"]; + new_addr["serial"] = handle->get_serial(); + } + catch(const uhd::exception &){ + //set these values as empty string so the device may still be found + //and the filter's below can still operate on the discovered device + new_addr["name"] = ""; + new_addr["serial"] = ""; + } + + //this is a found b100 when the hint serial and name match or blank + if ( + (not hint.has_key("name") or hint["name"] == new_addr["name"]) and + (not hint.has_key("serial") or hint["serial"] == new_addr["serial"]) + ){ + b100_addrs.push_back(new_addr); + } + } + + return b100_addrs; +} + +/*********************************************************************** + * Make + **********************************************************************/ +static device::sptr b100_make(const device_addr_t &device_addr){ + + //extract the FPGA path for the B100 + std::string b100_fpga_image = find_image_path( + device_addr.has_key("fpga")? device_addr["fpga"] : "usrp_b100_fpga_c3.bin" + ); + + //try to match the given device address with something on the USB bus + std::vector device_list = + usb_device_handle::get_device_list(B100_VENDOR_ID, B100_PRODUCT_ID); + + //locate the matching handle in the device list + usb_device_handle::sptr handle; + BOOST_FOREACH(usb_device_handle::sptr dev_handle, device_list) { + if (dev_handle->get_serial() == device_addr["serial"]){ + handle = dev_handle; + break; + } + } + UHD_ASSERT_THROW(handle.get() != NULL); //better be found + + //create control objects and a data transport + usb_control::sptr fx2_transport = usb_control::make(handle); + fx2_ctrl::sptr fx2_ctrl = fx2_ctrl::make(fx2_transport); + fx2_ctrl->usrp_load_fpga(b100_fpga_image); + + device_addr_t data_xport_args; + data_xport_args["recv_frame_size"] = device_addr.get("recv_frame_size", "16384"); + data_xport_args["num_recv_frames"] = device_addr.get("num_recv_frames", "16"); + data_xport_args["send_frame_size"] = device_addr.get("send_frame_size", "16384"); + data_xport_args["num_send_frames"] = device_addr.get("num_send_frames", "16"); + + usb_zero_copy::sptr data_transport = usb_zero_copy::make_wrapper( + usb_zero_copy::make( + handle, // identifier + 6, // IN endpoint + 2, // OUT endpoint + data_xport_args // param hints + ) + ); + + //create the control transport + device_addr_t ctrl_xport_args; + ctrl_xport_args["recv_frame_size"] = boost::lexical_cast(CTRL_PACKET_LENGTH); + ctrl_xport_args["num_recv_frames"] = "16"; + ctrl_xport_args["send_frame_size"] = boost::lexical_cast(CTRL_PACKET_LENGTH); + ctrl_xport_args["num_send_frames"] = "4"; + + usb_zero_copy::sptr ctrl_transport = usb_zero_copy::make( + handle, + 8, + 4, + ctrl_xport_args + ); + + const double master_clock_rate = device_addr.cast("master_clock_rate", 64e6); + + + //create the b100 implementation guts + return device::sptr(new b100_impl(data_transport, ctrl_transport, fx2_ctrl, master_clock_rate)); +} + +UHD_STATIC_BLOCK(register_b100_device){ + device::register_device(&b100_find, &b100_make); +} + +/*********************************************************************** + * Structors + **********************************************************************/ +b100_impl::b100_impl(uhd::transport::usb_zero_copy::sptr data_transport, + uhd::transport::usb_zero_copy::sptr ctrl_transport, + uhd::usrp::fx2_ctrl::sptr fx2_ctrl, + const double master_clock_rate) + : _data_transport(data_transport), _fx2_ctrl(fx2_ctrl) +{ + //this is the handler object for FPGA control packets + _fpga_ctrl = b100_ctrl::make(ctrl_transport); + + _iface = b100_iface::make(_fx2_ctrl, _fpga_ctrl); + + //create clock interface + _clock_ctrl = b100_clock_ctrl::make(_iface, master_clock_rate); + + //create codec interface + _codec_ctrl = b100_codec_ctrl::make(_iface); + + //initialize the codecs + codec_init(); + + //initialize the mboard + mboard_init(); + + //initialize the dboards + dboard_init(); + + //initialize the dsps + rx_ddc_init(); + + //initialize the dsps + tx_duc_init(); + + //init the subdev specs + this->mboard_set(MBOARD_PROP_RX_SUBDEV_SPEC, subdev_spec_t()); + this->mboard_set(MBOARD_PROP_TX_SUBDEV_SPEC, subdev_spec_t()); + + //initialize the send/recv buffs + io_init(); +} + +b100_impl::~b100_impl(void){ + /* NOP */ +} + +bool b100_impl::recv_async_msg(uhd::async_metadata_t &md, double timeout){ + return _fpga_ctrl->recv_async_msg(md, timeout); +} + +/*********************************************************************** + * Device Get + **********************************************************************/ +void b100_impl::get(const wax::obj &key_, wax::obj &val) +{ + named_prop_t key = named_prop_t::extract(key_); + + //handle the get request conditioned on the key + switch(key.as()){ + case DEVICE_PROP_NAME: + val = std::string("USRP-B100 device"); + return; + + case DEVICE_PROP_MBOARD: + UHD_ASSERT_THROW(key.name == ""); + val = _mboard_proxy->get_link(); + return; + + case DEVICE_PROP_MBOARD_NAMES: + val = prop_names_t(1, ""); //vector of size 1 with empty string + return; + + default: UHD_THROW_PROP_GET_ERROR(); + } +} + +/*********************************************************************** + * Device Set + **********************************************************************/ +void b100_impl::set(const wax::obj &, const wax::obj &) +{ + UHD_THROW_PROP_SET_ERROR(); +} diff --git a/host/lib/usrp/usrp_b100/b100_impl.hpp b/host/lib/usrp/usrp_b100/b100_impl.hpp new file mode 100644 index 000000000..daec70bca --- /dev/null +++ b/host/lib/usrp/usrp_b100/b100_impl.hpp @@ -0,0 +1,204 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "b100_iface.hpp" +#include "b100_ctrl.hpp" +#include "clock_ctrl.hpp" +#include "codec_ctrl.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef INCLUDED_B100_IMPL_HPP +#define INCLUDED_B100_IMPL_HPP + +/*! + * Make a b100 dboard interface. + * \param iface the b100 interface object + * \param clock the clock control interface + * \param codec the codec control interface + * \return a sptr to a new dboard interface + */ +uhd::usrp::dboard_iface::sptr make_b100_dboard_iface( + b100_iface::sptr iface, + b100_clock_ctrl::sptr clock, + b100_codec_ctrl::sptr codec +); + +/*! + * Simple wax obj proxy class: + * Provides a wax obj interface for a set and a get function. + * This allows us to create nested properties structures + * while maintaining flattened code within the implementation. + */ +class wax_obj_proxy : public wax::obj { +public: + typedef boost::function get_t; + typedef boost::function set_t; + typedef boost::shared_ptr sptr; + + static sptr make(const get_t &get, const set_t &set){ + return sptr(new wax_obj_proxy(get, set)); + } + +private: + get_t _get; set_t _set; + wax_obj_proxy(const get_t &get, const set_t &set): _get(get), _set(set) {}; + void get(const wax::obj &key, wax::obj &val) {return _get(key, val);} + void set(const wax::obj &key, const wax::obj &val) {return _set(key, val);} +}; + +/*! + * USRP1 implementation guts: + * The implementation details are encapsulated here. + * Handles properties on the mboard, dboard, dsps... + */ +class b100_impl : public uhd::device { +public: + //structors + b100_impl(uhd::transport::usb_zero_copy::sptr data_transport, + uhd::transport::usb_zero_copy::sptr ctrl_transport, + uhd::usrp::fx2_ctrl::sptr fx2_ctrl, + double master_clock_rate); + + ~b100_impl(void); + + //the io interface + size_t send(const send_buffs_type &, + size_t, + const uhd::tx_metadata_t &, + const uhd::io_type_t &, + send_mode_t, double); + + size_t recv(const recv_buffs_type &, + size_t, uhd::rx_metadata_t &, + const uhd::io_type_t &, + recv_mode_t, double); + + size_t get_max_send_samps_per_packet(void) const; + + size_t get_max_recv_samps_per_packet(void) const; + + bool recv_async_msg(uhd::async_metadata_t &, double); + +private: + //clock control + b100_clock_ctrl::sptr _clock_ctrl; + + //interface to ioctls and file descriptor + b100_iface::sptr _iface; + + //handle io stuff + uhd::transport::zero_copy_if::sptr _data_transport; + UHD_PIMPL_DECL(io_impl) _io_impl; + void update_transport_channel_mapping(void); + void io_init(void); + void issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd); + void handle_overrun(size_t); + + //otw types + uhd::otw_type_t _recv_otw_type; + uhd::otw_type_t _send_otw_type; + + //configuration shadows + uhd::clock_config_t _clock_config; + uhd::usrp::subdev_spec_t _rx_subdev_spec, _tx_subdev_spec; + + //ad9862 codec control interface + b100_codec_ctrl::sptr _codec_ctrl; + + //codec properties interfaces + void codec_init(void); + void rx_codec_get(const wax::obj &, wax::obj &); + void rx_codec_set(const wax::obj &, const wax::obj &); + void tx_codec_get(const wax::obj &, wax::obj &); + void tx_codec_set(const wax::obj &, const wax::obj &); + wax_obj_proxy::sptr _rx_codec_proxy, _tx_codec_proxy; + + //device functions and settings + void get(const wax::obj &, wax::obj &); + void set(const wax::obj &, const wax::obj &); + + //mboard functions and settings + void mboard_init(void); + void mboard_get(const wax::obj &, wax::obj &); + void mboard_set(const wax::obj &, const wax::obj &); + void update_clock_config(void); + wax_obj_proxy::sptr _mboard_proxy; + + /*! + * Make a usrp1 dboard interface. + * \param iface the usrp1 interface object + * \param clock the clock control interface + * \param codec the codec control interface + * \param dboard_slot the slot identifier + * \param rx_dboard_id the db id for the rx board (used for evil dbsrx purposes) + * \return a sptr to a new dboard interface + */ + static uhd::usrp::dboard_iface::sptr make_dboard_iface( + b100_iface::sptr iface, + b100_clock_ctrl::sptr clock, + b100_codec_ctrl::sptr codec, + const uhd::usrp::dboard_id_t &rx_dboard_id + ); + + //xx dboard functions and settings + void dboard_init(void); + uhd::usrp::dboard_manager::sptr _dboard_manager; + uhd::usrp::dboard_iface::sptr _dboard_iface; + + //rx dboard functions and settings + uhd::usrp::dboard_eeprom_t _rx_db_eeprom; + void rx_dboard_get(const wax::obj &, wax::obj &); + void rx_dboard_set(const wax::obj &, const wax::obj &); + wax_obj_proxy::sptr _rx_dboard_proxy; + + //tx dboard functions and settings + uhd::usrp::dboard_eeprom_t _tx_db_eeprom, _gdb_eeprom; + void tx_dboard_get(const wax::obj &, wax::obj &); + void tx_dboard_set(const wax::obj &, const wax::obj &); + wax_obj_proxy::sptr _tx_dboard_proxy; + + //rx ddc functions and settings + void rx_ddc_init(void); + void rx_ddc_get(const wax::obj &, wax::obj &); + void rx_ddc_set(const wax::obj &, const wax::obj &); + double _ddc_freq; size_t _ddc_decim; + wax_obj_proxy::sptr _rx_ddc_proxy; + + //tx duc functions and settings + void tx_duc_init(void); + void tx_duc_get(const wax::obj &, wax::obj &); + void tx_duc_set(const wax::obj &, const wax::obj &); + double _duc_freq; size_t _duc_interp; + wax_obj_proxy::sptr _tx_duc_proxy; + + //transports + b100_ctrl::sptr _fpga_ctrl; + uhd::usrp::fx2_ctrl::sptr _fx2_ctrl; + +}; + +#endif /* INCLUDED_b100_IMPL_HPP */ diff --git a/host/lib/usrp/usrp_b100/b100_regs.hpp b/host/lib/usrp/usrp_b100/b100_regs.hpp new file mode 100644 index 000000000..010df283e --- /dev/null +++ b/host/lib/usrp/usrp_b100/b100_regs.hpp @@ -0,0 +1,264 @@ + + +//////////////////////////////////////////////////////////////// +// +// Memory map for wishbone bus +// +//////////////////////////////////////////////////////////////// + +// All addresses are byte addresses. All accesses are word (16-bit) accesses. +// This means that address bit 0 is usually 0. +// There are 11 bits of address for the control. + +#ifndef __B100_REGS_H +#define __B100_REGS_H + +///////////////////////////////////////////////////// +// Slave pointers + +#define B100_REG_SLAVE(n) ((n)<<7) +#define B100_REG_SR_ADDR(n) ((B100_REG_SETTINGS_BASE) + (4*(n))) + +///////////////////////////////////////////////////// +// Slave 0 -- Misc Regs + +#define B100_REG_MISC_BASE B100_REG_SLAVE(0) + +#define B100_REG_MISC_LED B100_REG_MISC_BASE + 0 +#define B100_REG_MISC_SW B100_REG_MISC_BASE + 2 +#define B100_REG_MISC_CGEN_CTRL B100_REG_MISC_BASE + 4 +#define B100_REG_MISC_CGEN_ST B100_REG_MISC_BASE + 6 +#define B100_REG_MISC_TEST B100_REG_MISC_BASE + 8 +#define B100_REG_MISC_RX_LEN B100_REG_MISC_BASE + 10 +#define B100_REG_MISC_TX_LEN B100_REG_MISC_BASE + 12 +#define B100_REG_MISC_XFER_RATE B100_REG_MISC_BASE + 14 +#define B100_REG_MISC_COMPAT B100_REG_MISC_BASE + 16 + +///////////////////////////////////////////////////// +// Slave 1 -- UART +// CLKDIV is 16 bits, others are only 8 + +#define B100_REG_UART_BASE B100_REG_SLAVE(1) + +#define B100_REG_UART_CLKDIV B100_REG_UART_BASE + 0 +#define B100_REG_UART_TXLEVEL B100_REG_UART_BASE + 2 +#define B100_REG_UART_RXLEVEL B100_REG_UART_BASE + 4 +#define B100_REG_UART_TXCHAR B100_REG_UART_BASE + 6 +#define B100_REG_UART_RXCHAR B100_REG_UART_BASE + 8 + +///////////////////////////////////////////////////// +// Slave 2 -- SPI Core +//these are 32-bit registers mapped onto the 16-bit Wishbone bus. +//Using peek32/poke32 should allow transparent use of these registers. +#define B100_REG_SPI_BASE B100_REG_SLAVE(2) +#define B100_REG_SPI_TXRX0 B100_REG_SPI_BASE + 0 +#define B100_REG_SPI_TXRX1 B100_REG_SPI_BASE + 4 +#define B100_REG_SPI_TXRX2 B100_REG_SPI_BASE + 8 +#define B100_REG_SPI_TXRX3 B100_REG_SPI_BASE + 12 +#define B100_REG_SPI_CTRL B100_REG_SPI_BASE + 16 +#define B100_REG_SPI_DIV B100_REG_SPI_BASE + 20 +#define B100_REG_SPI_SS B100_REG_SPI_BASE + 24 + +//spi slave constants +#define B100_SPI_SS_AD9862 (1 << 2) +#define B100_SPI_SS_TX_DB (1 << 1) +#define B100_SPI_SS_RX_DB (1 << 0) + +//spi ctrl register bit definitions +#define SPI_CTRL_ASS (1<<13) +#define SPI_CTRL_IE (1<<12) +#define SPI_CTRL_LSB (1<<11) +#define SPI_CTRL_TXNEG (1<<10) //mosi edge, push on falling edge when 1 +#define SPI_CTRL_RXNEG (1<< 9) //miso edge, latch on falling edge when 1 +#define SPI_CTRL_GO_BSY (1<< 8) +#define SPI_CTRL_CHAR_LEN_MASK 0x7F + +//////////////////////////////////////////////// +// Slave 3 -- I2C Core + +#define B100_REG_I2C_BASE B100_REG_SLAVE(3) +#define B100_REG_I2C_PRESCALER_LO B100_REG_I2C_BASE + 0 +#define B100_REG_I2C_PRESCALER_HI B100_REG_I2C_BASE + 2 +#define B100_REG_I2C_CTRL B100_REG_I2C_BASE + 4 +#define B100_REG_I2C_DATA B100_REG_I2C_BASE + 6 +#define B100_REG_I2C_CMD_STATUS B100_REG_I2C_BASE + 8 + +//and while we're here... + +// +// STA, STO, RD, WR, and IACK bits are cleared automatically +// + +#define I2C_CTRL_EN (1 << 7) // core enable +#define I2C_CTRL_IE (1 << 6) // interrupt enable + +#define I2C_CMD_START (1 << 7) // generate (repeated) start condition +#define I2C_CMD_STOP (1 << 6) // generate stop condition +#define I2C_CMD_RD (1 << 5) // read from slave +#define I2C_CMD_WR (1 << 4) // write to slave +#define I2C_CMD_NACK (1 << 3) // when a rcvr, send ACK (ACK=0) or NACK (ACK=1) +#define I2C_CMD_RSVD_2 (1 << 2) // reserved +#define I2C_CMD_RSVD_1 (1 << 1) // reserved +#define I2C_CMD_IACK (1 << 0) // set to clear pending interrupt + +#define I2C_ST_RXACK (1 << 7) // Received acknowledgement from slave (1 = NAK, 0 = ACK) +#define I2C_ST_BUSY (1 << 6) // 1 after START signal detected; 0 after STOP signal detected +#define I2C_ST_AL (1 << 5) // Arbitration lost. 1 when core lost arbitration +#define I2C_ST_RSVD_4 (1 << 4) // reserved +#define I2C_ST_RSVD_3 (1 << 3) // reserved +#define I2C_ST_RSVD_2 (1 << 2) // reserved +#define I2C_ST_TIP (1 << 1) // Transfer-in-progress +#define I2C_ST_IP (1 << 0) // Interrupt pending + +//////////////////////////////////////////////// +// Slave 4 -- GPIO + +#define B100_REG_GPIO_BASE B100_REG_SLAVE(4) + +#define B100_REG_GPIO_RX_IO B100_REG_GPIO_BASE + 0 +#define B100_REG_GPIO_TX_IO B100_REG_GPIO_BASE + 2 +#define B100_REG_GPIO_RX_DDR B100_REG_GPIO_BASE + 4 +#define B100_REG_GPIO_TX_DDR B100_REG_GPIO_BASE + 6 +#define B100_REG_GPIO_RX_SEL B100_REG_GPIO_BASE + 8 +#define B100_REG_GPIO_TX_SEL B100_REG_GPIO_BASE + 10 +#define B100_REG_GPIO_RX_DBG B100_REG_GPIO_BASE + 12 +#define B100_REG_GPIO_TX_DBG B100_REG_GPIO_BASE + 14 + +//possible bit values for sel when dbg is 0: +#define GPIO_SEL_SW 0 // if pin is an output, set by software in the io reg +#define GPIO_SEL_ATR 1 // if pin is an output, set by ATR logic + +//possible bit values for sel when dbg is 1: +#define GPIO_SEL_DEBUG_0 0 // if pin is an output, debug lines from FPGA fabric +#define GPIO_SEL_DEBUG_1 1 // if pin is an output, debug lines from FPGA fabric + +/////////////////////////////////////////////////// +// Slave 6 -- ATR Controller +// 16 regs + +#define B100_REG_ATR_BASE B100_REG_SLAVE(6) + +#define B100_REG_ATR_IDLE_RXSIDE B100_REG_ATR_BASE + 0 +#define B100_REG_ATR_IDLE_TXSIDE B100_REG_ATR_BASE + 2 +#define B100_REG_ATR_INTX_RXSIDE B100_REG_ATR_BASE + 4 +#define B100_REG_ATR_INTX_TXSIDE B100_REG_ATR_BASE + 6 +#define B100_REG_ATR_INRX_RXSIDE B100_REG_ATR_BASE + 8 +#define B100_REG_ATR_INRX_TXSIDE B100_REG_ATR_BASE + 10 +#define B100_REG_ATR_FULL_RXSIDE B100_REG_ATR_BASE + 12 +#define B100_REG_ATR_FULL_TXSIDE B100_REG_ATR_BASE + 14 + +/////////////////////////////////////////////////// +// Slave 7 -- Readback Mux 32 + +#define B100_REG_RB_MUX_32_BASE B100_REG_SLAVE(7) + +#define B100_REG_RB_TIME_NOW_SECS B100_REG_RB_MUX_32_BASE + 0 +#define B100_REG_RB_TIME_NOW_TICKS B100_REG_RB_MUX_32_BASE + 4 +#define B100_REG_RB_TIME_PPS_SECS B100_REG_RB_MUX_32_BASE + 8 +#define B100_REG_RB_TIME_PPS_TICKS B100_REG_RB_MUX_32_BASE + 12 +#define B100_REG_RB_MISC_TEST32 B100_REG_RB_MUX_32_BASE + 16 + +//////////////////////////////////////////////////// +// Slaves 8 & 9 -- Settings Bus +// +// Output-only, no readback, 64 registers total +// Each register must be written 32 bits at a time +// First the address xxx_xx00 and then xxx_xx10 + +#define B100_REG_SETTINGS_BASE_ADDR(n) (B100_REG_SLAVE(8) + (4*(n))) + +#define B100_REG_SR_MISC_TEST32 B100_REG_SETTINGS_BASE_ADDR(52) + +///////////////////////////////////////////////// +// DSP RX Regs +//////////////////////////////////////////////// +#define B100_REG_DSP_RX_ADDR(n) (B100_REG_SETTINGS_BASE_ADDR(16) + (4*(n))) +#define B100_REG_DSP_RX_FREQ B100_REG_DSP_RX_ADDR(0) +#define B100_REG_DSP_RX_SCALE_IQ B100_REG_DSP_RX_ADDR(1) // {scale_i,scale_q} +#define B100_REG_DSP_RX_DECIM_RATE B100_REG_DSP_RX_ADDR(2) // hb and decim rate +#define B100_REG_DSP_RX_DCOFFSET_I B100_REG_DSP_RX_ADDR(3) // Bit 31 high sets fixed offset mode, using lower 14 bits, // otherwise it is automatic +#define B100_REG_DSP_RX_DCOFFSET_Q B100_REG_DSP_RX_ADDR(4) // Bit 31 high sets fixed offset mode, using lower 14 bits +#define B100_REG_DSP_RX_MUX B100_REG_DSP_RX_ADDR(5) + +/////////////////////////////////////////////////// +// VITA RX CTRL regs +/////////////////////////////////////////////////// +// The following 3 are logically a single command register. +// They are clocked into the underlying fifo when time_ticks is written. +#define B100_REG_CTRL_RX_ADDR(n) (B100_REG_SETTINGS_BASE_ADDR(0) + (4*(n))) +#define B100_REG_CTRL_RX_STREAM_CMD B100_REG_CTRL_RX_ADDR(0) // {now, chain, num_samples(30) +#define B100_REG_CTRL_RX_TIME_SECS B100_REG_CTRL_RX_ADDR(1) +#define B100_REG_CTRL_RX_TIME_TICKS B100_REG_CTRL_RX_ADDR(2) +#define B100_REG_CTRL_RX_CLEAR_OVERRUN B100_REG_CTRL_RX_ADDR(3) // write anything to clear overrun +#define B100_REG_CTRL_RX_VRT_HEADER B100_REG_CTRL_RX_ADDR(4) // word 0 of packet. FPGA fills in packet counter +#define B100_REG_CTRL_RX_VRT_STREAM_ID B100_REG_CTRL_RX_ADDR(5) // word 1 of packet. +#define B100_REG_CTRL_RX_VRT_TRAILER B100_REG_CTRL_RX_ADDR(6) +#define B100_REG_CTRL_RX_NSAMPS_PER_PKT B100_REG_CTRL_RX_ADDR(7) +#define B100_REG_CTRL_RX_NCHANNELS B100_REG_CTRL_RX_ADDR(8) // 1 in basic case, up to 4 for vector sources + +///////////////////////////////////////////////// +// DSP TX Regs +//////////////////////////////////////////////// +#define B100_REG_DSP_TX_ADDR(n) (B100_REG_SETTINGS_BASE_ADDR(32) + (4*(n))) +#define B100_REG_DSP_TX_FREQ B100_REG_DSP_TX_ADDR(0) +#define B100_REG_DSP_TX_SCALE_IQ B100_REG_DSP_TX_ADDR(1) // {scale_i,scale_q} +#define B100_REG_DSP_TX_INTERP_RATE B100_REG_DSP_TX_ADDR(2) +#define B100_REG_DSP_TX_UNUSED B100_REG_DSP_TX_ADDR(3) +#define B100_REG_DSP_TX_MUX B100_REG_DSP_TX_ADDR(4) + +///////////////////////////////////////////////// +// VITA TX CTRL regs +//////////////////////////////////////////////// +#define B100_REG_CTRL_TX_ADDR(n) (B100_REG_SETTINGS_BASE_ADDR(24) + (4*(n))) +#define B100_REG_CTRL_TX_NCHANNELS B100_REG_CTRL_TX_ADDR(0) +#define B100_REG_CTRL_TX_CLEAR_UNDERRUN B100_REG_CTRL_TX_ADDR(1) +#define B100_REG_CTRL_TX_REPORT_SID B100_REG_CTRL_TX_ADDR(2) +#define B100_REG_CTRL_TX_POLICY B100_REG_CTRL_TX_ADDR(3) + +#define B100_FLAG_CTRL_TX_POLICY_WAIT (0x1 << 0) +#define B100_FLAG_CTRL_TX_POLICY_NEXT_PACKET (0x1 << 1) +#define B100_FLAG_CTRL_TX_POLICY_NEXT_BURST (0x1 << 2) + +///////////////////////////////////////////////// +// VITA49 64 bit time (write only) +//////////////////////////////////////////////// + /*! + * \brief Time 64 flags + * + *
+   *
+   *    3                   2                   1
+   *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+   * +-----------------------------------------------------------+-+-+
+   * |                                                           |S|P|
+   * +-----------------------------------------------------------+-+-+
+   *
+   * P - PPS edge selection (0=negedge, 1=posedge, default=0)
+   * S - Source (0=sma, 1=mimo, 0=default)
+   *
+   * 
+ */ +#define B100_REG_TIME64_ADDR(n) (B100_REG_SETTINGS_BASE_ADDR(40) + (4*(n))) +#define B100_REG_TIME64_SECS B100_REG_TIME64_ADDR(0) // value to set absolute secs to on next PPS +#define B100_REG_TIME64_TICKS B100_REG_TIME64_ADDR(1) // value to set absolute ticks to on next PPS +#define B100_REG_TIME64_FLAGS B100_REG_TIME64_ADDR(2) // flags - see chart above +#define B100_REG_TIME64_IMM B100_REG_TIME64_ADDR(3) // set immediate (0=latch on next pps, 1=latch immediate, default=0) +#define B100_REG_TIME64_TPS B100_REG_TIME64_ADDR(4) // clock ticks per second (counter rollover) + +//pps flags (see above) +#define B100_FLAG_TIME64_PPS_NEGEDGE (0 << 0) +#define B100_FLAG_TIME64_PPS_POSEDGE (1 << 0) +#define B100_FLAG_TIME64_PPS_SMA (0 << 1) +#define B100_FLAG_TIME64_PPS_MIMO (1 << 1) + +#define B100_FLAG_TIME64_LATCH_NOW 1 +#define B100_FLAG_TIME64_LATCH_NEXT_PPS 0 + +#define B100_REG_CLEAR_RX_FIFO B100_REG_SETTINGS_BASE_ADDR(48) +#define B100_REG_CLEAR_TX_FIFO B100_REG_SETTINGS_BASE_ADDR(49) + +#define B100_REG_GLOBAL_RESET B100_REG_SETTINGS_BASE_ADDR(50) +#define B100_REG_TEST32 B100_REG_SETTINGS_BASE_ADDR(52) + +#endif + diff --git a/host/lib/usrp/usrp_b100/clock_ctrl.cpp b/host/lib/usrp/usrp_b100/clock_ctrl.cpp new file mode 100644 index 000000000..e138242d1 --- /dev/null +++ b/host/lib/usrp/usrp_b100/clock_ctrl.cpp @@ -0,0 +1,525 @@ +// +// Copyright 2010-2011 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "clock_ctrl.hpp" +#include "ad9522_regs.hpp" +#include +#include +#include +#include +#include +#include +#include "b100_regs.hpp" //spi slave constants +#include +#include +#include +#include +#include //gcd +#include +#include + +using namespace uhd; + +/*********************************************************************** + * Constants + **********************************************************************/ +static const bool ENABLE_THE_TEST_OUT = true; +static const double REFERENCE_INPUT_RATE = 10e6; + +/*********************************************************************** + * Helpers + **********************************************************************/ +template static void set_clock_divider( + size_t divider, div_type &low, div_type &high, bypass_type &bypass +){ + high = divider/2 - 1; + low = divider - high - 2; + bypass = (divider == 1)? 1 : 0; +} + +/*********************************************************************** + * Clock rate calculation stuff: + * Using the internal VCO between 1400 and 1800 MHz + **********************************************************************/ +struct clock_settings_type{ + size_t ref_clock_doubler, r_counter, a_counter, b_counter, prescaler, vco_divider, chan_divider; + size_t get_n_counter(void) const{return prescaler * b_counter + a_counter;} + double get_ref_rate(void) const{return REFERENCE_INPUT_RATE * ref_clock_doubler;} + double get_vco_rate(void) const{return get_ref_rate()/r_counter * get_n_counter();} + double get_chan_rate(void) const{return get_vco_rate()/vco_divider;} + double get_out_rate(void) const{return get_chan_rate()/chan_divider;} + std::string to_pp_string(void) const{ + return str(boost::format( + " r_counter: %d\n" + " a_counter: %d\n" + " b_counter: %d\n" + " prescaler: %d\n" + " vco_divider: %d\n" + " chan_divider: %d\n" + " vco_rate: %fMHz\n" + " chan_rate: %fMHz\n" + " out_rate: %fMHz\n" + ) + % r_counter + % a_counter + % b_counter + % prescaler + % vco_divider + % chan_divider + % (get_vco_rate()/1e6) + % (get_chan_rate()/1e6) + % (get_out_rate()/1e6) + ); + } +}; + +//! gives the greatest divisor of num between 1 and max inclusive +template static inline T greatest_divisor(T num, T max){ + for (T i = max; i > 1; i--) if (num%i == 0) return i; return 1; +} + +//! gives the least divisor of num between min and num exclusive +template static inline T least_divisor(T num, T min){ + for (T i = min; i < num; i++) if (num%i == 0) return i; return 1; +} + +static clock_settings_type get_clock_settings(double rate){ + clock_settings_type cs; + cs.ref_clock_doubler = 2; //always doubling + cs.prescaler = 8; //set to 8 when input is under 2400 MHz + + //basic formulas used below: + //out_rate*X = ref_rate*Y + //X = i*ref_rate/gcd + //Y = i*out_rate/gcd + //X = chan_div * vco_div * R + //Y = P*B + A + + const boost::uint64_t out_rate = boost::uint64_t(rate); + const boost::uint64_t ref_rate = boost::uint64_t(cs.get_ref_rate()); + const size_t gcd = size_t(boost::math::gcd(ref_rate, out_rate)); + + for (size_t i = 1; i <= 100; i++){ + const size_t X = i*ref_rate/gcd; + const size_t Y = i*out_rate/gcd; + + //determine A and B (P is fixed) + cs.b_counter = Y/cs.prescaler; + cs.a_counter = Y - cs.b_counter*cs.prescaler; + + static const double vco_bound_pad = 100e6; + for ( //calculate an r divider that fits into the bounds of the vco + cs.r_counter = size_t(cs.get_n_counter()*cs.get_ref_rate()/(1800e6 - vco_bound_pad)); + cs.r_counter <= size_t(cs.get_n_counter()*cs.get_ref_rate()/(1400e6 + vco_bound_pad)) + and cs.r_counter > 0; cs.r_counter++ + ){ + + //determine chan_div and vco_div + //and fill in that order of preference + cs.chan_divider = greatest_divisor(X/cs.r_counter, 32); + cs.vco_divider = greatest_divisor(X/cs.chan_divider/cs.r_counter, 6); + + //avoid a vco divider of 1 (if possible) + if (cs.vco_divider == 1){ + cs.vco_divider = least_divisor(cs.chan_divider, 2); + cs.chan_divider /= cs.vco_divider; + } + + UHD_LOGV(always) + << "gcd " << gcd << std::endl + << "X " << X << std::endl + << "Y " << Y << std::endl + << cs.to_pp_string() << std::endl + ; + + //filter limits on the counters + if (cs.vco_divider == 1) continue; + if (cs.r_counter >= (1<<14)) continue; + if (cs.b_counter == 2) continue; + if (cs.b_counter == 1 and cs.a_counter != 0) continue; + if (cs.b_counter >= (1<<13)) continue; + if (cs.a_counter >= (1<<6)) continue; + + UHD_MSG(status) << "USRP-B100 clock control: " << i << std::endl << cs.to_pp_string() << std::endl; + return cs; + } + } + + throw uhd::runtime_error(str(boost::format( + "USRP-B100 clock control: could not calculate settings for clock rate %fMHz" + ) % (rate/1e6))); +} + +/*********************************************************************** + * Clock Control Implementation + **********************************************************************/ +class b100_clock_ctrl_impl : public b100_clock_ctrl{ +public: + b100_clock_ctrl_impl(b100_iface::sptr iface, double master_clock_rate){ + _iface = iface; + _chan_rate = 0.0; + _out_rate = 0.0; + + //init the clock gen registers + _ad9522_regs.sdo_active = ad9522_regs_t::SDO_ACTIVE_SDO_SDIO; + _ad9522_regs.enb_stat_eeprom_at_stat_pin = 0; //use status pin + _ad9522_regs.status_pin_control = 0x1; //n divider + _ad9522_regs.ld_pin_control = 0x00; //dld + _ad9522_regs.refmon_pin_control = 0x12; //show ref2 + _ad9522_regs.lock_detect_counter = ad9522_regs_t::LOCK_DETECT_COUNTER_16CYC; + + this->use_internal_ref(); + + this->set_fpga_clock_rate(master_clock_rate); //initialize to something + + this->enable_fpga_clock(true); + this->enable_test_clock(ENABLE_THE_TEST_OUT); + this->enable_rx_dboard_clock(false); + this->enable_tx_dboard_clock(false); + } + + ~b100_clock_ctrl_impl(void){ + UHD_SAFE_CALL( + this->enable_test_clock(ENABLE_THE_TEST_OUT); + this->enable_rx_dboard_clock(false); + this->enable_tx_dboard_clock(false); + //this->enable_fpga_clock(false); //FIXME + ) + } + + /*********************************************************************** + * Clock rate control: + * - set clock rate w/ internal VCO + * - set clock rate w/ external VCXO + **********************************************************************/ + void set_clock_settings_with_internal_vco(double rate){ + const clock_settings_type cs = get_clock_settings(rate); + + //set the rates to private variables so the implementation knows! + _chan_rate = cs.get_chan_rate(); + _out_rate = cs.get_out_rate(); + + _ad9522_regs.enable_clock_doubler = (cs.ref_clock_doubler == 2)? 1 : 0; + + _ad9522_regs.set_r_counter(cs.r_counter); + _ad9522_regs.a_counter = cs.a_counter; + _ad9522_regs.set_b_counter(cs.b_counter); + UHD_ASSERT_THROW(cs.prescaler == 8); //assumes this below: + _ad9522_regs.prescaler_p = ad9522_regs_t::PRESCALER_P_DIV8_9; + + _ad9522_regs.pll_power_down = ad9522_regs_t::PLL_POWER_DOWN_NORMAL; + _ad9522_regs.cp_current = ad9522_regs_t::CP_CURRENT_1_2MA; + + _ad9522_regs.bypass_vco_divider = 0; + switch(cs.vco_divider){ + case 1: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV1; break; + case 2: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV2; break; + case 3: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV3; break; + case 4: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV4; break; + case 5: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV5; break; + case 6: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV6; break; + } + _ad9522_regs.select_vco_or_clock = ad9522_regs_t::SELECT_VCO_OR_CLOCK_VCO; + + //setup fpga master clock + _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS; + set_clock_divider(cs.chan_divider, + _ad9522_regs.divider0_low_cycles, + _ad9522_regs.divider0_high_cycles, + _ad9522_regs.divider0_bypass + ); + + //setup codec clock + _ad9522_regs.out3_format = ad9522_regs_t::OUT3_FORMAT_LVDS; + set_clock_divider(cs.chan_divider, + _ad9522_regs.divider1_low_cycles, + _ad9522_regs.divider1_high_cycles, + _ad9522_regs.divider1_bypass + ); + + this->send_all_regs(); + calibrate_now(); + } + + void set_clock_settings_with_external_vcxo(double rate){ + //set the rates to private variables so the implementation knows! + _chan_rate = rate; + _out_rate = rate; + + _ad9522_regs.enable_clock_doubler = 1; //doubler always on + const double ref_rate = REFERENCE_INPUT_RATE*2; + + //bypass prescaler such that N = B + long gcd = boost::math::gcd(long(ref_rate), long(rate)); + _ad9522_regs.set_r_counter(int(ref_rate/gcd)); + _ad9522_regs.a_counter = 0; + _ad9522_regs.set_b_counter(int(rate/gcd)); + _ad9522_regs.prescaler_p = ad9522_regs_t::PRESCALER_P_DIV1; + + //setup external vcxo + _ad9522_regs.pll_power_down = ad9522_regs_t::PLL_POWER_DOWN_NORMAL; + _ad9522_regs.cp_current = ad9522_regs_t::CP_CURRENT_1_2MA; + _ad9522_regs.bypass_vco_divider = 1; + _ad9522_regs.select_vco_or_clock = ad9522_regs_t::SELECT_VCO_OR_CLOCK_EXTERNAL; + + //setup fpga master clock + _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS; + _ad9522_regs.divider0_bypass = 1; + + //setup codec clock + _ad9522_regs.out3_format = ad9522_regs_t::OUT3_FORMAT_LVDS; + _ad9522_regs.divider1_bypass = 1; + + this->send_all_regs(); + } + + void set_fpga_clock_rate(double rate){ + if (_out_rate == rate) return; + if (rate == 61.44e6) set_clock_settings_with_external_vcxo(rate); + else set_clock_settings_with_internal_vco(rate); + //clock rate changed! update dboard clocks and FPGA ticks per second + set_rx_dboard_clock_rate(rate); + set_tx_dboard_clock_rate(rate); + _iface->poke32(B100_REG_TIME64_TPS, boost::uint32_t(get_fpga_clock_rate())); + } + + double get_fpga_clock_rate(void){ + return this->_out_rate; + } + + /*********************************************************************** + * FPGA clock enable + **********************************************************************/ + void enable_fpga_clock(bool enb){ + _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS; + _ad9522_regs.out0_lvds_power_down = !enb; + this->send_reg(0x0F0); + this->latch_regs(); + } + + /*********************************************************************** + * Special test clock output + **********************************************************************/ + void enable_test_clock(bool enb){ + //setup test clock (same divider as codec clock) + _ad9522_regs.out4_format = ad9522_regs_t::OUT4_FORMAT_CMOS; + _ad9522_regs.out4_cmos_configuration = (enb)? + ad9522_regs_t::OUT4_CMOS_CONFIGURATION_A_ON : + ad9522_regs_t::OUT4_CMOS_CONFIGURATION_OFF; + this->send_reg(0x0F4); + this->latch_regs(); + } + + /*********************************************************************** + * RX Dboard Clock Control (output 9, divider 3) + **********************************************************************/ + void enable_rx_dboard_clock(bool enb){ + _ad9522_regs.out9_format = ad9522_regs_t::OUT9_FORMAT_CMOS; + _ad9522_regs.out9_cmos_configuration = (enb)? + ad9522_regs_t::OUT9_CMOS_CONFIGURATION_B_ON : + ad9522_regs_t::OUT9_CMOS_CONFIGURATION_OFF; + this->send_reg(0x0F9); + this->latch_regs(); + } + + std::vector get_rx_dboard_clock_rates(void){ + std::vector rates; + for(size_t div = 1; div <= 16+16; div++) + rates.push_back(this->_chan_rate/div); + return rates; + } + + void set_rx_dboard_clock_rate(double rate){ + assert_has(get_rx_dboard_clock_rates(), rate, "rx dboard clock rate"); + _rx_clock_rate = rate; + size_t divider = size_t(this->_chan_rate/rate); + //set the divider registers + set_clock_divider(divider, + _ad9522_regs.divider3_low_cycles, + _ad9522_regs.divider3_high_cycles, + _ad9522_regs.divider3_bypass + ); + this->send_reg(0x199); + this->send_reg(0x19a); + this->soft_sync(); + } + + double get_rx_clock_rate(void){ + return _rx_clock_rate; + } + + /*********************************************************************** + * TX Dboard Clock Control (output 6, divider 2) + **********************************************************************/ + void enable_tx_dboard_clock(bool enb){ + _ad9522_regs.out6_format = ad9522_regs_t::OUT6_FORMAT_CMOS; + _ad9522_regs.out6_cmos_configuration = (enb)? + ad9522_regs_t::OUT6_CMOS_CONFIGURATION_B_ON : + ad9522_regs_t::OUT6_CMOS_CONFIGURATION_OFF; + this->send_reg(0x0F6); + this->latch_regs(); + } + + std::vector get_tx_dboard_clock_rates(void){ + return get_rx_dboard_clock_rates(); //same master clock, same dividers... + } + + void set_tx_dboard_clock_rate(double rate){ + assert_has(get_tx_dboard_clock_rates(), rate, "tx dboard clock rate"); + _tx_clock_rate = rate; + size_t divider = size_t(this->_chan_rate/rate); + //set the divider registers + set_clock_divider(divider, + _ad9522_regs.divider2_low_cycles, + _ad9522_regs.divider2_high_cycles, + _ad9522_regs.divider2_bypass + ); + this->send_reg(0x196); + this->send_reg(0x197); + this->soft_sync(); + } + + double get_tx_clock_rate(void){ + return _tx_clock_rate; + } + + /*********************************************************************** + * Clock reference control + **********************************************************************/ + void use_internal_ref(void) { + _ad9522_regs.enable_ref2 = 1; + _ad9522_regs.enable_ref1 = 0; + _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF2; + _ad9522_regs.enb_auto_ref_switchover = ad9522_regs_t::ENB_AUTO_REF_SWITCHOVER_MANUAL; + this->send_reg(0x01C); + this->latch_regs(); + } + + void use_external_ref(void) { + _ad9522_regs.enable_ref2 = 0; + _ad9522_regs.enable_ref1 = 1; + _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF1; + _ad9522_regs.enb_auto_ref_switchover = ad9522_regs_t::ENB_AUTO_REF_SWITCHOVER_MANUAL; + this->send_reg(0x01C); + this->latch_regs(); + } + + void use_auto_ref(void) { + _ad9522_regs.enable_ref2 = 1; + _ad9522_regs.enable_ref1 = 1; + _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF1; + _ad9522_regs.enb_auto_ref_switchover = ad9522_regs_t::ENB_AUTO_REF_SWITCHOVER_AUTO; + this->send_reg(0x01C); + this->latch_regs(); + } + +private: + b100_iface::sptr _iface; + ad9522_regs_t _ad9522_regs; + double _out_rate; //rate at the fpga and codec + double _chan_rate; //rate before final dividers + double _rx_clock_rate, _tx_clock_rate; + + void latch_regs(void){ + _ad9522_regs.io_update = 1; + this->send_reg(0x232); + } + + void send_reg(boost::uint16_t addr){ + boost::uint32_t reg = _ad9522_regs.get_write_reg(addr); + UHD_LOGV(often) << "clock control write reg: " << std::hex << reg << std::endl; + byte_vector_t buf; + buf.push_back(boost::uint8_t(reg >> 16)); + buf.push_back(boost::uint8_t(reg >> 8)); + buf.push_back(boost::uint8_t(reg & 0xff)); + + _iface->get_fx2_i2c_iface().write_i2c(0x5C, buf); + } + + boost::uint8_t read_reg(boost::uint16_t addr){ + byte_vector_t buf; + buf.push_back(boost::uint8_t(addr >> 8)); + buf.push_back(boost::uint8_t(addr & 0xff)); + _iface->get_fx2_i2c_iface().write_i2c(0x5C, buf); + + buf = _iface->get_fx2_i2c_iface().read_i2c(0x5C, 1); + + return boost::uint32_t(buf[0] & 0xFF); + } + + void calibrate_now(void){ + //vco calibration routine: + _ad9522_regs.vco_calibration_now = 0; + this->send_reg(0x18); + this->latch_regs(); + _ad9522_regs.vco_calibration_now = 1; + this->send_reg(0x18); + this->latch_regs(); + //wait for calibration done: + static const boost::uint8_t addr = 0x01F; + for (size_t ms10 = 0; ms10 < 100; ms10++){ + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + boost::uint32_t reg = read_reg(addr); + _ad9522_regs.set_reg(addr, reg); + if (_ad9522_regs.vco_calibration_finished) goto wait_for_ld; + } + UHD_MSG(error) << "USRP-B100 clock control: VCO calibration timeout" << std::endl; + wait_for_ld: + //wait for digital lock detect: + for (size_t ms10 = 0; ms10 < 100; ms10++){ + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + boost::uint32_t reg = read_reg(addr); + _ad9522_regs.set_reg(addr, reg); + if (_ad9522_regs.digital_lock_detect) return; + } + UHD_MSG(error) << "USRP-B100 clock control: lock detection timeout" << std::endl; + } + + void soft_sync(void){ + _ad9522_regs.soft_sync = 1; + this->send_reg(0x230); + this->latch_regs(); + _ad9522_regs.soft_sync = 0; + this->send_reg(0x230); + this->latch_regs(); + } + + void send_all_regs(void){ + //setup a list of register ranges to write + typedef std::pair range_t; + static const std::vector ranges = boost::assign::list_of + (range_t(0x000, 0x000)) (range_t(0x010, 0x01F)) + (range_t(0x0F0, 0x0FD)) (range_t(0x190, 0x19B)) + (range_t(0x1E0, 0x1E1)) (range_t(0x230, 0x230)) + ; + + //write initial register values and latch/update + BOOST_FOREACH(const range_t &range, ranges){ + for(boost::uint16_t addr = range.first; addr <= range.second; addr++){ + this->send_reg(addr); + } + } + this->latch_regs(); + } +}; + +/*********************************************************************** + * Clock Control Make + **********************************************************************/ +b100_clock_ctrl::sptr b100_clock_ctrl::make(b100_iface::sptr iface, double master_clock_rate){ + return sptr(new b100_clock_ctrl_impl(iface, master_clock_rate)); +} diff --git a/host/lib/usrp/usrp_b100/clock_ctrl.hpp b/host/lib/usrp/usrp_b100/clock_ctrl.hpp new file mode 100644 index 000000000..2a2e74024 --- /dev/null +++ b/host/lib/usrp/usrp_b100/clock_ctrl.hpp @@ -0,0 +1,118 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INCLUDED_B100_CLOCK_CTRL_HPP +#define INCLUDED_B100_CLOCK_CTRL_HPP + +#include "b100_iface.hpp" +#include +#include +#include + +/*! + * The usrp-e clock control: + * - Setup system clocks. + * - Disable/enable clock lines. + */ +class b100_clock_ctrl : boost::noncopyable{ +public: + typedef boost::shared_ptr sptr; + + /*! + * Make a new clock control object. + * \param iface the b100 iface object + * \param master_clock_rate the master FPGA/sample clock rate + * \return the clock control object + */ + static sptr make(b100_iface::sptr iface, double master_clock_rate); + + /*! + * Set the rate of the fpga clock line. + * Throws if rate is not valid. + * \param rate the new rate in Hz + */ + virtual void set_fpga_clock_rate(double rate) = 0; + + /*! + * Get the rate of the fpga clock line. + * \return the fpga clock rate in Hz + */ + virtual double get_fpga_clock_rate(void) = 0; + + /*! + * Get the possible rates of the rx dboard clock. + * \return a vector of clock rates in Hz + */ + virtual std::vector get_rx_dboard_clock_rates(void) = 0; + + /*! + * Get the possible rates of the tx dboard clock. + * \return a vector of clock rates in Hz + */ + virtual std::vector get_tx_dboard_clock_rates(void) = 0; + + /*! + * Set the rx dboard clock rate to a possible rate. + * \param rate the new clock rate in Hz + * \throw exception when rate cannot be achieved + */ + virtual void set_rx_dboard_clock_rate(double rate) = 0; + + /*! + * Set the tx dboard clock rate to a possible rate. + * \param rate the new clock rate in Hz + * \throw exception when rate cannot be achieved + */ + virtual void set_tx_dboard_clock_rate(double rate) = 0; + + /*! + * Enable/disable the FPGA clock. + * \param enb true to enable + */ + + virtual void enable_fpga_clock(bool enb) = 0; + + /*! + * Enable/disable the rx dboard clock. + * \param enb true to enable + */ + virtual void enable_rx_dboard_clock(bool enb) = 0; + + /*! + * Enable/disable the tx dboard clock. + * \param enb true to enable + */ + virtual void enable_tx_dboard_clock(bool enb) = 0; + + /*! + * Use the internal TCXO reference + */ + virtual void use_internal_ref(void) = 0; + + /*! + * Use the external SMA reference + */ + virtual void use_external_ref(void) = 0; + + /*! + * Use external if available, internal otherwise + */ + virtual void use_auto_ref(void) = 0; + +}; + +#endif /* INCLUDED_B100_CLOCK_CTRL_HPP */ diff --git a/host/lib/usrp/usrp_b100/codec_ctrl.cpp b/host/lib/usrp/usrp_b100/codec_ctrl.cpp new file mode 100644 index 000000000..4d118b68b --- /dev/null +++ b/host/lib/usrp/usrp_b100/codec_ctrl.cpp @@ -0,0 +1,283 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "codec_ctrl.hpp" +#include "ad9862_regs.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include "b100_regs.hpp" //spi slave constants +#include + +using namespace uhd; + +const gain_range_t b100_codec_ctrl::tx_pga_gain_range(-20, 0, double(0.1)); +const gain_range_t b100_codec_ctrl::rx_pga_gain_range(0, 20, 1); + +/*********************************************************************** + * Codec Control Implementation + **********************************************************************/ +class b100_codec_ctrl_impl : public b100_codec_ctrl{ +public: + //structors + b100_codec_ctrl_impl(b100_iface::sptr iface); + ~b100_codec_ctrl_impl(void); + + //aux adc and dac control + double read_aux_adc(aux_adc_t which); + void write_aux_dac(aux_dac_t which, double volts); + + //pga gain control + void set_tx_pga_gain(double); + double get_tx_pga_gain(void); + void set_rx_pga_gain(double, char); + double get_rx_pga_gain(char); + +private: + b100_iface::sptr _iface; + ad9862_regs_t _ad9862_regs; + void send_reg(boost::uint8_t addr); + void recv_reg(boost::uint8_t addr); +}; + +/*********************************************************************** + * Codec Control Structors + **********************************************************************/ +b100_codec_ctrl_impl::b100_codec_ctrl_impl(b100_iface::sptr iface){ + _iface = iface; + + //soft reset + _ad9862_regs.soft_reset = 1; + this->send_reg(0); + + //initialize the codec register settings + _ad9862_regs.sdio_bidir = ad9862_regs_t::SDIO_BIDIR_SDIO_SDO; + _ad9862_regs.lsb_first = ad9862_regs_t::LSB_FIRST_MSB; + _ad9862_regs.soft_reset = 0; + + //setup rx side of codec + _ad9862_regs.byp_buffer_a = 1; + _ad9862_regs.byp_buffer_b = 1; + _ad9862_regs.buffer_a_pd = 1; + _ad9862_regs.buffer_b_pd = 1; + _ad9862_regs.mux_out = ad9862_regs_t::MUX_OUT_RX_MUX_MODE; //B100 uses interleaved RX->FPGA + _ad9862_regs.rx_pga_a = 0;//0x1f; //TODO bring under api control + _ad9862_regs.rx_pga_b = 0;//0x1f; //TODO bring under api control + _ad9862_regs.rx_twos_comp = 1; + _ad9862_regs.rx_hilbert = ad9862_regs_t::RX_HILBERT_DIS; + + //setup tx side of codec + _ad9862_regs.two_data_paths = ad9862_regs_t::TWO_DATA_PATHS_BOTH; + _ad9862_regs.interleaved = ad9862_regs_t::INTERLEAVED_INTERLEAVED; + _ad9862_regs.tx_retime = ad9862_regs_t::TX_RETIME_CLKOUT2; + _ad9862_regs.tx_pga_gain = 199; //TODO bring under api control + _ad9862_regs.tx_hilbert = ad9862_regs_t::TX_HILBERT_DIS; + _ad9862_regs.interp = ad9862_regs_t::INTERP_2; + _ad9862_regs.tx_twos_comp = 1; + _ad9862_regs.fine_mode = ad9862_regs_t::FINE_MODE_BYPASS; + _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_BYPASS; + _ad9862_regs.dac_a_coarse_gain = 0x3; + _ad9862_regs.dac_b_coarse_gain = 0x3; + _ad9862_regs.edges = ad9862_regs_t::EDGES_NORMAL; + + //setup the dll + _ad9862_regs.input_clk_ctrl = ad9862_regs_t::INPUT_CLK_CTRL_EXTERNAL; + _ad9862_regs.dll_mult = ad9862_regs_t::DLL_MULT_2; + _ad9862_regs.dll_mode = ad9862_regs_t::DLL_MODE_FAST; + + //write the register settings to the codec + for (uint8_t addr = 0; addr <= 25; addr++){ + this->send_reg(addr); + } + + //always start conversions for aux ADC + _ad9862_regs.start_a = 1; + _ad9862_regs.start_b = 1; + + //aux adc clock + _ad9862_regs.clk_4 = ad9862_regs_t::CLK_4_1_4; + this->send_reg(34); +} + +b100_codec_ctrl_impl::~b100_codec_ctrl_impl(void){ + UHD_SAFE_CALL( + //set aux dacs to zero + this->write_aux_dac(AUX_DAC_A, 0); + this->write_aux_dac(AUX_DAC_B, 0); + this->write_aux_dac(AUX_DAC_C, 0); + this->write_aux_dac(AUX_DAC_D, 0); + + //power down + _ad9862_regs.all_rx_pd = 1; + this->send_reg(1); + _ad9862_regs.tx_digital_pd = 1; + _ad9862_regs.tx_analog_pd = ad9862_regs_t::TX_ANALOG_PD_BOTH; + this->send_reg(8); + ) +} + +/*********************************************************************** + * Codec Control Gain Control Methods + **********************************************************************/ +static const int mtpgw = 255; //maximum tx pga gain word + +void b100_codec_ctrl_impl::set_tx_pga_gain(double gain){ + int gain_word = int(mtpgw*(gain - tx_pga_gain_range.start())/(tx_pga_gain_range.stop() - tx_pga_gain_range.start())); + _ad9862_regs.tx_pga_gain = uhd::clip(gain_word, 0, mtpgw); + this->send_reg(16); +} + +double b100_codec_ctrl_impl::get_tx_pga_gain(void){ + return (_ad9862_regs.tx_pga_gain*(tx_pga_gain_range.stop() - tx_pga_gain_range.start())/mtpgw) + tx_pga_gain_range.start(); +} + +static const int mrpgw = 0x14; //maximum rx pga gain word + +void b100_codec_ctrl_impl::set_rx_pga_gain(double gain, char which){ + int gain_word = int(mrpgw*(gain - rx_pga_gain_range.start())/(rx_pga_gain_range.stop() - rx_pga_gain_range.start())); + gain_word = uhd::clip(gain_word, 0, mrpgw); + switch(which){ + case 'A': + _ad9862_regs.rx_pga_a = gain_word; + this->send_reg(2); + return; + case 'B': + _ad9862_regs.rx_pga_b = gain_word; + this->send_reg(3); + return; + default: UHD_THROW_INVALID_CODE_PATH(); + } +} + +double b100_codec_ctrl_impl::get_rx_pga_gain(char which){ + int gain_word; + switch(which){ + case 'A': gain_word = _ad9862_regs.rx_pga_a; break; + case 'B': gain_word = _ad9862_regs.rx_pga_b; break; + default: UHD_THROW_INVALID_CODE_PATH(); + } + return (gain_word*(rx_pga_gain_range.stop() - rx_pga_gain_range.start())/mrpgw) + rx_pga_gain_range.start(); +} + +/*********************************************************************** + * Codec Control AUX ADC Methods + **********************************************************************/ +static double aux_adc_to_volts(boost::uint8_t high, boost::uint8_t low){ + return double((boost::uint16_t(high) << 2) | low)*3.3/0x3ff; +} + +double b100_codec_ctrl_impl::read_aux_adc(aux_adc_t which){ + switch(which){ + + case AUX_ADC_A1: + _ad9862_regs.select_a = ad9862_regs_t::SELECT_A_AUX_ADC1; + this->send_reg(34); //start conversion and select mux + this->recv_reg(28); //read the value (2 bytes, 2 reads) + this->recv_reg(29); + return aux_adc_to_volts(_ad9862_regs.aux_adc_a1_9_2, _ad9862_regs.aux_adc_a1_1_0); + case AUX_ADC_A2: + _ad9862_regs.select_a = ad9862_regs_t::SELECT_A_AUX_ADC2; + this->send_reg(34); //start conversion and select mux + this->recv_reg(26); //read the value (2 bytes, 2 reads) + this->recv_reg(27); + return aux_adc_to_volts(_ad9862_regs.aux_adc_a2_9_2, _ad9862_regs.aux_adc_a2_1_0); + + case AUX_ADC_B1: + _ad9862_regs.select_b = ad9862_regs_t::SELECT_B_AUX_ADC1; + this->send_reg(34); //start conversion and select mux + this->recv_reg(32); //read the value (2 bytes, 2 reads) + this->recv_reg(33); + return aux_adc_to_volts(_ad9862_regs.aux_adc_b1_9_2, _ad9862_regs.aux_adc_b1_1_0); + case AUX_ADC_B2: + _ad9862_regs.select_b = ad9862_regs_t::SELECT_B_AUX_ADC2; + this->send_reg(34); //start conversion and select mux + this->recv_reg(30); //read the value (2 bytes, 2 reads) + this->recv_reg(31); + return aux_adc_to_volts(_ad9862_regs.aux_adc_b2_9_2, _ad9862_regs.aux_adc_b2_1_0); + } + UHD_THROW_INVALID_CODE_PATH(); +} + +/*********************************************************************** + * Codec Control AUX DAC Methods + **********************************************************************/ +void b100_codec_ctrl_impl::write_aux_dac(aux_dac_t which, double volts){ + //special case for aux dac d (aka sigma delta word) + if (which == AUX_DAC_D){ + boost::uint16_t dac_word = uhd::clip(boost::math::iround(volts*0xfff/3.3), 0, 0xfff); + _ad9862_regs.sig_delt_11_4 = boost::uint8_t(dac_word >> 4); + _ad9862_regs.sig_delt_3_0 = boost::uint8_t(dac_word & 0xf); + this->send_reg(42); + this->send_reg(43); + return; + } + + //calculate the dac word for aux dac a, b, c + boost::uint8_t dac_word = uhd::clip(boost::math::iround(volts*0xff/3.3), 0, 0xff); + + //setup a lookup table for the aux dac params (reg ref, reg addr) + typedef boost::tuple dac_params_t; + uhd::dict aux_dac_to_params = boost::assign::map_list_of + (AUX_DAC_A, dac_params_t(&_ad9862_regs.aux_dac_a, 36)) + (AUX_DAC_B, dac_params_t(&_ad9862_regs.aux_dac_b, 37)) + (AUX_DAC_C, dac_params_t(&_ad9862_regs.aux_dac_c, 38)) + ; + + //set the aux dac register + UHD_ASSERT_THROW(aux_dac_to_params.has_key(which)); + boost::uint8_t *reg_ref, reg_addr; + boost::tie(reg_ref, reg_addr) = aux_dac_to_params[which]; + *reg_ref = dac_word; + this->send_reg(reg_addr); +} + +/*********************************************************************** + * Codec Control SPI Methods + **********************************************************************/ +void b100_codec_ctrl_impl::send_reg(boost::uint8_t addr){ + boost::uint32_t reg = _ad9862_regs.get_write_reg(addr); + UHD_LOGV(rarely) << "codec control write reg: " << std::hex << reg << std::endl; + _iface->transact_spi( + B100_SPI_SS_AD9862, + spi_config_t::EDGE_RISE, + reg, 16, false /*no rb*/ + ); +} + +void b100_codec_ctrl_impl::recv_reg(boost::uint8_t addr){ + boost::uint32_t reg = _ad9862_regs.get_read_reg(addr); + UHD_LOGV(rarely) << "codec control read reg: " << std::hex << reg << std::endl; + boost::uint32_t ret = _iface->transact_spi( + B100_SPI_SS_AD9862, + spi_config_t::EDGE_RISE, + reg, 16, true /*rb*/ + ); + UHD_LOGV(rarely) << "codec control read ret: " << std::hex << boost::uint16_t(ret & 0xFF) << std::endl; + _ad9862_regs.set_reg(addr, boost::uint8_t(ret&0xff)); +} + +/*********************************************************************** + * Codec Control Make + **********************************************************************/ +b100_codec_ctrl::sptr b100_codec_ctrl::make(b100_iface::sptr iface){ + return sptr(new b100_codec_ctrl_impl(iface)); +} diff --git a/host/lib/usrp/usrp_b100/codec_ctrl.hpp b/host/lib/usrp/usrp_b100/codec_ctrl.hpp new file mode 100644 index 000000000..1bd579190 --- /dev/null +++ b/host/lib/usrp/usrp_b100/codec_ctrl.hpp @@ -0,0 +1,90 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INCLUDED_B100_CODEC_CTRL_HPP +#define INCLUDED_B100_CODEC_CTRL_HPP + +#include "b100_iface.hpp" +#include +#include +#include + +/*! + * The usrp-e codec control: + * - Init/power down codec. + * - Read aux adc, write aux dac. + */ +class b100_codec_ctrl : boost::noncopyable{ +public: + typedef boost::shared_ptr sptr; + + static const uhd::gain_range_t tx_pga_gain_range; + static const uhd::gain_range_t rx_pga_gain_range; + + /*! + * Make a new codec control object. + * \param iface the usrp_e iface object + * \return the codec control object + */ + static sptr make(b100_iface::sptr iface); + + //! aux adc identifier constants + enum aux_adc_t{ + AUX_ADC_A2 = 0xA2, + AUX_ADC_A1 = 0xA1, + AUX_ADC_B2 = 0xB2, + AUX_ADC_B1 = 0xB1 + }; + + /*! + * Read an auxiliary adc: + * The internals remember which aux adc was read last. + * Therefore, the aux adc switch is only changed as needed. + * \param which which of the 4 adcs + * \return a value in volts + */ + virtual double read_aux_adc(aux_adc_t which) = 0; + + //! aux dac identifier constants + enum aux_dac_t{ + AUX_DAC_A = 0xA, + AUX_DAC_B = 0xB, + AUX_DAC_C = 0xC, + AUX_DAC_D = 0xD //really the sigma delta output + }; + + /*! + * Write an auxiliary dac. + * \param which which of the 4 dacs + * \param volts the level in in volts + */ + virtual void write_aux_dac(aux_dac_t which, double volts) = 0; + + //! Set the TX PGA gain + virtual void set_tx_pga_gain(double gain) = 0; + + //! Get the TX PGA gain + virtual double get_tx_pga_gain(void) = 0; + + //! Set the RX PGA gain ('A' or 'B') + virtual void set_rx_pga_gain(double gain, char which) = 0; + + //! Get the RX PGA gain ('A' or 'B') + virtual double get_rx_pga_gain(char which) = 0; +}; + +#endif /* INCLUDED_B100_CODEC_CTRL_HPP */ diff --git a/host/lib/usrp/usrp_b100/codec_impl.cpp b/host/lib/usrp/usrp_b100/codec_impl.cpp new file mode 100644 index 000000000..de3ca3a66 --- /dev/null +++ b/host/lib/usrp/usrp_b100/codec_impl.cpp @@ -0,0 +1,149 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "b100_impl.hpp" +#include +#include +#include + +using namespace uhd; +using namespace uhd::usrp; + +/*********************************************************************** + * Helper Methods + **********************************************************************/ +void b100_impl::codec_init(void){ + //make proxies + _rx_codec_proxy = wax_obj_proxy::make( + boost::bind(&b100_impl::rx_codec_get, this, _1, _2), + boost::bind(&b100_impl::rx_codec_set, this, _1, _2) + ); + _tx_codec_proxy = wax_obj_proxy::make( + boost::bind(&b100_impl::tx_codec_get, this, _1, _2), + boost::bind(&b100_impl::tx_codec_set, this, _1, _2) + ); +} + +/*********************************************************************** + * RX Codec Properties + **********************************************************************/ +static const std::string ad9862_pga_gain_name = "ad9862 pga"; + +void b100_impl::rx_codec_get(const wax::obj &key_, wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + //handle the get request conditioned on the key + switch(key.as()){ + case CODEC_PROP_NAME: + val = std::string("b100 adc - ad9522"); + return; + + case CODEC_PROP_OTHERS: + val = prop_names_t(); + return; + + case CODEC_PROP_GAIN_NAMES: + val = prop_names_t(1, ad9862_pga_gain_name); + return; + + case CODEC_PROP_GAIN_RANGE: + UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); + val = b100_codec_ctrl::rx_pga_gain_range; + return; + + case CODEC_PROP_GAIN_I: + UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); + val = _codec_ctrl->get_rx_pga_gain('A'); + return; + + case CODEC_PROP_GAIN_Q: + UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); + val = _codec_ctrl->get_rx_pga_gain('B'); + return; + + default: UHD_THROW_PROP_GET_ERROR(); + } +} + +void b100_impl::rx_codec_set(const wax::obj &key_, const wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + //handle the set request conditioned on the key + switch(key.as()){ + case CODEC_PROP_GAIN_I: + UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); + _codec_ctrl->set_rx_pga_gain(val.as(), 'A'); + return; + + case CODEC_PROP_GAIN_Q: + UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); + _codec_ctrl->set_rx_pga_gain(val.as(), 'B'); + return; + + default: UHD_THROW_PROP_SET_ERROR(); + } +} + +/*********************************************************************** + * TX Codec Properties + **********************************************************************/ +void b100_impl::tx_codec_get(const wax::obj &key_, wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + //handle the get request conditioned on the key + switch(key.as()){ + case CODEC_PROP_NAME: + val = std::string("b100 dac - ad9522"); + return; + + case CODEC_PROP_OTHERS: + val = prop_names_t(); + return; + + case CODEC_PROP_GAIN_NAMES: + val = prop_names_t(1, ad9862_pga_gain_name); + return; + + case CODEC_PROP_GAIN_RANGE: + UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); + val = b100_codec_ctrl::tx_pga_gain_range; + return; + + case CODEC_PROP_GAIN_I: //only one gain for I and Q + case CODEC_PROP_GAIN_Q: + UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); + val = _codec_ctrl->get_tx_pga_gain(); + return; + + default: UHD_THROW_PROP_GET_ERROR(); + } +} + +void b100_impl::tx_codec_set(const wax::obj &key_, const wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + //handle the set request conditioned on the key + switch(key.as()){ + case CODEC_PROP_GAIN_I: //only one gain for I and Q + case CODEC_PROP_GAIN_Q: + UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); + _codec_ctrl->set_tx_pga_gain(val.as()); + return; + + default: UHD_THROW_PROP_SET_ERROR(); + } +} diff --git a/host/lib/usrp/usrp_b100/ctrl_packet.hpp b/host/lib/usrp/usrp_b100/ctrl_packet.hpp new file mode 100644 index 000000000..f504fc5aa --- /dev/null +++ b/host/lib/usrp/usrp_b100/ctrl_packet.hpp @@ -0,0 +1,75 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INCLUDED_CTRL_PACKET_HPP +#define INCLUDED_CTRL_PACKET_HPP + +#include +#include +#include + +typedef std::vector ctrl_data_t; + +/*! + * Control packet operation type + */ +enum ctrl_pkt_op_t { + CTRL_PKT_OP_WRITE = 1, + CTRL_PKT_OP_READ = 2, + CTRL_PKT_OP_READBACK = 3 +}; + +/*! + * Control packet transaction length + */ +const size_t CTRL_PACKET_LENGTH = 32; +const size_t CTRL_PACKET_HEADER_LENGTH = 8; +const size_t CTRL_PACKET_DATA_LENGTH = 24; //=length-header + +/*! + * Control packet header magic value + */ +const boost::uint8_t CTRL_PACKET_HEADER_MAGIC = 0xAA; + +/*! + * Callback triggers for readback operation + */ +//FIXME: these are not real numbers, callbacks aren't implemented yet +const boost::uint16_t CTRL_PACKET_CALLBACK_SPI = 0x0001; +const boost::uint16_t CTRL_PACKET_CALLBACK_I2C = 0x0002; +//and so on + +/*! + * Metadata structure to describe a control packet + */ +struct UHD_API ctrl_pkt_meta_t { + ctrl_pkt_op_t op; + boost::uint8_t callbacks; + boost::uint8_t seq; + boost::uint16_t len; + boost::uint32_t addr; +}; + +/*! + * Full control packet structure + */ +struct UHD_API ctrl_pkt_t { + ctrl_pkt_meta_t pkt_meta; + ctrl_data_t data; +}; + +#endif /* INCLUDED_CTRL_PACKET_HPP */ diff --git a/host/lib/usrp/usrp_b100/dboard_iface.cpp b/host/lib/usrp/usrp_b100/dboard_iface.cpp new file mode 100644 index 000000000..ec3da6220 --- /dev/null +++ b/host/lib/usrp/usrp_b100/dboard_iface.cpp @@ -0,0 +1,298 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "b100_iface.hpp" +#include "b100_regs.hpp" +#include "clock_ctrl.hpp" +#include "codec_ctrl.hpp" +#include +#include +#include +#include + + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + +class b100_dboard_iface : public dboard_iface{ +public: + + b100_dboard_iface( + b100_iface::sptr iface, + b100_clock_ctrl::sptr clock, + b100_codec_ctrl::sptr codec + ){ + _iface = iface; + _clock = clock; + _codec = codec; + + //init the clock rate shadows + this->set_clock_rate(UNIT_RX, _clock->get_fpga_clock_rate()); + this->set_clock_rate(UNIT_TX, _clock->get_fpga_clock_rate()); + + _iface->poke16(B100_REG_GPIO_RX_DBG, 0); + _iface->poke16(B100_REG_GPIO_TX_DBG, 0); + } + + ~b100_dboard_iface(void){ + /* NOP */ + } + + special_props_t get_special_props(void){ + special_props_t props; + props.soft_clock_divider = false; + props.mangle_i2c_addrs = false; + return props; + } + + void write_aux_dac(unit_t, aux_dac_t, double); + double read_aux_adc(unit_t, aux_adc_t); + + void _set_pin_ctrl(unit_t, boost::uint16_t); + void _set_atr_reg(unit_t, atr_reg_t, boost::uint16_t); + void _set_gpio_ddr(unit_t, boost::uint16_t); + void _set_gpio_out(unit_t, boost::uint16_t); + void set_gpio_debug(unit_t, int); + boost::uint16_t read_gpio(unit_t); + + void write_i2c(boost::uint8_t, const byte_vector_t &); + byte_vector_t read_i2c(boost::uint8_t, size_t); + + void write_spi( + unit_t unit, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits + ); + + boost::uint32_t read_write_spi( + unit_t unit, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits + ); + + void set_clock_rate(unit_t, double); + std::vector get_clock_rates(unit_t); + double get_clock_rate(unit_t); + void set_clock_enabled(unit_t, bool); + double get_codec_rate(unit_t); + +private: + b100_iface::sptr _iface; + b100_clock_ctrl::sptr _clock; + b100_codec_ctrl::sptr _codec; + uhd::dict _clock_rates; +}; + +/*********************************************************************** + * Make Function + **********************************************************************/ +dboard_iface::sptr make_b100_dboard_iface( + b100_iface::sptr iface, + b100_clock_ctrl::sptr clock, + b100_codec_ctrl::sptr codec +){ + return dboard_iface::sptr(new b100_dboard_iface(iface, clock, codec)); +} + +/*********************************************************************** + * Clock Rates + **********************************************************************/ +void b100_dboard_iface::set_clock_rate(unit_t unit, double rate){ + _clock_rates[unit] = rate; + switch(unit){ + case UNIT_RX: return _clock->set_rx_dboard_clock_rate(rate); + case UNIT_TX: return _clock->set_tx_dboard_clock_rate(rate); + } +} + +std::vector b100_dboard_iface::get_clock_rates(unit_t unit){ + switch(unit){ + case UNIT_RX: return _clock->get_rx_dboard_clock_rates(); + case UNIT_TX: return _clock->get_tx_dboard_clock_rates(); + default: UHD_THROW_INVALID_CODE_PATH(); + } +} + +double b100_dboard_iface::get_clock_rate(unit_t unit){ + return _clock_rates[unit]; +} + +void b100_dboard_iface::set_clock_enabled(unit_t unit, bool enb){ + switch(unit){ + case UNIT_RX: return _clock->enable_rx_dboard_clock(enb); + case UNIT_TX: return _clock->enable_tx_dboard_clock(enb); + } +} + +double b100_dboard_iface::get_codec_rate(unit_t){ + return _clock->get_fpga_clock_rate(); +} + +/*********************************************************************** + * GPIO + **********************************************************************/ +void b100_dboard_iface::_set_pin_ctrl(unit_t unit, boost::uint16_t value){ + UHD_ASSERT_THROW(GPIO_SEL_ATR == 1); //make this assumption + switch(unit){ + case UNIT_RX: _iface->poke16(B100_REG_GPIO_RX_SEL, value); return; + case UNIT_TX: _iface->poke16(B100_REG_GPIO_TX_SEL, value); return; + } +} + +void b100_dboard_iface::_set_gpio_ddr(unit_t unit, boost::uint16_t value){ + switch(unit){ + case UNIT_RX: _iface->poke16(B100_REG_GPIO_RX_DDR, value); return; + case UNIT_TX: _iface->poke16(B100_REG_GPIO_TX_DDR, value); return; + } +} + +void b100_dboard_iface::_set_gpio_out(unit_t unit, boost::uint16_t value){ + switch(unit){ + case UNIT_RX: _iface->poke16(B100_REG_GPIO_RX_IO, value); return; + case UNIT_TX: _iface->poke16(B100_REG_GPIO_TX_IO, value); return; + } +} + +boost::uint16_t b100_dboard_iface::read_gpio(unit_t unit){ + switch(unit){ + case UNIT_RX: return _iface->peek16(B100_REG_GPIO_RX_IO); + case UNIT_TX: return _iface->peek16(B100_REG_GPIO_TX_IO); + default: UHD_THROW_INVALID_CODE_PATH(); + } +} + +void b100_dboard_iface::_set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value){ + //define mapping of unit to atr regs to register address + static const uhd::dict< + unit_t, uhd::dict + > unit_to_atr_to_addr = map_list_of + (UNIT_RX, map_list_of + (ATR_REG_IDLE, B100_REG_ATR_IDLE_RXSIDE) + (ATR_REG_TX_ONLY, B100_REG_ATR_INTX_RXSIDE) + (ATR_REG_RX_ONLY, B100_REG_ATR_INRX_RXSIDE) + (ATR_REG_FULL_DUPLEX, B100_REG_ATR_FULL_RXSIDE) + ) + (UNIT_TX, map_list_of + (ATR_REG_IDLE, B100_REG_ATR_IDLE_TXSIDE) + (ATR_REG_TX_ONLY, B100_REG_ATR_INTX_TXSIDE) + (ATR_REG_RX_ONLY, B100_REG_ATR_INRX_TXSIDE) + (ATR_REG_FULL_DUPLEX, B100_REG_ATR_FULL_TXSIDE) + ) + ; + _iface->poke16(unit_to_atr_to_addr[unit][atr], value); +} + +void b100_dboard_iface::set_gpio_debug(unit_t unit, int which){ + //set this unit to all outputs + this->set_gpio_ddr(unit, 0xffff); + + //calculate the debug selections + boost::uint32_t dbg_sels = 0x0; + int sel = (which == 0)? GPIO_SEL_DEBUG_0 : GPIO_SEL_DEBUG_1; + for(size_t i = 0; i < 16; i++) dbg_sels |= sel << i; + + //set the debug on and which debug selection + switch(unit){ + case UNIT_RX: + _iface->poke16(B100_REG_GPIO_RX_DBG, 0xffff); + _iface->poke16(B100_REG_GPIO_RX_SEL, dbg_sels); + return; + + case UNIT_TX: + _iface->poke16(B100_REG_GPIO_TX_DBG, 0xffff); + _iface->poke16(B100_REG_GPIO_TX_SEL, dbg_sels); + return; + } +} + +/*********************************************************************** + * SPI + **********************************************************************/ +/*! + * Static function to convert a unit type to a spi slave device number. + * \param unit the dboard interface unit type enum + * \return the slave device number + */ +static boost::uint32_t unit_to_otw_spi_dev(dboard_iface::unit_t unit){ + switch(unit){ + case dboard_iface::UNIT_TX: return B100_SPI_SS_TX_DB; + case dboard_iface::UNIT_RX: return B100_SPI_SS_RX_DB; + } + throw std::invalid_argument("unknown unit type"); +} + +void b100_dboard_iface::write_spi( + unit_t unit, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits +){ + _iface->transact_spi(unit_to_otw_spi_dev(unit), config, data, num_bits, false /*no rb*/); +} + +boost::uint32_t b100_dboard_iface::read_write_spi( + unit_t unit, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits +){ + return _iface->transact_spi(unit_to_otw_spi_dev(unit), config, data, num_bits, true /*rb*/); +} + +/*********************************************************************** + * I2C + **********************************************************************/ +void b100_dboard_iface::write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){ + return _iface->write_i2c(addr, bytes); +} + +byte_vector_t b100_dboard_iface::read_i2c(boost::uint8_t addr, size_t num_bytes){ + return _iface->read_i2c(addr, num_bytes); +} + +/*********************************************************************** + * Aux DAX/ADC + **********************************************************************/ +void b100_dboard_iface::write_aux_dac(dboard_iface::unit_t, aux_dac_t which, double value){ + //same aux dacs for each unit + static const uhd::dict which_to_aux_dac = map_list_of + (AUX_DAC_A, b100_codec_ctrl::AUX_DAC_A) + (AUX_DAC_B, b100_codec_ctrl::AUX_DAC_B) + (AUX_DAC_C, b100_codec_ctrl::AUX_DAC_C) + (AUX_DAC_D, b100_codec_ctrl::AUX_DAC_D) + ; + _codec->write_aux_dac(which_to_aux_dac[which], value); +} + +double b100_dboard_iface::read_aux_adc(dboard_iface::unit_t unit, aux_adc_t which){ + static const uhd::dict< + unit_t, uhd::dict + > unit_to_which_to_aux_adc = map_list_of + (UNIT_RX, map_list_of + (AUX_ADC_A, b100_codec_ctrl::AUX_ADC_A1) + (AUX_ADC_B, b100_codec_ctrl::AUX_ADC_B1) + ) + (UNIT_TX, map_list_of + (AUX_ADC_A, b100_codec_ctrl::AUX_ADC_A2) + (AUX_ADC_B, b100_codec_ctrl::AUX_ADC_B2) + ) + ; + return _codec->read_aux_adc(unit_to_which_to_aux_adc[unit][which]); +} diff --git a/host/lib/usrp/usrp_b100/dboard_impl.cpp b/host/lib/usrp/usrp_b100/dboard_impl.cpp new file mode 100644 index 000000000..ba3776728 --- /dev/null +++ b/host/lib/usrp/usrp_b100/dboard_impl.cpp @@ -0,0 +1,185 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "b100_impl.hpp" +#include "b100_regs.hpp" +#include +#include +#include +#include +#include +#include "usrp_i2c_addr.h" + +using namespace uhd; +using namespace uhd::usrp; + +/*********************************************************************** + * Dboard Initialization + **********************************************************************/ +void b100_impl::dboard_init(void){ + //read the tx and rx dboard eeproms + _rx_db_eeprom.load(*_iface, I2C_ADDR_RX_A); + _tx_db_eeprom.load(*_iface, I2C_ADDR_TX_A); + _gdb_eeprom.load(*_iface, I2C_ADDR_TX_A ^ 5); + + //create a new dboard interface and manager + _dboard_iface = make_b100_dboard_iface( + _iface, _clock_ctrl, _codec_ctrl + ); + _dboard_manager = dboard_manager::make( + _rx_db_eeprom.id, + ((_gdb_eeprom.id == dboard_id_t::none())? _tx_db_eeprom : _gdb_eeprom).id, + _dboard_iface + ); + + //setup the dboard proxies + _rx_dboard_proxy = wax_obj_proxy::make( + boost::bind(&b100_impl::rx_dboard_get, this, _1, _2), + boost::bind(&b100_impl::rx_dboard_set, this, _1, _2) + ); + _tx_dboard_proxy = wax_obj_proxy::make( + boost::bind(&b100_impl::tx_dboard_get, this, _1, _2), + boost::bind(&b100_impl::tx_dboard_set, this, _1, _2) + ); +} + +/*********************************************************************** + * RX Dboard Get + **********************************************************************/ +void b100_impl::rx_dboard_get(const wax::obj &key_, wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + //handle the get request conditioned on the key + switch(key.as()){ + case DBOARD_PROP_NAME: + val = std::string("b100 dboard (rx unit)"); + return; + + case DBOARD_PROP_SUBDEV: + val = _dboard_manager->get_rx_subdev(key.name); + return; + + case DBOARD_PROP_SUBDEV_NAMES: + val = _dboard_manager->get_rx_subdev_names(); + return; + + case DBOARD_PROP_DBOARD_EEPROM: + val = _rx_db_eeprom; + return; + + case DBOARD_PROP_DBOARD_IFACE: + val = _dboard_iface; + return; + + case DBOARD_PROP_CODEC: + val = _rx_codec_proxy->get_link(); + return; + + case DBOARD_PROP_GAIN_GROUP: + val = make_gain_group( + _rx_db_eeprom.id, + _dboard_manager->get_rx_subdev(key.name), + _rx_codec_proxy->get_link(), + GAIN_GROUP_POLICY_RX + ); + return; + + default: UHD_THROW_PROP_GET_ERROR(); + } +} + +/*********************************************************************** + * RX Dboard Set + **********************************************************************/ +void b100_impl::rx_dboard_set(const wax::obj &key, const wax::obj &val){ + switch(key.as()){ + case DBOARD_PROP_DBOARD_EEPROM: + _rx_db_eeprom = val.as(); + _rx_db_eeprom.store(*_iface, I2C_ADDR_RX_A); + return; + + default: UHD_THROW_PROP_SET_ERROR(); + } +} + +/*********************************************************************** + * TX Dboard Get + **********************************************************************/ +void b100_impl::tx_dboard_get(const wax::obj &key_, wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + //handle the get request conditioned on the key + switch(key.as()){ + case DBOARD_PROP_NAME: + val = std::string("b100 dboard (tx unit)"); + return; + + case DBOARD_PROP_SUBDEV: + val = _dboard_manager->get_tx_subdev(key.name); + return; + + case DBOARD_PROP_SUBDEV_NAMES: + val = _dboard_manager->get_tx_subdev_names(); + return; + + case DBOARD_PROP_DBOARD_EEPROM: + val = _tx_db_eeprom; + return; + + case DBOARD_PROP_GBOARD_EEPROM: + val = _gdb_eeprom; + return; + + case DBOARD_PROP_DBOARD_IFACE: + val = _dboard_iface; + return; + + case DBOARD_PROP_CODEC: + val = _tx_codec_proxy->get_link(); + return; + + case DBOARD_PROP_GAIN_GROUP: + val = make_gain_group( + _tx_db_eeprom.id, + _dboard_manager->get_tx_subdev(key.name), + _tx_codec_proxy->get_link(), + GAIN_GROUP_POLICY_TX + ); + return; + + default: UHD_THROW_PROP_GET_ERROR(); + } +} + +/*********************************************************************** + * TX Dboard Set + **********************************************************************/ +void b100_impl::tx_dboard_set(const wax::obj &key, const wax::obj &val){ + switch(key.as()){ + case DBOARD_PROP_DBOARD_EEPROM: + _tx_db_eeprom = val.as(); + _tx_db_eeprom.store(*_iface, I2C_ADDR_TX_A); + return; + + case DBOARD_PROP_GBOARD_EEPROM: + _gdb_eeprom = val.as(); + _gdb_eeprom.store(*_iface, I2C_ADDR_TX_A ^ 5); + return; + + default: UHD_THROW_PROP_SET_ERROR(); + } +} diff --git a/host/lib/usrp/usrp_b100/dsp_impl.cpp b/host/lib/usrp/usrp_b100/dsp_impl.cpp new file mode 100644 index 000000000..c1bf6bedd --- /dev/null +++ b/host/lib/usrp/usrp_b100/dsp_impl.cpp @@ -0,0 +1,189 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "b100_impl.hpp" +#include "b100_regs.hpp" +#include +#include +#include +#include + +#define rint boost::math::iround + +using namespace uhd; +using namespace uhd::usrp; + +static const double MASTER_CLOCK_RATE = 64e6; //TODO get from clock control + +/*********************************************************************** + * RX DDC Initialization + **********************************************************************/ +void b100_impl::rx_ddc_init(void){ + _rx_ddc_proxy = wax_obj_proxy::make( + boost::bind(&b100_impl::rx_ddc_get, this, _1, _2), + boost::bind(&b100_impl::rx_ddc_set, this, _1, _2) + ); + + //initial config and update + rx_ddc_set(DSP_PROP_FREQ_SHIFT, double(0)); + rx_ddc_set(DSP_PROP_HOST_RATE, double(16e6)); +} + +/*********************************************************************** + * RX DDC Get + **********************************************************************/ +void b100_impl::rx_ddc_get(const wax::obj &key_, wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + switch(key.as()){ + case DSP_PROP_NAME: + val = std::string("USRP-B100 RX DSP"); + return; + + case DSP_PROP_OTHERS: + val = prop_names_t(); //empty + return; + + case DSP_PROP_FREQ_SHIFT: + val = _ddc_freq; + return; + + case DSP_PROP_CODEC_RATE: + val = _clock_ctrl->get_fpga_clock_rate(); + return; + + case DSP_PROP_HOST_RATE: + val = _clock_ctrl->get_fpga_clock_rate()/_ddc_decim; + return; + + default: UHD_THROW_PROP_GET_ERROR(); + } +} + +/*********************************************************************** + * RX DDC Set + **********************************************************************/ +void b100_impl::rx_ddc_set(const wax::obj &key_, const wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + switch(key.as()){ + case DSP_PROP_STREAM_CMD: + issue_stream_cmd(val.as()); + return; + + case DSP_PROP_FREQ_SHIFT:{ + double new_freq = val.as(); + _iface->poke32(B100_REG_DSP_RX_FREQ, + dsp_type1::calc_cordic_word_and_update(new_freq, _clock_ctrl->get_fpga_clock_rate()) + ); + _ddc_freq = new_freq; //shadow + } + return; + + case DSP_PROP_HOST_RATE:{ + //set the decimation + _ddc_decim = rint(_clock_ctrl->get_fpga_clock_rate()/val.as()); + _iface->poke32(B100_REG_DSP_RX_DECIM_RATE, dsp_type1::calc_cic_filter_word(_ddc_decim)); + + //set the scaling + static const boost::int16_t default_rx_scale_iq = 1024; + _iface->poke32(B100_REG_DSP_RX_SCALE_IQ, + dsp_type1::calc_iq_scale_word(default_rx_scale_iq, default_rx_scale_iq) + ); + } + return; + + default: UHD_THROW_PROP_SET_ERROR(); + } +} + +/*********************************************************************** + * TX DUC Initialization + **********************************************************************/ +void b100_impl::tx_duc_init(void){ + _tx_duc_proxy = wax_obj_proxy::make( + boost::bind(&b100_impl::tx_duc_get, this, _1, _2), + boost::bind(&b100_impl::tx_duc_set, this, _1, _2) + ); + + //initial config and update + tx_duc_set(DSP_PROP_FREQ_SHIFT, double(0)); + tx_duc_set(DSP_PROP_HOST_RATE, double(16e6)); +} + +/*********************************************************************** + * TX DUC Get + **********************************************************************/ +void b100_impl::tx_duc_get(const wax::obj &key_, wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + switch(key.as()){ + case DSP_PROP_NAME: + val = std::string("USRP-B100 TX DSP"); + return; + + case DSP_PROP_OTHERS: + val = prop_names_t(); //empty + return; + + case DSP_PROP_FREQ_SHIFT: + val = _duc_freq; + return; + + case DSP_PROP_CODEC_RATE: + val = _clock_ctrl->get_fpga_clock_rate(); + return; + + case DSP_PROP_HOST_RATE: + val = _clock_ctrl->get_fpga_clock_rate()/_duc_interp; + return; + + default: UHD_THROW_PROP_GET_ERROR(); + } +} + +/*********************************************************************** + * TX DUC Set + **********************************************************************/ +void b100_impl::tx_duc_set(const wax::obj &key_, const wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + switch(key.as()){ + + case DSP_PROP_FREQ_SHIFT:{ + double new_freq = val.as(); + _iface->poke32(B100_REG_DSP_TX_FREQ, + dsp_type1::calc_cordic_word_and_update(new_freq, _clock_ctrl->get_fpga_clock_rate()) + ); + _duc_freq = new_freq; //shadow + } + return; + + case DSP_PROP_HOST_RATE:{ + _duc_interp = rint(_clock_ctrl->get_fpga_clock_rate()/val.as()); + + //set the interpolation + _iface->poke32(B100_REG_DSP_TX_INTERP_RATE, dsp_type1::calc_cic_filter_word(_duc_interp)); + + //set the scaling + _iface->poke32(B100_REG_DSP_TX_SCALE_IQ, dsp_type1::calc_iq_scale_word(_duc_interp)); + } + return; + + default: UHD_THROW_PROP_SET_ERROR(); + } +} diff --git a/host/lib/usrp/usrp_b100/io_impl.cpp b/host/lib/usrp/usrp_b100/io_impl.cpp new file mode 100644 index 000000000..3978bea75 --- /dev/null +++ b/host/lib/usrp/usrp_b100/io_impl.cpp @@ -0,0 +1,210 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "../../transport/super_recv_packet_handler.hpp" +#include "../../transport/super_send_packet_handler.hpp" +#include "usrp_commands.h" +#include "b100_impl.hpp" +#include "b100_regs.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; +namespace asio = boost::asio; + +/*********************************************************************** + * IO Implementation Details + **********************************************************************/ +struct b100_impl::io_impl{ + io_impl(zero_copy_if::sptr data_transport): + data_transport(data_transport) + { + /* NOP */ + } + + ~io_impl(void){ + //drain the rx buffs + //while(data_transport->get_recv_buff().get() != NULL){ + /* NOP */ + //} + } + + zero_copy_if::sptr &data_transport; + + sph::recv_packet_handler recv_handler; + sph::send_packet_handler send_handler; + bool continuous_streaming; +}; + +/*********************************************************************** + * Initialize internals within this file + **********************************************************************/ +void b100_impl::io_init(void){ + _recv_otw_type.width = 16; + _recv_otw_type.shift = 0; + _recv_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; + + _send_otw_type.width = 16; + _send_otw_type.shift = 0; + _send_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; + + _iface->reset_gpif(6); + + //reset state machines + _iface->poke32(B100_REG_CTRL_TX_CLEAR_UNDERRUN, 0); + _iface->poke32(B100_REG_CTRL_RX_CLEAR_OVERRUN, 0); + + _io_impl = UHD_PIMPL_MAKE(io_impl, (_data_transport)); + + //setup rx data path + _iface->poke32(B100_REG_CTRL_RX_NSAMPS_PER_PKT, get_max_recv_samps_per_packet()); + UHD_LOGV(always) << "IO: Using " << get_max_recv_samps_per_packet() << " samples per packet" << std::endl; + _iface->poke32(B100_REG_CTRL_RX_NCHANNELS, 1); + _iface->poke32(B100_REG_CTRL_RX_VRT_HEADER, 0 + | (0x1 << 28) //if data with stream id + | (0x1 << 26) //has trailer + | (0x3 << 22) //integer time other + | (0x1 << 20) //fractional time sample count + ); + _iface->poke32(B100_REG_CTRL_RX_VRT_TRAILER, 0); + + //set the streamid to reset the seq num + _iface->poke32(B100_REG_CTRL_TX_REPORT_SID, 0); + //setup the tx policy + _iface->poke32(B100_REG_CTRL_TX_POLICY, B100_FLAG_CTRL_TX_POLICY_NEXT_PACKET); + + //set the expected packet size in USB frames + _iface->poke32(B100_REG_MISC_RX_LEN, 4); + + update_transport_channel_mapping(); +} + +void b100_impl::update_transport_channel_mapping(void){ + if (_io_impl.get() == NULL) return; //not inited yet + + //set all of the relevant properties on the handler + boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); + _io_impl->recv_handler.resize(_rx_subdev_spec.size()); + _io_impl->recv_handler.set_vrt_unpacker(&vrt::if_hdr_unpack_le); + _io_impl->recv_handler.set_tick_rate(_clock_ctrl->get_fpga_clock_rate()); + _io_impl->recv_handler.set_samp_rate(_rx_ddc_proxy->get_link()[DSP_PROP_HOST_RATE].as()); + for (size_t chan = 0; chan < _io_impl->recv_handler.size(); chan++){ + _io_impl->recv_handler.set_xport_chan_get_buff(chan, boost::bind( + &uhd::transport::zero_copy_if::get_recv_buff, _io_impl->data_transport, _1 + )); + _io_impl->recv_handler.set_overflow_handler(chan, boost::bind( + &b100_impl::handle_overrun, this, chan + )); + } + _io_impl->recv_handler.set_converter(_recv_otw_type); + + //set all of the relevant properties on the handler + boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); + _io_impl->send_handler.resize(_tx_subdev_spec.size()); + _io_impl->send_handler.set_vrt_packer(&vrt::if_hdr_pack_le); + _io_impl->send_handler.set_tick_rate(_clock_ctrl->get_fpga_clock_rate()); + _io_impl->send_handler.set_samp_rate(_tx_duc_proxy->get_link()[DSP_PROP_HOST_RATE].as()); + for (size_t chan = 0; chan < _io_impl->send_handler.size(); chan++){ + _io_impl->send_handler.set_xport_chan_get_buff(chan, boost::bind( + &uhd::transport::zero_copy_if::get_send_buff, _io_impl->data_transport, _1 + )); + } + _io_impl->send_handler.set_converter(_send_otw_type); + _io_impl->send_handler.set_max_samples_per_packet(get_max_send_samps_per_packet()); +} + +/*********************************************************************** + * Data send + helper functions + **********************************************************************/ +size_t b100_impl::get_max_send_samps_per_packet(void) const { + static const size_t hdr_size = 0 + + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) + - sizeof(vrt::if_packet_info_t().cid) //no class id ever used + ; + static const size_t bpp = 2048 - hdr_size; + return bpp / _send_otw_type.get_sample_size(); +} + +size_t b100_impl::send( + const send_buffs_type &buffs, size_t nsamps_per_buff, + const tx_metadata_t &metadata, const io_type_t &io_type, + send_mode_t send_mode, double timeout +){ + return _io_impl->send_handler.send( + buffs, nsamps_per_buff, + metadata, io_type, + send_mode, timeout + ); +} + +/*********************************************************************** + * Data recv + helper functions + **********************************************************************/ + +size_t b100_impl::get_max_recv_samps_per_packet(void) const { + static const size_t hdr_size = 0 + + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) + + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer + - sizeof(vrt::if_packet_info_t().cid) //no class id ever used + ; + size_t bpp = 2048 - hdr_size; //limited by FPGA pkt buffer size + return bpp/_recv_otw_type.get_sample_size(); +} + +size_t b100_impl::recv( + const recv_buffs_type &buffs, size_t nsamps_per_buff, + rx_metadata_t &metadata, const io_type_t &io_type, + recv_mode_t recv_mode, double timeout +){ + return _io_impl->recv_handler.recv( + buffs, nsamps_per_buff, + metadata, io_type, + recv_mode, timeout + ); +} + +void b100_impl::issue_stream_cmd(const stream_cmd_t &stream_cmd) +{ + _io_impl->continuous_streaming = (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + _iface->poke32(B100_REG_CTRL_RX_STREAM_CMD, dsp_type1::calc_stream_cmd_word(stream_cmd)); + _iface->poke32(B100_REG_CTRL_RX_TIME_SECS, boost::uint32_t(stream_cmd.time_spec.get_full_secs())); + _iface->poke32(B100_REG_CTRL_RX_TIME_TICKS, stream_cmd.time_spec.get_tick_count(_clock_ctrl->get_fpga_clock_rate())); + + if (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS) { + while(_io_impl->data_transport->get_recv_buff().get() != NULL){ + /* NOP */ + } + } +} + +void b100_impl::handle_overrun(size_t){ + if (_io_impl->continuous_streaming){ + this->issue_stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + } +} diff --git a/host/lib/usrp/usrp_b100/mboard_impl.cpp b/host/lib/usrp/usrp_b100/mboard_impl.cpp new file mode 100644 index 000000000..c651ff2a2 --- /dev/null +++ b/host/lib/usrp/usrp_b100/mboard_impl.cpp @@ -0,0 +1,246 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "b100_impl.hpp" +#include "usrp_commands.h" +#include "fpga_regs_standard.h" +#include "fpga_regs_common.h" +#include "b100_regs.hpp" +#include "usrp_i2c_addr.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace uhd; +using namespace uhd::usrp; + +static const bool b100_mboard_verbose = true; + +/*********************************************************************** + * Mboard Initialization + **********************************************************************/ +void b100_impl::mboard_init(void) +{ + _mboard_proxy = wax_obj_proxy::make( + boost::bind(&b100_impl::mboard_get, this, _1, _2), + boost::bind(&b100_impl::mboard_set, this, _1, _2)); + + //set the ticks per seconds into the vita time control + _iface->poke32(B100_REG_TIME64_TPS, + boost::uint32_t(_clock_ctrl->get_fpga_clock_rate()) + ); + + //init the clock config + _clock_config = clock_config_t::internal(); + update_clock_config(); +} + +void b100_impl::update_clock_config(void){ + boost::uint32_t pps_flags = 0; + + //translate pps polarity enums + switch(_clock_config.pps_polarity){ + case clock_config_t::PPS_POS: pps_flags |= B100_FLAG_TIME64_PPS_POSEDGE; break; + case clock_config_t::PPS_NEG: pps_flags |= B100_FLAG_TIME64_PPS_NEGEDGE; break; + default: throw uhd::runtime_error("unhandled clock configuration pps polarity"); + } + + //set the pps flags + _iface->poke32(B100_REG_TIME64_FLAGS, pps_flags); + + //clock source ref 10mhz + switch(_clock_config.ref_source){ + case clock_config_t::REF_AUTO: _clock_ctrl->use_auto_ref(); break; + case clock_config_t::REF_INT: _clock_ctrl->use_internal_ref(); break; + case clock_config_t::REF_SMA: _clock_ctrl->use_auto_ref(); break; + default: throw uhd::runtime_error("unhandled clock configuration ref source"); + } +} + +/*********************************************************************** + * Mboard Get + **********************************************************************/ +void b100_impl::mboard_get(const wax::obj &key_, wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + //handle the get request conditioned on the key + switch(key.as()){ + case MBOARD_PROP_NAME: + val = std::string("USRP-B100 mboard"); + return; + + case MBOARD_PROP_OTHERS: + val = prop_names_t(); + return; + + case MBOARD_PROP_RX_DBOARD: + UHD_ASSERT_THROW(key.name == ""); + val = _rx_dboard_proxy->get_link(); + return; + + case MBOARD_PROP_RX_DBOARD_NAMES: + val = prop_names_t(1, ""); //vector of size 1 with empty string + return; + + case MBOARD_PROP_TX_DBOARD: + UHD_ASSERT_THROW(key.name == ""); + val = _tx_dboard_proxy->get_link(); + return; + + case MBOARD_PROP_TX_DBOARD_NAMES: + val = prop_names_t(1, ""); //vector of size 1 with empty string + return; + + case MBOARD_PROP_RX_DSP: + UHD_ASSERT_THROW(key.name == ""); + val = _rx_ddc_proxy->get_link(); + return; + + case MBOARD_PROP_RX_DSP_NAMES: + val = prop_names_t(1, ""); + return; + + case MBOARD_PROP_TX_DSP: + UHD_ASSERT_THROW(key.name == ""); + val = _tx_duc_proxy->get_link(); + return; + + case MBOARD_PROP_TX_DSP_NAMES: + val = prop_names_t(1, ""); + return; + + case MBOARD_PROP_CLOCK_CONFIG: + val = _clock_config; + return; + + case MBOARD_PROP_RX_SUBDEV_SPEC: + val = _rx_subdev_spec; + return; + + case MBOARD_PROP_TX_SUBDEV_SPEC: + val = _tx_subdev_spec; + return; + + case MBOARD_PROP_EEPROM_MAP: + val = _iface->mb_eeprom; + return; + + case MBOARD_PROP_TIME_NOW:while(true){ + uint32_t secs = _iface->peek32(B100_REG_RB_TIME_NOW_SECS); + uint32_t ticks = _iface->peek32(B100_REG_RB_TIME_NOW_TICKS); + if (secs != _iface->peek32(B100_REG_RB_TIME_NOW_SECS)) continue; + val = time_spec_t(secs, ticks, _clock_ctrl->get_fpga_clock_rate()); + return; + } + + case MBOARD_PROP_TIME_PPS: while(true){ + uint32_t secs = _iface->peek32(B100_REG_RB_TIME_PPS_SECS); + uint32_t ticks = _iface->peek32(B100_REG_RB_TIME_PPS_TICKS); + if (secs != _iface->peek32(B100_REG_RB_TIME_PPS_SECS)) continue; + val = time_spec_t(secs, ticks, _clock_ctrl->get_fpga_clock_rate()); + return; + } + + case MBOARD_PROP_CLOCK_RATE: + val = _clock_ctrl->get_fpga_clock_rate(); + return; + + default: UHD_THROW_PROP_GET_ERROR(); + } +} + +/*********************************************************************** + * Mboard Set + **********************************************************************/ +void b100_impl::mboard_set(const wax::obj &key, const wax::obj &val) +{ + if(key.type() == typeid(std::string)) { + if(key.as() == "load_eeprom") { + std::string b100_eeprom_image = val.as(); + UHD_MSG(status) << "B100 EEPROM image: " << b100_eeprom_image << std::endl; + _fx2_ctrl->usrp_load_eeprom(val.as()); + } + return; + } + + //handle the get request conditioned on the key + switch(key.as()){ + case MBOARD_PROP_TIME_NOW: + case MBOARD_PROP_TIME_PPS:{ + time_spec_t time_spec = val.as(); + _iface->poke32(B100_REG_TIME64_TICKS, time_spec.get_tick_count(_clock_ctrl->get_fpga_clock_rate())); + boost::uint32_t imm_flags = (key.as() == MBOARD_PROP_TIME_NOW)? 1 : 0; + _iface->poke32(B100_REG_TIME64_IMM, imm_flags); + _iface->poke32(B100_REG_TIME64_SECS, boost::uint32_t(time_spec.get_full_secs())); + } + return; + + case MBOARD_PROP_RX_SUBDEV_SPEC: + _rx_subdev_spec = val.as(); + verify_rx_subdev_spec(_rx_subdev_spec, _mboard_proxy->get_link()); + UHD_ASSERT_THROW(_rx_subdev_spec.size() == 1); + //set the mux + _iface->poke32(B100_REG_DSP_RX_MUX, dsp_type1::calc_rx_mux_word( + _dboard_manager->get_rx_subdev(_rx_subdev_spec.front().sd_name)[SUBDEV_PROP_CONNECTION].as() + )); + return; + + case MBOARD_PROP_TX_SUBDEV_SPEC: + _tx_subdev_spec = val.as(); + verify_tx_subdev_spec(_tx_subdev_spec, _mboard_proxy->get_link()); + UHD_ASSERT_THROW(_tx_subdev_spec.size() == 1); + //set the mux and set the number of tx channels + _iface->poke32(B100_REG_DSP_TX_MUX, dsp_type1::calc_tx_mux_word( + _dboard_manager->get_tx_subdev(_tx_subdev_spec.front().sd_name)[SUBDEV_PROP_CONNECTION].as() + )); + return; + + case MBOARD_PROP_EEPROM_MAP: + // Step1: commit the map, writing only those values set. + // Step2: readback the entire eeprom map into the iface. + val.as().commit(*_iface, mboard_eeprom_t::MAP_B000); + _iface->mb_eeprom = mboard_eeprom_t(*_iface, mboard_eeprom_t::MAP_B000); + return; + + case MBOARD_PROP_CLOCK_CONFIG: + _clock_config = val.as(); + update_clock_config(); + return; + + case MBOARD_PROP_CLOCK_RATE: + UHD_MSG(warning) + << "You are setting the master clock rate from the API.\n" + << "You may want to pass this into the device address as master_clock_rate=.\n" + << "This way, the clock rate is guaranteed to be initialized first.\n" + << "See the application notes for USRP-B100 for further instructions.\n" + ; + _clock_ctrl->set_fpga_clock_rate(val.as()); + update_transport_channel_mapping(); + return; + + default: UHD_THROW_PROP_SET_ERROR(); + } +} diff --git a/host/lib/usrp/usrp_e100/usrp_e100_impl.cpp b/host/lib/usrp/usrp_e100/usrp_e100_impl.cpp index 01a49c129..5b039aafc 100644 --- a/host/lib/usrp/usrp_e100/usrp_e100_impl.cpp +++ b/host/lib/usrp/usrp_e100/usrp_e100_impl.cpp @@ -39,7 +39,7 @@ static device_addrs_t usrp_e100_find(const device_addr_t &hint){ device_addrs_t usrp_e100_addrs; //return an empty list of addresses when type is set to non-usrp-e - if (hint.has_key("type") and hint["type"] != "usrp-e") return usrp_e100_addrs; + if (hint.has_key("type") and hint["type"] != "e100") return usrp_e100_addrs; //device node not provided, assume its 0 if (not hint.has_key("node")){ diff --git a/host/utils/usrp1p_gpif_loopback.cpp b/host/utils/usrp1p_gpif_loopback.cpp new file mode 100644 index 000000000..3b9da4304 --- /dev/null +++ b/host/utils/usrp1p_gpif_loopback.cpp @@ -0,0 +1,111 @@ +//USB->GPIF->FPGA loopback test for USRP1P +//uses UHD libusb transport + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//so the goal is to open a USB device to endpoints (2,6), submit a buffer, receive a reply, and compare them. +//use usb_zero_copy::make() to get a usb_zero_copy object and then start submitting. +//need to get a usb dev handle to pass to make +//use static std::vector get_device_list(boost::uint16_t vid, boost::uint16_t pid) to get a device handle +//then get_send_buffer, send, etc. +using namespace uhd; +using namespace uhd::transport; + +const boost::uint16_t data_xfer_size = 32; +const boost::uint16_t ctrl_xfer_size = 32; + +int main(int argc, char *argv[]) { + std::cout << "USRP1+ GPIF loopback test" << std::endl; + //step 1: get a handle on it + std::vector handles = usb_device_handle::get_device_list(0xfffe, 0x0003); + if(handles.size() == 0) { + std::cout << "No USRP1+ found." << std::endl; + return ~0; + } + + bool verbose = false; + if(argc > 1) if(std::string(argv[1]) == "-v") verbose = true; + + usb_device_handle::sptr handle = handles.front(); + + usb_zero_copy::sptr data_transport; + usb_control::sptr ctrl_transport = usb_control::make(handle); //just in case + + data_transport = usb_zero_copy::make( + handle, // identifier + 8, // IN endpoint + 4, // OUT endpoint + uhd::device_addr_t("recv_frame_size=32, num_recv_frames=1, send_frame_size=32, num_send_frames=1") //args + ); + + if(verbose) std::cout << "Made." << std::endl; + + //ok now we're made. time to get a buffer and start sending data. + + boost::uint8_t localbuf[data_xfer_size]; + + managed_send_buffer::sptr sbuf; + managed_recv_buffer::sptr rbuf; + size_t xfercount = 0; + + srand(time(0)); + while(1) { + + if(verbose) std::cout << "Getting send buffer." << std::endl; + sbuf = data_transport->get_send_buff(); + if(sbuf == 0) { + std::cout << "Failed to get a send buffer." << std::endl; + return ~0; + } + for(int i = 0; i < data_xfer_size; i++) { + boost::uint8_t x = rand(); + sbuf->cast()[i] = x; + localbuf[i] = x; + } + + if(verbose) std::cout << "Buffer loaded" << std::endl; + + sbuf->commit(data_xfer_size); + if(verbose) std::cout << "Committed." << std::endl; + + rbuf = data_transport->get_recv_buff(0.3); //timeout + + if(rbuf == 0) { + std::cout << "Failed to get receive buffer (timeout?)" << std::endl; + return ~0; + } + + if(verbose) std::cout << "# " << xfercount << std::endl; + + if(!memcmp(rbuf->cast(), localbuf, data_xfer_size)) { + std::cout << "."; + } else { + if(verbose) { + int i = 0; + for(int j = 0; j < 32; j++) { + std::cout << boost::format("%02X ") % int(rbuf->cast()[i*32+j]); + } + std::cout << std::endl; + } + else std::cout << "x"; + + } + sbuf.reset(); + rbuf.reset(); + xfercount++; + //if(verbose) std::cout << "sptrs reset" << std::endl; + } + + return 0; +} diff --git a/host/utils/usrp1p_poketest.cpp b/host/utils/usrp1p_poketest.cpp new file mode 100644 index 000000000..ca7628e01 --- /dev/null +++ b/host/utils/usrp1p_poketest.cpp @@ -0,0 +1,134 @@ +//FPGA register poke test for USRP1P +//uses UHD libusb transport + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../lib/usrp/usrp1p/ctrl_packet.hpp" + +//so the goal is to open a USB device to endpoints (2,6), submit a buffer, receive a reply, and compare them. +//use usb_zero_copy::make() to get a usb_zero_copy object and then start submitting. +//need to get a usb dev handle to pass to make +//use static std::vector get_device_list(boost::uint16_t vid, boost::uint16_t pid) to get a device handle +//then get_send_buffer, send, etc. +using namespace uhd; +using namespace uhd::transport; + +const boost::uint16_t ctrl_xfer_size = 32; + +int main(int argc, char *argv[]) { + std::cout << "USRP1+ GPIF poke test" << std::endl; + //step 1: get a handle on it + std::vector handles = usb_device_handle::get_device_list(0xfffe, 0x0003); + if(handles.size() == 0) { + std::cout << "No USRP1+ found." << std::endl; + return ~0; + } + + bool verbose = false; + if(argc > 1) if(std::string(argv[1]) == "-v") verbose = true; + + usb_device_handle::sptr handle = handles.front(); + + usb_zero_copy::sptr data_transport; + usb_control::sptr ctrl_transport = usb_control::make(handle); //just in case + + data_transport = usb_zero_copy::make( + handle, // identifier + 8, // IN endpoint + 4, // OUT endpoint + uhd::device_addr_t("recv_frame_size=32, num_recv_frames=1, send_frame_size=32, num_send_frames=1") //args + ); + + if(verbose) std::cout << "Made." << std::endl; + + //ok now we're made. time to get a buffer and start sending data. + + managed_send_buffer::sptr sbuf; + managed_recv_buffer::sptr rbuf; + size_t xfercount = 0; + + static uint8_t sequence = 0; + //uhd::usrp::ctrl_packet_out_t outpkt; + //memset(outpkt.data, 0x00, sizeof(outpkt.data)); +// outpkt.op = uhd::usrp::CTRL_PACKET_WRITE; +// outpkt.callbacks = 0; +// outpkt.seq = sequence++; +// outpkt.len = 4; +// outpkt.addr = 0x00000000; +// outpkt.data[0] = 0xff; +// outpkt.data[1] = 0xfe; +// outpkt.data[2] = 0xfd; +// outpkt.data[3] = 0xfc; + + boost::uint16_t outpkt[16]; + /* Packet format: + * Command: 2 bits + * Callbacks: 6 bits + * Seq num: 8 bits + * Length: 16 bits + * Addr LSW: 16 bits + * Addr MSW: 16 bits + * Data: 24 bytes/12 words + * Lengths are in lines + * + * readback: + * AA00 LEN(16) SEQ(16) ADDR(32) DATA(16bx12B) + */ + memset(outpkt, 0x00, sizeof(outpkt)); + outpkt[0] = 0x8000; //read cmd + callbacks (0) + seq + outpkt[1] = 0x0001; //len + outpkt[2] = 0x0000; //addr LSW + outpkt[3] = 0x0000; //addr MSW + outpkt[4] = 0x0A0A; //data + outpkt[5] = 0xFFFF; + + + srand(time(0)); +// while(1) { + + if(verbose) std::cout << "Getting send buffer." << std::endl; + sbuf = data_transport->get_send_buff(); + if(sbuf == 0) { + std::cout << "Failed to get a send buffer." << std::endl; + return ~0; + } + + for(int i = 0; i < ctrl_xfer_size; i++) { + sbuf->cast()[i] = ((boost::uint8_t *)&outpkt)[i]; + } + + if(verbose) std::cout << "Buffer loaded" << std::endl; + + sbuf->commit(ctrl_xfer_size); + if(verbose) std::cout << "Committed." << std::endl; + + rbuf = data_transport->get_recv_buff(0.3); //timeout + + if(rbuf == 0) { + std::cout << "Failed to get receive buffer (timeout?)" << std::endl; + return ~0; + } + + for(int j = 0; j < 32; j++) { + std::cout << boost::format("%02X ") % int(rbuf->cast()[j]); + } + std::cout << std::endl; + + sbuf.reset(); + rbuf.reset(); + xfercount++; + //if(verbose) std::cout << "sptrs reset" << std::endl; +// } + + return 0; +} -- cgit v1.2.3 From 3d771334a820cb54870cbca23eab49bb84571905 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Tue, 14 Jun 2011 19:08:26 -0700 Subject: b100: added images to Makefile --- host/lib/usrp/usrp_b100/b100_impl.cpp | 4 ++-- images/Makefile | 24 ++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) (limited to 'host/lib/usrp') diff --git a/host/lib/usrp/usrp_b100/b100_impl.cpp b/host/lib/usrp/usrp_b100/b100_impl.cpp index 066831a92..772da8f42 100644 --- a/host/lib/usrp/usrp_b100/b100_impl.cpp +++ b/host/lib/usrp/usrp_b100/b100_impl.cpp @@ -58,7 +58,7 @@ static device_addrs_t b100_find(const device_addr_t &hint) std::string b100_fw_image; try{ b100_fw_image = find_image_path( - hint.has_key("fw")? hint["fw"] : "usrp_b100_fw_c2.ihx" + hint.has_key("fw")? hint["fw"] : "usrp_b100_fw.ihx" ); } catch(...){ @@ -129,7 +129,7 @@ static device::sptr b100_make(const device_addr_t &device_addr){ //extract the FPGA path for the B100 std::string b100_fpga_image = find_image_path( - device_addr.has_key("fpga")? device_addr["fpga"] : "usrp_b100_fpga_c3.bin" + device_addr.has_key("fpga")? device_addr["fpga"] : "usrp_b100_fpga.bin" ); //try to match the given device address with something on the USB bus diff --git a/images/Makefile b/images/Makefile index f2ba21bdc..4b0034bf0 100644 --- a/images/Makefile +++ b/images/Makefile @@ -52,13 +52,14 @@ ifeq ($(shell xtclsh -h > /dev/null 2>&1 && echo $$?),0) endif ######################################################################## -# USRP1 firmware +# USRP1 and B100 firmware ######################################################################## ifdef HAS_SDCC _usrp1_fw_dir = $(TOP_FW_DIR)/fx2 _usrp1_fw_ihx = $(BUILT_IMAGES_DIR)/usrp1_fw.ihx -IMAGES_LIST += $(_usrp1_fw_ihx) +_usrp_b100_fw_ihx = $(BUILT_IMAGES_DIR)/usrp_b100_fw.ihx +IMAGES_LIST += $(_usrp1_fw_ihx) $(_usrp_b100_fw_ihx) $(_usrp1_fw_ihx): $(GLOBAL_DEPS) cd $(_usrp1_fw_dir) && rm -rf build @@ -67,6 +68,9 @@ $(_usrp1_fw_ihx): $(GLOBAL_DEPS) cd $(_usrp1_fw_dir)/build && make cp $(_usrp1_fw_dir)/build/usrp1/usrp1_fw.ihx $@ +$(_usrp_b100_fw_ihx): $(_usrp1_fw_ihx) + cp $(_usrp1_fw_dir)/build/b100/b100_fw.ihx $(_usrp_b100_fw_ihx) + endif ######################################################################## @@ -83,6 +87,22 @@ $(_usrp1_fpga_rbf): $(GLOBAL_DEPS) $(_usrp1_fpga_4rx_rbf): $(GLOBAL_DEPS) cp $(_usrp1_fpga_dir)/std_4rx_0tx.rbf $@ +######################################################################## +# USRP-B100 fpga +######################################################################## +ifdef HAS_XTCLSH + +_usrp_b100_fpga_dir = $(TOP_FPGA_DIR)/usrp2/top/B100 +_usrp_b100_fpga_bin = $(BUILT_IMAGES_DIR)/usrp_b100_fpga.bin +IMAGES_LIST += $(_usrp_b100_fpga_bin) + +$(_usrp_b100_fpga_bin): $(GLOBAL_DEPS) + cd $(_usrp_b100_fpga_dir) && make clean + cd $(_usrp_b100_fpga_dir) && make bin + cp $(_usrp_b100_fpga_dir)/build/u1plus.bin $@ + +endif + ######################################################################## # USRP2 and N Series firmware ######################################################################## -- cgit v1.2.3 From ed3439eebb51ab5ef8f184624212ded30a8544bb Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Tue, 14 Jun 2011 23:58:06 -0700 Subject: b100: fix fx2_ctrl include to use relative path --- host/lib/usrp/usrp_b100/b100_iface.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'host/lib/usrp') diff --git a/host/lib/usrp/usrp_b100/b100_iface.hpp b/host/lib/usrp/usrp_b100/b100_iface.hpp index 4da43681d..57ed6a45c 100644 --- a/host/lib/usrp/usrp_b100/b100_iface.hpp +++ b/host/lib/usrp/usrp_b100/b100_iface.hpp @@ -23,7 +23,7 @@ #include #include #include -#include "fx2_ctrl.hpp" +#include "../fx2/fx2_ctrl.hpp" #include "b100_ctrl.hpp" /*! -- cgit v1.2.3 From 1bfb999b0a322ca6dae302c8808749bfb5f6fcb6 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Wed, 15 Jun 2011 11:34:25 -0700 Subject: b100: removed usrp prefix from files --- host/lib/usrp/CMakeLists.txt | 2 +- host/lib/usrp/b100/CMakeLists.txt | 47 +++ host/lib/usrp/b100/b100_ctrl.cpp | 245 +++++++++++++++ host/lib/usrp/b100/b100_ctrl.hpp | 69 ++++ host/lib/usrp/b100/b100_iface.cpp | 336 ++++++++++++++++++++ host/lib/usrp/b100/b100_iface.hpp | 73 +++++ host/lib/usrp/b100/b100_impl.cpp | 277 ++++++++++++++++ host/lib/usrp/b100/b100_impl.hpp | 204 ++++++++++++ host/lib/usrp/b100/b100_regs.hpp | 264 ++++++++++++++++ host/lib/usrp/b100/clock_ctrl.cpp | 525 +++++++++++++++++++++++++++++++ host/lib/usrp/b100/clock_ctrl.hpp | 118 +++++++ host/lib/usrp/b100/codec_ctrl.cpp | 283 +++++++++++++++++ host/lib/usrp/b100/codec_ctrl.hpp | 90 ++++++ host/lib/usrp/b100/codec_impl.cpp | 149 +++++++++ host/lib/usrp/b100/ctrl_packet.hpp | 75 +++++ host/lib/usrp/b100/dboard_iface.cpp | 298 ++++++++++++++++++ host/lib/usrp/b100/dboard_impl.cpp | 185 +++++++++++ host/lib/usrp/b100/dsp_impl.cpp | 189 +++++++++++ host/lib/usrp/b100/io_impl.cpp | 210 +++++++++++++ host/lib/usrp/b100/mboard_impl.cpp | 246 +++++++++++++++ host/lib/usrp/usrp_b100/CMakeLists.txt | 47 --- host/lib/usrp/usrp_b100/b100_ctrl.cpp | 245 --------------- host/lib/usrp/usrp_b100/b100_ctrl.hpp | 69 ---- host/lib/usrp/usrp_b100/b100_iface.cpp | 336 -------------------- host/lib/usrp/usrp_b100/b100_iface.hpp | 73 ----- host/lib/usrp/usrp_b100/b100_impl.cpp | 277 ---------------- host/lib/usrp/usrp_b100/b100_impl.hpp | 204 ------------ host/lib/usrp/usrp_b100/b100_regs.hpp | 264 ---------------- host/lib/usrp/usrp_b100/clock_ctrl.cpp | 525 ------------------------------- host/lib/usrp/usrp_b100/clock_ctrl.hpp | 118 ------- host/lib/usrp/usrp_b100/codec_ctrl.cpp | 283 ----------------- host/lib/usrp/usrp_b100/codec_ctrl.hpp | 90 ------ host/lib/usrp/usrp_b100/codec_impl.cpp | 149 --------- host/lib/usrp/usrp_b100/ctrl_packet.hpp | 75 ----- host/lib/usrp/usrp_b100/dboard_iface.cpp | 298 ------------------ host/lib/usrp/usrp_b100/dboard_impl.cpp | 185 ----------- host/lib/usrp/usrp_b100/dsp_impl.cpp | 189 ----------- host/lib/usrp/usrp_b100/io_impl.cpp | 210 ------------- host/lib/usrp/usrp_b100/mboard_impl.cpp | 246 --------------- 39 files changed, 3884 insertions(+), 3884 deletions(-) create mode 100644 host/lib/usrp/b100/CMakeLists.txt create mode 100644 host/lib/usrp/b100/b100_ctrl.cpp create mode 100644 host/lib/usrp/b100/b100_ctrl.hpp create mode 100644 host/lib/usrp/b100/b100_iface.cpp create mode 100644 host/lib/usrp/b100/b100_iface.hpp create mode 100644 host/lib/usrp/b100/b100_impl.cpp create mode 100644 host/lib/usrp/b100/b100_impl.hpp create mode 100644 host/lib/usrp/b100/b100_regs.hpp create mode 100644 host/lib/usrp/b100/clock_ctrl.cpp create mode 100644 host/lib/usrp/b100/clock_ctrl.hpp create mode 100644 host/lib/usrp/b100/codec_ctrl.cpp create mode 100644 host/lib/usrp/b100/codec_ctrl.hpp create mode 100644 host/lib/usrp/b100/codec_impl.cpp create mode 100644 host/lib/usrp/b100/ctrl_packet.hpp create mode 100644 host/lib/usrp/b100/dboard_iface.cpp create mode 100644 host/lib/usrp/b100/dboard_impl.cpp create mode 100644 host/lib/usrp/b100/dsp_impl.cpp create mode 100644 host/lib/usrp/b100/io_impl.cpp create mode 100644 host/lib/usrp/b100/mboard_impl.cpp delete mode 100644 host/lib/usrp/usrp_b100/CMakeLists.txt delete mode 100644 host/lib/usrp/usrp_b100/b100_ctrl.cpp delete mode 100644 host/lib/usrp/usrp_b100/b100_ctrl.hpp delete mode 100644 host/lib/usrp/usrp_b100/b100_iface.cpp delete mode 100644 host/lib/usrp/usrp_b100/b100_iface.hpp delete mode 100644 host/lib/usrp/usrp_b100/b100_impl.cpp delete mode 100644 host/lib/usrp/usrp_b100/b100_impl.hpp delete mode 100644 host/lib/usrp/usrp_b100/b100_regs.hpp delete mode 100644 host/lib/usrp/usrp_b100/clock_ctrl.cpp delete mode 100644 host/lib/usrp/usrp_b100/clock_ctrl.hpp delete mode 100644 host/lib/usrp/usrp_b100/codec_ctrl.cpp delete mode 100644 host/lib/usrp/usrp_b100/codec_ctrl.hpp delete mode 100644 host/lib/usrp/usrp_b100/codec_impl.cpp delete mode 100644 host/lib/usrp/usrp_b100/ctrl_packet.hpp delete mode 100644 host/lib/usrp/usrp_b100/dboard_iface.cpp delete mode 100644 host/lib/usrp/usrp_b100/dboard_impl.cpp delete mode 100644 host/lib/usrp/usrp_b100/dsp_impl.cpp delete mode 100644 host/lib/usrp/usrp_b100/io_impl.cpp delete mode 100644 host/lib/usrp/usrp_b100/mboard_impl.cpp (limited to 'host/lib/usrp') diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt index a30a45977..80f4bf45e 100644 --- a/host/lib/usrp/CMakeLists.txt +++ b/host/lib/usrp/CMakeLists.txt @@ -37,5 +37,5 @@ INCLUDE_SUBDIRECTORY(dboard) INCLUDE_SUBDIRECTORY(fx2) INCLUDE_SUBDIRECTORY(usrp1) INCLUDE_SUBDIRECTORY(usrp2) -INCLUDE_SUBDIRECTORY(usrp_b100) +INCLUDE_SUBDIRECTORY(b100) INCLUDE_SUBDIRECTORY(usrp_e100) diff --git a/host/lib/usrp/b100/CMakeLists.txt b/host/lib/usrp/b100/CMakeLists.txt new file mode 100644 index 000000000..e1618a49c --- /dev/null +++ b/host/lib/usrp/b100/CMakeLists.txt @@ -0,0 +1,47 @@ +# +# Copyright 2010-2011 Ettus Research LLC +# +# 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 +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## + +######################################################################## +# Conditionally configure the B100 support +######################################################################## +LIBUHD_REGISTER_COMPONENT("B100" ENABLE_B100 ON "ENABLE_LIBUHD;ENABLE_USB" OFF) + +IF(ENABLE_B100) + LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/codec_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ctrl_packet.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/dboard_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dboard_iface.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dsp_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/io_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/mboard_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/b100_ctrl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/b100_ctrl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/b100_iface.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/b100_iface.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/b100_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/b100_impl.hpp + ) +ENDIF(ENABLE_B100) diff --git a/host/lib/usrp/b100/b100_ctrl.cpp b/host/lib/usrp/b100/b100_ctrl.cpp new file mode 100644 index 000000000..4d4520e1e --- /dev/null +++ b/host/lib/usrp/b100/b100_ctrl.cpp @@ -0,0 +1,245 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "../../transport/super_recv_packet_handler.hpp" +#include "b100_ctrl.hpp" +#include "b100_impl.hpp" +#include +#include +#include +#include +#include +#include +#include +#include "ctrl_packet.hpp" +#include +#include + +using namespace uhd::transport; +using namespace uhd; + +bool b100_ctrl_debug = false; + +class b100_ctrl_impl : public b100_ctrl { +public: + b100_ctrl_impl(uhd::transport::usb_zero_copy::sptr ctrl_transport) : + sync_ctrl_fifo(2), + async_msg_fifo(100), + _ctrl_transport(ctrl_transport), + _seq(0) + { + boost::barrier spawn_barrier(2); + viking_marauders.create_thread(boost::bind(&b100_ctrl_impl::viking_marauder_loop, this, boost::ref(spawn_barrier))); + spawn_barrier.wait(); + } + + int write(boost::uint32_t addr, const ctrl_data_t &data); + ctrl_data_t read(boost::uint32_t addr, size_t len); + + ~b100_ctrl_impl(void) { + bbl_out_marauding = false; + viking_marauders.interrupt_all(); + viking_marauders.join_all(); + } + + bool get_ctrl_data(ctrl_data_t &pkt_data, double timeout); + bool recv_async_msg(uhd::async_metadata_t &async_metadata, double timeout); + +private: + int send_pkt(boost::uint16_t *cmd); + + //änd hërë wë gö ä-Vïkïng för äsynchronous control packets + void viking_marauder_loop(boost::barrier &); + bounded_buffer sync_ctrl_fifo; + bounded_buffer async_msg_fifo; + boost::thread_group viking_marauders; + bool bbl_out_marauding; + + uhd::transport::usb_zero_copy::sptr _ctrl_transport; + boost::uint8_t _seq; +}; + +/*********************************************************************** + * helper functions for packing/unpacking control packets + **********************************************************************/ +void pack_ctrl_pkt(boost::uint16_t *pkt_buff, + const ctrl_pkt_t &pkt){ + //first two bits are OP + //next six bits are CALLBACKS + //next 8 bits are SEQUENCE + //next 16 bits are LENGTH (16-bit word) + //next 32 bits are ADDRESS (16-bit word LSW) + //then DATA (28 16-bit words) + pkt_buff[0] = (boost::uint16_t(pkt.pkt_meta.op) << 14) | (boost::uint16_t(pkt.pkt_meta.callbacks) << 8) | pkt.pkt_meta.seq; + pkt_buff[1] = pkt.pkt_meta.len; + pkt_buff[2] = (pkt.pkt_meta.addr & 0x00000FFF); + pkt_buff[3] = 0x0000; //address high bits always 0 on this device + + for(size_t i = 0; i < pkt.data.size(); i++) { + pkt_buff[4+i] = pkt.data[i]; + } +} + +void unpack_ctrl_pkt(const boost::uint16_t *pkt_buff, + ctrl_pkt_t &pkt){ + pkt.pkt_meta.seq = pkt_buff[0] & 0xFF; + pkt.pkt_meta.op = CTRL_PKT_OP_READ; //really this is useless + pkt.pkt_meta.len = pkt_buff[1]; + pkt.pkt_meta.callbacks = 0; //callbacks aren't implemented yet + pkt.pkt_meta.addr = pkt_buff[2] | boost::uint32_t(pkt_buff[3] << 16); + + //let's check this so we don't go pushing 64K of crap onto the pkt + if(pkt.pkt_meta.len > CTRL_PACKET_DATA_LENGTH) { + throw uhd::runtime_error("Received control packet too long"); + } + + for(int i = 4; i < 4+pkt.pkt_meta.len; i++) pkt.data.push_back(pkt_buff[i]); +} + +int b100_ctrl_impl::send_pkt(boost::uint16_t *cmd) { + managed_send_buffer::sptr sbuf = _ctrl_transport->get_send_buff(); + if(!sbuf.get()) { + throw uhd::runtime_error("Control channel send error"); + } + + //FIXME there's a better way to do this + for(size_t i = 0; i < (CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)); i++) { + sbuf->cast()[i] = cmd[i]; + } + sbuf->commit(CTRL_PACKET_LENGTH); //fixed size transaction + return 0; +} + +int b100_ctrl_impl::write(boost::uint32_t addr, const ctrl_data_t &data) { + UHD_ASSERT_THROW(data.size() <= (CTRL_PACKET_DATA_LENGTH / sizeof(boost::uint16_t))); + ctrl_pkt_t pkt; + pkt.data = data; + pkt.pkt_meta.op = CTRL_PKT_OP_WRITE; + pkt.pkt_meta.callbacks = 0; + pkt.pkt_meta.seq = _seq++; + pkt.pkt_meta.len = pkt.data.size(); + pkt.pkt_meta.addr = addr; + boost::uint16_t pkt_buff[CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)]; + + pack_ctrl_pkt(pkt_buff, pkt); + size_t result = send_pkt(pkt_buff); + return result; +} + +ctrl_data_t b100_ctrl_impl::read(boost::uint32_t addr, size_t len) { + UHD_ASSERT_THROW(len <= (CTRL_PACKET_DATA_LENGTH / sizeof(boost::uint16_t))); + + ctrl_pkt_t pkt; + pkt.pkt_meta.op = CTRL_PKT_OP_READ; + pkt.pkt_meta.callbacks = 0; + pkt.pkt_meta.seq = _seq++; + pkt.pkt_meta.len = len; + pkt.pkt_meta.addr = addr; + boost::uint16_t pkt_buff[CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)]; + + pack_ctrl_pkt(pkt_buff, pkt); + send_pkt(pkt_buff); + + //loop around waiting for the response to appear + while(!get_ctrl_data(pkt.data, 0.05)); + + return pkt.data; +} + +/*********************************************************************** + * Viking marauders go pillaging for asynchronous control packets in the + * control response endpoint. Sync packets go in sync_ctrl_fifo, + * async TX error messages go in async_msg_fifo. sync_ctrl_fifo should + * never have more than 1 message in it, since it's expected that we'll + * wait for a control operation to finish before starting another one. + **********************************************************************/ +void b100_ctrl_impl::viking_marauder_loop(boost::barrier &spawn_barrier) { + bbl_out_marauding = true; + spawn_barrier.wait(); + set_thread_priority_safe(); + + while(bbl_out_marauding){ + managed_recv_buffer::sptr rbuf = _ctrl_transport->get_recv_buff(); + if(!rbuf.get()) continue; //that's ok, there are plenty of villages to pillage! + const boost::uint16_t *pkt_buf = rbuf->cast(); + + if(pkt_buf[0] >> 8 == CTRL_PACKET_HEADER_MAGIC) { + //so it's got a control packet header, let's parse it. + ctrl_pkt_t pkt; + unpack_ctrl_pkt(pkt_buf, pkt); + + if(pkt.pkt_meta.seq != boost::uint8_t(_seq - 1)) { + throw uhd::runtime_error("Sequence error on control channel"); + } + if(pkt.pkt_meta.len > (CTRL_PACKET_LENGTH - CTRL_PACKET_HEADER_LENGTH)) { + throw uhd::runtime_error("Control channel packet length too long"); + } + + //push it onto the queue + sync_ctrl_fifo.push_with_wait(pkt.data); + } else { //it's an async status pkt + //extract the vrt header packet info + vrt::if_packet_info_t if_packet_info; + if_packet_info.num_packet_words32 = rbuf->size()/sizeof(boost::uint32_t); + const boost::uint32_t *vrt_hdr = rbuf->cast(); + vrt::if_hdr_unpack_le(vrt_hdr, if_packet_info); + + if( if_packet_info.sid == 0 + and if_packet_info.packet_type != vrt::if_packet_info_t::PACKET_TYPE_DATA){ + //fill in the async metadata + async_metadata_t metadata; + metadata.channel = 0; + metadata.has_time_spec = if_packet_info.has_tsi and if_packet_info.has_tsf; + metadata.time_spec = time_spec_t( + time_t(if_packet_info.tsi), size_t(if_packet_info.tsf), 64e6 //FIXME get from clock_ctrl + ); + metadata.event_code = async_metadata_t::event_code_t(sph::get_context_code(vrt_hdr, if_packet_info)); + //print the famous U, and push the metadata into the message queue + if (metadata.event_code & + ( async_metadata_t::EVENT_CODE_UNDERFLOW + | async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET) ) + UHD_MSG(fastpath) << "U"; + + if (metadata.event_code & + ( async_metadata_t::EVENT_CODE_SEQ_ERROR + | async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST) ) + UHD_MSG(fastpath) << "S"; + + async_msg_fifo.push_with_pop_on_full(metadata); + continue; + } + throw uhd::runtime_error("Control: unknown async response"); + } + } +} + +bool b100_ctrl_impl::get_ctrl_data(ctrl_data_t &pkt_data, double timeout){ + boost::this_thread::disable_interruption di; //disable because the wait can throw + return sync_ctrl_fifo.pop_with_timed_wait(pkt_data, timeout); +} + +bool b100_ctrl_impl::recv_async_msg(uhd::async_metadata_t &async_metadata, double timeout) { + boost::this_thread::disable_interruption di; //disable because the wait can throw + return async_msg_fifo.pop_with_timed_wait(async_metadata, timeout); +} + +/*********************************************************************** + * Public make function for b100_ctrl interface + **********************************************************************/ +b100_ctrl::sptr b100_ctrl::make(uhd::transport::usb_zero_copy::sptr ctrl_transport){ + return sptr(new b100_ctrl_impl(ctrl_transport)); +} diff --git a/host/lib/usrp/b100/b100_ctrl.hpp b/host/lib/usrp/b100/b100_ctrl.hpp new file mode 100644 index 000000000..ae706dbb4 --- /dev/null +++ b/host/lib/usrp/b100/b100_ctrl.hpp @@ -0,0 +1,69 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INCLUDED_B100_CTRL_HPP +#define INCLUDED_B100_CTRL_HPP + +#include +#include +#include +#include +#include +#include +#include "ctrl_packet.hpp" +#include + +class b100_ctrl : boost::noncopyable{ +public: + typedef boost::shared_ptr sptr; + + /*! + * Make a USRP control object from a data transport + * \param ctrl_transport a USB data transport + * \return a new b100 control object + */ + static sptr make(uhd::transport::usb_zero_copy::sptr ctrl_transport); + + /*! + * Write a byte vector to an FPGA register + * \param addr the FPGA register address + * \param bytes the data to write + * \return 0 on success, error code on failure + */ + virtual int write(boost::uint32_t addr, const ctrl_data_t &data) = 0; + + /*! + * Read a byte vector from an FPGA register (blocking read) + * \param addr the FPGA register address + * \param len the length of the read + * \return a vector of bytes from the register(s) in question + */ + virtual ctrl_data_t read(boost::uint32_t addr, size_t len) = 0; + + /*! + * Get a sync ctrl packet (blocking) + * \param the packet data buffer + * \param the timeout value + * \return true if it got something + */ + virtual bool get_ctrl_data(ctrl_data_t &pkt_data, double timeout) = 0; + + virtual bool recv_async_msg(uhd::async_metadata_t &async_metadata, double timeout) = 0; + +}; + +#endif /* INCLUDED_B100_CTRL_HPP */ diff --git a/host/lib/usrp/b100/b100_iface.cpp b/host/lib/usrp/b100/b100_iface.cpp new file mode 100644 index 000000000..17ea2e6ad --- /dev/null +++ b/host/lib/usrp/b100/b100_iface.cpp @@ -0,0 +1,336 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "b100_iface.hpp" +#include "usrp_commands.h" +#include +#include +#include +#include +#include +#include +#include + +//FOR TESTING ONLY +#include "b100_regs.hpp" +#include +#include "usrp_i2c_addr.h" + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +/*********************************************************************** + * Constants + **********************************************************************/ +static const bool iface_debug = true; +static const boost::uint16_t USRP_B_FW_COMPAT_NUM = 0x02; +static const boost::uint16_t USRP_B_FPGA_COMPAT_NUM = 0x03; + +/*********************************************************************** + * I2C + FX2 implementation wrapper + **********************************************************************/ +class b100_i2c_fx2_iface : public i2c_iface{ +public: + b100_i2c_fx2_iface(uhd::usrp::fx2_ctrl::sptr fx2_ctrl){ + _fx2_ctrl = fx2_ctrl; + } + + void write_i2c(boost::uint8_t addr, const byte_vector_t &bytes) + { + UHD_ASSERT_THROW(bytes.size() < max_i2c_data_bytes); + + unsigned char buff[max_i2c_data_bytes]; + std::copy(bytes.begin(), bytes.end(), buff); + + int ret = _fx2_ctrl->usrp_i2c_write(addr & 0xff, + buff, + bytes.size()); + + if (iface_debug && (ret < 0)) + uhd::runtime_error("USRP: failed i2c write"); + } + + byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes) + { + UHD_ASSERT_THROW(num_bytes < max_i2c_data_bytes); + + unsigned char buff[max_i2c_data_bytes]; + int ret = _fx2_ctrl->usrp_i2c_read(addr & 0xff, + buff, + num_bytes); + + if (iface_debug && ((ret < 0) || (unsigned)ret < (num_bytes))) + uhd::runtime_error("USRP: failed i2c read"); + + byte_vector_t out_bytes; + for (size_t i = 0; i < num_bytes; i++) + out_bytes.push_back(buff[i]); + + return out_bytes; + } + +private: + static const size_t max_i2c_data_bytes = 64; + uhd::usrp::fx2_ctrl::sptr _fx2_ctrl; +}; + +/*********************************************************************** + * USRP-E100 interface implementation + **********************************************************************/ +class b100_iface_impl : public b100_iface{ +public: + /******************************************************************* + * Structors + ******************************************************************/ + b100_iface_impl(uhd::usrp::fx2_ctrl::sptr fx2_ctrl, + b100_ctrl::sptr fpga_ctrl) : + _fx2_i2c_iface(fx2_ctrl), + _fx2_ctrl(fx2_ctrl), + _fpga_ctrl(fpga_ctrl) + { + this->check_fw_compat(); + if (fpga_ctrl.get() != NULL){ + enable_gpif(1); + i2c_init(); + this->check_fpga_compat(); + } + mb_eeprom = mboard_eeprom_t(get_fx2_i2c_iface(), mboard_eeprom_t::MAP_B000); + } + + void check_fw_compat(void){ + unsigned char data[4]; //useless data buffer + const boost::uint16_t fw_compat_num = _fx2_ctrl->usrp_control_read( + VRQ_FW_COMPAT, 0, 0, data, sizeof(data) + ); + if (fw_compat_num != USRP_B_FW_COMPAT_NUM){ + throw uhd::runtime_error(str(boost::format( + "Expected firmware compatibility number 0x%x, but got 0x%x:\n" + "The firmware build is not compatible with the host code build." + ) % USRP_B_FW_COMPAT_NUM % fw_compat_num)); + } + } + + void check_fpga_compat(void){ + const boost::uint16_t fpga_compat_num = this->peek16(B100_REG_MISC_COMPAT); + if (fpga_compat_num != USRP_B_FPGA_COMPAT_NUM){ + throw uhd::runtime_error(str(boost::format( + "Expected FPGA compatibility number 0x%x, but got 0x%x:\n" + "The FPGA build is not compatible with the host code build." + ) % USRP_B_FPGA_COMPAT_NUM % fpga_compat_num)); + } + } + + ~b100_iface_impl(void) + { + /* NOP */ + } + + /******************************************************************* + * Peek and Poke + ******************************************************************/ + + void poke(boost::uint32_t addr, const ctrl_data_t &data) { + boost::mutex::scoped_lock lock(_ctrl_mutex); + _fpga_ctrl->write(addr, data); + } + + ctrl_data_t peek(boost::uint32_t addr, size_t len) { + boost::mutex::scoped_lock lock(_ctrl_mutex); + return _fpga_ctrl->read(addr, len); + } + + void poke16(boost::uint32_t addr, boost::uint16_t value) + { + ctrl_data_t words(1); + words[0] = value; + poke(addr, words); + } + + void poke32(boost::uint32_t addr, boost::uint32_t value) + { + //just a subset of poke() to maintain compatibility + ctrl_data_t words(2); + words[0] = value & 0x0000FFFF; + words[1] = value >> 16; + poke(addr, words); + } + + boost::uint32_t peek32(boost::uint32_t addr) + { + ctrl_data_t words = peek(addr, 2); + return boost::uint32_t((boost::uint32_t(words[1]) << 16) | words[0]); + } + + boost::uint16_t peek16(boost::uint32_t addr) + { + ctrl_data_t words = peek(addr, 1); + return boost::uint16_t(words[0]); + } + + /******************************************************************* + * I2C + ******************************************************************/ + static const boost::uint32_t i2c_datarate = 400000; + static const boost::uint32_t wishbone_clk = 64000000; //FIXME should go somewhere else + + void i2c_init(void) { + //init I2C FPGA interface. + poke16(B100_REG_I2C_CTRL, 0x0000); + //set prescalers to operate at 400kHz: WB_CLK is 64MHz... + boost::uint16_t prescaler = wishbone_clk / (i2c_datarate*5) - 1; + poke16(B100_REG_I2C_PRESCALER_LO, prescaler & 0xFF); + poke16(B100_REG_I2C_PRESCALER_HI, (prescaler >> 8) & 0xFF); + poke16(B100_REG_I2C_CTRL, I2C_CTRL_EN); //enable I2C core + } + + static const size_t max_i2c_data_bytes = 64; + + void i2c_wait_for_xfer(void) + { + while(this->peek16(B100_REG_I2C_CMD_STATUS) & I2C_ST_TIP) + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + } + + bool wait_chk_ack(void) { + i2c_wait_for_xfer(); + return (this->peek16(B100_REG_I2C_CMD_STATUS) & I2C_ST_RXACK) == 0; + } + + void write_i2c(boost::uint8_t addr, const byte_vector_t &bytes) + { + poke16(B100_REG_I2C_DATA, (addr << 1) | 0); //addr and read bit (0) + poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_WR | I2C_CMD_START | (bytes.size() == 0 ? I2C_CMD_STOP : 0)); + + //wait for previous transfer to complete + if(!wait_chk_ack()) { + poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_STOP); + return; + } + + for(size_t i = 0; i < bytes.size(); i++) { + poke16(B100_REG_I2C_DATA, bytes[i]); + poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_WR | ((i == (bytes.size() - 1)) ? I2C_CMD_STOP : 0)); + if(!wait_chk_ack()) { + poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_STOP); + return; + } + } + } + + byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes) + { + byte_vector_t bytes; + if(num_bytes == 0) return bytes; + + while (peek16(B100_REG_I2C_CMD_STATUS) & I2C_ST_BUSY); + + poke16(B100_REG_I2C_DATA, (addr << 1) | 1); //addr and read bit (1) + poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_WR | I2C_CMD_START); + //wait for previous transfer to complete + if(!wait_chk_ack()) { + poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_STOP); + } + + for(; num_bytes > 0; num_bytes--) { + poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_RD | ((num_bytes == 1) ? (I2C_CMD_STOP | I2C_CMD_NACK) : 0)); + i2c_wait_for_xfer(); + boost::uint8_t readback = peek16(B100_REG_I2C_DATA) & 0xFF; + bytes.push_back(readback); + } + return bytes; + } + + i2c_iface &get_fx2_i2c_iface(void){ + return _fx2_i2c_iface; + } + + /******************************************************************* + * SPI interface + * Eventually this will be replaced with a control-channel system + * to let the firmware do the actual write/readback cycles. + * This keeps the bandwidth on the control channel down. + ******************************************************************/ + + void spi_wait(void) { + while(peek32(B100_REG_SPI_CTRL) & SPI_CTRL_GO_BSY); + } + + boost::uint32_t transact_spi(int which_slave, + const spi_config_t &config, + boost::uint32_t bits, + size_t num_bits, + bool readback) + { + UHD_ASSERT_THROW((num_bits <= 32) && !(num_bits % 8)); + + int edge_flags = ((config.miso_edge==spi_config_t::EDGE_FALL) ? SPI_CTRL_RXNEG : 0) | + ((config.mosi_edge==spi_config_t::EDGE_FALL) ? 0 : SPI_CTRL_TXNEG) + ; + + boost::uint16_t ctrl = SPI_CTRL_ASS | (SPI_CTRL_CHAR_LEN_MASK & num_bits) | edge_flags; + + poke16(B100_REG_SPI_DIV, 0x0001); // = fpga_clk / 4 + poke32(B100_REG_SPI_SS, which_slave & 0xFFFF); + poke32(B100_REG_SPI_TXRX0, bits); + poke16(B100_REG_SPI_CTRL, ctrl); + + poke16(B100_REG_SPI_CTRL, ctrl | SPI_CTRL_GO_BSY); + if(readback) { + spi_wait(); + return peek32(B100_REG_SPI_TXRX0); + } + else { + return 0; + } + } + + void reset_gpif(boost::uint16_t ep) { + _fx2_ctrl->usrp_control_write(VRQ_RESET_GPIF, ep, ep, 0, 0); + } + + void enable_gpif(bool en) { + _fx2_ctrl->usrp_control_write(VRQ_ENABLE_GPIF, en ? 1 : 0, 0, 0, 0); + } + + void clear_fpga_fifo(void) { + _fx2_ctrl->usrp_control_write(VRQ_CLEAR_FPGA_FIFO, 0, 0, 0, 0); + } + + void write_uart(boost::uint8_t, const std::string &) { + throw uhd::not_implemented_error("Unhandled command write_uart()"); + } + + std::string read_uart(boost::uint8_t) { + throw uhd::not_implemented_error("Unhandled command read_uart()"); + } + +private: + b100_i2c_fx2_iface _fx2_i2c_iface; + uhd::usrp::fx2_ctrl::sptr _fx2_ctrl; + b100_ctrl::sptr _fpga_ctrl; + boost::mutex _ctrl_mutex; +}; + +/*********************************************************************** + * Public Make Function + **********************************************************************/ +b100_iface::sptr b100_iface::make(uhd::usrp::fx2_ctrl::sptr fx2_ctrl, + b100_ctrl::sptr fpga_ctrl) +{ + return b100_iface::sptr(new b100_iface_impl(fx2_ctrl, fpga_ctrl)); +} diff --git a/host/lib/usrp/b100/b100_iface.hpp b/host/lib/usrp/b100/b100_iface.hpp new file mode 100644 index 000000000..57ed6a45c --- /dev/null +++ b/host/lib/usrp/b100/b100_iface.hpp @@ -0,0 +1,73 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INCLUDED_B100_IFACE_HPP +#define INCLUDED_B100_IFACE_HPP + +#include +#include +#include +#include +#include +#include "../fx2/fx2_ctrl.hpp" +#include "b100_ctrl.hpp" + +/*! + * The usrp1 interface class: + * Provides a set of functions to implementation layer. + * Including spi, peek, poke, control... + */ +class b100_iface : boost::noncopyable, public uhd::usrp::mboard_iface{ +public: + typedef boost::shared_ptr sptr; + + /*! + * Make a new b100 interface with the control transport. + * \param fx2_ctrl the usrp control object + * \param fpga_ctrl the FPGA interface control object + * \return a new usrp1 interface object + */ + static sptr make(uhd::usrp::fx2_ctrl::sptr fx2_ctrl, + b100_ctrl::sptr fpga_ctrl = b100_ctrl::sptr() + ); + + /*! + * Reset the GPIF interface on the FX2 + * \param which endpoint to reset + * \return + */ + virtual void reset_gpif(boost::uint16_t ep) = 0; + + /*! + * Clear the GPIF FIFOs on the FPGA + * \return + */ + virtual void clear_fpga_fifo(void) = 0; + + /*! + * Enable/disable the GPIF interfaces on the FX2 + * \return + */ + virtual void enable_gpif(bool en) = 0; + + //! Get access to the FX2 I2C interface + virtual uhd::i2c_iface &get_fx2_i2c_iface(void) = 0; + + uhd::usrp::mboard_eeprom_t mb_eeprom; +}; + +#endif /* INCLUDED_USRP1_IFACE_HPP */ diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp new file mode 100644 index 000000000..772da8f42 --- /dev/null +++ b/host/lib/usrp/b100/b100_impl.cpp @@ -0,0 +1,277 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "b100_impl.hpp" +#include "b100_ctrl.hpp" +#include "fpga_regs_standard.h" +#include "usrp_spi_defs.h" +#include +#include "ctrl_packet.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "b100_regs.hpp" + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +const boost::uint16_t B100_VENDOR_ID = 0x2500; +const boost::uint16_t B100_PRODUCT_ID = 0x0001; +const boost::uint16_t FX2_VENDOR_ID = 0x04b4; +const boost::uint16_t FX2_PRODUCT_ID = 0x8613; + +/*********************************************************************** + * Discovery + **********************************************************************/ +static device_addrs_t b100_find(const device_addr_t &hint) +{ + device_addrs_t b100_addrs; + + //return an empty list of addresses when type is set to non-b100 + if (hint.has_key("type") and hint["type"] != "b100") return b100_addrs; + + //extract the firmware path for the b100 + std::string b100_fw_image; + try{ + b100_fw_image = find_image_path( + hint.has_key("fw")? hint["fw"] : "usrp_b100_fw.ihx" + ); + } + catch(...){ + UHD_MSG(warning) << boost::format( + "Could not locate B100 firmware.\n" + "Please install the images package.\n" + ); + return b100_addrs; + } + + boost::uint16_t vid = hint.has_key("uninit") ? FX2_VENDOR_ID : B100_VENDOR_ID; + boost::uint16_t pid = hint.has_key("uninit") ? FX2_PRODUCT_ID : B100_PRODUCT_ID; + + // Important note: + // The get device list calls are nested inside the for loop. + // This allows the usb guts to decontruct when not in use, + // so that re-enumeration after fw load can occur successfully. + // This requirement is a courtesy of libusb1.0 on windows. + + //find the usrps and load firmware + BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { + try { + fx2_ctrl::make(usb_control::make(handle))->usrp_load_firmware(b100_fw_image); + } catch (...) { + UHD_MSG(status) << "Interface claimed, ignoring device" << std::endl; + } + } + + //get descriptors again with serial number, but using the initialized VID/PID now since we have firmware + vid = B100_VENDOR_ID; + pid = B100_PRODUCT_ID; + + BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { + device_addr_t new_addr; + new_addr["type"] = "b100"; + + //Attempt to read the name from the EEPROM and perform filtering. + //This operation can throw due to compatibility mismatch. + try{ + usb_control::sptr control = usb_control::make(handle); + b100_iface::sptr iface = b100_iface::make(fx2_ctrl::make(control)); + new_addr["name"] = iface->mb_eeprom["name"]; + new_addr["serial"] = handle->get_serial(); + } + catch(const uhd::exception &){ + //set these values as empty string so the device may still be found + //and the filter's below can still operate on the discovered device + new_addr["name"] = ""; + new_addr["serial"] = ""; + } + + //this is a found b100 when the hint serial and name match or blank + if ( + (not hint.has_key("name") or hint["name"] == new_addr["name"]) and + (not hint.has_key("serial") or hint["serial"] == new_addr["serial"]) + ){ + b100_addrs.push_back(new_addr); + } + } + + return b100_addrs; +} + +/*********************************************************************** + * Make + **********************************************************************/ +static device::sptr b100_make(const device_addr_t &device_addr){ + + //extract the FPGA path for the B100 + std::string b100_fpga_image = find_image_path( + device_addr.has_key("fpga")? device_addr["fpga"] : "usrp_b100_fpga.bin" + ); + + //try to match the given device address with something on the USB bus + std::vector device_list = + usb_device_handle::get_device_list(B100_VENDOR_ID, B100_PRODUCT_ID); + + //locate the matching handle in the device list + usb_device_handle::sptr handle; + BOOST_FOREACH(usb_device_handle::sptr dev_handle, device_list) { + if (dev_handle->get_serial() == device_addr["serial"]){ + handle = dev_handle; + break; + } + } + UHD_ASSERT_THROW(handle.get() != NULL); //better be found + + //create control objects and a data transport + usb_control::sptr fx2_transport = usb_control::make(handle); + fx2_ctrl::sptr fx2_ctrl = fx2_ctrl::make(fx2_transport); + fx2_ctrl->usrp_load_fpga(b100_fpga_image); + + device_addr_t data_xport_args; + data_xport_args["recv_frame_size"] = device_addr.get("recv_frame_size", "16384"); + data_xport_args["num_recv_frames"] = device_addr.get("num_recv_frames", "16"); + data_xport_args["send_frame_size"] = device_addr.get("send_frame_size", "16384"); + data_xport_args["num_send_frames"] = device_addr.get("num_send_frames", "16"); + + usb_zero_copy::sptr data_transport = usb_zero_copy::make_wrapper( + usb_zero_copy::make( + handle, // identifier + 6, // IN endpoint + 2, // OUT endpoint + data_xport_args // param hints + ) + ); + + //create the control transport + device_addr_t ctrl_xport_args; + ctrl_xport_args["recv_frame_size"] = boost::lexical_cast(CTRL_PACKET_LENGTH); + ctrl_xport_args["num_recv_frames"] = "16"; + ctrl_xport_args["send_frame_size"] = boost::lexical_cast(CTRL_PACKET_LENGTH); + ctrl_xport_args["num_send_frames"] = "4"; + + usb_zero_copy::sptr ctrl_transport = usb_zero_copy::make( + handle, + 8, + 4, + ctrl_xport_args + ); + + const double master_clock_rate = device_addr.cast("master_clock_rate", 64e6); + + + //create the b100 implementation guts + return device::sptr(new b100_impl(data_transport, ctrl_transport, fx2_ctrl, master_clock_rate)); +} + +UHD_STATIC_BLOCK(register_b100_device){ + device::register_device(&b100_find, &b100_make); +} + +/*********************************************************************** + * Structors + **********************************************************************/ +b100_impl::b100_impl(uhd::transport::usb_zero_copy::sptr data_transport, + uhd::transport::usb_zero_copy::sptr ctrl_transport, + uhd::usrp::fx2_ctrl::sptr fx2_ctrl, + const double master_clock_rate) + : _data_transport(data_transport), _fx2_ctrl(fx2_ctrl) +{ + //this is the handler object for FPGA control packets + _fpga_ctrl = b100_ctrl::make(ctrl_transport); + + _iface = b100_iface::make(_fx2_ctrl, _fpga_ctrl); + + //create clock interface + _clock_ctrl = b100_clock_ctrl::make(_iface, master_clock_rate); + + //create codec interface + _codec_ctrl = b100_codec_ctrl::make(_iface); + + //initialize the codecs + codec_init(); + + //initialize the mboard + mboard_init(); + + //initialize the dboards + dboard_init(); + + //initialize the dsps + rx_ddc_init(); + + //initialize the dsps + tx_duc_init(); + + //init the subdev specs + this->mboard_set(MBOARD_PROP_RX_SUBDEV_SPEC, subdev_spec_t()); + this->mboard_set(MBOARD_PROP_TX_SUBDEV_SPEC, subdev_spec_t()); + + //initialize the send/recv buffs + io_init(); +} + +b100_impl::~b100_impl(void){ + /* NOP */ +} + +bool b100_impl::recv_async_msg(uhd::async_metadata_t &md, double timeout){ + return _fpga_ctrl->recv_async_msg(md, timeout); +} + +/*********************************************************************** + * Device Get + **********************************************************************/ +void b100_impl::get(const wax::obj &key_, wax::obj &val) +{ + named_prop_t key = named_prop_t::extract(key_); + + //handle the get request conditioned on the key + switch(key.as()){ + case DEVICE_PROP_NAME: + val = std::string("USRP-B100 device"); + return; + + case DEVICE_PROP_MBOARD: + UHD_ASSERT_THROW(key.name == ""); + val = _mboard_proxy->get_link(); + return; + + case DEVICE_PROP_MBOARD_NAMES: + val = prop_names_t(1, ""); //vector of size 1 with empty string + return; + + default: UHD_THROW_PROP_GET_ERROR(); + } +} + +/*********************************************************************** + * Device Set + **********************************************************************/ +void b100_impl::set(const wax::obj &, const wax::obj &) +{ + UHD_THROW_PROP_SET_ERROR(); +} diff --git a/host/lib/usrp/b100/b100_impl.hpp b/host/lib/usrp/b100/b100_impl.hpp new file mode 100644 index 000000000..daec70bca --- /dev/null +++ b/host/lib/usrp/b100/b100_impl.hpp @@ -0,0 +1,204 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "b100_iface.hpp" +#include "b100_ctrl.hpp" +#include "clock_ctrl.hpp" +#include "codec_ctrl.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef INCLUDED_B100_IMPL_HPP +#define INCLUDED_B100_IMPL_HPP + +/*! + * Make a b100 dboard interface. + * \param iface the b100 interface object + * \param clock the clock control interface + * \param codec the codec control interface + * \return a sptr to a new dboard interface + */ +uhd::usrp::dboard_iface::sptr make_b100_dboard_iface( + b100_iface::sptr iface, + b100_clock_ctrl::sptr clock, + b100_codec_ctrl::sptr codec +); + +/*! + * Simple wax obj proxy class: + * Provides a wax obj interface for a set and a get function. + * This allows us to create nested properties structures + * while maintaining flattened code within the implementation. + */ +class wax_obj_proxy : public wax::obj { +public: + typedef boost::function get_t; + typedef boost::function set_t; + typedef boost::shared_ptr sptr; + + static sptr make(const get_t &get, const set_t &set){ + return sptr(new wax_obj_proxy(get, set)); + } + +private: + get_t _get; set_t _set; + wax_obj_proxy(const get_t &get, const set_t &set): _get(get), _set(set) {}; + void get(const wax::obj &key, wax::obj &val) {return _get(key, val);} + void set(const wax::obj &key, const wax::obj &val) {return _set(key, val);} +}; + +/*! + * USRP1 implementation guts: + * The implementation details are encapsulated here. + * Handles properties on the mboard, dboard, dsps... + */ +class b100_impl : public uhd::device { +public: + //structors + b100_impl(uhd::transport::usb_zero_copy::sptr data_transport, + uhd::transport::usb_zero_copy::sptr ctrl_transport, + uhd::usrp::fx2_ctrl::sptr fx2_ctrl, + double master_clock_rate); + + ~b100_impl(void); + + //the io interface + size_t send(const send_buffs_type &, + size_t, + const uhd::tx_metadata_t &, + const uhd::io_type_t &, + send_mode_t, double); + + size_t recv(const recv_buffs_type &, + size_t, uhd::rx_metadata_t &, + const uhd::io_type_t &, + recv_mode_t, double); + + size_t get_max_send_samps_per_packet(void) const; + + size_t get_max_recv_samps_per_packet(void) const; + + bool recv_async_msg(uhd::async_metadata_t &, double); + +private: + //clock control + b100_clock_ctrl::sptr _clock_ctrl; + + //interface to ioctls and file descriptor + b100_iface::sptr _iface; + + //handle io stuff + uhd::transport::zero_copy_if::sptr _data_transport; + UHD_PIMPL_DECL(io_impl) _io_impl; + void update_transport_channel_mapping(void); + void io_init(void); + void issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd); + void handle_overrun(size_t); + + //otw types + uhd::otw_type_t _recv_otw_type; + uhd::otw_type_t _send_otw_type; + + //configuration shadows + uhd::clock_config_t _clock_config; + uhd::usrp::subdev_spec_t _rx_subdev_spec, _tx_subdev_spec; + + //ad9862 codec control interface + b100_codec_ctrl::sptr _codec_ctrl; + + //codec properties interfaces + void codec_init(void); + void rx_codec_get(const wax::obj &, wax::obj &); + void rx_codec_set(const wax::obj &, const wax::obj &); + void tx_codec_get(const wax::obj &, wax::obj &); + void tx_codec_set(const wax::obj &, const wax::obj &); + wax_obj_proxy::sptr _rx_codec_proxy, _tx_codec_proxy; + + //device functions and settings + void get(const wax::obj &, wax::obj &); + void set(const wax::obj &, const wax::obj &); + + //mboard functions and settings + void mboard_init(void); + void mboard_get(const wax::obj &, wax::obj &); + void mboard_set(const wax::obj &, const wax::obj &); + void update_clock_config(void); + wax_obj_proxy::sptr _mboard_proxy; + + /*! + * Make a usrp1 dboard interface. + * \param iface the usrp1 interface object + * \param clock the clock control interface + * \param codec the codec control interface + * \param dboard_slot the slot identifier + * \param rx_dboard_id the db id for the rx board (used for evil dbsrx purposes) + * \return a sptr to a new dboard interface + */ + static uhd::usrp::dboard_iface::sptr make_dboard_iface( + b100_iface::sptr iface, + b100_clock_ctrl::sptr clock, + b100_codec_ctrl::sptr codec, + const uhd::usrp::dboard_id_t &rx_dboard_id + ); + + //xx dboard functions and settings + void dboard_init(void); + uhd::usrp::dboard_manager::sptr _dboard_manager; + uhd::usrp::dboard_iface::sptr _dboard_iface; + + //rx dboard functions and settings + uhd::usrp::dboard_eeprom_t _rx_db_eeprom; + void rx_dboard_get(const wax::obj &, wax::obj &); + void rx_dboard_set(const wax::obj &, const wax::obj &); + wax_obj_proxy::sptr _rx_dboard_proxy; + + //tx dboard functions and settings + uhd::usrp::dboard_eeprom_t _tx_db_eeprom, _gdb_eeprom; + void tx_dboard_get(const wax::obj &, wax::obj &); + void tx_dboard_set(const wax::obj &, const wax::obj &); + wax_obj_proxy::sptr _tx_dboard_proxy; + + //rx ddc functions and settings + void rx_ddc_init(void); + void rx_ddc_get(const wax::obj &, wax::obj &); + void rx_ddc_set(const wax::obj &, const wax::obj &); + double _ddc_freq; size_t _ddc_decim; + wax_obj_proxy::sptr _rx_ddc_proxy; + + //tx duc functions and settings + void tx_duc_init(void); + void tx_duc_get(const wax::obj &, wax::obj &); + void tx_duc_set(const wax::obj &, const wax::obj &); + double _duc_freq; size_t _duc_interp; + wax_obj_proxy::sptr _tx_duc_proxy; + + //transports + b100_ctrl::sptr _fpga_ctrl; + uhd::usrp::fx2_ctrl::sptr _fx2_ctrl; + +}; + +#endif /* INCLUDED_b100_IMPL_HPP */ diff --git a/host/lib/usrp/b100/b100_regs.hpp b/host/lib/usrp/b100/b100_regs.hpp new file mode 100644 index 000000000..010df283e --- /dev/null +++ b/host/lib/usrp/b100/b100_regs.hpp @@ -0,0 +1,264 @@ + + +//////////////////////////////////////////////////////////////// +// +// Memory map for wishbone bus +// +//////////////////////////////////////////////////////////////// + +// All addresses are byte addresses. All accesses are word (16-bit) accesses. +// This means that address bit 0 is usually 0. +// There are 11 bits of address for the control. + +#ifndef __B100_REGS_H +#define __B100_REGS_H + +///////////////////////////////////////////////////// +// Slave pointers + +#define B100_REG_SLAVE(n) ((n)<<7) +#define B100_REG_SR_ADDR(n) ((B100_REG_SETTINGS_BASE) + (4*(n))) + +///////////////////////////////////////////////////// +// Slave 0 -- Misc Regs + +#define B100_REG_MISC_BASE B100_REG_SLAVE(0) + +#define B100_REG_MISC_LED B100_REG_MISC_BASE + 0 +#define B100_REG_MISC_SW B100_REG_MISC_BASE + 2 +#define B100_REG_MISC_CGEN_CTRL B100_REG_MISC_BASE + 4 +#define B100_REG_MISC_CGEN_ST B100_REG_MISC_BASE + 6 +#define B100_REG_MISC_TEST B100_REG_MISC_BASE + 8 +#define B100_REG_MISC_RX_LEN B100_REG_MISC_BASE + 10 +#define B100_REG_MISC_TX_LEN B100_REG_MISC_BASE + 12 +#define B100_REG_MISC_XFER_RATE B100_REG_MISC_BASE + 14 +#define B100_REG_MISC_COMPAT B100_REG_MISC_BASE + 16 + +///////////////////////////////////////////////////// +// Slave 1 -- UART +// CLKDIV is 16 bits, others are only 8 + +#define B100_REG_UART_BASE B100_REG_SLAVE(1) + +#define B100_REG_UART_CLKDIV B100_REG_UART_BASE + 0 +#define B100_REG_UART_TXLEVEL B100_REG_UART_BASE + 2 +#define B100_REG_UART_RXLEVEL B100_REG_UART_BASE + 4 +#define B100_REG_UART_TXCHAR B100_REG_UART_BASE + 6 +#define B100_REG_UART_RXCHAR B100_REG_UART_BASE + 8 + +///////////////////////////////////////////////////// +// Slave 2 -- SPI Core +//these are 32-bit registers mapped onto the 16-bit Wishbone bus. +//Using peek32/poke32 should allow transparent use of these registers. +#define B100_REG_SPI_BASE B100_REG_SLAVE(2) +#define B100_REG_SPI_TXRX0 B100_REG_SPI_BASE + 0 +#define B100_REG_SPI_TXRX1 B100_REG_SPI_BASE + 4 +#define B100_REG_SPI_TXRX2 B100_REG_SPI_BASE + 8 +#define B100_REG_SPI_TXRX3 B100_REG_SPI_BASE + 12 +#define B100_REG_SPI_CTRL B100_REG_SPI_BASE + 16 +#define B100_REG_SPI_DIV B100_REG_SPI_BASE + 20 +#define B100_REG_SPI_SS B100_REG_SPI_BASE + 24 + +//spi slave constants +#define B100_SPI_SS_AD9862 (1 << 2) +#define B100_SPI_SS_TX_DB (1 << 1) +#define B100_SPI_SS_RX_DB (1 << 0) + +//spi ctrl register bit definitions +#define SPI_CTRL_ASS (1<<13) +#define SPI_CTRL_IE (1<<12) +#define SPI_CTRL_LSB (1<<11) +#define SPI_CTRL_TXNEG (1<<10) //mosi edge, push on falling edge when 1 +#define SPI_CTRL_RXNEG (1<< 9) //miso edge, latch on falling edge when 1 +#define SPI_CTRL_GO_BSY (1<< 8) +#define SPI_CTRL_CHAR_LEN_MASK 0x7F + +//////////////////////////////////////////////// +// Slave 3 -- I2C Core + +#define B100_REG_I2C_BASE B100_REG_SLAVE(3) +#define B100_REG_I2C_PRESCALER_LO B100_REG_I2C_BASE + 0 +#define B100_REG_I2C_PRESCALER_HI B100_REG_I2C_BASE + 2 +#define B100_REG_I2C_CTRL B100_REG_I2C_BASE + 4 +#define B100_REG_I2C_DATA B100_REG_I2C_BASE + 6 +#define B100_REG_I2C_CMD_STATUS B100_REG_I2C_BASE + 8 + +//and while we're here... + +// +// STA, STO, RD, WR, and IACK bits are cleared automatically +// + +#define I2C_CTRL_EN (1 << 7) // core enable +#define I2C_CTRL_IE (1 << 6) // interrupt enable + +#define I2C_CMD_START (1 << 7) // generate (repeated) start condition +#define I2C_CMD_STOP (1 << 6) // generate stop condition +#define I2C_CMD_RD (1 << 5) // read from slave +#define I2C_CMD_WR (1 << 4) // write to slave +#define I2C_CMD_NACK (1 << 3) // when a rcvr, send ACK (ACK=0) or NACK (ACK=1) +#define I2C_CMD_RSVD_2 (1 << 2) // reserved +#define I2C_CMD_RSVD_1 (1 << 1) // reserved +#define I2C_CMD_IACK (1 << 0) // set to clear pending interrupt + +#define I2C_ST_RXACK (1 << 7) // Received acknowledgement from slave (1 = NAK, 0 = ACK) +#define I2C_ST_BUSY (1 << 6) // 1 after START signal detected; 0 after STOP signal detected +#define I2C_ST_AL (1 << 5) // Arbitration lost. 1 when core lost arbitration +#define I2C_ST_RSVD_4 (1 << 4) // reserved +#define I2C_ST_RSVD_3 (1 << 3) // reserved +#define I2C_ST_RSVD_2 (1 << 2) // reserved +#define I2C_ST_TIP (1 << 1) // Transfer-in-progress +#define I2C_ST_IP (1 << 0) // Interrupt pending + +//////////////////////////////////////////////// +// Slave 4 -- GPIO + +#define B100_REG_GPIO_BASE B100_REG_SLAVE(4) + +#define B100_REG_GPIO_RX_IO B100_REG_GPIO_BASE + 0 +#define B100_REG_GPIO_TX_IO B100_REG_GPIO_BASE + 2 +#define B100_REG_GPIO_RX_DDR B100_REG_GPIO_BASE + 4 +#define B100_REG_GPIO_TX_DDR B100_REG_GPIO_BASE + 6 +#define B100_REG_GPIO_RX_SEL B100_REG_GPIO_BASE + 8 +#define B100_REG_GPIO_TX_SEL B100_REG_GPIO_BASE + 10 +#define B100_REG_GPIO_RX_DBG B100_REG_GPIO_BASE + 12 +#define B100_REG_GPIO_TX_DBG B100_REG_GPIO_BASE + 14 + +//possible bit values for sel when dbg is 0: +#define GPIO_SEL_SW 0 // if pin is an output, set by software in the io reg +#define GPIO_SEL_ATR 1 // if pin is an output, set by ATR logic + +//possible bit values for sel when dbg is 1: +#define GPIO_SEL_DEBUG_0 0 // if pin is an output, debug lines from FPGA fabric +#define GPIO_SEL_DEBUG_1 1 // if pin is an output, debug lines from FPGA fabric + +/////////////////////////////////////////////////// +// Slave 6 -- ATR Controller +// 16 regs + +#define B100_REG_ATR_BASE B100_REG_SLAVE(6) + +#define B100_REG_ATR_IDLE_RXSIDE B100_REG_ATR_BASE + 0 +#define B100_REG_ATR_IDLE_TXSIDE B100_REG_ATR_BASE + 2 +#define B100_REG_ATR_INTX_RXSIDE B100_REG_ATR_BASE + 4 +#define B100_REG_ATR_INTX_TXSIDE B100_REG_ATR_BASE + 6 +#define B100_REG_ATR_INRX_RXSIDE B100_REG_ATR_BASE + 8 +#define B100_REG_ATR_INRX_TXSIDE B100_REG_ATR_BASE + 10 +#define B100_REG_ATR_FULL_RXSIDE B100_REG_ATR_BASE + 12 +#define B100_REG_ATR_FULL_TXSIDE B100_REG_ATR_BASE + 14 + +/////////////////////////////////////////////////// +// Slave 7 -- Readback Mux 32 + +#define B100_REG_RB_MUX_32_BASE B100_REG_SLAVE(7) + +#define B100_REG_RB_TIME_NOW_SECS B100_REG_RB_MUX_32_BASE + 0 +#define B100_REG_RB_TIME_NOW_TICKS B100_REG_RB_MUX_32_BASE + 4 +#define B100_REG_RB_TIME_PPS_SECS B100_REG_RB_MUX_32_BASE + 8 +#define B100_REG_RB_TIME_PPS_TICKS B100_REG_RB_MUX_32_BASE + 12 +#define B100_REG_RB_MISC_TEST32 B100_REG_RB_MUX_32_BASE + 16 + +//////////////////////////////////////////////////// +// Slaves 8 & 9 -- Settings Bus +// +// Output-only, no readback, 64 registers total +// Each register must be written 32 bits at a time +// First the address xxx_xx00 and then xxx_xx10 + +#define B100_REG_SETTINGS_BASE_ADDR(n) (B100_REG_SLAVE(8) + (4*(n))) + +#define B100_REG_SR_MISC_TEST32 B100_REG_SETTINGS_BASE_ADDR(52) + +///////////////////////////////////////////////// +// DSP RX Regs +//////////////////////////////////////////////// +#define B100_REG_DSP_RX_ADDR(n) (B100_REG_SETTINGS_BASE_ADDR(16) + (4*(n))) +#define B100_REG_DSP_RX_FREQ B100_REG_DSP_RX_ADDR(0) +#define B100_REG_DSP_RX_SCALE_IQ B100_REG_DSP_RX_ADDR(1) // {scale_i,scale_q} +#define B100_REG_DSP_RX_DECIM_RATE B100_REG_DSP_RX_ADDR(2) // hb and decim rate +#define B100_REG_DSP_RX_DCOFFSET_I B100_REG_DSP_RX_ADDR(3) // Bit 31 high sets fixed offset mode, using lower 14 bits, // otherwise it is automatic +#define B100_REG_DSP_RX_DCOFFSET_Q B100_REG_DSP_RX_ADDR(4) // Bit 31 high sets fixed offset mode, using lower 14 bits +#define B100_REG_DSP_RX_MUX B100_REG_DSP_RX_ADDR(5) + +/////////////////////////////////////////////////// +// VITA RX CTRL regs +/////////////////////////////////////////////////// +// The following 3 are logically a single command register. +// They are clocked into the underlying fifo when time_ticks is written. +#define B100_REG_CTRL_RX_ADDR(n) (B100_REG_SETTINGS_BASE_ADDR(0) + (4*(n))) +#define B100_REG_CTRL_RX_STREAM_CMD B100_REG_CTRL_RX_ADDR(0) // {now, chain, num_samples(30) +#define B100_REG_CTRL_RX_TIME_SECS B100_REG_CTRL_RX_ADDR(1) +#define B100_REG_CTRL_RX_TIME_TICKS B100_REG_CTRL_RX_ADDR(2) +#define B100_REG_CTRL_RX_CLEAR_OVERRUN B100_REG_CTRL_RX_ADDR(3) // write anything to clear overrun +#define B100_REG_CTRL_RX_VRT_HEADER B100_REG_CTRL_RX_ADDR(4) // word 0 of packet. FPGA fills in packet counter +#define B100_REG_CTRL_RX_VRT_STREAM_ID B100_REG_CTRL_RX_ADDR(5) // word 1 of packet. +#define B100_REG_CTRL_RX_VRT_TRAILER B100_REG_CTRL_RX_ADDR(6) +#define B100_REG_CTRL_RX_NSAMPS_PER_PKT B100_REG_CTRL_RX_ADDR(7) +#define B100_REG_CTRL_RX_NCHANNELS B100_REG_CTRL_RX_ADDR(8) // 1 in basic case, up to 4 for vector sources + +///////////////////////////////////////////////// +// DSP TX Regs +//////////////////////////////////////////////// +#define B100_REG_DSP_TX_ADDR(n) (B100_REG_SETTINGS_BASE_ADDR(32) + (4*(n))) +#define B100_REG_DSP_TX_FREQ B100_REG_DSP_TX_ADDR(0) +#define B100_REG_DSP_TX_SCALE_IQ B100_REG_DSP_TX_ADDR(1) // {scale_i,scale_q} +#define B100_REG_DSP_TX_INTERP_RATE B100_REG_DSP_TX_ADDR(2) +#define B100_REG_DSP_TX_UNUSED B100_REG_DSP_TX_ADDR(3) +#define B100_REG_DSP_TX_MUX B100_REG_DSP_TX_ADDR(4) + +///////////////////////////////////////////////// +// VITA TX CTRL regs +//////////////////////////////////////////////// +#define B100_REG_CTRL_TX_ADDR(n) (B100_REG_SETTINGS_BASE_ADDR(24) + (4*(n))) +#define B100_REG_CTRL_TX_NCHANNELS B100_REG_CTRL_TX_ADDR(0) +#define B100_REG_CTRL_TX_CLEAR_UNDERRUN B100_REG_CTRL_TX_ADDR(1) +#define B100_REG_CTRL_TX_REPORT_SID B100_REG_CTRL_TX_ADDR(2) +#define B100_REG_CTRL_TX_POLICY B100_REG_CTRL_TX_ADDR(3) + +#define B100_FLAG_CTRL_TX_POLICY_WAIT (0x1 << 0) +#define B100_FLAG_CTRL_TX_POLICY_NEXT_PACKET (0x1 << 1) +#define B100_FLAG_CTRL_TX_POLICY_NEXT_BURST (0x1 << 2) + +///////////////////////////////////////////////// +// VITA49 64 bit time (write only) +//////////////////////////////////////////////// + /*! + * \brief Time 64 flags + * + *
+   *
+   *    3                   2                   1
+   *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+   * +-----------------------------------------------------------+-+-+
+   * |                                                           |S|P|
+   * +-----------------------------------------------------------+-+-+
+   *
+   * P - PPS edge selection (0=negedge, 1=posedge, default=0)
+   * S - Source (0=sma, 1=mimo, 0=default)
+   *
+   * 
+ */ +#define B100_REG_TIME64_ADDR(n) (B100_REG_SETTINGS_BASE_ADDR(40) + (4*(n))) +#define B100_REG_TIME64_SECS B100_REG_TIME64_ADDR(0) // value to set absolute secs to on next PPS +#define B100_REG_TIME64_TICKS B100_REG_TIME64_ADDR(1) // value to set absolute ticks to on next PPS +#define B100_REG_TIME64_FLAGS B100_REG_TIME64_ADDR(2) // flags - see chart above +#define B100_REG_TIME64_IMM B100_REG_TIME64_ADDR(3) // set immediate (0=latch on next pps, 1=latch immediate, default=0) +#define B100_REG_TIME64_TPS B100_REG_TIME64_ADDR(4) // clock ticks per second (counter rollover) + +//pps flags (see above) +#define B100_FLAG_TIME64_PPS_NEGEDGE (0 << 0) +#define B100_FLAG_TIME64_PPS_POSEDGE (1 << 0) +#define B100_FLAG_TIME64_PPS_SMA (0 << 1) +#define B100_FLAG_TIME64_PPS_MIMO (1 << 1) + +#define B100_FLAG_TIME64_LATCH_NOW 1 +#define B100_FLAG_TIME64_LATCH_NEXT_PPS 0 + +#define B100_REG_CLEAR_RX_FIFO B100_REG_SETTINGS_BASE_ADDR(48) +#define B100_REG_CLEAR_TX_FIFO B100_REG_SETTINGS_BASE_ADDR(49) + +#define B100_REG_GLOBAL_RESET B100_REG_SETTINGS_BASE_ADDR(50) +#define B100_REG_TEST32 B100_REG_SETTINGS_BASE_ADDR(52) + +#endif + diff --git a/host/lib/usrp/b100/clock_ctrl.cpp b/host/lib/usrp/b100/clock_ctrl.cpp new file mode 100644 index 000000000..e138242d1 --- /dev/null +++ b/host/lib/usrp/b100/clock_ctrl.cpp @@ -0,0 +1,525 @@ +// +// Copyright 2010-2011 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "clock_ctrl.hpp" +#include "ad9522_regs.hpp" +#include +#include +#include +#include +#include +#include +#include "b100_regs.hpp" //spi slave constants +#include +#include +#include +#include +#include //gcd +#include +#include + +using namespace uhd; + +/*********************************************************************** + * Constants + **********************************************************************/ +static const bool ENABLE_THE_TEST_OUT = true; +static const double REFERENCE_INPUT_RATE = 10e6; + +/*********************************************************************** + * Helpers + **********************************************************************/ +template static void set_clock_divider( + size_t divider, div_type &low, div_type &high, bypass_type &bypass +){ + high = divider/2 - 1; + low = divider - high - 2; + bypass = (divider == 1)? 1 : 0; +} + +/*********************************************************************** + * Clock rate calculation stuff: + * Using the internal VCO between 1400 and 1800 MHz + **********************************************************************/ +struct clock_settings_type{ + size_t ref_clock_doubler, r_counter, a_counter, b_counter, prescaler, vco_divider, chan_divider; + size_t get_n_counter(void) const{return prescaler * b_counter + a_counter;} + double get_ref_rate(void) const{return REFERENCE_INPUT_RATE * ref_clock_doubler;} + double get_vco_rate(void) const{return get_ref_rate()/r_counter * get_n_counter();} + double get_chan_rate(void) const{return get_vco_rate()/vco_divider;} + double get_out_rate(void) const{return get_chan_rate()/chan_divider;} + std::string to_pp_string(void) const{ + return str(boost::format( + " r_counter: %d\n" + " a_counter: %d\n" + " b_counter: %d\n" + " prescaler: %d\n" + " vco_divider: %d\n" + " chan_divider: %d\n" + " vco_rate: %fMHz\n" + " chan_rate: %fMHz\n" + " out_rate: %fMHz\n" + ) + % r_counter + % a_counter + % b_counter + % prescaler + % vco_divider + % chan_divider + % (get_vco_rate()/1e6) + % (get_chan_rate()/1e6) + % (get_out_rate()/1e6) + ); + } +}; + +//! gives the greatest divisor of num between 1 and max inclusive +template static inline T greatest_divisor(T num, T max){ + for (T i = max; i > 1; i--) if (num%i == 0) return i; return 1; +} + +//! gives the least divisor of num between min and num exclusive +template static inline T least_divisor(T num, T min){ + for (T i = min; i < num; i++) if (num%i == 0) return i; return 1; +} + +static clock_settings_type get_clock_settings(double rate){ + clock_settings_type cs; + cs.ref_clock_doubler = 2; //always doubling + cs.prescaler = 8; //set to 8 when input is under 2400 MHz + + //basic formulas used below: + //out_rate*X = ref_rate*Y + //X = i*ref_rate/gcd + //Y = i*out_rate/gcd + //X = chan_div * vco_div * R + //Y = P*B + A + + const boost::uint64_t out_rate = boost::uint64_t(rate); + const boost::uint64_t ref_rate = boost::uint64_t(cs.get_ref_rate()); + const size_t gcd = size_t(boost::math::gcd(ref_rate, out_rate)); + + for (size_t i = 1; i <= 100; i++){ + const size_t X = i*ref_rate/gcd; + const size_t Y = i*out_rate/gcd; + + //determine A and B (P is fixed) + cs.b_counter = Y/cs.prescaler; + cs.a_counter = Y - cs.b_counter*cs.prescaler; + + static const double vco_bound_pad = 100e6; + for ( //calculate an r divider that fits into the bounds of the vco + cs.r_counter = size_t(cs.get_n_counter()*cs.get_ref_rate()/(1800e6 - vco_bound_pad)); + cs.r_counter <= size_t(cs.get_n_counter()*cs.get_ref_rate()/(1400e6 + vco_bound_pad)) + and cs.r_counter > 0; cs.r_counter++ + ){ + + //determine chan_div and vco_div + //and fill in that order of preference + cs.chan_divider = greatest_divisor(X/cs.r_counter, 32); + cs.vco_divider = greatest_divisor(X/cs.chan_divider/cs.r_counter, 6); + + //avoid a vco divider of 1 (if possible) + if (cs.vco_divider == 1){ + cs.vco_divider = least_divisor(cs.chan_divider, 2); + cs.chan_divider /= cs.vco_divider; + } + + UHD_LOGV(always) + << "gcd " << gcd << std::endl + << "X " << X << std::endl + << "Y " << Y << std::endl + << cs.to_pp_string() << std::endl + ; + + //filter limits on the counters + if (cs.vco_divider == 1) continue; + if (cs.r_counter >= (1<<14)) continue; + if (cs.b_counter == 2) continue; + if (cs.b_counter == 1 and cs.a_counter != 0) continue; + if (cs.b_counter >= (1<<13)) continue; + if (cs.a_counter >= (1<<6)) continue; + + UHD_MSG(status) << "USRP-B100 clock control: " << i << std::endl << cs.to_pp_string() << std::endl; + return cs; + } + } + + throw uhd::runtime_error(str(boost::format( + "USRP-B100 clock control: could not calculate settings for clock rate %fMHz" + ) % (rate/1e6))); +} + +/*********************************************************************** + * Clock Control Implementation + **********************************************************************/ +class b100_clock_ctrl_impl : public b100_clock_ctrl{ +public: + b100_clock_ctrl_impl(b100_iface::sptr iface, double master_clock_rate){ + _iface = iface; + _chan_rate = 0.0; + _out_rate = 0.0; + + //init the clock gen registers + _ad9522_regs.sdo_active = ad9522_regs_t::SDO_ACTIVE_SDO_SDIO; + _ad9522_regs.enb_stat_eeprom_at_stat_pin = 0; //use status pin + _ad9522_regs.status_pin_control = 0x1; //n divider + _ad9522_regs.ld_pin_control = 0x00; //dld + _ad9522_regs.refmon_pin_control = 0x12; //show ref2 + _ad9522_regs.lock_detect_counter = ad9522_regs_t::LOCK_DETECT_COUNTER_16CYC; + + this->use_internal_ref(); + + this->set_fpga_clock_rate(master_clock_rate); //initialize to something + + this->enable_fpga_clock(true); + this->enable_test_clock(ENABLE_THE_TEST_OUT); + this->enable_rx_dboard_clock(false); + this->enable_tx_dboard_clock(false); + } + + ~b100_clock_ctrl_impl(void){ + UHD_SAFE_CALL( + this->enable_test_clock(ENABLE_THE_TEST_OUT); + this->enable_rx_dboard_clock(false); + this->enable_tx_dboard_clock(false); + //this->enable_fpga_clock(false); //FIXME + ) + } + + /*********************************************************************** + * Clock rate control: + * - set clock rate w/ internal VCO + * - set clock rate w/ external VCXO + **********************************************************************/ + void set_clock_settings_with_internal_vco(double rate){ + const clock_settings_type cs = get_clock_settings(rate); + + //set the rates to private variables so the implementation knows! + _chan_rate = cs.get_chan_rate(); + _out_rate = cs.get_out_rate(); + + _ad9522_regs.enable_clock_doubler = (cs.ref_clock_doubler == 2)? 1 : 0; + + _ad9522_regs.set_r_counter(cs.r_counter); + _ad9522_regs.a_counter = cs.a_counter; + _ad9522_regs.set_b_counter(cs.b_counter); + UHD_ASSERT_THROW(cs.prescaler == 8); //assumes this below: + _ad9522_regs.prescaler_p = ad9522_regs_t::PRESCALER_P_DIV8_9; + + _ad9522_regs.pll_power_down = ad9522_regs_t::PLL_POWER_DOWN_NORMAL; + _ad9522_regs.cp_current = ad9522_regs_t::CP_CURRENT_1_2MA; + + _ad9522_regs.bypass_vco_divider = 0; + switch(cs.vco_divider){ + case 1: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV1; break; + case 2: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV2; break; + case 3: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV3; break; + case 4: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV4; break; + case 5: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV5; break; + case 6: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV6; break; + } + _ad9522_regs.select_vco_or_clock = ad9522_regs_t::SELECT_VCO_OR_CLOCK_VCO; + + //setup fpga master clock + _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS; + set_clock_divider(cs.chan_divider, + _ad9522_regs.divider0_low_cycles, + _ad9522_regs.divider0_high_cycles, + _ad9522_regs.divider0_bypass + ); + + //setup codec clock + _ad9522_regs.out3_format = ad9522_regs_t::OUT3_FORMAT_LVDS; + set_clock_divider(cs.chan_divider, + _ad9522_regs.divider1_low_cycles, + _ad9522_regs.divider1_high_cycles, + _ad9522_regs.divider1_bypass + ); + + this->send_all_regs(); + calibrate_now(); + } + + void set_clock_settings_with_external_vcxo(double rate){ + //set the rates to private variables so the implementation knows! + _chan_rate = rate; + _out_rate = rate; + + _ad9522_regs.enable_clock_doubler = 1; //doubler always on + const double ref_rate = REFERENCE_INPUT_RATE*2; + + //bypass prescaler such that N = B + long gcd = boost::math::gcd(long(ref_rate), long(rate)); + _ad9522_regs.set_r_counter(int(ref_rate/gcd)); + _ad9522_regs.a_counter = 0; + _ad9522_regs.set_b_counter(int(rate/gcd)); + _ad9522_regs.prescaler_p = ad9522_regs_t::PRESCALER_P_DIV1; + + //setup external vcxo + _ad9522_regs.pll_power_down = ad9522_regs_t::PLL_POWER_DOWN_NORMAL; + _ad9522_regs.cp_current = ad9522_regs_t::CP_CURRENT_1_2MA; + _ad9522_regs.bypass_vco_divider = 1; + _ad9522_regs.select_vco_or_clock = ad9522_regs_t::SELECT_VCO_OR_CLOCK_EXTERNAL; + + //setup fpga master clock + _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS; + _ad9522_regs.divider0_bypass = 1; + + //setup codec clock + _ad9522_regs.out3_format = ad9522_regs_t::OUT3_FORMAT_LVDS; + _ad9522_regs.divider1_bypass = 1; + + this->send_all_regs(); + } + + void set_fpga_clock_rate(double rate){ + if (_out_rate == rate) return; + if (rate == 61.44e6) set_clock_settings_with_external_vcxo(rate); + else set_clock_settings_with_internal_vco(rate); + //clock rate changed! update dboard clocks and FPGA ticks per second + set_rx_dboard_clock_rate(rate); + set_tx_dboard_clock_rate(rate); + _iface->poke32(B100_REG_TIME64_TPS, boost::uint32_t(get_fpga_clock_rate())); + } + + double get_fpga_clock_rate(void){ + return this->_out_rate; + } + + /*********************************************************************** + * FPGA clock enable + **********************************************************************/ + void enable_fpga_clock(bool enb){ + _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS; + _ad9522_regs.out0_lvds_power_down = !enb; + this->send_reg(0x0F0); + this->latch_regs(); + } + + /*********************************************************************** + * Special test clock output + **********************************************************************/ + void enable_test_clock(bool enb){ + //setup test clock (same divider as codec clock) + _ad9522_regs.out4_format = ad9522_regs_t::OUT4_FORMAT_CMOS; + _ad9522_regs.out4_cmos_configuration = (enb)? + ad9522_regs_t::OUT4_CMOS_CONFIGURATION_A_ON : + ad9522_regs_t::OUT4_CMOS_CONFIGURATION_OFF; + this->send_reg(0x0F4); + this->latch_regs(); + } + + /*********************************************************************** + * RX Dboard Clock Control (output 9, divider 3) + **********************************************************************/ + void enable_rx_dboard_clock(bool enb){ + _ad9522_regs.out9_format = ad9522_regs_t::OUT9_FORMAT_CMOS; + _ad9522_regs.out9_cmos_configuration = (enb)? + ad9522_regs_t::OUT9_CMOS_CONFIGURATION_B_ON : + ad9522_regs_t::OUT9_CMOS_CONFIGURATION_OFF; + this->send_reg(0x0F9); + this->latch_regs(); + } + + std::vector get_rx_dboard_clock_rates(void){ + std::vector rates; + for(size_t div = 1; div <= 16+16; div++) + rates.push_back(this->_chan_rate/div); + return rates; + } + + void set_rx_dboard_clock_rate(double rate){ + assert_has(get_rx_dboard_clock_rates(), rate, "rx dboard clock rate"); + _rx_clock_rate = rate; + size_t divider = size_t(this->_chan_rate/rate); + //set the divider registers + set_clock_divider(divider, + _ad9522_regs.divider3_low_cycles, + _ad9522_regs.divider3_high_cycles, + _ad9522_regs.divider3_bypass + ); + this->send_reg(0x199); + this->send_reg(0x19a); + this->soft_sync(); + } + + double get_rx_clock_rate(void){ + return _rx_clock_rate; + } + + /*********************************************************************** + * TX Dboard Clock Control (output 6, divider 2) + **********************************************************************/ + void enable_tx_dboard_clock(bool enb){ + _ad9522_regs.out6_format = ad9522_regs_t::OUT6_FORMAT_CMOS; + _ad9522_regs.out6_cmos_configuration = (enb)? + ad9522_regs_t::OUT6_CMOS_CONFIGURATION_B_ON : + ad9522_regs_t::OUT6_CMOS_CONFIGURATION_OFF; + this->send_reg(0x0F6); + this->latch_regs(); + } + + std::vector get_tx_dboard_clock_rates(void){ + return get_rx_dboard_clock_rates(); //same master clock, same dividers... + } + + void set_tx_dboard_clock_rate(double rate){ + assert_has(get_tx_dboard_clock_rates(), rate, "tx dboard clock rate"); + _tx_clock_rate = rate; + size_t divider = size_t(this->_chan_rate/rate); + //set the divider registers + set_clock_divider(divider, + _ad9522_regs.divider2_low_cycles, + _ad9522_regs.divider2_high_cycles, + _ad9522_regs.divider2_bypass + ); + this->send_reg(0x196); + this->send_reg(0x197); + this->soft_sync(); + } + + double get_tx_clock_rate(void){ + return _tx_clock_rate; + } + + /*********************************************************************** + * Clock reference control + **********************************************************************/ + void use_internal_ref(void) { + _ad9522_regs.enable_ref2 = 1; + _ad9522_regs.enable_ref1 = 0; + _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF2; + _ad9522_regs.enb_auto_ref_switchover = ad9522_regs_t::ENB_AUTO_REF_SWITCHOVER_MANUAL; + this->send_reg(0x01C); + this->latch_regs(); + } + + void use_external_ref(void) { + _ad9522_regs.enable_ref2 = 0; + _ad9522_regs.enable_ref1 = 1; + _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF1; + _ad9522_regs.enb_auto_ref_switchover = ad9522_regs_t::ENB_AUTO_REF_SWITCHOVER_MANUAL; + this->send_reg(0x01C); + this->latch_regs(); + } + + void use_auto_ref(void) { + _ad9522_regs.enable_ref2 = 1; + _ad9522_regs.enable_ref1 = 1; + _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF1; + _ad9522_regs.enb_auto_ref_switchover = ad9522_regs_t::ENB_AUTO_REF_SWITCHOVER_AUTO; + this->send_reg(0x01C); + this->latch_regs(); + } + +private: + b100_iface::sptr _iface; + ad9522_regs_t _ad9522_regs; + double _out_rate; //rate at the fpga and codec + double _chan_rate; //rate before final dividers + double _rx_clock_rate, _tx_clock_rate; + + void latch_regs(void){ + _ad9522_regs.io_update = 1; + this->send_reg(0x232); + } + + void send_reg(boost::uint16_t addr){ + boost::uint32_t reg = _ad9522_regs.get_write_reg(addr); + UHD_LOGV(often) << "clock control write reg: " << std::hex << reg << std::endl; + byte_vector_t buf; + buf.push_back(boost::uint8_t(reg >> 16)); + buf.push_back(boost::uint8_t(reg >> 8)); + buf.push_back(boost::uint8_t(reg & 0xff)); + + _iface->get_fx2_i2c_iface().write_i2c(0x5C, buf); + } + + boost::uint8_t read_reg(boost::uint16_t addr){ + byte_vector_t buf; + buf.push_back(boost::uint8_t(addr >> 8)); + buf.push_back(boost::uint8_t(addr & 0xff)); + _iface->get_fx2_i2c_iface().write_i2c(0x5C, buf); + + buf = _iface->get_fx2_i2c_iface().read_i2c(0x5C, 1); + + return boost::uint32_t(buf[0] & 0xFF); + } + + void calibrate_now(void){ + //vco calibration routine: + _ad9522_regs.vco_calibration_now = 0; + this->send_reg(0x18); + this->latch_regs(); + _ad9522_regs.vco_calibration_now = 1; + this->send_reg(0x18); + this->latch_regs(); + //wait for calibration done: + static const boost::uint8_t addr = 0x01F; + for (size_t ms10 = 0; ms10 < 100; ms10++){ + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + boost::uint32_t reg = read_reg(addr); + _ad9522_regs.set_reg(addr, reg); + if (_ad9522_regs.vco_calibration_finished) goto wait_for_ld; + } + UHD_MSG(error) << "USRP-B100 clock control: VCO calibration timeout" << std::endl; + wait_for_ld: + //wait for digital lock detect: + for (size_t ms10 = 0; ms10 < 100; ms10++){ + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + boost::uint32_t reg = read_reg(addr); + _ad9522_regs.set_reg(addr, reg); + if (_ad9522_regs.digital_lock_detect) return; + } + UHD_MSG(error) << "USRP-B100 clock control: lock detection timeout" << std::endl; + } + + void soft_sync(void){ + _ad9522_regs.soft_sync = 1; + this->send_reg(0x230); + this->latch_regs(); + _ad9522_regs.soft_sync = 0; + this->send_reg(0x230); + this->latch_regs(); + } + + void send_all_regs(void){ + //setup a list of register ranges to write + typedef std::pair range_t; + static const std::vector ranges = boost::assign::list_of + (range_t(0x000, 0x000)) (range_t(0x010, 0x01F)) + (range_t(0x0F0, 0x0FD)) (range_t(0x190, 0x19B)) + (range_t(0x1E0, 0x1E1)) (range_t(0x230, 0x230)) + ; + + //write initial register values and latch/update + BOOST_FOREACH(const range_t &range, ranges){ + for(boost::uint16_t addr = range.first; addr <= range.second; addr++){ + this->send_reg(addr); + } + } + this->latch_regs(); + } +}; + +/*********************************************************************** + * Clock Control Make + **********************************************************************/ +b100_clock_ctrl::sptr b100_clock_ctrl::make(b100_iface::sptr iface, double master_clock_rate){ + return sptr(new b100_clock_ctrl_impl(iface, master_clock_rate)); +} diff --git a/host/lib/usrp/b100/clock_ctrl.hpp b/host/lib/usrp/b100/clock_ctrl.hpp new file mode 100644 index 000000000..2a2e74024 --- /dev/null +++ b/host/lib/usrp/b100/clock_ctrl.hpp @@ -0,0 +1,118 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INCLUDED_B100_CLOCK_CTRL_HPP +#define INCLUDED_B100_CLOCK_CTRL_HPP + +#include "b100_iface.hpp" +#include +#include +#include + +/*! + * The usrp-e clock control: + * - Setup system clocks. + * - Disable/enable clock lines. + */ +class b100_clock_ctrl : boost::noncopyable{ +public: + typedef boost::shared_ptr sptr; + + /*! + * Make a new clock control object. + * \param iface the b100 iface object + * \param master_clock_rate the master FPGA/sample clock rate + * \return the clock control object + */ + static sptr make(b100_iface::sptr iface, double master_clock_rate); + + /*! + * Set the rate of the fpga clock line. + * Throws if rate is not valid. + * \param rate the new rate in Hz + */ + virtual void set_fpga_clock_rate(double rate) = 0; + + /*! + * Get the rate of the fpga clock line. + * \return the fpga clock rate in Hz + */ + virtual double get_fpga_clock_rate(void) = 0; + + /*! + * Get the possible rates of the rx dboard clock. + * \return a vector of clock rates in Hz + */ + virtual std::vector get_rx_dboard_clock_rates(void) = 0; + + /*! + * Get the possible rates of the tx dboard clock. + * \return a vector of clock rates in Hz + */ + virtual std::vector get_tx_dboard_clock_rates(void) = 0; + + /*! + * Set the rx dboard clock rate to a possible rate. + * \param rate the new clock rate in Hz + * \throw exception when rate cannot be achieved + */ + virtual void set_rx_dboard_clock_rate(double rate) = 0; + + /*! + * Set the tx dboard clock rate to a possible rate. + * \param rate the new clock rate in Hz + * \throw exception when rate cannot be achieved + */ + virtual void set_tx_dboard_clock_rate(double rate) = 0; + + /*! + * Enable/disable the FPGA clock. + * \param enb true to enable + */ + + virtual void enable_fpga_clock(bool enb) = 0; + + /*! + * Enable/disable the rx dboard clock. + * \param enb true to enable + */ + virtual void enable_rx_dboard_clock(bool enb) = 0; + + /*! + * Enable/disable the tx dboard clock. + * \param enb true to enable + */ + virtual void enable_tx_dboard_clock(bool enb) = 0; + + /*! + * Use the internal TCXO reference + */ + virtual void use_internal_ref(void) = 0; + + /*! + * Use the external SMA reference + */ + virtual void use_external_ref(void) = 0; + + /*! + * Use external if available, internal otherwise + */ + virtual void use_auto_ref(void) = 0; + +}; + +#endif /* INCLUDED_B100_CLOCK_CTRL_HPP */ diff --git a/host/lib/usrp/b100/codec_ctrl.cpp b/host/lib/usrp/b100/codec_ctrl.cpp new file mode 100644 index 000000000..4d118b68b --- /dev/null +++ b/host/lib/usrp/b100/codec_ctrl.cpp @@ -0,0 +1,283 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "codec_ctrl.hpp" +#include "ad9862_regs.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include "b100_regs.hpp" //spi slave constants +#include + +using namespace uhd; + +const gain_range_t b100_codec_ctrl::tx_pga_gain_range(-20, 0, double(0.1)); +const gain_range_t b100_codec_ctrl::rx_pga_gain_range(0, 20, 1); + +/*********************************************************************** + * Codec Control Implementation + **********************************************************************/ +class b100_codec_ctrl_impl : public b100_codec_ctrl{ +public: + //structors + b100_codec_ctrl_impl(b100_iface::sptr iface); + ~b100_codec_ctrl_impl(void); + + //aux adc and dac control + double read_aux_adc(aux_adc_t which); + void write_aux_dac(aux_dac_t which, double volts); + + //pga gain control + void set_tx_pga_gain(double); + double get_tx_pga_gain(void); + void set_rx_pga_gain(double, char); + double get_rx_pga_gain(char); + +private: + b100_iface::sptr _iface; + ad9862_regs_t _ad9862_regs; + void send_reg(boost::uint8_t addr); + void recv_reg(boost::uint8_t addr); +}; + +/*********************************************************************** + * Codec Control Structors + **********************************************************************/ +b100_codec_ctrl_impl::b100_codec_ctrl_impl(b100_iface::sptr iface){ + _iface = iface; + + //soft reset + _ad9862_regs.soft_reset = 1; + this->send_reg(0); + + //initialize the codec register settings + _ad9862_regs.sdio_bidir = ad9862_regs_t::SDIO_BIDIR_SDIO_SDO; + _ad9862_regs.lsb_first = ad9862_regs_t::LSB_FIRST_MSB; + _ad9862_regs.soft_reset = 0; + + //setup rx side of codec + _ad9862_regs.byp_buffer_a = 1; + _ad9862_regs.byp_buffer_b = 1; + _ad9862_regs.buffer_a_pd = 1; + _ad9862_regs.buffer_b_pd = 1; + _ad9862_regs.mux_out = ad9862_regs_t::MUX_OUT_RX_MUX_MODE; //B100 uses interleaved RX->FPGA + _ad9862_regs.rx_pga_a = 0;//0x1f; //TODO bring under api control + _ad9862_regs.rx_pga_b = 0;//0x1f; //TODO bring under api control + _ad9862_regs.rx_twos_comp = 1; + _ad9862_regs.rx_hilbert = ad9862_regs_t::RX_HILBERT_DIS; + + //setup tx side of codec + _ad9862_regs.two_data_paths = ad9862_regs_t::TWO_DATA_PATHS_BOTH; + _ad9862_regs.interleaved = ad9862_regs_t::INTERLEAVED_INTERLEAVED; + _ad9862_regs.tx_retime = ad9862_regs_t::TX_RETIME_CLKOUT2; + _ad9862_regs.tx_pga_gain = 199; //TODO bring under api control + _ad9862_regs.tx_hilbert = ad9862_regs_t::TX_HILBERT_DIS; + _ad9862_regs.interp = ad9862_regs_t::INTERP_2; + _ad9862_regs.tx_twos_comp = 1; + _ad9862_regs.fine_mode = ad9862_regs_t::FINE_MODE_BYPASS; + _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_BYPASS; + _ad9862_regs.dac_a_coarse_gain = 0x3; + _ad9862_regs.dac_b_coarse_gain = 0x3; + _ad9862_regs.edges = ad9862_regs_t::EDGES_NORMAL; + + //setup the dll + _ad9862_regs.input_clk_ctrl = ad9862_regs_t::INPUT_CLK_CTRL_EXTERNAL; + _ad9862_regs.dll_mult = ad9862_regs_t::DLL_MULT_2; + _ad9862_regs.dll_mode = ad9862_regs_t::DLL_MODE_FAST; + + //write the register settings to the codec + for (uint8_t addr = 0; addr <= 25; addr++){ + this->send_reg(addr); + } + + //always start conversions for aux ADC + _ad9862_regs.start_a = 1; + _ad9862_regs.start_b = 1; + + //aux adc clock + _ad9862_regs.clk_4 = ad9862_regs_t::CLK_4_1_4; + this->send_reg(34); +} + +b100_codec_ctrl_impl::~b100_codec_ctrl_impl(void){ + UHD_SAFE_CALL( + //set aux dacs to zero + this->write_aux_dac(AUX_DAC_A, 0); + this->write_aux_dac(AUX_DAC_B, 0); + this->write_aux_dac(AUX_DAC_C, 0); + this->write_aux_dac(AUX_DAC_D, 0); + + //power down + _ad9862_regs.all_rx_pd = 1; + this->send_reg(1); + _ad9862_regs.tx_digital_pd = 1; + _ad9862_regs.tx_analog_pd = ad9862_regs_t::TX_ANALOG_PD_BOTH; + this->send_reg(8); + ) +} + +/*********************************************************************** + * Codec Control Gain Control Methods + **********************************************************************/ +static const int mtpgw = 255; //maximum tx pga gain word + +void b100_codec_ctrl_impl::set_tx_pga_gain(double gain){ + int gain_word = int(mtpgw*(gain - tx_pga_gain_range.start())/(tx_pga_gain_range.stop() - tx_pga_gain_range.start())); + _ad9862_regs.tx_pga_gain = uhd::clip(gain_word, 0, mtpgw); + this->send_reg(16); +} + +double b100_codec_ctrl_impl::get_tx_pga_gain(void){ + return (_ad9862_regs.tx_pga_gain*(tx_pga_gain_range.stop() - tx_pga_gain_range.start())/mtpgw) + tx_pga_gain_range.start(); +} + +static const int mrpgw = 0x14; //maximum rx pga gain word + +void b100_codec_ctrl_impl::set_rx_pga_gain(double gain, char which){ + int gain_word = int(mrpgw*(gain - rx_pga_gain_range.start())/(rx_pga_gain_range.stop() - rx_pga_gain_range.start())); + gain_word = uhd::clip(gain_word, 0, mrpgw); + switch(which){ + case 'A': + _ad9862_regs.rx_pga_a = gain_word; + this->send_reg(2); + return; + case 'B': + _ad9862_regs.rx_pga_b = gain_word; + this->send_reg(3); + return; + default: UHD_THROW_INVALID_CODE_PATH(); + } +} + +double b100_codec_ctrl_impl::get_rx_pga_gain(char which){ + int gain_word; + switch(which){ + case 'A': gain_word = _ad9862_regs.rx_pga_a; break; + case 'B': gain_word = _ad9862_regs.rx_pga_b; break; + default: UHD_THROW_INVALID_CODE_PATH(); + } + return (gain_word*(rx_pga_gain_range.stop() - rx_pga_gain_range.start())/mrpgw) + rx_pga_gain_range.start(); +} + +/*********************************************************************** + * Codec Control AUX ADC Methods + **********************************************************************/ +static double aux_adc_to_volts(boost::uint8_t high, boost::uint8_t low){ + return double((boost::uint16_t(high) << 2) | low)*3.3/0x3ff; +} + +double b100_codec_ctrl_impl::read_aux_adc(aux_adc_t which){ + switch(which){ + + case AUX_ADC_A1: + _ad9862_regs.select_a = ad9862_regs_t::SELECT_A_AUX_ADC1; + this->send_reg(34); //start conversion and select mux + this->recv_reg(28); //read the value (2 bytes, 2 reads) + this->recv_reg(29); + return aux_adc_to_volts(_ad9862_regs.aux_adc_a1_9_2, _ad9862_regs.aux_adc_a1_1_0); + case AUX_ADC_A2: + _ad9862_regs.select_a = ad9862_regs_t::SELECT_A_AUX_ADC2; + this->send_reg(34); //start conversion and select mux + this->recv_reg(26); //read the value (2 bytes, 2 reads) + this->recv_reg(27); + return aux_adc_to_volts(_ad9862_regs.aux_adc_a2_9_2, _ad9862_regs.aux_adc_a2_1_0); + + case AUX_ADC_B1: + _ad9862_regs.select_b = ad9862_regs_t::SELECT_B_AUX_ADC1; + this->send_reg(34); //start conversion and select mux + this->recv_reg(32); //read the value (2 bytes, 2 reads) + this->recv_reg(33); + return aux_adc_to_volts(_ad9862_regs.aux_adc_b1_9_2, _ad9862_regs.aux_adc_b1_1_0); + case AUX_ADC_B2: + _ad9862_regs.select_b = ad9862_regs_t::SELECT_B_AUX_ADC2; + this->send_reg(34); //start conversion and select mux + this->recv_reg(30); //read the value (2 bytes, 2 reads) + this->recv_reg(31); + return aux_adc_to_volts(_ad9862_regs.aux_adc_b2_9_2, _ad9862_regs.aux_adc_b2_1_0); + } + UHD_THROW_INVALID_CODE_PATH(); +} + +/*********************************************************************** + * Codec Control AUX DAC Methods + **********************************************************************/ +void b100_codec_ctrl_impl::write_aux_dac(aux_dac_t which, double volts){ + //special case for aux dac d (aka sigma delta word) + if (which == AUX_DAC_D){ + boost::uint16_t dac_word = uhd::clip(boost::math::iround(volts*0xfff/3.3), 0, 0xfff); + _ad9862_regs.sig_delt_11_4 = boost::uint8_t(dac_word >> 4); + _ad9862_regs.sig_delt_3_0 = boost::uint8_t(dac_word & 0xf); + this->send_reg(42); + this->send_reg(43); + return; + } + + //calculate the dac word for aux dac a, b, c + boost::uint8_t dac_word = uhd::clip(boost::math::iround(volts*0xff/3.3), 0, 0xff); + + //setup a lookup table for the aux dac params (reg ref, reg addr) + typedef boost::tuple dac_params_t; + uhd::dict aux_dac_to_params = boost::assign::map_list_of + (AUX_DAC_A, dac_params_t(&_ad9862_regs.aux_dac_a, 36)) + (AUX_DAC_B, dac_params_t(&_ad9862_regs.aux_dac_b, 37)) + (AUX_DAC_C, dac_params_t(&_ad9862_regs.aux_dac_c, 38)) + ; + + //set the aux dac register + UHD_ASSERT_THROW(aux_dac_to_params.has_key(which)); + boost::uint8_t *reg_ref, reg_addr; + boost::tie(reg_ref, reg_addr) = aux_dac_to_params[which]; + *reg_ref = dac_word; + this->send_reg(reg_addr); +} + +/*********************************************************************** + * Codec Control SPI Methods + **********************************************************************/ +void b100_codec_ctrl_impl::send_reg(boost::uint8_t addr){ + boost::uint32_t reg = _ad9862_regs.get_write_reg(addr); + UHD_LOGV(rarely) << "codec control write reg: " << std::hex << reg << std::endl; + _iface->transact_spi( + B100_SPI_SS_AD9862, + spi_config_t::EDGE_RISE, + reg, 16, false /*no rb*/ + ); +} + +void b100_codec_ctrl_impl::recv_reg(boost::uint8_t addr){ + boost::uint32_t reg = _ad9862_regs.get_read_reg(addr); + UHD_LOGV(rarely) << "codec control read reg: " << std::hex << reg << std::endl; + boost::uint32_t ret = _iface->transact_spi( + B100_SPI_SS_AD9862, + spi_config_t::EDGE_RISE, + reg, 16, true /*rb*/ + ); + UHD_LOGV(rarely) << "codec control read ret: " << std::hex << boost::uint16_t(ret & 0xFF) << std::endl; + _ad9862_regs.set_reg(addr, boost::uint8_t(ret&0xff)); +} + +/*********************************************************************** + * Codec Control Make + **********************************************************************/ +b100_codec_ctrl::sptr b100_codec_ctrl::make(b100_iface::sptr iface){ + return sptr(new b100_codec_ctrl_impl(iface)); +} diff --git a/host/lib/usrp/b100/codec_ctrl.hpp b/host/lib/usrp/b100/codec_ctrl.hpp new file mode 100644 index 000000000..1bd579190 --- /dev/null +++ b/host/lib/usrp/b100/codec_ctrl.hpp @@ -0,0 +1,90 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INCLUDED_B100_CODEC_CTRL_HPP +#define INCLUDED_B100_CODEC_CTRL_HPP + +#include "b100_iface.hpp" +#include +#include +#include + +/*! + * The usrp-e codec control: + * - Init/power down codec. + * - Read aux adc, write aux dac. + */ +class b100_codec_ctrl : boost::noncopyable{ +public: + typedef boost::shared_ptr sptr; + + static const uhd::gain_range_t tx_pga_gain_range; + static const uhd::gain_range_t rx_pga_gain_range; + + /*! + * Make a new codec control object. + * \param iface the usrp_e iface object + * \return the codec control object + */ + static sptr make(b100_iface::sptr iface); + + //! aux adc identifier constants + enum aux_adc_t{ + AUX_ADC_A2 = 0xA2, + AUX_ADC_A1 = 0xA1, + AUX_ADC_B2 = 0xB2, + AUX_ADC_B1 = 0xB1 + }; + + /*! + * Read an auxiliary adc: + * The internals remember which aux adc was read last. + * Therefore, the aux adc switch is only changed as needed. + * \param which which of the 4 adcs + * \return a value in volts + */ + virtual double read_aux_adc(aux_adc_t which) = 0; + + //! aux dac identifier constants + enum aux_dac_t{ + AUX_DAC_A = 0xA, + AUX_DAC_B = 0xB, + AUX_DAC_C = 0xC, + AUX_DAC_D = 0xD //really the sigma delta output + }; + + /*! + * Write an auxiliary dac. + * \param which which of the 4 dacs + * \param volts the level in in volts + */ + virtual void write_aux_dac(aux_dac_t which, double volts) = 0; + + //! Set the TX PGA gain + virtual void set_tx_pga_gain(double gain) = 0; + + //! Get the TX PGA gain + virtual double get_tx_pga_gain(void) = 0; + + //! Set the RX PGA gain ('A' or 'B') + virtual void set_rx_pga_gain(double gain, char which) = 0; + + //! Get the RX PGA gain ('A' or 'B') + virtual double get_rx_pga_gain(char which) = 0; +}; + +#endif /* INCLUDED_B100_CODEC_CTRL_HPP */ diff --git a/host/lib/usrp/b100/codec_impl.cpp b/host/lib/usrp/b100/codec_impl.cpp new file mode 100644 index 000000000..de3ca3a66 --- /dev/null +++ b/host/lib/usrp/b100/codec_impl.cpp @@ -0,0 +1,149 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "b100_impl.hpp" +#include +#include +#include + +using namespace uhd; +using namespace uhd::usrp; + +/*********************************************************************** + * Helper Methods + **********************************************************************/ +void b100_impl::codec_init(void){ + //make proxies + _rx_codec_proxy = wax_obj_proxy::make( + boost::bind(&b100_impl::rx_codec_get, this, _1, _2), + boost::bind(&b100_impl::rx_codec_set, this, _1, _2) + ); + _tx_codec_proxy = wax_obj_proxy::make( + boost::bind(&b100_impl::tx_codec_get, this, _1, _2), + boost::bind(&b100_impl::tx_codec_set, this, _1, _2) + ); +} + +/*********************************************************************** + * RX Codec Properties + **********************************************************************/ +static const std::string ad9862_pga_gain_name = "ad9862 pga"; + +void b100_impl::rx_codec_get(const wax::obj &key_, wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + //handle the get request conditioned on the key + switch(key.as()){ + case CODEC_PROP_NAME: + val = std::string("b100 adc - ad9522"); + return; + + case CODEC_PROP_OTHERS: + val = prop_names_t(); + return; + + case CODEC_PROP_GAIN_NAMES: + val = prop_names_t(1, ad9862_pga_gain_name); + return; + + case CODEC_PROP_GAIN_RANGE: + UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); + val = b100_codec_ctrl::rx_pga_gain_range; + return; + + case CODEC_PROP_GAIN_I: + UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); + val = _codec_ctrl->get_rx_pga_gain('A'); + return; + + case CODEC_PROP_GAIN_Q: + UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); + val = _codec_ctrl->get_rx_pga_gain('B'); + return; + + default: UHD_THROW_PROP_GET_ERROR(); + } +} + +void b100_impl::rx_codec_set(const wax::obj &key_, const wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + //handle the set request conditioned on the key + switch(key.as()){ + case CODEC_PROP_GAIN_I: + UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); + _codec_ctrl->set_rx_pga_gain(val.as(), 'A'); + return; + + case CODEC_PROP_GAIN_Q: + UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); + _codec_ctrl->set_rx_pga_gain(val.as(), 'B'); + return; + + default: UHD_THROW_PROP_SET_ERROR(); + } +} + +/*********************************************************************** + * TX Codec Properties + **********************************************************************/ +void b100_impl::tx_codec_get(const wax::obj &key_, wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + //handle the get request conditioned on the key + switch(key.as()){ + case CODEC_PROP_NAME: + val = std::string("b100 dac - ad9522"); + return; + + case CODEC_PROP_OTHERS: + val = prop_names_t(); + return; + + case CODEC_PROP_GAIN_NAMES: + val = prop_names_t(1, ad9862_pga_gain_name); + return; + + case CODEC_PROP_GAIN_RANGE: + UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); + val = b100_codec_ctrl::tx_pga_gain_range; + return; + + case CODEC_PROP_GAIN_I: //only one gain for I and Q + case CODEC_PROP_GAIN_Q: + UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); + val = _codec_ctrl->get_tx_pga_gain(); + return; + + default: UHD_THROW_PROP_GET_ERROR(); + } +} + +void b100_impl::tx_codec_set(const wax::obj &key_, const wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + //handle the set request conditioned on the key + switch(key.as()){ + case CODEC_PROP_GAIN_I: //only one gain for I and Q + case CODEC_PROP_GAIN_Q: + UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); + _codec_ctrl->set_tx_pga_gain(val.as()); + return; + + default: UHD_THROW_PROP_SET_ERROR(); + } +} diff --git a/host/lib/usrp/b100/ctrl_packet.hpp b/host/lib/usrp/b100/ctrl_packet.hpp new file mode 100644 index 000000000..f504fc5aa --- /dev/null +++ b/host/lib/usrp/b100/ctrl_packet.hpp @@ -0,0 +1,75 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INCLUDED_CTRL_PACKET_HPP +#define INCLUDED_CTRL_PACKET_HPP + +#include +#include +#include + +typedef std::vector ctrl_data_t; + +/*! + * Control packet operation type + */ +enum ctrl_pkt_op_t { + CTRL_PKT_OP_WRITE = 1, + CTRL_PKT_OP_READ = 2, + CTRL_PKT_OP_READBACK = 3 +}; + +/*! + * Control packet transaction length + */ +const size_t CTRL_PACKET_LENGTH = 32; +const size_t CTRL_PACKET_HEADER_LENGTH = 8; +const size_t CTRL_PACKET_DATA_LENGTH = 24; //=length-header + +/*! + * Control packet header magic value + */ +const boost::uint8_t CTRL_PACKET_HEADER_MAGIC = 0xAA; + +/*! + * Callback triggers for readback operation + */ +//FIXME: these are not real numbers, callbacks aren't implemented yet +const boost::uint16_t CTRL_PACKET_CALLBACK_SPI = 0x0001; +const boost::uint16_t CTRL_PACKET_CALLBACK_I2C = 0x0002; +//and so on + +/*! + * Metadata structure to describe a control packet + */ +struct UHD_API ctrl_pkt_meta_t { + ctrl_pkt_op_t op; + boost::uint8_t callbacks; + boost::uint8_t seq; + boost::uint16_t len; + boost::uint32_t addr; +}; + +/*! + * Full control packet structure + */ +struct UHD_API ctrl_pkt_t { + ctrl_pkt_meta_t pkt_meta; + ctrl_data_t data; +}; + +#endif /* INCLUDED_CTRL_PACKET_HPP */ diff --git a/host/lib/usrp/b100/dboard_iface.cpp b/host/lib/usrp/b100/dboard_iface.cpp new file mode 100644 index 000000000..ec3da6220 --- /dev/null +++ b/host/lib/usrp/b100/dboard_iface.cpp @@ -0,0 +1,298 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "b100_iface.hpp" +#include "b100_regs.hpp" +#include "clock_ctrl.hpp" +#include "codec_ctrl.hpp" +#include +#include +#include +#include + + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + +class b100_dboard_iface : public dboard_iface{ +public: + + b100_dboard_iface( + b100_iface::sptr iface, + b100_clock_ctrl::sptr clock, + b100_codec_ctrl::sptr codec + ){ + _iface = iface; + _clock = clock; + _codec = codec; + + //init the clock rate shadows + this->set_clock_rate(UNIT_RX, _clock->get_fpga_clock_rate()); + this->set_clock_rate(UNIT_TX, _clock->get_fpga_clock_rate()); + + _iface->poke16(B100_REG_GPIO_RX_DBG, 0); + _iface->poke16(B100_REG_GPIO_TX_DBG, 0); + } + + ~b100_dboard_iface(void){ + /* NOP */ + } + + special_props_t get_special_props(void){ + special_props_t props; + props.soft_clock_divider = false; + props.mangle_i2c_addrs = false; + return props; + } + + void write_aux_dac(unit_t, aux_dac_t, double); + double read_aux_adc(unit_t, aux_adc_t); + + void _set_pin_ctrl(unit_t, boost::uint16_t); + void _set_atr_reg(unit_t, atr_reg_t, boost::uint16_t); + void _set_gpio_ddr(unit_t, boost::uint16_t); + void _set_gpio_out(unit_t, boost::uint16_t); + void set_gpio_debug(unit_t, int); + boost::uint16_t read_gpio(unit_t); + + void write_i2c(boost::uint8_t, const byte_vector_t &); + byte_vector_t read_i2c(boost::uint8_t, size_t); + + void write_spi( + unit_t unit, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits + ); + + boost::uint32_t read_write_spi( + unit_t unit, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits + ); + + void set_clock_rate(unit_t, double); + std::vector get_clock_rates(unit_t); + double get_clock_rate(unit_t); + void set_clock_enabled(unit_t, bool); + double get_codec_rate(unit_t); + +private: + b100_iface::sptr _iface; + b100_clock_ctrl::sptr _clock; + b100_codec_ctrl::sptr _codec; + uhd::dict _clock_rates; +}; + +/*********************************************************************** + * Make Function + **********************************************************************/ +dboard_iface::sptr make_b100_dboard_iface( + b100_iface::sptr iface, + b100_clock_ctrl::sptr clock, + b100_codec_ctrl::sptr codec +){ + return dboard_iface::sptr(new b100_dboard_iface(iface, clock, codec)); +} + +/*********************************************************************** + * Clock Rates + **********************************************************************/ +void b100_dboard_iface::set_clock_rate(unit_t unit, double rate){ + _clock_rates[unit] = rate; + switch(unit){ + case UNIT_RX: return _clock->set_rx_dboard_clock_rate(rate); + case UNIT_TX: return _clock->set_tx_dboard_clock_rate(rate); + } +} + +std::vector b100_dboard_iface::get_clock_rates(unit_t unit){ + switch(unit){ + case UNIT_RX: return _clock->get_rx_dboard_clock_rates(); + case UNIT_TX: return _clock->get_tx_dboard_clock_rates(); + default: UHD_THROW_INVALID_CODE_PATH(); + } +} + +double b100_dboard_iface::get_clock_rate(unit_t unit){ + return _clock_rates[unit]; +} + +void b100_dboard_iface::set_clock_enabled(unit_t unit, bool enb){ + switch(unit){ + case UNIT_RX: return _clock->enable_rx_dboard_clock(enb); + case UNIT_TX: return _clock->enable_tx_dboard_clock(enb); + } +} + +double b100_dboard_iface::get_codec_rate(unit_t){ + return _clock->get_fpga_clock_rate(); +} + +/*********************************************************************** + * GPIO + **********************************************************************/ +void b100_dboard_iface::_set_pin_ctrl(unit_t unit, boost::uint16_t value){ + UHD_ASSERT_THROW(GPIO_SEL_ATR == 1); //make this assumption + switch(unit){ + case UNIT_RX: _iface->poke16(B100_REG_GPIO_RX_SEL, value); return; + case UNIT_TX: _iface->poke16(B100_REG_GPIO_TX_SEL, value); return; + } +} + +void b100_dboard_iface::_set_gpio_ddr(unit_t unit, boost::uint16_t value){ + switch(unit){ + case UNIT_RX: _iface->poke16(B100_REG_GPIO_RX_DDR, value); return; + case UNIT_TX: _iface->poke16(B100_REG_GPIO_TX_DDR, value); return; + } +} + +void b100_dboard_iface::_set_gpio_out(unit_t unit, boost::uint16_t value){ + switch(unit){ + case UNIT_RX: _iface->poke16(B100_REG_GPIO_RX_IO, value); return; + case UNIT_TX: _iface->poke16(B100_REG_GPIO_TX_IO, value); return; + } +} + +boost::uint16_t b100_dboard_iface::read_gpio(unit_t unit){ + switch(unit){ + case UNIT_RX: return _iface->peek16(B100_REG_GPIO_RX_IO); + case UNIT_TX: return _iface->peek16(B100_REG_GPIO_TX_IO); + default: UHD_THROW_INVALID_CODE_PATH(); + } +} + +void b100_dboard_iface::_set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value){ + //define mapping of unit to atr regs to register address + static const uhd::dict< + unit_t, uhd::dict + > unit_to_atr_to_addr = map_list_of + (UNIT_RX, map_list_of + (ATR_REG_IDLE, B100_REG_ATR_IDLE_RXSIDE) + (ATR_REG_TX_ONLY, B100_REG_ATR_INTX_RXSIDE) + (ATR_REG_RX_ONLY, B100_REG_ATR_INRX_RXSIDE) + (ATR_REG_FULL_DUPLEX, B100_REG_ATR_FULL_RXSIDE) + ) + (UNIT_TX, map_list_of + (ATR_REG_IDLE, B100_REG_ATR_IDLE_TXSIDE) + (ATR_REG_TX_ONLY, B100_REG_ATR_INTX_TXSIDE) + (ATR_REG_RX_ONLY, B100_REG_ATR_INRX_TXSIDE) + (ATR_REG_FULL_DUPLEX, B100_REG_ATR_FULL_TXSIDE) + ) + ; + _iface->poke16(unit_to_atr_to_addr[unit][atr], value); +} + +void b100_dboard_iface::set_gpio_debug(unit_t unit, int which){ + //set this unit to all outputs + this->set_gpio_ddr(unit, 0xffff); + + //calculate the debug selections + boost::uint32_t dbg_sels = 0x0; + int sel = (which == 0)? GPIO_SEL_DEBUG_0 : GPIO_SEL_DEBUG_1; + for(size_t i = 0; i < 16; i++) dbg_sels |= sel << i; + + //set the debug on and which debug selection + switch(unit){ + case UNIT_RX: + _iface->poke16(B100_REG_GPIO_RX_DBG, 0xffff); + _iface->poke16(B100_REG_GPIO_RX_SEL, dbg_sels); + return; + + case UNIT_TX: + _iface->poke16(B100_REG_GPIO_TX_DBG, 0xffff); + _iface->poke16(B100_REG_GPIO_TX_SEL, dbg_sels); + return; + } +} + +/*********************************************************************** + * SPI + **********************************************************************/ +/*! + * Static function to convert a unit type to a spi slave device number. + * \param unit the dboard interface unit type enum + * \return the slave device number + */ +static boost::uint32_t unit_to_otw_spi_dev(dboard_iface::unit_t unit){ + switch(unit){ + case dboard_iface::UNIT_TX: return B100_SPI_SS_TX_DB; + case dboard_iface::UNIT_RX: return B100_SPI_SS_RX_DB; + } + throw std::invalid_argument("unknown unit type"); +} + +void b100_dboard_iface::write_spi( + unit_t unit, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits +){ + _iface->transact_spi(unit_to_otw_spi_dev(unit), config, data, num_bits, false /*no rb*/); +} + +boost::uint32_t b100_dboard_iface::read_write_spi( + unit_t unit, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits +){ + return _iface->transact_spi(unit_to_otw_spi_dev(unit), config, data, num_bits, true /*rb*/); +} + +/*********************************************************************** + * I2C + **********************************************************************/ +void b100_dboard_iface::write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){ + return _iface->write_i2c(addr, bytes); +} + +byte_vector_t b100_dboard_iface::read_i2c(boost::uint8_t addr, size_t num_bytes){ + return _iface->read_i2c(addr, num_bytes); +} + +/*********************************************************************** + * Aux DAX/ADC + **********************************************************************/ +void b100_dboard_iface::write_aux_dac(dboard_iface::unit_t, aux_dac_t which, double value){ + //same aux dacs for each unit + static const uhd::dict which_to_aux_dac = map_list_of + (AUX_DAC_A, b100_codec_ctrl::AUX_DAC_A) + (AUX_DAC_B, b100_codec_ctrl::AUX_DAC_B) + (AUX_DAC_C, b100_codec_ctrl::AUX_DAC_C) + (AUX_DAC_D, b100_codec_ctrl::AUX_DAC_D) + ; + _codec->write_aux_dac(which_to_aux_dac[which], value); +} + +double b100_dboard_iface::read_aux_adc(dboard_iface::unit_t unit, aux_adc_t which){ + static const uhd::dict< + unit_t, uhd::dict + > unit_to_which_to_aux_adc = map_list_of + (UNIT_RX, map_list_of + (AUX_ADC_A, b100_codec_ctrl::AUX_ADC_A1) + (AUX_ADC_B, b100_codec_ctrl::AUX_ADC_B1) + ) + (UNIT_TX, map_list_of + (AUX_ADC_A, b100_codec_ctrl::AUX_ADC_A2) + (AUX_ADC_B, b100_codec_ctrl::AUX_ADC_B2) + ) + ; + return _codec->read_aux_adc(unit_to_which_to_aux_adc[unit][which]); +} diff --git a/host/lib/usrp/b100/dboard_impl.cpp b/host/lib/usrp/b100/dboard_impl.cpp new file mode 100644 index 000000000..ba3776728 --- /dev/null +++ b/host/lib/usrp/b100/dboard_impl.cpp @@ -0,0 +1,185 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "b100_impl.hpp" +#include "b100_regs.hpp" +#include +#include +#include +#include +#include +#include "usrp_i2c_addr.h" + +using namespace uhd; +using namespace uhd::usrp; + +/*********************************************************************** + * Dboard Initialization + **********************************************************************/ +void b100_impl::dboard_init(void){ + //read the tx and rx dboard eeproms + _rx_db_eeprom.load(*_iface, I2C_ADDR_RX_A); + _tx_db_eeprom.load(*_iface, I2C_ADDR_TX_A); + _gdb_eeprom.load(*_iface, I2C_ADDR_TX_A ^ 5); + + //create a new dboard interface and manager + _dboard_iface = make_b100_dboard_iface( + _iface, _clock_ctrl, _codec_ctrl + ); + _dboard_manager = dboard_manager::make( + _rx_db_eeprom.id, + ((_gdb_eeprom.id == dboard_id_t::none())? _tx_db_eeprom : _gdb_eeprom).id, + _dboard_iface + ); + + //setup the dboard proxies + _rx_dboard_proxy = wax_obj_proxy::make( + boost::bind(&b100_impl::rx_dboard_get, this, _1, _2), + boost::bind(&b100_impl::rx_dboard_set, this, _1, _2) + ); + _tx_dboard_proxy = wax_obj_proxy::make( + boost::bind(&b100_impl::tx_dboard_get, this, _1, _2), + boost::bind(&b100_impl::tx_dboard_set, this, _1, _2) + ); +} + +/*********************************************************************** + * RX Dboard Get + **********************************************************************/ +void b100_impl::rx_dboard_get(const wax::obj &key_, wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + //handle the get request conditioned on the key + switch(key.as()){ + case DBOARD_PROP_NAME: + val = std::string("b100 dboard (rx unit)"); + return; + + case DBOARD_PROP_SUBDEV: + val = _dboard_manager->get_rx_subdev(key.name); + return; + + case DBOARD_PROP_SUBDEV_NAMES: + val = _dboard_manager->get_rx_subdev_names(); + return; + + case DBOARD_PROP_DBOARD_EEPROM: + val = _rx_db_eeprom; + return; + + case DBOARD_PROP_DBOARD_IFACE: + val = _dboard_iface; + return; + + case DBOARD_PROP_CODEC: + val = _rx_codec_proxy->get_link(); + return; + + case DBOARD_PROP_GAIN_GROUP: + val = make_gain_group( + _rx_db_eeprom.id, + _dboard_manager->get_rx_subdev(key.name), + _rx_codec_proxy->get_link(), + GAIN_GROUP_POLICY_RX + ); + return; + + default: UHD_THROW_PROP_GET_ERROR(); + } +} + +/*********************************************************************** + * RX Dboard Set + **********************************************************************/ +void b100_impl::rx_dboard_set(const wax::obj &key, const wax::obj &val){ + switch(key.as()){ + case DBOARD_PROP_DBOARD_EEPROM: + _rx_db_eeprom = val.as(); + _rx_db_eeprom.store(*_iface, I2C_ADDR_RX_A); + return; + + default: UHD_THROW_PROP_SET_ERROR(); + } +} + +/*********************************************************************** + * TX Dboard Get + **********************************************************************/ +void b100_impl::tx_dboard_get(const wax::obj &key_, wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + //handle the get request conditioned on the key + switch(key.as()){ + case DBOARD_PROP_NAME: + val = std::string("b100 dboard (tx unit)"); + return; + + case DBOARD_PROP_SUBDEV: + val = _dboard_manager->get_tx_subdev(key.name); + return; + + case DBOARD_PROP_SUBDEV_NAMES: + val = _dboard_manager->get_tx_subdev_names(); + return; + + case DBOARD_PROP_DBOARD_EEPROM: + val = _tx_db_eeprom; + return; + + case DBOARD_PROP_GBOARD_EEPROM: + val = _gdb_eeprom; + return; + + case DBOARD_PROP_DBOARD_IFACE: + val = _dboard_iface; + return; + + case DBOARD_PROP_CODEC: + val = _tx_codec_proxy->get_link(); + return; + + case DBOARD_PROP_GAIN_GROUP: + val = make_gain_group( + _tx_db_eeprom.id, + _dboard_manager->get_tx_subdev(key.name), + _tx_codec_proxy->get_link(), + GAIN_GROUP_POLICY_TX + ); + return; + + default: UHD_THROW_PROP_GET_ERROR(); + } +} + +/*********************************************************************** + * TX Dboard Set + **********************************************************************/ +void b100_impl::tx_dboard_set(const wax::obj &key, const wax::obj &val){ + switch(key.as()){ + case DBOARD_PROP_DBOARD_EEPROM: + _tx_db_eeprom = val.as(); + _tx_db_eeprom.store(*_iface, I2C_ADDR_TX_A); + return; + + case DBOARD_PROP_GBOARD_EEPROM: + _gdb_eeprom = val.as(); + _gdb_eeprom.store(*_iface, I2C_ADDR_TX_A ^ 5); + return; + + default: UHD_THROW_PROP_SET_ERROR(); + } +} diff --git a/host/lib/usrp/b100/dsp_impl.cpp b/host/lib/usrp/b100/dsp_impl.cpp new file mode 100644 index 000000000..c1bf6bedd --- /dev/null +++ b/host/lib/usrp/b100/dsp_impl.cpp @@ -0,0 +1,189 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "b100_impl.hpp" +#include "b100_regs.hpp" +#include +#include +#include +#include + +#define rint boost::math::iround + +using namespace uhd; +using namespace uhd::usrp; + +static const double MASTER_CLOCK_RATE = 64e6; //TODO get from clock control + +/*********************************************************************** + * RX DDC Initialization + **********************************************************************/ +void b100_impl::rx_ddc_init(void){ + _rx_ddc_proxy = wax_obj_proxy::make( + boost::bind(&b100_impl::rx_ddc_get, this, _1, _2), + boost::bind(&b100_impl::rx_ddc_set, this, _1, _2) + ); + + //initial config and update + rx_ddc_set(DSP_PROP_FREQ_SHIFT, double(0)); + rx_ddc_set(DSP_PROP_HOST_RATE, double(16e6)); +} + +/*********************************************************************** + * RX DDC Get + **********************************************************************/ +void b100_impl::rx_ddc_get(const wax::obj &key_, wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + switch(key.as()){ + case DSP_PROP_NAME: + val = std::string("USRP-B100 RX DSP"); + return; + + case DSP_PROP_OTHERS: + val = prop_names_t(); //empty + return; + + case DSP_PROP_FREQ_SHIFT: + val = _ddc_freq; + return; + + case DSP_PROP_CODEC_RATE: + val = _clock_ctrl->get_fpga_clock_rate(); + return; + + case DSP_PROP_HOST_RATE: + val = _clock_ctrl->get_fpga_clock_rate()/_ddc_decim; + return; + + default: UHD_THROW_PROP_GET_ERROR(); + } +} + +/*********************************************************************** + * RX DDC Set + **********************************************************************/ +void b100_impl::rx_ddc_set(const wax::obj &key_, const wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + switch(key.as()){ + case DSP_PROP_STREAM_CMD: + issue_stream_cmd(val.as()); + return; + + case DSP_PROP_FREQ_SHIFT:{ + double new_freq = val.as(); + _iface->poke32(B100_REG_DSP_RX_FREQ, + dsp_type1::calc_cordic_word_and_update(new_freq, _clock_ctrl->get_fpga_clock_rate()) + ); + _ddc_freq = new_freq; //shadow + } + return; + + case DSP_PROP_HOST_RATE:{ + //set the decimation + _ddc_decim = rint(_clock_ctrl->get_fpga_clock_rate()/val.as()); + _iface->poke32(B100_REG_DSP_RX_DECIM_RATE, dsp_type1::calc_cic_filter_word(_ddc_decim)); + + //set the scaling + static const boost::int16_t default_rx_scale_iq = 1024; + _iface->poke32(B100_REG_DSP_RX_SCALE_IQ, + dsp_type1::calc_iq_scale_word(default_rx_scale_iq, default_rx_scale_iq) + ); + } + return; + + default: UHD_THROW_PROP_SET_ERROR(); + } +} + +/*********************************************************************** + * TX DUC Initialization + **********************************************************************/ +void b100_impl::tx_duc_init(void){ + _tx_duc_proxy = wax_obj_proxy::make( + boost::bind(&b100_impl::tx_duc_get, this, _1, _2), + boost::bind(&b100_impl::tx_duc_set, this, _1, _2) + ); + + //initial config and update + tx_duc_set(DSP_PROP_FREQ_SHIFT, double(0)); + tx_duc_set(DSP_PROP_HOST_RATE, double(16e6)); +} + +/*********************************************************************** + * TX DUC Get + **********************************************************************/ +void b100_impl::tx_duc_get(const wax::obj &key_, wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + switch(key.as()){ + case DSP_PROP_NAME: + val = std::string("USRP-B100 TX DSP"); + return; + + case DSP_PROP_OTHERS: + val = prop_names_t(); //empty + return; + + case DSP_PROP_FREQ_SHIFT: + val = _duc_freq; + return; + + case DSP_PROP_CODEC_RATE: + val = _clock_ctrl->get_fpga_clock_rate(); + return; + + case DSP_PROP_HOST_RATE: + val = _clock_ctrl->get_fpga_clock_rate()/_duc_interp; + return; + + default: UHD_THROW_PROP_GET_ERROR(); + } +} + +/*********************************************************************** + * TX DUC Set + **********************************************************************/ +void b100_impl::tx_duc_set(const wax::obj &key_, const wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + switch(key.as()){ + + case DSP_PROP_FREQ_SHIFT:{ + double new_freq = val.as(); + _iface->poke32(B100_REG_DSP_TX_FREQ, + dsp_type1::calc_cordic_word_and_update(new_freq, _clock_ctrl->get_fpga_clock_rate()) + ); + _duc_freq = new_freq; //shadow + } + return; + + case DSP_PROP_HOST_RATE:{ + _duc_interp = rint(_clock_ctrl->get_fpga_clock_rate()/val.as()); + + //set the interpolation + _iface->poke32(B100_REG_DSP_TX_INTERP_RATE, dsp_type1::calc_cic_filter_word(_duc_interp)); + + //set the scaling + _iface->poke32(B100_REG_DSP_TX_SCALE_IQ, dsp_type1::calc_iq_scale_word(_duc_interp)); + } + return; + + default: UHD_THROW_PROP_SET_ERROR(); + } +} diff --git a/host/lib/usrp/b100/io_impl.cpp b/host/lib/usrp/b100/io_impl.cpp new file mode 100644 index 000000000..3978bea75 --- /dev/null +++ b/host/lib/usrp/b100/io_impl.cpp @@ -0,0 +1,210 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "../../transport/super_recv_packet_handler.hpp" +#include "../../transport/super_send_packet_handler.hpp" +#include "usrp_commands.h" +#include "b100_impl.hpp" +#include "b100_regs.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; +namespace asio = boost::asio; + +/*********************************************************************** + * IO Implementation Details + **********************************************************************/ +struct b100_impl::io_impl{ + io_impl(zero_copy_if::sptr data_transport): + data_transport(data_transport) + { + /* NOP */ + } + + ~io_impl(void){ + //drain the rx buffs + //while(data_transport->get_recv_buff().get() != NULL){ + /* NOP */ + //} + } + + zero_copy_if::sptr &data_transport; + + sph::recv_packet_handler recv_handler; + sph::send_packet_handler send_handler; + bool continuous_streaming; +}; + +/*********************************************************************** + * Initialize internals within this file + **********************************************************************/ +void b100_impl::io_init(void){ + _recv_otw_type.width = 16; + _recv_otw_type.shift = 0; + _recv_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; + + _send_otw_type.width = 16; + _send_otw_type.shift = 0; + _send_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; + + _iface->reset_gpif(6); + + //reset state machines + _iface->poke32(B100_REG_CTRL_TX_CLEAR_UNDERRUN, 0); + _iface->poke32(B100_REG_CTRL_RX_CLEAR_OVERRUN, 0); + + _io_impl = UHD_PIMPL_MAKE(io_impl, (_data_transport)); + + //setup rx data path + _iface->poke32(B100_REG_CTRL_RX_NSAMPS_PER_PKT, get_max_recv_samps_per_packet()); + UHD_LOGV(always) << "IO: Using " << get_max_recv_samps_per_packet() << " samples per packet" << std::endl; + _iface->poke32(B100_REG_CTRL_RX_NCHANNELS, 1); + _iface->poke32(B100_REG_CTRL_RX_VRT_HEADER, 0 + | (0x1 << 28) //if data with stream id + | (0x1 << 26) //has trailer + | (0x3 << 22) //integer time other + | (0x1 << 20) //fractional time sample count + ); + _iface->poke32(B100_REG_CTRL_RX_VRT_TRAILER, 0); + + //set the streamid to reset the seq num + _iface->poke32(B100_REG_CTRL_TX_REPORT_SID, 0); + //setup the tx policy + _iface->poke32(B100_REG_CTRL_TX_POLICY, B100_FLAG_CTRL_TX_POLICY_NEXT_PACKET); + + //set the expected packet size in USB frames + _iface->poke32(B100_REG_MISC_RX_LEN, 4); + + update_transport_channel_mapping(); +} + +void b100_impl::update_transport_channel_mapping(void){ + if (_io_impl.get() == NULL) return; //not inited yet + + //set all of the relevant properties on the handler + boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); + _io_impl->recv_handler.resize(_rx_subdev_spec.size()); + _io_impl->recv_handler.set_vrt_unpacker(&vrt::if_hdr_unpack_le); + _io_impl->recv_handler.set_tick_rate(_clock_ctrl->get_fpga_clock_rate()); + _io_impl->recv_handler.set_samp_rate(_rx_ddc_proxy->get_link()[DSP_PROP_HOST_RATE].as()); + for (size_t chan = 0; chan < _io_impl->recv_handler.size(); chan++){ + _io_impl->recv_handler.set_xport_chan_get_buff(chan, boost::bind( + &uhd::transport::zero_copy_if::get_recv_buff, _io_impl->data_transport, _1 + )); + _io_impl->recv_handler.set_overflow_handler(chan, boost::bind( + &b100_impl::handle_overrun, this, chan + )); + } + _io_impl->recv_handler.set_converter(_recv_otw_type); + + //set all of the relevant properties on the handler + boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); + _io_impl->send_handler.resize(_tx_subdev_spec.size()); + _io_impl->send_handler.set_vrt_packer(&vrt::if_hdr_pack_le); + _io_impl->send_handler.set_tick_rate(_clock_ctrl->get_fpga_clock_rate()); + _io_impl->send_handler.set_samp_rate(_tx_duc_proxy->get_link()[DSP_PROP_HOST_RATE].as()); + for (size_t chan = 0; chan < _io_impl->send_handler.size(); chan++){ + _io_impl->send_handler.set_xport_chan_get_buff(chan, boost::bind( + &uhd::transport::zero_copy_if::get_send_buff, _io_impl->data_transport, _1 + )); + } + _io_impl->send_handler.set_converter(_send_otw_type); + _io_impl->send_handler.set_max_samples_per_packet(get_max_send_samps_per_packet()); +} + +/*********************************************************************** + * Data send + helper functions + **********************************************************************/ +size_t b100_impl::get_max_send_samps_per_packet(void) const { + static const size_t hdr_size = 0 + + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) + - sizeof(vrt::if_packet_info_t().cid) //no class id ever used + ; + static const size_t bpp = 2048 - hdr_size; + return bpp / _send_otw_type.get_sample_size(); +} + +size_t b100_impl::send( + const send_buffs_type &buffs, size_t nsamps_per_buff, + const tx_metadata_t &metadata, const io_type_t &io_type, + send_mode_t send_mode, double timeout +){ + return _io_impl->send_handler.send( + buffs, nsamps_per_buff, + metadata, io_type, + send_mode, timeout + ); +} + +/*********************************************************************** + * Data recv + helper functions + **********************************************************************/ + +size_t b100_impl::get_max_recv_samps_per_packet(void) const { + static const size_t hdr_size = 0 + + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) + + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer + - sizeof(vrt::if_packet_info_t().cid) //no class id ever used + ; + size_t bpp = 2048 - hdr_size; //limited by FPGA pkt buffer size + return bpp/_recv_otw_type.get_sample_size(); +} + +size_t b100_impl::recv( + const recv_buffs_type &buffs, size_t nsamps_per_buff, + rx_metadata_t &metadata, const io_type_t &io_type, + recv_mode_t recv_mode, double timeout +){ + return _io_impl->recv_handler.recv( + buffs, nsamps_per_buff, + metadata, io_type, + recv_mode, timeout + ); +} + +void b100_impl::issue_stream_cmd(const stream_cmd_t &stream_cmd) +{ + _io_impl->continuous_streaming = (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + _iface->poke32(B100_REG_CTRL_RX_STREAM_CMD, dsp_type1::calc_stream_cmd_word(stream_cmd)); + _iface->poke32(B100_REG_CTRL_RX_TIME_SECS, boost::uint32_t(stream_cmd.time_spec.get_full_secs())); + _iface->poke32(B100_REG_CTRL_RX_TIME_TICKS, stream_cmd.time_spec.get_tick_count(_clock_ctrl->get_fpga_clock_rate())); + + if (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS) { + while(_io_impl->data_transport->get_recv_buff().get() != NULL){ + /* NOP */ + } + } +} + +void b100_impl::handle_overrun(size_t){ + if (_io_impl->continuous_streaming){ + this->issue_stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + } +} diff --git a/host/lib/usrp/b100/mboard_impl.cpp b/host/lib/usrp/b100/mboard_impl.cpp new file mode 100644 index 000000000..c651ff2a2 --- /dev/null +++ b/host/lib/usrp/b100/mboard_impl.cpp @@ -0,0 +1,246 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "b100_impl.hpp" +#include "usrp_commands.h" +#include "fpga_regs_standard.h" +#include "fpga_regs_common.h" +#include "b100_regs.hpp" +#include "usrp_i2c_addr.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace uhd; +using namespace uhd::usrp; + +static const bool b100_mboard_verbose = true; + +/*********************************************************************** + * Mboard Initialization + **********************************************************************/ +void b100_impl::mboard_init(void) +{ + _mboard_proxy = wax_obj_proxy::make( + boost::bind(&b100_impl::mboard_get, this, _1, _2), + boost::bind(&b100_impl::mboard_set, this, _1, _2)); + + //set the ticks per seconds into the vita time control + _iface->poke32(B100_REG_TIME64_TPS, + boost::uint32_t(_clock_ctrl->get_fpga_clock_rate()) + ); + + //init the clock config + _clock_config = clock_config_t::internal(); + update_clock_config(); +} + +void b100_impl::update_clock_config(void){ + boost::uint32_t pps_flags = 0; + + //translate pps polarity enums + switch(_clock_config.pps_polarity){ + case clock_config_t::PPS_POS: pps_flags |= B100_FLAG_TIME64_PPS_POSEDGE; break; + case clock_config_t::PPS_NEG: pps_flags |= B100_FLAG_TIME64_PPS_NEGEDGE; break; + default: throw uhd::runtime_error("unhandled clock configuration pps polarity"); + } + + //set the pps flags + _iface->poke32(B100_REG_TIME64_FLAGS, pps_flags); + + //clock source ref 10mhz + switch(_clock_config.ref_source){ + case clock_config_t::REF_AUTO: _clock_ctrl->use_auto_ref(); break; + case clock_config_t::REF_INT: _clock_ctrl->use_internal_ref(); break; + case clock_config_t::REF_SMA: _clock_ctrl->use_auto_ref(); break; + default: throw uhd::runtime_error("unhandled clock configuration ref source"); + } +} + +/*********************************************************************** + * Mboard Get + **********************************************************************/ +void b100_impl::mboard_get(const wax::obj &key_, wax::obj &val){ + named_prop_t key = named_prop_t::extract(key_); + + //handle the get request conditioned on the key + switch(key.as()){ + case MBOARD_PROP_NAME: + val = std::string("USRP-B100 mboard"); + return; + + case MBOARD_PROP_OTHERS: + val = prop_names_t(); + return; + + case MBOARD_PROP_RX_DBOARD: + UHD_ASSERT_THROW(key.name == ""); + val = _rx_dboard_proxy->get_link(); + return; + + case MBOARD_PROP_RX_DBOARD_NAMES: + val = prop_names_t(1, ""); //vector of size 1 with empty string + return; + + case MBOARD_PROP_TX_DBOARD: + UHD_ASSERT_THROW(key.name == ""); + val = _tx_dboard_proxy->get_link(); + return; + + case MBOARD_PROP_TX_DBOARD_NAMES: + val = prop_names_t(1, ""); //vector of size 1 with empty string + return; + + case MBOARD_PROP_RX_DSP: + UHD_ASSERT_THROW(key.name == ""); + val = _rx_ddc_proxy->get_link(); + return; + + case MBOARD_PROP_RX_DSP_NAMES: + val = prop_names_t(1, ""); + return; + + case MBOARD_PROP_TX_DSP: + UHD_ASSERT_THROW(key.name == ""); + val = _tx_duc_proxy->get_link(); + return; + + case MBOARD_PROP_TX_DSP_NAMES: + val = prop_names_t(1, ""); + return; + + case MBOARD_PROP_CLOCK_CONFIG: + val = _clock_config; + return; + + case MBOARD_PROP_RX_SUBDEV_SPEC: + val = _rx_subdev_spec; + return; + + case MBOARD_PROP_TX_SUBDEV_SPEC: + val = _tx_subdev_spec; + return; + + case MBOARD_PROP_EEPROM_MAP: + val = _iface->mb_eeprom; + return; + + case MBOARD_PROP_TIME_NOW:while(true){ + uint32_t secs = _iface->peek32(B100_REG_RB_TIME_NOW_SECS); + uint32_t ticks = _iface->peek32(B100_REG_RB_TIME_NOW_TICKS); + if (secs != _iface->peek32(B100_REG_RB_TIME_NOW_SECS)) continue; + val = time_spec_t(secs, ticks, _clock_ctrl->get_fpga_clock_rate()); + return; + } + + case MBOARD_PROP_TIME_PPS: while(true){ + uint32_t secs = _iface->peek32(B100_REG_RB_TIME_PPS_SECS); + uint32_t ticks = _iface->peek32(B100_REG_RB_TIME_PPS_TICKS); + if (secs != _iface->peek32(B100_REG_RB_TIME_PPS_SECS)) continue; + val = time_spec_t(secs, ticks, _clock_ctrl->get_fpga_clock_rate()); + return; + } + + case MBOARD_PROP_CLOCK_RATE: + val = _clock_ctrl->get_fpga_clock_rate(); + return; + + default: UHD_THROW_PROP_GET_ERROR(); + } +} + +/*********************************************************************** + * Mboard Set + **********************************************************************/ +void b100_impl::mboard_set(const wax::obj &key, const wax::obj &val) +{ + if(key.type() == typeid(std::string)) { + if(key.as() == "load_eeprom") { + std::string b100_eeprom_image = val.as(); + UHD_MSG(status) << "B100 EEPROM image: " << b100_eeprom_image << std::endl; + _fx2_ctrl->usrp_load_eeprom(val.as()); + } + return; + } + + //handle the get request conditioned on the key + switch(key.as()){ + case MBOARD_PROP_TIME_NOW: + case MBOARD_PROP_TIME_PPS:{ + time_spec_t time_spec = val.as(); + _iface->poke32(B100_REG_TIME64_TICKS, time_spec.get_tick_count(_clock_ctrl->get_fpga_clock_rate())); + boost::uint32_t imm_flags = (key.as() == MBOARD_PROP_TIME_NOW)? 1 : 0; + _iface->poke32(B100_REG_TIME64_IMM, imm_flags); + _iface->poke32(B100_REG_TIME64_SECS, boost::uint32_t(time_spec.get_full_secs())); + } + return; + + case MBOARD_PROP_RX_SUBDEV_SPEC: + _rx_subdev_spec = val.as(); + verify_rx_subdev_spec(_rx_subdev_spec, _mboard_proxy->get_link()); + UHD_ASSERT_THROW(_rx_subdev_spec.size() == 1); + //set the mux + _iface->poke32(B100_REG_DSP_RX_MUX, dsp_type1::calc_rx_mux_word( + _dboard_manager->get_rx_subdev(_rx_subdev_spec.front().sd_name)[SUBDEV_PROP_CONNECTION].as() + )); + return; + + case MBOARD_PROP_TX_SUBDEV_SPEC: + _tx_subdev_spec = val.as(); + verify_tx_subdev_spec(_tx_subdev_spec, _mboard_proxy->get_link()); + UHD_ASSERT_THROW(_tx_subdev_spec.size() == 1); + //set the mux and set the number of tx channels + _iface->poke32(B100_REG_DSP_TX_MUX, dsp_type1::calc_tx_mux_word( + _dboard_manager->get_tx_subdev(_tx_subdev_spec.front().sd_name)[SUBDEV_PROP_CONNECTION].as() + )); + return; + + case MBOARD_PROP_EEPROM_MAP: + // Step1: commit the map, writing only those values set. + // Step2: readback the entire eeprom map into the iface. + val.as().commit(*_iface, mboard_eeprom_t::MAP_B000); + _iface->mb_eeprom = mboard_eeprom_t(*_iface, mboard_eeprom_t::MAP_B000); + return; + + case MBOARD_PROP_CLOCK_CONFIG: + _clock_config = val.as(); + update_clock_config(); + return; + + case MBOARD_PROP_CLOCK_RATE: + UHD_MSG(warning) + << "You are setting the master clock rate from the API.\n" + << "You may want to pass this into the device address as master_clock_rate=.\n" + << "This way, the clock rate is guaranteed to be initialized first.\n" + << "See the application notes for USRP-B100 for further instructions.\n" + ; + _clock_ctrl->set_fpga_clock_rate(val.as()); + update_transport_channel_mapping(); + return; + + default: UHD_THROW_PROP_SET_ERROR(); + } +} diff --git a/host/lib/usrp/usrp_b100/CMakeLists.txt b/host/lib/usrp/usrp_b100/CMakeLists.txt deleted file mode 100644 index f83a10935..000000000 --- a/host/lib/usrp/usrp_b100/CMakeLists.txt +++ /dev/null @@ -1,47 +0,0 @@ -# -# Copyright 2010-2011 Ettus Research LLC -# -# 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 -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -######################################################################## -# This file included, use CMake directory variables -######################################################################## - -######################################################################## -# Conditionally configure the B100 support -######################################################################## -LIBUHD_REGISTER_COMPONENT("B100" ENABLE_B100 ON "ENABLE_LIBUHD;ENABLE_USB" OFF) - -IF(ENABLE_B100) - LIBUHD_APPEND_SOURCES( - ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/clock_ctrl.cpp - ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/clock_ctrl.hpp - ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/codec_ctrl.cpp - ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/codec_ctrl.hpp - ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/codec_impl.cpp - ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/ctrl_packet.hpp - ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/dboard_impl.cpp - ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/dboard_iface.cpp - ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/dsp_impl.cpp - ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/io_impl.cpp - ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/mboard_impl.cpp - ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/b100_ctrl.cpp - ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/b100_ctrl.hpp - ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/b100_iface.cpp - ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/b100_iface.hpp - ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/b100_impl.cpp - ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/b100_impl.hpp - ) -ENDIF(ENABLE_B100) diff --git a/host/lib/usrp/usrp_b100/b100_ctrl.cpp b/host/lib/usrp/usrp_b100/b100_ctrl.cpp deleted file mode 100644 index 4d4520e1e..000000000 --- a/host/lib/usrp/usrp_b100/b100_ctrl.cpp +++ /dev/null @@ -1,245 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -#include "../../transport/super_recv_packet_handler.hpp" -#include "b100_ctrl.hpp" -#include "b100_impl.hpp" -#include -#include -#include -#include -#include -#include -#include -#include "ctrl_packet.hpp" -#include -#include - -using namespace uhd::transport; -using namespace uhd; - -bool b100_ctrl_debug = false; - -class b100_ctrl_impl : public b100_ctrl { -public: - b100_ctrl_impl(uhd::transport::usb_zero_copy::sptr ctrl_transport) : - sync_ctrl_fifo(2), - async_msg_fifo(100), - _ctrl_transport(ctrl_transport), - _seq(0) - { - boost::barrier spawn_barrier(2); - viking_marauders.create_thread(boost::bind(&b100_ctrl_impl::viking_marauder_loop, this, boost::ref(spawn_barrier))); - spawn_barrier.wait(); - } - - int write(boost::uint32_t addr, const ctrl_data_t &data); - ctrl_data_t read(boost::uint32_t addr, size_t len); - - ~b100_ctrl_impl(void) { - bbl_out_marauding = false; - viking_marauders.interrupt_all(); - viking_marauders.join_all(); - } - - bool get_ctrl_data(ctrl_data_t &pkt_data, double timeout); - bool recv_async_msg(uhd::async_metadata_t &async_metadata, double timeout); - -private: - int send_pkt(boost::uint16_t *cmd); - - //änd hërë wë gö ä-Vïkïng för äsynchronous control packets - void viking_marauder_loop(boost::barrier &); - bounded_buffer sync_ctrl_fifo; - bounded_buffer async_msg_fifo; - boost::thread_group viking_marauders; - bool bbl_out_marauding; - - uhd::transport::usb_zero_copy::sptr _ctrl_transport; - boost::uint8_t _seq; -}; - -/*********************************************************************** - * helper functions for packing/unpacking control packets - **********************************************************************/ -void pack_ctrl_pkt(boost::uint16_t *pkt_buff, - const ctrl_pkt_t &pkt){ - //first two bits are OP - //next six bits are CALLBACKS - //next 8 bits are SEQUENCE - //next 16 bits are LENGTH (16-bit word) - //next 32 bits are ADDRESS (16-bit word LSW) - //then DATA (28 16-bit words) - pkt_buff[0] = (boost::uint16_t(pkt.pkt_meta.op) << 14) | (boost::uint16_t(pkt.pkt_meta.callbacks) << 8) | pkt.pkt_meta.seq; - pkt_buff[1] = pkt.pkt_meta.len; - pkt_buff[2] = (pkt.pkt_meta.addr & 0x00000FFF); - pkt_buff[3] = 0x0000; //address high bits always 0 on this device - - for(size_t i = 0; i < pkt.data.size(); i++) { - pkt_buff[4+i] = pkt.data[i]; - } -} - -void unpack_ctrl_pkt(const boost::uint16_t *pkt_buff, - ctrl_pkt_t &pkt){ - pkt.pkt_meta.seq = pkt_buff[0] & 0xFF; - pkt.pkt_meta.op = CTRL_PKT_OP_READ; //really this is useless - pkt.pkt_meta.len = pkt_buff[1]; - pkt.pkt_meta.callbacks = 0; //callbacks aren't implemented yet - pkt.pkt_meta.addr = pkt_buff[2] | boost::uint32_t(pkt_buff[3] << 16); - - //let's check this so we don't go pushing 64K of crap onto the pkt - if(pkt.pkt_meta.len > CTRL_PACKET_DATA_LENGTH) { - throw uhd::runtime_error("Received control packet too long"); - } - - for(int i = 4; i < 4+pkt.pkt_meta.len; i++) pkt.data.push_back(pkt_buff[i]); -} - -int b100_ctrl_impl::send_pkt(boost::uint16_t *cmd) { - managed_send_buffer::sptr sbuf = _ctrl_transport->get_send_buff(); - if(!sbuf.get()) { - throw uhd::runtime_error("Control channel send error"); - } - - //FIXME there's a better way to do this - for(size_t i = 0; i < (CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)); i++) { - sbuf->cast()[i] = cmd[i]; - } - sbuf->commit(CTRL_PACKET_LENGTH); //fixed size transaction - return 0; -} - -int b100_ctrl_impl::write(boost::uint32_t addr, const ctrl_data_t &data) { - UHD_ASSERT_THROW(data.size() <= (CTRL_PACKET_DATA_LENGTH / sizeof(boost::uint16_t))); - ctrl_pkt_t pkt; - pkt.data = data; - pkt.pkt_meta.op = CTRL_PKT_OP_WRITE; - pkt.pkt_meta.callbacks = 0; - pkt.pkt_meta.seq = _seq++; - pkt.pkt_meta.len = pkt.data.size(); - pkt.pkt_meta.addr = addr; - boost::uint16_t pkt_buff[CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)]; - - pack_ctrl_pkt(pkt_buff, pkt); - size_t result = send_pkt(pkt_buff); - return result; -} - -ctrl_data_t b100_ctrl_impl::read(boost::uint32_t addr, size_t len) { - UHD_ASSERT_THROW(len <= (CTRL_PACKET_DATA_LENGTH / sizeof(boost::uint16_t))); - - ctrl_pkt_t pkt; - pkt.pkt_meta.op = CTRL_PKT_OP_READ; - pkt.pkt_meta.callbacks = 0; - pkt.pkt_meta.seq = _seq++; - pkt.pkt_meta.len = len; - pkt.pkt_meta.addr = addr; - boost::uint16_t pkt_buff[CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)]; - - pack_ctrl_pkt(pkt_buff, pkt); - send_pkt(pkt_buff); - - //loop around waiting for the response to appear - while(!get_ctrl_data(pkt.data, 0.05)); - - return pkt.data; -} - -/*********************************************************************** - * Viking marauders go pillaging for asynchronous control packets in the - * control response endpoint. Sync packets go in sync_ctrl_fifo, - * async TX error messages go in async_msg_fifo. sync_ctrl_fifo should - * never have more than 1 message in it, since it's expected that we'll - * wait for a control operation to finish before starting another one. - **********************************************************************/ -void b100_ctrl_impl::viking_marauder_loop(boost::barrier &spawn_barrier) { - bbl_out_marauding = true; - spawn_barrier.wait(); - set_thread_priority_safe(); - - while(bbl_out_marauding){ - managed_recv_buffer::sptr rbuf = _ctrl_transport->get_recv_buff(); - if(!rbuf.get()) continue; //that's ok, there are plenty of villages to pillage! - const boost::uint16_t *pkt_buf = rbuf->cast(); - - if(pkt_buf[0] >> 8 == CTRL_PACKET_HEADER_MAGIC) { - //so it's got a control packet header, let's parse it. - ctrl_pkt_t pkt; - unpack_ctrl_pkt(pkt_buf, pkt); - - if(pkt.pkt_meta.seq != boost::uint8_t(_seq - 1)) { - throw uhd::runtime_error("Sequence error on control channel"); - } - if(pkt.pkt_meta.len > (CTRL_PACKET_LENGTH - CTRL_PACKET_HEADER_LENGTH)) { - throw uhd::runtime_error("Control channel packet length too long"); - } - - //push it onto the queue - sync_ctrl_fifo.push_with_wait(pkt.data); - } else { //it's an async status pkt - //extract the vrt header packet info - vrt::if_packet_info_t if_packet_info; - if_packet_info.num_packet_words32 = rbuf->size()/sizeof(boost::uint32_t); - const boost::uint32_t *vrt_hdr = rbuf->cast(); - vrt::if_hdr_unpack_le(vrt_hdr, if_packet_info); - - if( if_packet_info.sid == 0 - and if_packet_info.packet_type != vrt::if_packet_info_t::PACKET_TYPE_DATA){ - //fill in the async metadata - async_metadata_t metadata; - metadata.channel = 0; - metadata.has_time_spec = if_packet_info.has_tsi and if_packet_info.has_tsf; - metadata.time_spec = time_spec_t( - time_t(if_packet_info.tsi), size_t(if_packet_info.tsf), 64e6 //FIXME get from clock_ctrl - ); - metadata.event_code = async_metadata_t::event_code_t(sph::get_context_code(vrt_hdr, if_packet_info)); - //print the famous U, and push the metadata into the message queue - if (metadata.event_code & - ( async_metadata_t::EVENT_CODE_UNDERFLOW - | async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET) ) - UHD_MSG(fastpath) << "U"; - - if (metadata.event_code & - ( async_metadata_t::EVENT_CODE_SEQ_ERROR - | async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST) ) - UHD_MSG(fastpath) << "S"; - - async_msg_fifo.push_with_pop_on_full(metadata); - continue; - } - throw uhd::runtime_error("Control: unknown async response"); - } - } -} - -bool b100_ctrl_impl::get_ctrl_data(ctrl_data_t &pkt_data, double timeout){ - boost::this_thread::disable_interruption di; //disable because the wait can throw - return sync_ctrl_fifo.pop_with_timed_wait(pkt_data, timeout); -} - -bool b100_ctrl_impl::recv_async_msg(uhd::async_metadata_t &async_metadata, double timeout) { - boost::this_thread::disable_interruption di; //disable because the wait can throw - return async_msg_fifo.pop_with_timed_wait(async_metadata, timeout); -} - -/*********************************************************************** - * Public make function for b100_ctrl interface - **********************************************************************/ -b100_ctrl::sptr b100_ctrl::make(uhd::transport::usb_zero_copy::sptr ctrl_transport){ - return sptr(new b100_ctrl_impl(ctrl_transport)); -} diff --git a/host/lib/usrp/usrp_b100/b100_ctrl.hpp b/host/lib/usrp/usrp_b100/b100_ctrl.hpp deleted file mode 100644 index ae706dbb4..000000000 --- a/host/lib/usrp/usrp_b100/b100_ctrl.hpp +++ /dev/null @@ -1,69 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -#ifndef INCLUDED_B100_CTRL_HPP -#define INCLUDED_B100_CTRL_HPP - -#include -#include -#include -#include -#include -#include -#include "ctrl_packet.hpp" -#include - -class b100_ctrl : boost::noncopyable{ -public: - typedef boost::shared_ptr sptr; - - /*! - * Make a USRP control object from a data transport - * \param ctrl_transport a USB data transport - * \return a new b100 control object - */ - static sptr make(uhd::transport::usb_zero_copy::sptr ctrl_transport); - - /*! - * Write a byte vector to an FPGA register - * \param addr the FPGA register address - * \param bytes the data to write - * \return 0 on success, error code on failure - */ - virtual int write(boost::uint32_t addr, const ctrl_data_t &data) = 0; - - /*! - * Read a byte vector from an FPGA register (blocking read) - * \param addr the FPGA register address - * \param len the length of the read - * \return a vector of bytes from the register(s) in question - */ - virtual ctrl_data_t read(boost::uint32_t addr, size_t len) = 0; - - /*! - * Get a sync ctrl packet (blocking) - * \param the packet data buffer - * \param the timeout value - * \return true if it got something - */ - virtual bool get_ctrl_data(ctrl_data_t &pkt_data, double timeout) = 0; - - virtual bool recv_async_msg(uhd::async_metadata_t &async_metadata, double timeout) = 0; - -}; - -#endif /* INCLUDED_B100_CTRL_HPP */ diff --git a/host/lib/usrp/usrp_b100/b100_iface.cpp b/host/lib/usrp/usrp_b100/b100_iface.cpp deleted file mode 100644 index 17ea2e6ad..000000000 --- a/host/lib/usrp/usrp_b100/b100_iface.cpp +++ /dev/null @@ -1,336 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -#include "b100_iface.hpp" -#include "usrp_commands.h" -#include -#include -#include -#include -#include -#include -#include - -//FOR TESTING ONLY -#include "b100_regs.hpp" -#include -#include "usrp_i2c_addr.h" - -using namespace uhd; -using namespace uhd::usrp; -using namespace uhd::transport; - -/*********************************************************************** - * Constants - **********************************************************************/ -static const bool iface_debug = true; -static const boost::uint16_t USRP_B_FW_COMPAT_NUM = 0x02; -static const boost::uint16_t USRP_B_FPGA_COMPAT_NUM = 0x03; - -/*********************************************************************** - * I2C + FX2 implementation wrapper - **********************************************************************/ -class b100_i2c_fx2_iface : public i2c_iface{ -public: - b100_i2c_fx2_iface(uhd::usrp::fx2_ctrl::sptr fx2_ctrl){ - _fx2_ctrl = fx2_ctrl; - } - - void write_i2c(boost::uint8_t addr, const byte_vector_t &bytes) - { - UHD_ASSERT_THROW(bytes.size() < max_i2c_data_bytes); - - unsigned char buff[max_i2c_data_bytes]; - std::copy(bytes.begin(), bytes.end(), buff); - - int ret = _fx2_ctrl->usrp_i2c_write(addr & 0xff, - buff, - bytes.size()); - - if (iface_debug && (ret < 0)) - uhd::runtime_error("USRP: failed i2c write"); - } - - byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes) - { - UHD_ASSERT_THROW(num_bytes < max_i2c_data_bytes); - - unsigned char buff[max_i2c_data_bytes]; - int ret = _fx2_ctrl->usrp_i2c_read(addr & 0xff, - buff, - num_bytes); - - if (iface_debug && ((ret < 0) || (unsigned)ret < (num_bytes))) - uhd::runtime_error("USRP: failed i2c read"); - - byte_vector_t out_bytes; - for (size_t i = 0; i < num_bytes; i++) - out_bytes.push_back(buff[i]); - - return out_bytes; - } - -private: - static const size_t max_i2c_data_bytes = 64; - uhd::usrp::fx2_ctrl::sptr _fx2_ctrl; -}; - -/*********************************************************************** - * USRP-E100 interface implementation - **********************************************************************/ -class b100_iface_impl : public b100_iface{ -public: - /******************************************************************* - * Structors - ******************************************************************/ - b100_iface_impl(uhd::usrp::fx2_ctrl::sptr fx2_ctrl, - b100_ctrl::sptr fpga_ctrl) : - _fx2_i2c_iface(fx2_ctrl), - _fx2_ctrl(fx2_ctrl), - _fpga_ctrl(fpga_ctrl) - { - this->check_fw_compat(); - if (fpga_ctrl.get() != NULL){ - enable_gpif(1); - i2c_init(); - this->check_fpga_compat(); - } - mb_eeprom = mboard_eeprom_t(get_fx2_i2c_iface(), mboard_eeprom_t::MAP_B000); - } - - void check_fw_compat(void){ - unsigned char data[4]; //useless data buffer - const boost::uint16_t fw_compat_num = _fx2_ctrl->usrp_control_read( - VRQ_FW_COMPAT, 0, 0, data, sizeof(data) - ); - if (fw_compat_num != USRP_B_FW_COMPAT_NUM){ - throw uhd::runtime_error(str(boost::format( - "Expected firmware compatibility number 0x%x, but got 0x%x:\n" - "The firmware build is not compatible with the host code build." - ) % USRP_B_FW_COMPAT_NUM % fw_compat_num)); - } - } - - void check_fpga_compat(void){ - const boost::uint16_t fpga_compat_num = this->peek16(B100_REG_MISC_COMPAT); - if (fpga_compat_num != USRP_B_FPGA_COMPAT_NUM){ - throw uhd::runtime_error(str(boost::format( - "Expected FPGA compatibility number 0x%x, but got 0x%x:\n" - "The FPGA build is not compatible with the host code build." - ) % USRP_B_FPGA_COMPAT_NUM % fpga_compat_num)); - } - } - - ~b100_iface_impl(void) - { - /* NOP */ - } - - /******************************************************************* - * Peek and Poke - ******************************************************************/ - - void poke(boost::uint32_t addr, const ctrl_data_t &data) { - boost::mutex::scoped_lock lock(_ctrl_mutex); - _fpga_ctrl->write(addr, data); - } - - ctrl_data_t peek(boost::uint32_t addr, size_t len) { - boost::mutex::scoped_lock lock(_ctrl_mutex); - return _fpga_ctrl->read(addr, len); - } - - void poke16(boost::uint32_t addr, boost::uint16_t value) - { - ctrl_data_t words(1); - words[0] = value; - poke(addr, words); - } - - void poke32(boost::uint32_t addr, boost::uint32_t value) - { - //just a subset of poke() to maintain compatibility - ctrl_data_t words(2); - words[0] = value & 0x0000FFFF; - words[1] = value >> 16; - poke(addr, words); - } - - boost::uint32_t peek32(boost::uint32_t addr) - { - ctrl_data_t words = peek(addr, 2); - return boost::uint32_t((boost::uint32_t(words[1]) << 16) | words[0]); - } - - boost::uint16_t peek16(boost::uint32_t addr) - { - ctrl_data_t words = peek(addr, 1); - return boost::uint16_t(words[0]); - } - - /******************************************************************* - * I2C - ******************************************************************/ - static const boost::uint32_t i2c_datarate = 400000; - static const boost::uint32_t wishbone_clk = 64000000; //FIXME should go somewhere else - - void i2c_init(void) { - //init I2C FPGA interface. - poke16(B100_REG_I2C_CTRL, 0x0000); - //set prescalers to operate at 400kHz: WB_CLK is 64MHz... - boost::uint16_t prescaler = wishbone_clk / (i2c_datarate*5) - 1; - poke16(B100_REG_I2C_PRESCALER_LO, prescaler & 0xFF); - poke16(B100_REG_I2C_PRESCALER_HI, (prescaler >> 8) & 0xFF); - poke16(B100_REG_I2C_CTRL, I2C_CTRL_EN); //enable I2C core - } - - static const size_t max_i2c_data_bytes = 64; - - void i2c_wait_for_xfer(void) - { - while(this->peek16(B100_REG_I2C_CMD_STATUS) & I2C_ST_TIP) - boost::this_thread::sleep(boost::posix_time::milliseconds(10)); - } - - bool wait_chk_ack(void) { - i2c_wait_for_xfer(); - return (this->peek16(B100_REG_I2C_CMD_STATUS) & I2C_ST_RXACK) == 0; - } - - void write_i2c(boost::uint8_t addr, const byte_vector_t &bytes) - { - poke16(B100_REG_I2C_DATA, (addr << 1) | 0); //addr and read bit (0) - poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_WR | I2C_CMD_START | (bytes.size() == 0 ? I2C_CMD_STOP : 0)); - - //wait for previous transfer to complete - if(!wait_chk_ack()) { - poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_STOP); - return; - } - - for(size_t i = 0; i < bytes.size(); i++) { - poke16(B100_REG_I2C_DATA, bytes[i]); - poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_WR | ((i == (bytes.size() - 1)) ? I2C_CMD_STOP : 0)); - if(!wait_chk_ack()) { - poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_STOP); - return; - } - } - } - - byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes) - { - byte_vector_t bytes; - if(num_bytes == 0) return bytes; - - while (peek16(B100_REG_I2C_CMD_STATUS) & I2C_ST_BUSY); - - poke16(B100_REG_I2C_DATA, (addr << 1) | 1); //addr and read bit (1) - poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_WR | I2C_CMD_START); - //wait for previous transfer to complete - if(!wait_chk_ack()) { - poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_STOP); - } - - for(; num_bytes > 0; num_bytes--) { - poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_RD | ((num_bytes == 1) ? (I2C_CMD_STOP | I2C_CMD_NACK) : 0)); - i2c_wait_for_xfer(); - boost::uint8_t readback = peek16(B100_REG_I2C_DATA) & 0xFF; - bytes.push_back(readback); - } - return bytes; - } - - i2c_iface &get_fx2_i2c_iface(void){ - return _fx2_i2c_iface; - } - - /******************************************************************* - * SPI interface - * Eventually this will be replaced with a control-channel system - * to let the firmware do the actual write/readback cycles. - * This keeps the bandwidth on the control channel down. - ******************************************************************/ - - void spi_wait(void) { - while(peek32(B100_REG_SPI_CTRL) & SPI_CTRL_GO_BSY); - } - - boost::uint32_t transact_spi(int which_slave, - const spi_config_t &config, - boost::uint32_t bits, - size_t num_bits, - bool readback) - { - UHD_ASSERT_THROW((num_bits <= 32) && !(num_bits % 8)); - - int edge_flags = ((config.miso_edge==spi_config_t::EDGE_FALL) ? SPI_CTRL_RXNEG : 0) | - ((config.mosi_edge==spi_config_t::EDGE_FALL) ? 0 : SPI_CTRL_TXNEG) - ; - - boost::uint16_t ctrl = SPI_CTRL_ASS | (SPI_CTRL_CHAR_LEN_MASK & num_bits) | edge_flags; - - poke16(B100_REG_SPI_DIV, 0x0001); // = fpga_clk / 4 - poke32(B100_REG_SPI_SS, which_slave & 0xFFFF); - poke32(B100_REG_SPI_TXRX0, bits); - poke16(B100_REG_SPI_CTRL, ctrl); - - poke16(B100_REG_SPI_CTRL, ctrl | SPI_CTRL_GO_BSY); - if(readback) { - spi_wait(); - return peek32(B100_REG_SPI_TXRX0); - } - else { - return 0; - } - } - - void reset_gpif(boost::uint16_t ep) { - _fx2_ctrl->usrp_control_write(VRQ_RESET_GPIF, ep, ep, 0, 0); - } - - void enable_gpif(bool en) { - _fx2_ctrl->usrp_control_write(VRQ_ENABLE_GPIF, en ? 1 : 0, 0, 0, 0); - } - - void clear_fpga_fifo(void) { - _fx2_ctrl->usrp_control_write(VRQ_CLEAR_FPGA_FIFO, 0, 0, 0, 0); - } - - void write_uart(boost::uint8_t, const std::string &) { - throw uhd::not_implemented_error("Unhandled command write_uart()"); - } - - std::string read_uart(boost::uint8_t) { - throw uhd::not_implemented_error("Unhandled command read_uart()"); - } - -private: - b100_i2c_fx2_iface _fx2_i2c_iface; - uhd::usrp::fx2_ctrl::sptr _fx2_ctrl; - b100_ctrl::sptr _fpga_ctrl; - boost::mutex _ctrl_mutex; -}; - -/*********************************************************************** - * Public Make Function - **********************************************************************/ -b100_iface::sptr b100_iface::make(uhd::usrp::fx2_ctrl::sptr fx2_ctrl, - b100_ctrl::sptr fpga_ctrl) -{ - return b100_iface::sptr(new b100_iface_impl(fx2_ctrl, fpga_ctrl)); -} diff --git a/host/lib/usrp/usrp_b100/b100_iface.hpp b/host/lib/usrp/usrp_b100/b100_iface.hpp deleted file mode 100644 index 57ed6a45c..000000000 --- a/host/lib/usrp/usrp_b100/b100_iface.hpp +++ /dev/null @@ -1,73 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -#ifndef INCLUDED_B100_IFACE_HPP -#define INCLUDED_B100_IFACE_HPP - -#include -#include -#include -#include -#include -#include "../fx2/fx2_ctrl.hpp" -#include "b100_ctrl.hpp" - -/*! - * The usrp1 interface class: - * Provides a set of functions to implementation layer. - * Including spi, peek, poke, control... - */ -class b100_iface : boost::noncopyable, public uhd::usrp::mboard_iface{ -public: - typedef boost::shared_ptr sptr; - - /*! - * Make a new b100 interface with the control transport. - * \param fx2_ctrl the usrp control object - * \param fpga_ctrl the FPGA interface control object - * \return a new usrp1 interface object - */ - static sptr make(uhd::usrp::fx2_ctrl::sptr fx2_ctrl, - b100_ctrl::sptr fpga_ctrl = b100_ctrl::sptr() - ); - - /*! - * Reset the GPIF interface on the FX2 - * \param which endpoint to reset - * \return - */ - virtual void reset_gpif(boost::uint16_t ep) = 0; - - /*! - * Clear the GPIF FIFOs on the FPGA - * \return - */ - virtual void clear_fpga_fifo(void) = 0; - - /*! - * Enable/disable the GPIF interfaces on the FX2 - * \return - */ - virtual void enable_gpif(bool en) = 0; - - //! Get access to the FX2 I2C interface - virtual uhd::i2c_iface &get_fx2_i2c_iface(void) = 0; - - uhd::usrp::mboard_eeprom_t mb_eeprom; -}; - -#endif /* INCLUDED_USRP1_IFACE_HPP */ diff --git a/host/lib/usrp/usrp_b100/b100_impl.cpp b/host/lib/usrp/usrp_b100/b100_impl.cpp deleted file mode 100644 index 772da8f42..000000000 --- a/host/lib/usrp/usrp_b100/b100_impl.cpp +++ /dev/null @@ -1,277 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -#include "b100_impl.hpp" -#include "b100_ctrl.hpp" -#include "fpga_regs_standard.h" -#include "usrp_spi_defs.h" -#include -#include "ctrl_packet.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "b100_regs.hpp" - -using namespace uhd; -using namespace uhd::usrp; -using namespace uhd::transport; - -const boost::uint16_t B100_VENDOR_ID = 0x2500; -const boost::uint16_t B100_PRODUCT_ID = 0x0001; -const boost::uint16_t FX2_VENDOR_ID = 0x04b4; -const boost::uint16_t FX2_PRODUCT_ID = 0x8613; - -/*********************************************************************** - * Discovery - **********************************************************************/ -static device_addrs_t b100_find(const device_addr_t &hint) -{ - device_addrs_t b100_addrs; - - //return an empty list of addresses when type is set to non-b100 - if (hint.has_key("type") and hint["type"] != "b100") return b100_addrs; - - //extract the firmware path for the b100 - std::string b100_fw_image; - try{ - b100_fw_image = find_image_path( - hint.has_key("fw")? hint["fw"] : "usrp_b100_fw.ihx" - ); - } - catch(...){ - UHD_MSG(warning) << boost::format( - "Could not locate B100 firmware.\n" - "Please install the images package.\n" - ); - return b100_addrs; - } - - boost::uint16_t vid = hint.has_key("uninit") ? FX2_VENDOR_ID : B100_VENDOR_ID; - boost::uint16_t pid = hint.has_key("uninit") ? FX2_PRODUCT_ID : B100_PRODUCT_ID; - - // Important note: - // The get device list calls are nested inside the for loop. - // This allows the usb guts to decontruct when not in use, - // so that re-enumeration after fw load can occur successfully. - // This requirement is a courtesy of libusb1.0 on windows. - - //find the usrps and load firmware - BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { - try { - fx2_ctrl::make(usb_control::make(handle))->usrp_load_firmware(b100_fw_image); - } catch (...) { - UHD_MSG(status) << "Interface claimed, ignoring device" << std::endl; - } - } - - //get descriptors again with serial number, but using the initialized VID/PID now since we have firmware - vid = B100_VENDOR_ID; - pid = B100_PRODUCT_ID; - - BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { - device_addr_t new_addr; - new_addr["type"] = "b100"; - - //Attempt to read the name from the EEPROM and perform filtering. - //This operation can throw due to compatibility mismatch. - try{ - usb_control::sptr control = usb_control::make(handle); - b100_iface::sptr iface = b100_iface::make(fx2_ctrl::make(control)); - new_addr["name"] = iface->mb_eeprom["name"]; - new_addr["serial"] = handle->get_serial(); - } - catch(const uhd::exception &){ - //set these values as empty string so the device may still be found - //and the filter's below can still operate on the discovered device - new_addr["name"] = ""; - new_addr["serial"] = ""; - } - - //this is a found b100 when the hint serial and name match or blank - if ( - (not hint.has_key("name") or hint["name"] == new_addr["name"]) and - (not hint.has_key("serial") or hint["serial"] == new_addr["serial"]) - ){ - b100_addrs.push_back(new_addr); - } - } - - return b100_addrs; -} - -/*********************************************************************** - * Make - **********************************************************************/ -static device::sptr b100_make(const device_addr_t &device_addr){ - - //extract the FPGA path for the B100 - std::string b100_fpga_image = find_image_path( - device_addr.has_key("fpga")? device_addr["fpga"] : "usrp_b100_fpga.bin" - ); - - //try to match the given device address with something on the USB bus - std::vector device_list = - usb_device_handle::get_device_list(B100_VENDOR_ID, B100_PRODUCT_ID); - - //locate the matching handle in the device list - usb_device_handle::sptr handle; - BOOST_FOREACH(usb_device_handle::sptr dev_handle, device_list) { - if (dev_handle->get_serial() == device_addr["serial"]){ - handle = dev_handle; - break; - } - } - UHD_ASSERT_THROW(handle.get() != NULL); //better be found - - //create control objects and a data transport - usb_control::sptr fx2_transport = usb_control::make(handle); - fx2_ctrl::sptr fx2_ctrl = fx2_ctrl::make(fx2_transport); - fx2_ctrl->usrp_load_fpga(b100_fpga_image); - - device_addr_t data_xport_args; - data_xport_args["recv_frame_size"] = device_addr.get("recv_frame_size", "16384"); - data_xport_args["num_recv_frames"] = device_addr.get("num_recv_frames", "16"); - data_xport_args["send_frame_size"] = device_addr.get("send_frame_size", "16384"); - data_xport_args["num_send_frames"] = device_addr.get("num_send_frames", "16"); - - usb_zero_copy::sptr data_transport = usb_zero_copy::make_wrapper( - usb_zero_copy::make( - handle, // identifier - 6, // IN endpoint - 2, // OUT endpoint - data_xport_args // param hints - ) - ); - - //create the control transport - device_addr_t ctrl_xport_args; - ctrl_xport_args["recv_frame_size"] = boost::lexical_cast(CTRL_PACKET_LENGTH); - ctrl_xport_args["num_recv_frames"] = "16"; - ctrl_xport_args["send_frame_size"] = boost::lexical_cast(CTRL_PACKET_LENGTH); - ctrl_xport_args["num_send_frames"] = "4"; - - usb_zero_copy::sptr ctrl_transport = usb_zero_copy::make( - handle, - 8, - 4, - ctrl_xport_args - ); - - const double master_clock_rate = device_addr.cast("master_clock_rate", 64e6); - - - //create the b100 implementation guts - return device::sptr(new b100_impl(data_transport, ctrl_transport, fx2_ctrl, master_clock_rate)); -} - -UHD_STATIC_BLOCK(register_b100_device){ - device::register_device(&b100_find, &b100_make); -} - -/*********************************************************************** - * Structors - **********************************************************************/ -b100_impl::b100_impl(uhd::transport::usb_zero_copy::sptr data_transport, - uhd::transport::usb_zero_copy::sptr ctrl_transport, - uhd::usrp::fx2_ctrl::sptr fx2_ctrl, - const double master_clock_rate) - : _data_transport(data_transport), _fx2_ctrl(fx2_ctrl) -{ - //this is the handler object for FPGA control packets - _fpga_ctrl = b100_ctrl::make(ctrl_transport); - - _iface = b100_iface::make(_fx2_ctrl, _fpga_ctrl); - - //create clock interface - _clock_ctrl = b100_clock_ctrl::make(_iface, master_clock_rate); - - //create codec interface - _codec_ctrl = b100_codec_ctrl::make(_iface); - - //initialize the codecs - codec_init(); - - //initialize the mboard - mboard_init(); - - //initialize the dboards - dboard_init(); - - //initialize the dsps - rx_ddc_init(); - - //initialize the dsps - tx_duc_init(); - - //init the subdev specs - this->mboard_set(MBOARD_PROP_RX_SUBDEV_SPEC, subdev_spec_t()); - this->mboard_set(MBOARD_PROP_TX_SUBDEV_SPEC, subdev_spec_t()); - - //initialize the send/recv buffs - io_init(); -} - -b100_impl::~b100_impl(void){ - /* NOP */ -} - -bool b100_impl::recv_async_msg(uhd::async_metadata_t &md, double timeout){ - return _fpga_ctrl->recv_async_msg(md, timeout); -} - -/*********************************************************************** - * Device Get - **********************************************************************/ -void b100_impl::get(const wax::obj &key_, wax::obj &val) -{ - named_prop_t key = named_prop_t::extract(key_); - - //handle the get request conditioned on the key - switch(key.as()){ - case DEVICE_PROP_NAME: - val = std::string("USRP-B100 device"); - return; - - case DEVICE_PROP_MBOARD: - UHD_ASSERT_THROW(key.name == ""); - val = _mboard_proxy->get_link(); - return; - - case DEVICE_PROP_MBOARD_NAMES: - val = prop_names_t(1, ""); //vector of size 1 with empty string - return; - - default: UHD_THROW_PROP_GET_ERROR(); - } -} - -/*********************************************************************** - * Device Set - **********************************************************************/ -void b100_impl::set(const wax::obj &, const wax::obj &) -{ - UHD_THROW_PROP_SET_ERROR(); -} diff --git a/host/lib/usrp/usrp_b100/b100_impl.hpp b/host/lib/usrp/usrp_b100/b100_impl.hpp deleted file mode 100644 index daec70bca..000000000 --- a/host/lib/usrp/usrp_b100/b100_impl.hpp +++ /dev/null @@ -1,204 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -#include "b100_iface.hpp" -#include "b100_ctrl.hpp" -#include "clock_ctrl.hpp" -#include "codec_ctrl.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef INCLUDED_B100_IMPL_HPP -#define INCLUDED_B100_IMPL_HPP - -/*! - * Make a b100 dboard interface. - * \param iface the b100 interface object - * \param clock the clock control interface - * \param codec the codec control interface - * \return a sptr to a new dboard interface - */ -uhd::usrp::dboard_iface::sptr make_b100_dboard_iface( - b100_iface::sptr iface, - b100_clock_ctrl::sptr clock, - b100_codec_ctrl::sptr codec -); - -/*! - * Simple wax obj proxy class: - * Provides a wax obj interface for a set and a get function. - * This allows us to create nested properties structures - * while maintaining flattened code within the implementation. - */ -class wax_obj_proxy : public wax::obj { -public: - typedef boost::function get_t; - typedef boost::function set_t; - typedef boost::shared_ptr sptr; - - static sptr make(const get_t &get, const set_t &set){ - return sptr(new wax_obj_proxy(get, set)); - } - -private: - get_t _get; set_t _set; - wax_obj_proxy(const get_t &get, const set_t &set): _get(get), _set(set) {}; - void get(const wax::obj &key, wax::obj &val) {return _get(key, val);} - void set(const wax::obj &key, const wax::obj &val) {return _set(key, val);} -}; - -/*! - * USRP1 implementation guts: - * The implementation details are encapsulated here. - * Handles properties on the mboard, dboard, dsps... - */ -class b100_impl : public uhd::device { -public: - //structors - b100_impl(uhd::transport::usb_zero_copy::sptr data_transport, - uhd::transport::usb_zero_copy::sptr ctrl_transport, - uhd::usrp::fx2_ctrl::sptr fx2_ctrl, - double master_clock_rate); - - ~b100_impl(void); - - //the io interface - size_t send(const send_buffs_type &, - size_t, - const uhd::tx_metadata_t &, - const uhd::io_type_t &, - send_mode_t, double); - - size_t recv(const recv_buffs_type &, - size_t, uhd::rx_metadata_t &, - const uhd::io_type_t &, - recv_mode_t, double); - - size_t get_max_send_samps_per_packet(void) const; - - size_t get_max_recv_samps_per_packet(void) const; - - bool recv_async_msg(uhd::async_metadata_t &, double); - -private: - //clock control - b100_clock_ctrl::sptr _clock_ctrl; - - //interface to ioctls and file descriptor - b100_iface::sptr _iface; - - //handle io stuff - uhd::transport::zero_copy_if::sptr _data_transport; - UHD_PIMPL_DECL(io_impl) _io_impl; - void update_transport_channel_mapping(void); - void io_init(void); - void issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd); - void handle_overrun(size_t); - - //otw types - uhd::otw_type_t _recv_otw_type; - uhd::otw_type_t _send_otw_type; - - //configuration shadows - uhd::clock_config_t _clock_config; - uhd::usrp::subdev_spec_t _rx_subdev_spec, _tx_subdev_spec; - - //ad9862 codec control interface - b100_codec_ctrl::sptr _codec_ctrl; - - //codec properties interfaces - void codec_init(void); - void rx_codec_get(const wax::obj &, wax::obj &); - void rx_codec_set(const wax::obj &, const wax::obj &); - void tx_codec_get(const wax::obj &, wax::obj &); - void tx_codec_set(const wax::obj &, const wax::obj &); - wax_obj_proxy::sptr _rx_codec_proxy, _tx_codec_proxy; - - //device functions and settings - void get(const wax::obj &, wax::obj &); - void set(const wax::obj &, const wax::obj &); - - //mboard functions and settings - void mboard_init(void); - void mboard_get(const wax::obj &, wax::obj &); - void mboard_set(const wax::obj &, const wax::obj &); - void update_clock_config(void); - wax_obj_proxy::sptr _mboard_proxy; - - /*! - * Make a usrp1 dboard interface. - * \param iface the usrp1 interface object - * \param clock the clock control interface - * \param codec the codec control interface - * \param dboard_slot the slot identifier - * \param rx_dboard_id the db id for the rx board (used for evil dbsrx purposes) - * \return a sptr to a new dboard interface - */ - static uhd::usrp::dboard_iface::sptr make_dboard_iface( - b100_iface::sptr iface, - b100_clock_ctrl::sptr clock, - b100_codec_ctrl::sptr codec, - const uhd::usrp::dboard_id_t &rx_dboard_id - ); - - //xx dboard functions and settings - void dboard_init(void); - uhd::usrp::dboard_manager::sptr _dboard_manager; - uhd::usrp::dboard_iface::sptr _dboard_iface; - - //rx dboard functions and settings - uhd::usrp::dboard_eeprom_t _rx_db_eeprom; - void rx_dboard_get(const wax::obj &, wax::obj &); - void rx_dboard_set(const wax::obj &, const wax::obj &); - wax_obj_proxy::sptr _rx_dboard_proxy; - - //tx dboard functions and settings - uhd::usrp::dboard_eeprom_t _tx_db_eeprom, _gdb_eeprom; - void tx_dboard_get(const wax::obj &, wax::obj &); - void tx_dboard_set(const wax::obj &, const wax::obj &); - wax_obj_proxy::sptr _tx_dboard_proxy; - - //rx ddc functions and settings - void rx_ddc_init(void); - void rx_ddc_get(const wax::obj &, wax::obj &); - void rx_ddc_set(const wax::obj &, const wax::obj &); - double _ddc_freq; size_t _ddc_decim; - wax_obj_proxy::sptr _rx_ddc_proxy; - - //tx duc functions and settings - void tx_duc_init(void); - void tx_duc_get(const wax::obj &, wax::obj &); - void tx_duc_set(const wax::obj &, const wax::obj &); - double _duc_freq; size_t _duc_interp; - wax_obj_proxy::sptr _tx_duc_proxy; - - //transports - b100_ctrl::sptr _fpga_ctrl; - uhd::usrp::fx2_ctrl::sptr _fx2_ctrl; - -}; - -#endif /* INCLUDED_b100_IMPL_HPP */ diff --git a/host/lib/usrp/usrp_b100/b100_regs.hpp b/host/lib/usrp/usrp_b100/b100_regs.hpp deleted file mode 100644 index 010df283e..000000000 --- a/host/lib/usrp/usrp_b100/b100_regs.hpp +++ /dev/null @@ -1,264 +0,0 @@ - - -//////////////////////////////////////////////////////////////// -// -// Memory map for wishbone bus -// -//////////////////////////////////////////////////////////////// - -// All addresses are byte addresses. All accesses are word (16-bit) accesses. -// This means that address bit 0 is usually 0. -// There are 11 bits of address for the control. - -#ifndef __B100_REGS_H -#define __B100_REGS_H - -///////////////////////////////////////////////////// -// Slave pointers - -#define B100_REG_SLAVE(n) ((n)<<7) -#define B100_REG_SR_ADDR(n) ((B100_REG_SETTINGS_BASE) + (4*(n))) - -///////////////////////////////////////////////////// -// Slave 0 -- Misc Regs - -#define B100_REG_MISC_BASE B100_REG_SLAVE(0) - -#define B100_REG_MISC_LED B100_REG_MISC_BASE + 0 -#define B100_REG_MISC_SW B100_REG_MISC_BASE + 2 -#define B100_REG_MISC_CGEN_CTRL B100_REG_MISC_BASE + 4 -#define B100_REG_MISC_CGEN_ST B100_REG_MISC_BASE + 6 -#define B100_REG_MISC_TEST B100_REG_MISC_BASE + 8 -#define B100_REG_MISC_RX_LEN B100_REG_MISC_BASE + 10 -#define B100_REG_MISC_TX_LEN B100_REG_MISC_BASE + 12 -#define B100_REG_MISC_XFER_RATE B100_REG_MISC_BASE + 14 -#define B100_REG_MISC_COMPAT B100_REG_MISC_BASE + 16 - -///////////////////////////////////////////////////// -// Slave 1 -- UART -// CLKDIV is 16 bits, others are only 8 - -#define B100_REG_UART_BASE B100_REG_SLAVE(1) - -#define B100_REG_UART_CLKDIV B100_REG_UART_BASE + 0 -#define B100_REG_UART_TXLEVEL B100_REG_UART_BASE + 2 -#define B100_REG_UART_RXLEVEL B100_REG_UART_BASE + 4 -#define B100_REG_UART_TXCHAR B100_REG_UART_BASE + 6 -#define B100_REG_UART_RXCHAR B100_REG_UART_BASE + 8 - -///////////////////////////////////////////////////// -// Slave 2 -- SPI Core -//these are 32-bit registers mapped onto the 16-bit Wishbone bus. -//Using peek32/poke32 should allow transparent use of these registers. -#define B100_REG_SPI_BASE B100_REG_SLAVE(2) -#define B100_REG_SPI_TXRX0 B100_REG_SPI_BASE + 0 -#define B100_REG_SPI_TXRX1 B100_REG_SPI_BASE + 4 -#define B100_REG_SPI_TXRX2 B100_REG_SPI_BASE + 8 -#define B100_REG_SPI_TXRX3 B100_REG_SPI_BASE + 12 -#define B100_REG_SPI_CTRL B100_REG_SPI_BASE + 16 -#define B100_REG_SPI_DIV B100_REG_SPI_BASE + 20 -#define B100_REG_SPI_SS B100_REG_SPI_BASE + 24 - -//spi slave constants -#define B100_SPI_SS_AD9862 (1 << 2) -#define B100_SPI_SS_TX_DB (1 << 1) -#define B100_SPI_SS_RX_DB (1 << 0) - -//spi ctrl register bit definitions -#define SPI_CTRL_ASS (1<<13) -#define SPI_CTRL_IE (1<<12) -#define SPI_CTRL_LSB (1<<11) -#define SPI_CTRL_TXNEG (1<<10) //mosi edge, push on falling edge when 1 -#define SPI_CTRL_RXNEG (1<< 9) //miso edge, latch on falling edge when 1 -#define SPI_CTRL_GO_BSY (1<< 8) -#define SPI_CTRL_CHAR_LEN_MASK 0x7F - -//////////////////////////////////////////////// -// Slave 3 -- I2C Core - -#define B100_REG_I2C_BASE B100_REG_SLAVE(3) -#define B100_REG_I2C_PRESCALER_LO B100_REG_I2C_BASE + 0 -#define B100_REG_I2C_PRESCALER_HI B100_REG_I2C_BASE + 2 -#define B100_REG_I2C_CTRL B100_REG_I2C_BASE + 4 -#define B100_REG_I2C_DATA B100_REG_I2C_BASE + 6 -#define B100_REG_I2C_CMD_STATUS B100_REG_I2C_BASE + 8 - -//and while we're here... - -// -// STA, STO, RD, WR, and IACK bits are cleared automatically -// - -#define I2C_CTRL_EN (1 << 7) // core enable -#define I2C_CTRL_IE (1 << 6) // interrupt enable - -#define I2C_CMD_START (1 << 7) // generate (repeated) start condition -#define I2C_CMD_STOP (1 << 6) // generate stop condition -#define I2C_CMD_RD (1 << 5) // read from slave -#define I2C_CMD_WR (1 << 4) // write to slave -#define I2C_CMD_NACK (1 << 3) // when a rcvr, send ACK (ACK=0) or NACK (ACK=1) -#define I2C_CMD_RSVD_2 (1 << 2) // reserved -#define I2C_CMD_RSVD_1 (1 << 1) // reserved -#define I2C_CMD_IACK (1 << 0) // set to clear pending interrupt - -#define I2C_ST_RXACK (1 << 7) // Received acknowledgement from slave (1 = NAK, 0 = ACK) -#define I2C_ST_BUSY (1 << 6) // 1 after START signal detected; 0 after STOP signal detected -#define I2C_ST_AL (1 << 5) // Arbitration lost. 1 when core lost arbitration -#define I2C_ST_RSVD_4 (1 << 4) // reserved -#define I2C_ST_RSVD_3 (1 << 3) // reserved -#define I2C_ST_RSVD_2 (1 << 2) // reserved -#define I2C_ST_TIP (1 << 1) // Transfer-in-progress -#define I2C_ST_IP (1 << 0) // Interrupt pending - -//////////////////////////////////////////////// -// Slave 4 -- GPIO - -#define B100_REG_GPIO_BASE B100_REG_SLAVE(4) - -#define B100_REG_GPIO_RX_IO B100_REG_GPIO_BASE + 0 -#define B100_REG_GPIO_TX_IO B100_REG_GPIO_BASE + 2 -#define B100_REG_GPIO_RX_DDR B100_REG_GPIO_BASE + 4 -#define B100_REG_GPIO_TX_DDR B100_REG_GPIO_BASE + 6 -#define B100_REG_GPIO_RX_SEL B100_REG_GPIO_BASE + 8 -#define B100_REG_GPIO_TX_SEL B100_REG_GPIO_BASE + 10 -#define B100_REG_GPIO_RX_DBG B100_REG_GPIO_BASE + 12 -#define B100_REG_GPIO_TX_DBG B100_REG_GPIO_BASE + 14 - -//possible bit values for sel when dbg is 0: -#define GPIO_SEL_SW 0 // if pin is an output, set by software in the io reg -#define GPIO_SEL_ATR 1 // if pin is an output, set by ATR logic - -//possible bit values for sel when dbg is 1: -#define GPIO_SEL_DEBUG_0 0 // if pin is an output, debug lines from FPGA fabric -#define GPIO_SEL_DEBUG_1 1 // if pin is an output, debug lines from FPGA fabric - -/////////////////////////////////////////////////// -// Slave 6 -- ATR Controller -// 16 regs - -#define B100_REG_ATR_BASE B100_REG_SLAVE(6) - -#define B100_REG_ATR_IDLE_RXSIDE B100_REG_ATR_BASE + 0 -#define B100_REG_ATR_IDLE_TXSIDE B100_REG_ATR_BASE + 2 -#define B100_REG_ATR_INTX_RXSIDE B100_REG_ATR_BASE + 4 -#define B100_REG_ATR_INTX_TXSIDE B100_REG_ATR_BASE + 6 -#define B100_REG_ATR_INRX_RXSIDE B100_REG_ATR_BASE + 8 -#define B100_REG_ATR_INRX_TXSIDE B100_REG_ATR_BASE + 10 -#define B100_REG_ATR_FULL_RXSIDE B100_REG_ATR_BASE + 12 -#define B100_REG_ATR_FULL_TXSIDE B100_REG_ATR_BASE + 14 - -/////////////////////////////////////////////////// -// Slave 7 -- Readback Mux 32 - -#define B100_REG_RB_MUX_32_BASE B100_REG_SLAVE(7) - -#define B100_REG_RB_TIME_NOW_SECS B100_REG_RB_MUX_32_BASE + 0 -#define B100_REG_RB_TIME_NOW_TICKS B100_REG_RB_MUX_32_BASE + 4 -#define B100_REG_RB_TIME_PPS_SECS B100_REG_RB_MUX_32_BASE + 8 -#define B100_REG_RB_TIME_PPS_TICKS B100_REG_RB_MUX_32_BASE + 12 -#define B100_REG_RB_MISC_TEST32 B100_REG_RB_MUX_32_BASE + 16 - -//////////////////////////////////////////////////// -// Slaves 8 & 9 -- Settings Bus -// -// Output-only, no readback, 64 registers total -// Each register must be written 32 bits at a time -// First the address xxx_xx00 and then xxx_xx10 - -#define B100_REG_SETTINGS_BASE_ADDR(n) (B100_REG_SLAVE(8) + (4*(n))) - -#define B100_REG_SR_MISC_TEST32 B100_REG_SETTINGS_BASE_ADDR(52) - -///////////////////////////////////////////////// -// DSP RX Regs -//////////////////////////////////////////////// -#define B100_REG_DSP_RX_ADDR(n) (B100_REG_SETTINGS_BASE_ADDR(16) + (4*(n))) -#define B100_REG_DSP_RX_FREQ B100_REG_DSP_RX_ADDR(0) -#define B100_REG_DSP_RX_SCALE_IQ B100_REG_DSP_RX_ADDR(1) // {scale_i,scale_q} -#define B100_REG_DSP_RX_DECIM_RATE B100_REG_DSP_RX_ADDR(2) // hb and decim rate -#define B100_REG_DSP_RX_DCOFFSET_I B100_REG_DSP_RX_ADDR(3) // Bit 31 high sets fixed offset mode, using lower 14 bits, // otherwise it is automatic -#define B100_REG_DSP_RX_DCOFFSET_Q B100_REG_DSP_RX_ADDR(4) // Bit 31 high sets fixed offset mode, using lower 14 bits -#define B100_REG_DSP_RX_MUX B100_REG_DSP_RX_ADDR(5) - -/////////////////////////////////////////////////// -// VITA RX CTRL regs -/////////////////////////////////////////////////// -// The following 3 are logically a single command register. -// They are clocked into the underlying fifo when time_ticks is written. -#define B100_REG_CTRL_RX_ADDR(n) (B100_REG_SETTINGS_BASE_ADDR(0) + (4*(n))) -#define B100_REG_CTRL_RX_STREAM_CMD B100_REG_CTRL_RX_ADDR(0) // {now, chain, num_samples(30) -#define B100_REG_CTRL_RX_TIME_SECS B100_REG_CTRL_RX_ADDR(1) -#define B100_REG_CTRL_RX_TIME_TICKS B100_REG_CTRL_RX_ADDR(2) -#define B100_REG_CTRL_RX_CLEAR_OVERRUN B100_REG_CTRL_RX_ADDR(3) // write anything to clear overrun -#define B100_REG_CTRL_RX_VRT_HEADER B100_REG_CTRL_RX_ADDR(4) // word 0 of packet. FPGA fills in packet counter -#define B100_REG_CTRL_RX_VRT_STREAM_ID B100_REG_CTRL_RX_ADDR(5) // word 1 of packet. -#define B100_REG_CTRL_RX_VRT_TRAILER B100_REG_CTRL_RX_ADDR(6) -#define B100_REG_CTRL_RX_NSAMPS_PER_PKT B100_REG_CTRL_RX_ADDR(7) -#define B100_REG_CTRL_RX_NCHANNELS B100_REG_CTRL_RX_ADDR(8) // 1 in basic case, up to 4 for vector sources - -///////////////////////////////////////////////// -// DSP TX Regs -//////////////////////////////////////////////// -#define B100_REG_DSP_TX_ADDR(n) (B100_REG_SETTINGS_BASE_ADDR(32) + (4*(n))) -#define B100_REG_DSP_TX_FREQ B100_REG_DSP_TX_ADDR(0) -#define B100_REG_DSP_TX_SCALE_IQ B100_REG_DSP_TX_ADDR(1) // {scale_i,scale_q} -#define B100_REG_DSP_TX_INTERP_RATE B100_REG_DSP_TX_ADDR(2) -#define B100_REG_DSP_TX_UNUSED B100_REG_DSP_TX_ADDR(3) -#define B100_REG_DSP_TX_MUX B100_REG_DSP_TX_ADDR(4) - -///////////////////////////////////////////////// -// VITA TX CTRL regs -//////////////////////////////////////////////// -#define B100_REG_CTRL_TX_ADDR(n) (B100_REG_SETTINGS_BASE_ADDR(24) + (4*(n))) -#define B100_REG_CTRL_TX_NCHANNELS B100_REG_CTRL_TX_ADDR(0) -#define B100_REG_CTRL_TX_CLEAR_UNDERRUN B100_REG_CTRL_TX_ADDR(1) -#define B100_REG_CTRL_TX_REPORT_SID B100_REG_CTRL_TX_ADDR(2) -#define B100_REG_CTRL_TX_POLICY B100_REG_CTRL_TX_ADDR(3) - -#define B100_FLAG_CTRL_TX_POLICY_WAIT (0x1 << 0) -#define B100_FLAG_CTRL_TX_POLICY_NEXT_PACKET (0x1 << 1) -#define B100_FLAG_CTRL_TX_POLICY_NEXT_BURST (0x1 << 2) - -///////////////////////////////////////////////// -// VITA49 64 bit time (write only) -//////////////////////////////////////////////// - /*! - * \brief Time 64 flags - * - *
-   *
-   *    3                   2                   1
-   *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
-   * +-----------------------------------------------------------+-+-+
-   * |                                                           |S|P|
-   * +-----------------------------------------------------------+-+-+
-   *
-   * P - PPS edge selection (0=negedge, 1=posedge, default=0)
-   * S - Source (0=sma, 1=mimo, 0=default)
-   *
-   * 
- */ -#define B100_REG_TIME64_ADDR(n) (B100_REG_SETTINGS_BASE_ADDR(40) + (4*(n))) -#define B100_REG_TIME64_SECS B100_REG_TIME64_ADDR(0) // value to set absolute secs to on next PPS -#define B100_REG_TIME64_TICKS B100_REG_TIME64_ADDR(1) // value to set absolute ticks to on next PPS -#define B100_REG_TIME64_FLAGS B100_REG_TIME64_ADDR(2) // flags - see chart above -#define B100_REG_TIME64_IMM B100_REG_TIME64_ADDR(3) // set immediate (0=latch on next pps, 1=latch immediate, default=0) -#define B100_REG_TIME64_TPS B100_REG_TIME64_ADDR(4) // clock ticks per second (counter rollover) - -//pps flags (see above) -#define B100_FLAG_TIME64_PPS_NEGEDGE (0 << 0) -#define B100_FLAG_TIME64_PPS_POSEDGE (1 << 0) -#define B100_FLAG_TIME64_PPS_SMA (0 << 1) -#define B100_FLAG_TIME64_PPS_MIMO (1 << 1) - -#define B100_FLAG_TIME64_LATCH_NOW 1 -#define B100_FLAG_TIME64_LATCH_NEXT_PPS 0 - -#define B100_REG_CLEAR_RX_FIFO B100_REG_SETTINGS_BASE_ADDR(48) -#define B100_REG_CLEAR_TX_FIFO B100_REG_SETTINGS_BASE_ADDR(49) - -#define B100_REG_GLOBAL_RESET B100_REG_SETTINGS_BASE_ADDR(50) -#define B100_REG_TEST32 B100_REG_SETTINGS_BASE_ADDR(52) - -#endif - diff --git a/host/lib/usrp/usrp_b100/clock_ctrl.cpp b/host/lib/usrp/usrp_b100/clock_ctrl.cpp deleted file mode 100644 index e138242d1..000000000 --- a/host/lib/usrp/usrp_b100/clock_ctrl.cpp +++ /dev/null @@ -1,525 +0,0 @@ -// -// Copyright 2010-2011 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -#include "clock_ctrl.hpp" -#include "ad9522_regs.hpp" -#include -#include -#include -#include -#include -#include -#include "b100_regs.hpp" //spi slave constants -#include -#include -#include -#include -#include //gcd -#include -#include - -using namespace uhd; - -/*********************************************************************** - * Constants - **********************************************************************/ -static const bool ENABLE_THE_TEST_OUT = true; -static const double REFERENCE_INPUT_RATE = 10e6; - -/*********************************************************************** - * Helpers - **********************************************************************/ -template static void set_clock_divider( - size_t divider, div_type &low, div_type &high, bypass_type &bypass -){ - high = divider/2 - 1; - low = divider - high - 2; - bypass = (divider == 1)? 1 : 0; -} - -/*********************************************************************** - * Clock rate calculation stuff: - * Using the internal VCO between 1400 and 1800 MHz - **********************************************************************/ -struct clock_settings_type{ - size_t ref_clock_doubler, r_counter, a_counter, b_counter, prescaler, vco_divider, chan_divider; - size_t get_n_counter(void) const{return prescaler * b_counter + a_counter;} - double get_ref_rate(void) const{return REFERENCE_INPUT_RATE * ref_clock_doubler;} - double get_vco_rate(void) const{return get_ref_rate()/r_counter * get_n_counter();} - double get_chan_rate(void) const{return get_vco_rate()/vco_divider;} - double get_out_rate(void) const{return get_chan_rate()/chan_divider;} - std::string to_pp_string(void) const{ - return str(boost::format( - " r_counter: %d\n" - " a_counter: %d\n" - " b_counter: %d\n" - " prescaler: %d\n" - " vco_divider: %d\n" - " chan_divider: %d\n" - " vco_rate: %fMHz\n" - " chan_rate: %fMHz\n" - " out_rate: %fMHz\n" - ) - % r_counter - % a_counter - % b_counter - % prescaler - % vco_divider - % chan_divider - % (get_vco_rate()/1e6) - % (get_chan_rate()/1e6) - % (get_out_rate()/1e6) - ); - } -}; - -//! gives the greatest divisor of num between 1 and max inclusive -template static inline T greatest_divisor(T num, T max){ - for (T i = max; i > 1; i--) if (num%i == 0) return i; return 1; -} - -//! gives the least divisor of num between min and num exclusive -template static inline T least_divisor(T num, T min){ - for (T i = min; i < num; i++) if (num%i == 0) return i; return 1; -} - -static clock_settings_type get_clock_settings(double rate){ - clock_settings_type cs; - cs.ref_clock_doubler = 2; //always doubling - cs.prescaler = 8; //set to 8 when input is under 2400 MHz - - //basic formulas used below: - //out_rate*X = ref_rate*Y - //X = i*ref_rate/gcd - //Y = i*out_rate/gcd - //X = chan_div * vco_div * R - //Y = P*B + A - - const boost::uint64_t out_rate = boost::uint64_t(rate); - const boost::uint64_t ref_rate = boost::uint64_t(cs.get_ref_rate()); - const size_t gcd = size_t(boost::math::gcd(ref_rate, out_rate)); - - for (size_t i = 1; i <= 100; i++){ - const size_t X = i*ref_rate/gcd; - const size_t Y = i*out_rate/gcd; - - //determine A and B (P is fixed) - cs.b_counter = Y/cs.prescaler; - cs.a_counter = Y - cs.b_counter*cs.prescaler; - - static const double vco_bound_pad = 100e6; - for ( //calculate an r divider that fits into the bounds of the vco - cs.r_counter = size_t(cs.get_n_counter()*cs.get_ref_rate()/(1800e6 - vco_bound_pad)); - cs.r_counter <= size_t(cs.get_n_counter()*cs.get_ref_rate()/(1400e6 + vco_bound_pad)) - and cs.r_counter > 0; cs.r_counter++ - ){ - - //determine chan_div and vco_div - //and fill in that order of preference - cs.chan_divider = greatest_divisor(X/cs.r_counter, 32); - cs.vco_divider = greatest_divisor(X/cs.chan_divider/cs.r_counter, 6); - - //avoid a vco divider of 1 (if possible) - if (cs.vco_divider == 1){ - cs.vco_divider = least_divisor(cs.chan_divider, 2); - cs.chan_divider /= cs.vco_divider; - } - - UHD_LOGV(always) - << "gcd " << gcd << std::endl - << "X " << X << std::endl - << "Y " << Y << std::endl - << cs.to_pp_string() << std::endl - ; - - //filter limits on the counters - if (cs.vco_divider == 1) continue; - if (cs.r_counter >= (1<<14)) continue; - if (cs.b_counter == 2) continue; - if (cs.b_counter == 1 and cs.a_counter != 0) continue; - if (cs.b_counter >= (1<<13)) continue; - if (cs.a_counter >= (1<<6)) continue; - - UHD_MSG(status) << "USRP-B100 clock control: " << i << std::endl << cs.to_pp_string() << std::endl; - return cs; - } - } - - throw uhd::runtime_error(str(boost::format( - "USRP-B100 clock control: could not calculate settings for clock rate %fMHz" - ) % (rate/1e6))); -} - -/*********************************************************************** - * Clock Control Implementation - **********************************************************************/ -class b100_clock_ctrl_impl : public b100_clock_ctrl{ -public: - b100_clock_ctrl_impl(b100_iface::sptr iface, double master_clock_rate){ - _iface = iface; - _chan_rate = 0.0; - _out_rate = 0.0; - - //init the clock gen registers - _ad9522_regs.sdo_active = ad9522_regs_t::SDO_ACTIVE_SDO_SDIO; - _ad9522_regs.enb_stat_eeprom_at_stat_pin = 0; //use status pin - _ad9522_regs.status_pin_control = 0x1; //n divider - _ad9522_regs.ld_pin_control = 0x00; //dld - _ad9522_regs.refmon_pin_control = 0x12; //show ref2 - _ad9522_regs.lock_detect_counter = ad9522_regs_t::LOCK_DETECT_COUNTER_16CYC; - - this->use_internal_ref(); - - this->set_fpga_clock_rate(master_clock_rate); //initialize to something - - this->enable_fpga_clock(true); - this->enable_test_clock(ENABLE_THE_TEST_OUT); - this->enable_rx_dboard_clock(false); - this->enable_tx_dboard_clock(false); - } - - ~b100_clock_ctrl_impl(void){ - UHD_SAFE_CALL( - this->enable_test_clock(ENABLE_THE_TEST_OUT); - this->enable_rx_dboard_clock(false); - this->enable_tx_dboard_clock(false); - //this->enable_fpga_clock(false); //FIXME - ) - } - - /*********************************************************************** - * Clock rate control: - * - set clock rate w/ internal VCO - * - set clock rate w/ external VCXO - **********************************************************************/ - void set_clock_settings_with_internal_vco(double rate){ - const clock_settings_type cs = get_clock_settings(rate); - - //set the rates to private variables so the implementation knows! - _chan_rate = cs.get_chan_rate(); - _out_rate = cs.get_out_rate(); - - _ad9522_regs.enable_clock_doubler = (cs.ref_clock_doubler == 2)? 1 : 0; - - _ad9522_regs.set_r_counter(cs.r_counter); - _ad9522_regs.a_counter = cs.a_counter; - _ad9522_regs.set_b_counter(cs.b_counter); - UHD_ASSERT_THROW(cs.prescaler == 8); //assumes this below: - _ad9522_regs.prescaler_p = ad9522_regs_t::PRESCALER_P_DIV8_9; - - _ad9522_regs.pll_power_down = ad9522_regs_t::PLL_POWER_DOWN_NORMAL; - _ad9522_regs.cp_current = ad9522_regs_t::CP_CURRENT_1_2MA; - - _ad9522_regs.bypass_vco_divider = 0; - switch(cs.vco_divider){ - case 1: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV1; break; - case 2: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV2; break; - case 3: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV3; break; - case 4: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV4; break; - case 5: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV5; break; - case 6: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV6; break; - } - _ad9522_regs.select_vco_or_clock = ad9522_regs_t::SELECT_VCO_OR_CLOCK_VCO; - - //setup fpga master clock - _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS; - set_clock_divider(cs.chan_divider, - _ad9522_regs.divider0_low_cycles, - _ad9522_regs.divider0_high_cycles, - _ad9522_regs.divider0_bypass - ); - - //setup codec clock - _ad9522_regs.out3_format = ad9522_regs_t::OUT3_FORMAT_LVDS; - set_clock_divider(cs.chan_divider, - _ad9522_regs.divider1_low_cycles, - _ad9522_regs.divider1_high_cycles, - _ad9522_regs.divider1_bypass - ); - - this->send_all_regs(); - calibrate_now(); - } - - void set_clock_settings_with_external_vcxo(double rate){ - //set the rates to private variables so the implementation knows! - _chan_rate = rate; - _out_rate = rate; - - _ad9522_regs.enable_clock_doubler = 1; //doubler always on - const double ref_rate = REFERENCE_INPUT_RATE*2; - - //bypass prescaler such that N = B - long gcd = boost::math::gcd(long(ref_rate), long(rate)); - _ad9522_regs.set_r_counter(int(ref_rate/gcd)); - _ad9522_regs.a_counter = 0; - _ad9522_regs.set_b_counter(int(rate/gcd)); - _ad9522_regs.prescaler_p = ad9522_regs_t::PRESCALER_P_DIV1; - - //setup external vcxo - _ad9522_regs.pll_power_down = ad9522_regs_t::PLL_POWER_DOWN_NORMAL; - _ad9522_regs.cp_current = ad9522_regs_t::CP_CURRENT_1_2MA; - _ad9522_regs.bypass_vco_divider = 1; - _ad9522_regs.select_vco_or_clock = ad9522_regs_t::SELECT_VCO_OR_CLOCK_EXTERNAL; - - //setup fpga master clock - _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS; - _ad9522_regs.divider0_bypass = 1; - - //setup codec clock - _ad9522_regs.out3_format = ad9522_regs_t::OUT3_FORMAT_LVDS; - _ad9522_regs.divider1_bypass = 1; - - this->send_all_regs(); - } - - void set_fpga_clock_rate(double rate){ - if (_out_rate == rate) return; - if (rate == 61.44e6) set_clock_settings_with_external_vcxo(rate); - else set_clock_settings_with_internal_vco(rate); - //clock rate changed! update dboard clocks and FPGA ticks per second - set_rx_dboard_clock_rate(rate); - set_tx_dboard_clock_rate(rate); - _iface->poke32(B100_REG_TIME64_TPS, boost::uint32_t(get_fpga_clock_rate())); - } - - double get_fpga_clock_rate(void){ - return this->_out_rate; - } - - /*********************************************************************** - * FPGA clock enable - **********************************************************************/ - void enable_fpga_clock(bool enb){ - _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS; - _ad9522_regs.out0_lvds_power_down = !enb; - this->send_reg(0x0F0); - this->latch_regs(); - } - - /*********************************************************************** - * Special test clock output - **********************************************************************/ - void enable_test_clock(bool enb){ - //setup test clock (same divider as codec clock) - _ad9522_regs.out4_format = ad9522_regs_t::OUT4_FORMAT_CMOS; - _ad9522_regs.out4_cmos_configuration = (enb)? - ad9522_regs_t::OUT4_CMOS_CONFIGURATION_A_ON : - ad9522_regs_t::OUT4_CMOS_CONFIGURATION_OFF; - this->send_reg(0x0F4); - this->latch_regs(); - } - - /*********************************************************************** - * RX Dboard Clock Control (output 9, divider 3) - **********************************************************************/ - void enable_rx_dboard_clock(bool enb){ - _ad9522_regs.out9_format = ad9522_regs_t::OUT9_FORMAT_CMOS; - _ad9522_regs.out9_cmos_configuration = (enb)? - ad9522_regs_t::OUT9_CMOS_CONFIGURATION_B_ON : - ad9522_regs_t::OUT9_CMOS_CONFIGURATION_OFF; - this->send_reg(0x0F9); - this->latch_regs(); - } - - std::vector get_rx_dboard_clock_rates(void){ - std::vector rates; - for(size_t div = 1; div <= 16+16; div++) - rates.push_back(this->_chan_rate/div); - return rates; - } - - void set_rx_dboard_clock_rate(double rate){ - assert_has(get_rx_dboard_clock_rates(), rate, "rx dboard clock rate"); - _rx_clock_rate = rate; - size_t divider = size_t(this->_chan_rate/rate); - //set the divider registers - set_clock_divider(divider, - _ad9522_regs.divider3_low_cycles, - _ad9522_regs.divider3_high_cycles, - _ad9522_regs.divider3_bypass - ); - this->send_reg(0x199); - this->send_reg(0x19a); - this->soft_sync(); - } - - double get_rx_clock_rate(void){ - return _rx_clock_rate; - } - - /*********************************************************************** - * TX Dboard Clock Control (output 6, divider 2) - **********************************************************************/ - void enable_tx_dboard_clock(bool enb){ - _ad9522_regs.out6_format = ad9522_regs_t::OUT6_FORMAT_CMOS; - _ad9522_regs.out6_cmos_configuration = (enb)? - ad9522_regs_t::OUT6_CMOS_CONFIGURATION_B_ON : - ad9522_regs_t::OUT6_CMOS_CONFIGURATION_OFF; - this->send_reg(0x0F6); - this->latch_regs(); - } - - std::vector get_tx_dboard_clock_rates(void){ - return get_rx_dboard_clock_rates(); //same master clock, same dividers... - } - - void set_tx_dboard_clock_rate(double rate){ - assert_has(get_tx_dboard_clock_rates(), rate, "tx dboard clock rate"); - _tx_clock_rate = rate; - size_t divider = size_t(this->_chan_rate/rate); - //set the divider registers - set_clock_divider(divider, - _ad9522_regs.divider2_low_cycles, - _ad9522_regs.divider2_high_cycles, - _ad9522_regs.divider2_bypass - ); - this->send_reg(0x196); - this->send_reg(0x197); - this->soft_sync(); - } - - double get_tx_clock_rate(void){ - return _tx_clock_rate; - } - - /*********************************************************************** - * Clock reference control - **********************************************************************/ - void use_internal_ref(void) { - _ad9522_regs.enable_ref2 = 1; - _ad9522_regs.enable_ref1 = 0; - _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF2; - _ad9522_regs.enb_auto_ref_switchover = ad9522_regs_t::ENB_AUTO_REF_SWITCHOVER_MANUAL; - this->send_reg(0x01C); - this->latch_regs(); - } - - void use_external_ref(void) { - _ad9522_regs.enable_ref2 = 0; - _ad9522_regs.enable_ref1 = 1; - _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF1; - _ad9522_regs.enb_auto_ref_switchover = ad9522_regs_t::ENB_AUTO_REF_SWITCHOVER_MANUAL; - this->send_reg(0x01C); - this->latch_regs(); - } - - void use_auto_ref(void) { - _ad9522_regs.enable_ref2 = 1; - _ad9522_regs.enable_ref1 = 1; - _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF1; - _ad9522_regs.enb_auto_ref_switchover = ad9522_regs_t::ENB_AUTO_REF_SWITCHOVER_AUTO; - this->send_reg(0x01C); - this->latch_regs(); - } - -private: - b100_iface::sptr _iface; - ad9522_regs_t _ad9522_regs; - double _out_rate; //rate at the fpga and codec - double _chan_rate; //rate before final dividers - double _rx_clock_rate, _tx_clock_rate; - - void latch_regs(void){ - _ad9522_regs.io_update = 1; - this->send_reg(0x232); - } - - void send_reg(boost::uint16_t addr){ - boost::uint32_t reg = _ad9522_regs.get_write_reg(addr); - UHD_LOGV(often) << "clock control write reg: " << std::hex << reg << std::endl; - byte_vector_t buf; - buf.push_back(boost::uint8_t(reg >> 16)); - buf.push_back(boost::uint8_t(reg >> 8)); - buf.push_back(boost::uint8_t(reg & 0xff)); - - _iface->get_fx2_i2c_iface().write_i2c(0x5C, buf); - } - - boost::uint8_t read_reg(boost::uint16_t addr){ - byte_vector_t buf; - buf.push_back(boost::uint8_t(addr >> 8)); - buf.push_back(boost::uint8_t(addr & 0xff)); - _iface->get_fx2_i2c_iface().write_i2c(0x5C, buf); - - buf = _iface->get_fx2_i2c_iface().read_i2c(0x5C, 1); - - return boost::uint32_t(buf[0] & 0xFF); - } - - void calibrate_now(void){ - //vco calibration routine: - _ad9522_regs.vco_calibration_now = 0; - this->send_reg(0x18); - this->latch_regs(); - _ad9522_regs.vco_calibration_now = 1; - this->send_reg(0x18); - this->latch_regs(); - //wait for calibration done: - static const boost::uint8_t addr = 0x01F; - for (size_t ms10 = 0; ms10 < 100; ms10++){ - boost::this_thread::sleep(boost::posix_time::milliseconds(10)); - boost::uint32_t reg = read_reg(addr); - _ad9522_regs.set_reg(addr, reg); - if (_ad9522_regs.vco_calibration_finished) goto wait_for_ld; - } - UHD_MSG(error) << "USRP-B100 clock control: VCO calibration timeout" << std::endl; - wait_for_ld: - //wait for digital lock detect: - for (size_t ms10 = 0; ms10 < 100; ms10++){ - boost::this_thread::sleep(boost::posix_time::milliseconds(10)); - boost::uint32_t reg = read_reg(addr); - _ad9522_regs.set_reg(addr, reg); - if (_ad9522_regs.digital_lock_detect) return; - } - UHD_MSG(error) << "USRP-B100 clock control: lock detection timeout" << std::endl; - } - - void soft_sync(void){ - _ad9522_regs.soft_sync = 1; - this->send_reg(0x230); - this->latch_regs(); - _ad9522_regs.soft_sync = 0; - this->send_reg(0x230); - this->latch_regs(); - } - - void send_all_regs(void){ - //setup a list of register ranges to write - typedef std::pair range_t; - static const std::vector ranges = boost::assign::list_of - (range_t(0x000, 0x000)) (range_t(0x010, 0x01F)) - (range_t(0x0F0, 0x0FD)) (range_t(0x190, 0x19B)) - (range_t(0x1E0, 0x1E1)) (range_t(0x230, 0x230)) - ; - - //write initial register values and latch/update - BOOST_FOREACH(const range_t &range, ranges){ - for(boost::uint16_t addr = range.first; addr <= range.second; addr++){ - this->send_reg(addr); - } - } - this->latch_regs(); - } -}; - -/*********************************************************************** - * Clock Control Make - **********************************************************************/ -b100_clock_ctrl::sptr b100_clock_ctrl::make(b100_iface::sptr iface, double master_clock_rate){ - return sptr(new b100_clock_ctrl_impl(iface, master_clock_rate)); -} diff --git a/host/lib/usrp/usrp_b100/clock_ctrl.hpp b/host/lib/usrp/usrp_b100/clock_ctrl.hpp deleted file mode 100644 index 2a2e74024..000000000 --- a/host/lib/usrp/usrp_b100/clock_ctrl.hpp +++ /dev/null @@ -1,118 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -#ifndef INCLUDED_B100_CLOCK_CTRL_HPP -#define INCLUDED_B100_CLOCK_CTRL_HPP - -#include "b100_iface.hpp" -#include -#include -#include - -/*! - * The usrp-e clock control: - * - Setup system clocks. - * - Disable/enable clock lines. - */ -class b100_clock_ctrl : boost::noncopyable{ -public: - typedef boost::shared_ptr sptr; - - /*! - * Make a new clock control object. - * \param iface the b100 iface object - * \param master_clock_rate the master FPGA/sample clock rate - * \return the clock control object - */ - static sptr make(b100_iface::sptr iface, double master_clock_rate); - - /*! - * Set the rate of the fpga clock line. - * Throws if rate is not valid. - * \param rate the new rate in Hz - */ - virtual void set_fpga_clock_rate(double rate) = 0; - - /*! - * Get the rate of the fpga clock line. - * \return the fpga clock rate in Hz - */ - virtual double get_fpga_clock_rate(void) = 0; - - /*! - * Get the possible rates of the rx dboard clock. - * \return a vector of clock rates in Hz - */ - virtual std::vector get_rx_dboard_clock_rates(void) = 0; - - /*! - * Get the possible rates of the tx dboard clock. - * \return a vector of clock rates in Hz - */ - virtual std::vector get_tx_dboard_clock_rates(void) = 0; - - /*! - * Set the rx dboard clock rate to a possible rate. - * \param rate the new clock rate in Hz - * \throw exception when rate cannot be achieved - */ - virtual void set_rx_dboard_clock_rate(double rate) = 0; - - /*! - * Set the tx dboard clock rate to a possible rate. - * \param rate the new clock rate in Hz - * \throw exception when rate cannot be achieved - */ - virtual void set_tx_dboard_clock_rate(double rate) = 0; - - /*! - * Enable/disable the FPGA clock. - * \param enb true to enable - */ - - virtual void enable_fpga_clock(bool enb) = 0; - - /*! - * Enable/disable the rx dboard clock. - * \param enb true to enable - */ - virtual void enable_rx_dboard_clock(bool enb) = 0; - - /*! - * Enable/disable the tx dboard clock. - * \param enb true to enable - */ - virtual void enable_tx_dboard_clock(bool enb) = 0; - - /*! - * Use the internal TCXO reference - */ - virtual void use_internal_ref(void) = 0; - - /*! - * Use the external SMA reference - */ - virtual void use_external_ref(void) = 0; - - /*! - * Use external if available, internal otherwise - */ - virtual void use_auto_ref(void) = 0; - -}; - -#endif /* INCLUDED_B100_CLOCK_CTRL_HPP */ diff --git a/host/lib/usrp/usrp_b100/codec_ctrl.cpp b/host/lib/usrp/usrp_b100/codec_ctrl.cpp deleted file mode 100644 index 4d118b68b..000000000 --- a/host/lib/usrp/usrp_b100/codec_ctrl.cpp +++ /dev/null @@ -1,283 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -#include "codec_ctrl.hpp" -#include "ad9862_regs.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include "b100_regs.hpp" //spi slave constants -#include - -using namespace uhd; - -const gain_range_t b100_codec_ctrl::tx_pga_gain_range(-20, 0, double(0.1)); -const gain_range_t b100_codec_ctrl::rx_pga_gain_range(0, 20, 1); - -/*********************************************************************** - * Codec Control Implementation - **********************************************************************/ -class b100_codec_ctrl_impl : public b100_codec_ctrl{ -public: - //structors - b100_codec_ctrl_impl(b100_iface::sptr iface); - ~b100_codec_ctrl_impl(void); - - //aux adc and dac control - double read_aux_adc(aux_adc_t which); - void write_aux_dac(aux_dac_t which, double volts); - - //pga gain control - void set_tx_pga_gain(double); - double get_tx_pga_gain(void); - void set_rx_pga_gain(double, char); - double get_rx_pga_gain(char); - -private: - b100_iface::sptr _iface; - ad9862_regs_t _ad9862_regs; - void send_reg(boost::uint8_t addr); - void recv_reg(boost::uint8_t addr); -}; - -/*********************************************************************** - * Codec Control Structors - **********************************************************************/ -b100_codec_ctrl_impl::b100_codec_ctrl_impl(b100_iface::sptr iface){ - _iface = iface; - - //soft reset - _ad9862_regs.soft_reset = 1; - this->send_reg(0); - - //initialize the codec register settings - _ad9862_regs.sdio_bidir = ad9862_regs_t::SDIO_BIDIR_SDIO_SDO; - _ad9862_regs.lsb_first = ad9862_regs_t::LSB_FIRST_MSB; - _ad9862_regs.soft_reset = 0; - - //setup rx side of codec - _ad9862_regs.byp_buffer_a = 1; - _ad9862_regs.byp_buffer_b = 1; - _ad9862_regs.buffer_a_pd = 1; - _ad9862_regs.buffer_b_pd = 1; - _ad9862_regs.mux_out = ad9862_regs_t::MUX_OUT_RX_MUX_MODE; //B100 uses interleaved RX->FPGA - _ad9862_regs.rx_pga_a = 0;//0x1f; //TODO bring under api control - _ad9862_regs.rx_pga_b = 0;//0x1f; //TODO bring under api control - _ad9862_regs.rx_twos_comp = 1; - _ad9862_regs.rx_hilbert = ad9862_regs_t::RX_HILBERT_DIS; - - //setup tx side of codec - _ad9862_regs.two_data_paths = ad9862_regs_t::TWO_DATA_PATHS_BOTH; - _ad9862_regs.interleaved = ad9862_regs_t::INTERLEAVED_INTERLEAVED; - _ad9862_regs.tx_retime = ad9862_regs_t::TX_RETIME_CLKOUT2; - _ad9862_regs.tx_pga_gain = 199; //TODO bring under api control - _ad9862_regs.tx_hilbert = ad9862_regs_t::TX_HILBERT_DIS; - _ad9862_regs.interp = ad9862_regs_t::INTERP_2; - _ad9862_regs.tx_twos_comp = 1; - _ad9862_regs.fine_mode = ad9862_regs_t::FINE_MODE_BYPASS; - _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_BYPASS; - _ad9862_regs.dac_a_coarse_gain = 0x3; - _ad9862_regs.dac_b_coarse_gain = 0x3; - _ad9862_regs.edges = ad9862_regs_t::EDGES_NORMAL; - - //setup the dll - _ad9862_regs.input_clk_ctrl = ad9862_regs_t::INPUT_CLK_CTRL_EXTERNAL; - _ad9862_regs.dll_mult = ad9862_regs_t::DLL_MULT_2; - _ad9862_regs.dll_mode = ad9862_regs_t::DLL_MODE_FAST; - - //write the register settings to the codec - for (uint8_t addr = 0; addr <= 25; addr++){ - this->send_reg(addr); - } - - //always start conversions for aux ADC - _ad9862_regs.start_a = 1; - _ad9862_regs.start_b = 1; - - //aux adc clock - _ad9862_regs.clk_4 = ad9862_regs_t::CLK_4_1_4; - this->send_reg(34); -} - -b100_codec_ctrl_impl::~b100_codec_ctrl_impl(void){ - UHD_SAFE_CALL( - //set aux dacs to zero - this->write_aux_dac(AUX_DAC_A, 0); - this->write_aux_dac(AUX_DAC_B, 0); - this->write_aux_dac(AUX_DAC_C, 0); - this->write_aux_dac(AUX_DAC_D, 0); - - //power down - _ad9862_regs.all_rx_pd = 1; - this->send_reg(1); - _ad9862_regs.tx_digital_pd = 1; - _ad9862_regs.tx_analog_pd = ad9862_regs_t::TX_ANALOG_PD_BOTH; - this->send_reg(8); - ) -} - -/*********************************************************************** - * Codec Control Gain Control Methods - **********************************************************************/ -static const int mtpgw = 255; //maximum tx pga gain word - -void b100_codec_ctrl_impl::set_tx_pga_gain(double gain){ - int gain_word = int(mtpgw*(gain - tx_pga_gain_range.start())/(tx_pga_gain_range.stop() - tx_pga_gain_range.start())); - _ad9862_regs.tx_pga_gain = uhd::clip(gain_word, 0, mtpgw); - this->send_reg(16); -} - -double b100_codec_ctrl_impl::get_tx_pga_gain(void){ - return (_ad9862_regs.tx_pga_gain*(tx_pga_gain_range.stop() - tx_pga_gain_range.start())/mtpgw) + tx_pga_gain_range.start(); -} - -static const int mrpgw = 0x14; //maximum rx pga gain word - -void b100_codec_ctrl_impl::set_rx_pga_gain(double gain, char which){ - int gain_word = int(mrpgw*(gain - rx_pga_gain_range.start())/(rx_pga_gain_range.stop() - rx_pga_gain_range.start())); - gain_word = uhd::clip(gain_word, 0, mrpgw); - switch(which){ - case 'A': - _ad9862_regs.rx_pga_a = gain_word; - this->send_reg(2); - return; - case 'B': - _ad9862_regs.rx_pga_b = gain_word; - this->send_reg(3); - return; - default: UHD_THROW_INVALID_CODE_PATH(); - } -} - -double b100_codec_ctrl_impl::get_rx_pga_gain(char which){ - int gain_word; - switch(which){ - case 'A': gain_word = _ad9862_regs.rx_pga_a; break; - case 'B': gain_word = _ad9862_regs.rx_pga_b; break; - default: UHD_THROW_INVALID_CODE_PATH(); - } - return (gain_word*(rx_pga_gain_range.stop() - rx_pga_gain_range.start())/mrpgw) + rx_pga_gain_range.start(); -} - -/*********************************************************************** - * Codec Control AUX ADC Methods - **********************************************************************/ -static double aux_adc_to_volts(boost::uint8_t high, boost::uint8_t low){ - return double((boost::uint16_t(high) << 2) | low)*3.3/0x3ff; -} - -double b100_codec_ctrl_impl::read_aux_adc(aux_adc_t which){ - switch(which){ - - case AUX_ADC_A1: - _ad9862_regs.select_a = ad9862_regs_t::SELECT_A_AUX_ADC1; - this->send_reg(34); //start conversion and select mux - this->recv_reg(28); //read the value (2 bytes, 2 reads) - this->recv_reg(29); - return aux_adc_to_volts(_ad9862_regs.aux_adc_a1_9_2, _ad9862_regs.aux_adc_a1_1_0); - case AUX_ADC_A2: - _ad9862_regs.select_a = ad9862_regs_t::SELECT_A_AUX_ADC2; - this->send_reg(34); //start conversion and select mux - this->recv_reg(26); //read the value (2 bytes, 2 reads) - this->recv_reg(27); - return aux_adc_to_volts(_ad9862_regs.aux_adc_a2_9_2, _ad9862_regs.aux_adc_a2_1_0); - - case AUX_ADC_B1: - _ad9862_regs.select_b = ad9862_regs_t::SELECT_B_AUX_ADC1; - this->send_reg(34); //start conversion and select mux - this->recv_reg(32); //read the value (2 bytes, 2 reads) - this->recv_reg(33); - return aux_adc_to_volts(_ad9862_regs.aux_adc_b1_9_2, _ad9862_regs.aux_adc_b1_1_0); - case AUX_ADC_B2: - _ad9862_regs.select_b = ad9862_regs_t::SELECT_B_AUX_ADC2; - this->send_reg(34); //start conversion and select mux - this->recv_reg(30); //read the value (2 bytes, 2 reads) - this->recv_reg(31); - return aux_adc_to_volts(_ad9862_regs.aux_adc_b2_9_2, _ad9862_regs.aux_adc_b2_1_0); - } - UHD_THROW_INVALID_CODE_PATH(); -} - -/*********************************************************************** - * Codec Control AUX DAC Methods - **********************************************************************/ -void b100_codec_ctrl_impl::write_aux_dac(aux_dac_t which, double volts){ - //special case for aux dac d (aka sigma delta word) - if (which == AUX_DAC_D){ - boost::uint16_t dac_word = uhd::clip(boost::math::iround(volts*0xfff/3.3), 0, 0xfff); - _ad9862_regs.sig_delt_11_4 = boost::uint8_t(dac_word >> 4); - _ad9862_regs.sig_delt_3_0 = boost::uint8_t(dac_word & 0xf); - this->send_reg(42); - this->send_reg(43); - return; - } - - //calculate the dac word for aux dac a, b, c - boost::uint8_t dac_word = uhd::clip(boost::math::iround(volts*0xff/3.3), 0, 0xff); - - //setup a lookup table for the aux dac params (reg ref, reg addr) - typedef boost::tuple dac_params_t; - uhd::dict aux_dac_to_params = boost::assign::map_list_of - (AUX_DAC_A, dac_params_t(&_ad9862_regs.aux_dac_a, 36)) - (AUX_DAC_B, dac_params_t(&_ad9862_regs.aux_dac_b, 37)) - (AUX_DAC_C, dac_params_t(&_ad9862_regs.aux_dac_c, 38)) - ; - - //set the aux dac register - UHD_ASSERT_THROW(aux_dac_to_params.has_key(which)); - boost::uint8_t *reg_ref, reg_addr; - boost::tie(reg_ref, reg_addr) = aux_dac_to_params[which]; - *reg_ref = dac_word; - this->send_reg(reg_addr); -} - -/*********************************************************************** - * Codec Control SPI Methods - **********************************************************************/ -void b100_codec_ctrl_impl::send_reg(boost::uint8_t addr){ - boost::uint32_t reg = _ad9862_regs.get_write_reg(addr); - UHD_LOGV(rarely) << "codec control write reg: " << std::hex << reg << std::endl; - _iface->transact_spi( - B100_SPI_SS_AD9862, - spi_config_t::EDGE_RISE, - reg, 16, false /*no rb*/ - ); -} - -void b100_codec_ctrl_impl::recv_reg(boost::uint8_t addr){ - boost::uint32_t reg = _ad9862_regs.get_read_reg(addr); - UHD_LOGV(rarely) << "codec control read reg: " << std::hex << reg << std::endl; - boost::uint32_t ret = _iface->transact_spi( - B100_SPI_SS_AD9862, - spi_config_t::EDGE_RISE, - reg, 16, true /*rb*/ - ); - UHD_LOGV(rarely) << "codec control read ret: " << std::hex << boost::uint16_t(ret & 0xFF) << std::endl; - _ad9862_regs.set_reg(addr, boost::uint8_t(ret&0xff)); -} - -/*********************************************************************** - * Codec Control Make - **********************************************************************/ -b100_codec_ctrl::sptr b100_codec_ctrl::make(b100_iface::sptr iface){ - return sptr(new b100_codec_ctrl_impl(iface)); -} diff --git a/host/lib/usrp/usrp_b100/codec_ctrl.hpp b/host/lib/usrp/usrp_b100/codec_ctrl.hpp deleted file mode 100644 index 1bd579190..000000000 --- a/host/lib/usrp/usrp_b100/codec_ctrl.hpp +++ /dev/null @@ -1,90 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -#ifndef INCLUDED_B100_CODEC_CTRL_HPP -#define INCLUDED_B100_CODEC_CTRL_HPP - -#include "b100_iface.hpp" -#include -#include -#include - -/*! - * The usrp-e codec control: - * - Init/power down codec. - * - Read aux adc, write aux dac. - */ -class b100_codec_ctrl : boost::noncopyable{ -public: - typedef boost::shared_ptr sptr; - - static const uhd::gain_range_t tx_pga_gain_range; - static const uhd::gain_range_t rx_pga_gain_range; - - /*! - * Make a new codec control object. - * \param iface the usrp_e iface object - * \return the codec control object - */ - static sptr make(b100_iface::sptr iface); - - //! aux adc identifier constants - enum aux_adc_t{ - AUX_ADC_A2 = 0xA2, - AUX_ADC_A1 = 0xA1, - AUX_ADC_B2 = 0xB2, - AUX_ADC_B1 = 0xB1 - }; - - /*! - * Read an auxiliary adc: - * The internals remember which aux adc was read last. - * Therefore, the aux adc switch is only changed as needed. - * \param which which of the 4 adcs - * \return a value in volts - */ - virtual double read_aux_adc(aux_adc_t which) = 0; - - //! aux dac identifier constants - enum aux_dac_t{ - AUX_DAC_A = 0xA, - AUX_DAC_B = 0xB, - AUX_DAC_C = 0xC, - AUX_DAC_D = 0xD //really the sigma delta output - }; - - /*! - * Write an auxiliary dac. - * \param which which of the 4 dacs - * \param volts the level in in volts - */ - virtual void write_aux_dac(aux_dac_t which, double volts) = 0; - - //! Set the TX PGA gain - virtual void set_tx_pga_gain(double gain) = 0; - - //! Get the TX PGA gain - virtual double get_tx_pga_gain(void) = 0; - - //! Set the RX PGA gain ('A' or 'B') - virtual void set_rx_pga_gain(double gain, char which) = 0; - - //! Get the RX PGA gain ('A' or 'B') - virtual double get_rx_pga_gain(char which) = 0; -}; - -#endif /* INCLUDED_B100_CODEC_CTRL_HPP */ diff --git a/host/lib/usrp/usrp_b100/codec_impl.cpp b/host/lib/usrp/usrp_b100/codec_impl.cpp deleted file mode 100644 index de3ca3a66..000000000 --- a/host/lib/usrp/usrp_b100/codec_impl.cpp +++ /dev/null @@ -1,149 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -#include "b100_impl.hpp" -#include -#include -#include - -using namespace uhd; -using namespace uhd::usrp; - -/*********************************************************************** - * Helper Methods - **********************************************************************/ -void b100_impl::codec_init(void){ - //make proxies - _rx_codec_proxy = wax_obj_proxy::make( - boost::bind(&b100_impl::rx_codec_get, this, _1, _2), - boost::bind(&b100_impl::rx_codec_set, this, _1, _2) - ); - _tx_codec_proxy = wax_obj_proxy::make( - boost::bind(&b100_impl::tx_codec_get, this, _1, _2), - boost::bind(&b100_impl::tx_codec_set, this, _1, _2) - ); -} - -/*********************************************************************** - * RX Codec Properties - **********************************************************************/ -static const std::string ad9862_pga_gain_name = "ad9862 pga"; - -void b100_impl::rx_codec_get(const wax::obj &key_, wax::obj &val){ - named_prop_t key = named_prop_t::extract(key_); - - //handle the get request conditioned on the key - switch(key.as()){ - case CODEC_PROP_NAME: - val = std::string("b100 adc - ad9522"); - return; - - case CODEC_PROP_OTHERS: - val = prop_names_t(); - return; - - case CODEC_PROP_GAIN_NAMES: - val = prop_names_t(1, ad9862_pga_gain_name); - return; - - case CODEC_PROP_GAIN_RANGE: - UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); - val = b100_codec_ctrl::rx_pga_gain_range; - return; - - case CODEC_PROP_GAIN_I: - UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); - val = _codec_ctrl->get_rx_pga_gain('A'); - return; - - case CODEC_PROP_GAIN_Q: - UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); - val = _codec_ctrl->get_rx_pga_gain('B'); - return; - - default: UHD_THROW_PROP_GET_ERROR(); - } -} - -void b100_impl::rx_codec_set(const wax::obj &key_, const wax::obj &val){ - named_prop_t key = named_prop_t::extract(key_); - - //handle the set request conditioned on the key - switch(key.as()){ - case CODEC_PROP_GAIN_I: - UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); - _codec_ctrl->set_rx_pga_gain(val.as(), 'A'); - return; - - case CODEC_PROP_GAIN_Q: - UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); - _codec_ctrl->set_rx_pga_gain(val.as(), 'B'); - return; - - default: UHD_THROW_PROP_SET_ERROR(); - } -} - -/*********************************************************************** - * TX Codec Properties - **********************************************************************/ -void b100_impl::tx_codec_get(const wax::obj &key_, wax::obj &val){ - named_prop_t key = named_prop_t::extract(key_); - - //handle the get request conditioned on the key - switch(key.as()){ - case CODEC_PROP_NAME: - val = std::string("b100 dac - ad9522"); - return; - - case CODEC_PROP_OTHERS: - val = prop_names_t(); - return; - - case CODEC_PROP_GAIN_NAMES: - val = prop_names_t(1, ad9862_pga_gain_name); - return; - - case CODEC_PROP_GAIN_RANGE: - UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); - val = b100_codec_ctrl::tx_pga_gain_range; - return; - - case CODEC_PROP_GAIN_I: //only one gain for I and Q - case CODEC_PROP_GAIN_Q: - UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); - val = _codec_ctrl->get_tx_pga_gain(); - return; - - default: UHD_THROW_PROP_GET_ERROR(); - } -} - -void b100_impl::tx_codec_set(const wax::obj &key_, const wax::obj &val){ - named_prop_t key = named_prop_t::extract(key_); - - //handle the set request conditioned on the key - switch(key.as()){ - case CODEC_PROP_GAIN_I: //only one gain for I and Q - case CODEC_PROP_GAIN_Q: - UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); - _codec_ctrl->set_tx_pga_gain(val.as()); - return; - - default: UHD_THROW_PROP_SET_ERROR(); - } -} diff --git a/host/lib/usrp/usrp_b100/ctrl_packet.hpp b/host/lib/usrp/usrp_b100/ctrl_packet.hpp deleted file mode 100644 index f504fc5aa..000000000 --- a/host/lib/usrp/usrp_b100/ctrl_packet.hpp +++ /dev/null @@ -1,75 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -#ifndef INCLUDED_CTRL_PACKET_HPP -#define INCLUDED_CTRL_PACKET_HPP - -#include -#include -#include - -typedef std::vector ctrl_data_t; - -/*! - * Control packet operation type - */ -enum ctrl_pkt_op_t { - CTRL_PKT_OP_WRITE = 1, - CTRL_PKT_OP_READ = 2, - CTRL_PKT_OP_READBACK = 3 -}; - -/*! - * Control packet transaction length - */ -const size_t CTRL_PACKET_LENGTH = 32; -const size_t CTRL_PACKET_HEADER_LENGTH = 8; -const size_t CTRL_PACKET_DATA_LENGTH = 24; //=length-header - -/*! - * Control packet header magic value - */ -const boost::uint8_t CTRL_PACKET_HEADER_MAGIC = 0xAA; - -/*! - * Callback triggers for readback operation - */ -//FIXME: these are not real numbers, callbacks aren't implemented yet -const boost::uint16_t CTRL_PACKET_CALLBACK_SPI = 0x0001; -const boost::uint16_t CTRL_PACKET_CALLBACK_I2C = 0x0002; -//and so on - -/*! - * Metadata structure to describe a control packet - */ -struct UHD_API ctrl_pkt_meta_t { - ctrl_pkt_op_t op; - boost::uint8_t callbacks; - boost::uint8_t seq; - boost::uint16_t len; - boost::uint32_t addr; -}; - -/*! - * Full control packet structure - */ -struct UHD_API ctrl_pkt_t { - ctrl_pkt_meta_t pkt_meta; - ctrl_data_t data; -}; - -#endif /* INCLUDED_CTRL_PACKET_HPP */ diff --git a/host/lib/usrp/usrp_b100/dboard_iface.cpp b/host/lib/usrp/usrp_b100/dboard_iface.cpp deleted file mode 100644 index ec3da6220..000000000 --- a/host/lib/usrp/usrp_b100/dboard_iface.cpp +++ /dev/null @@ -1,298 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -#include "b100_iface.hpp" -#include "b100_regs.hpp" -#include "clock_ctrl.hpp" -#include "codec_ctrl.hpp" -#include -#include -#include -#include - - -using namespace uhd; -using namespace uhd::usrp; -using namespace boost::assign; - -class b100_dboard_iface : public dboard_iface{ -public: - - b100_dboard_iface( - b100_iface::sptr iface, - b100_clock_ctrl::sptr clock, - b100_codec_ctrl::sptr codec - ){ - _iface = iface; - _clock = clock; - _codec = codec; - - //init the clock rate shadows - this->set_clock_rate(UNIT_RX, _clock->get_fpga_clock_rate()); - this->set_clock_rate(UNIT_TX, _clock->get_fpga_clock_rate()); - - _iface->poke16(B100_REG_GPIO_RX_DBG, 0); - _iface->poke16(B100_REG_GPIO_TX_DBG, 0); - } - - ~b100_dboard_iface(void){ - /* NOP */ - } - - special_props_t get_special_props(void){ - special_props_t props; - props.soft_clock_divider = false; - props.mangle_i2c_addrs = false; - return props; - } - - void write_aux_dac(unit_t, aux_dac_t, double); - double read_aux_adc(unit_t, aux_adc_t); - - void _set_pin_ctrl(unit_t, boost::uint16_t); - void _set_atr_reg(unit_t, atr_reg_t, boost::uint16_t); - void _set_gpio_ddr(unit_t, boost::uint16_t); - void _set_gpio_out(unit_t, boost::uint16_t); - void set_gpio_debug(unit_t, int); - boost::uint16_t read_gpio(unit_t); - - void write_i2c(boost::uint8_t, const byte_vector_t &); - byte_vector_t read_i2c(boost::uint8_t, size_t); - - void write_spi( - unit_t unit, - const spi_config_t &config, - boost::uint32_t data, - size_t num_bits - ); - - boost::uint32_t read_write_spi( - unit_t unit, - const spi_config_t &config, - boost::uint32_t data, - size_t num_bits - ); - - void set_clock_rate(unit_t, double); - std::vector get_clock_rates(unit_t); - double get_clock_rate(unit_t); - void set_clock_enabled(unit_t, bool); - double get_codec_rate(unit_t); - -private: - b100_iface::sptr _iface; - b100_clock_ctrl::sptr _clock; - b100_codec_ctrl::sptr _codec; - uhd::dict _clock_rates; -}; - -/*********************************************************************** - * Make Function - **********************************************************************/ -dboard_iface::sptr make_b100_dboard_iface( - b100_iface::sptr iface, - b100_clock_ctrl::sptr clock, - b100_codec_ctrl::sptr codec -){ - return dboard_iface::sptr(new b100_dboard_iface(iface, clock, codec)); -} - -/*********************************************************************** - * Clock Rates - **********************************************************************/ -void b100_dboard_iface::set_clock_rate(unit_t unit, double rate){ - _clock_rates[unit] = rate; - switch(unit){ - case UNIT_RX: return _clock->set_rx_dboard_clock_rate(rate); - case UNIT_TX: return _clock->set_tx_dboard_clock_rate(rate); - } -} - -std::vector b100_dboard_iface::get_clock_rates(unit_t unit){ - switch(unit){ - case UNIT_RX: return _clock->get_rx_dboard_clock_rates(); - case UNIT_TX: return _clock->get_tx_dboard_clock_rates(); - default: UHD_THROW_INVALID_CODE_PATH(); - } -} - -double b100_dboard_iface::get_clock_rate(unit_t unit){ - return _clock_rates[unit]; -} - -void b100_dboard_iface::set_clock_enabled(unit_t unit, bool enb){ - switch(unit){ - case UNIT_RX: return _clock->enable_rx_dboard_clock(enb); - case UNIT_TX: return _clock->enable_tx_dboard_clock(enb); - } -} - -double b100_dboard_iface::get_codec_rate(unit_t){ - return _clock->get_fpga_clock_rate(); -} - -/*********************************************************************** - * GPIO - **********************************************************************/ -void b100_dboard_iface::_set_pin_ctrl(unit_t unit, boost::uint16_t value){ - UHD_ASSERT_THROW(GPIO_SEL_ATR == 1); //make this assumption - switch(unit){ - case UNIT_RX: _iface->poke16(B100_REG_GPIO_RX_SEL, value); return; - case UNIT_TX: _iface->poke16(B100_REG_GPIO_TX_SEL, value); return; - } -} - -void b100_dboard_iface::_set_gpio_ddr(unit_t unit, boost::uint16_t value){ - switch(unit){ - case UNIT_RX: _iface->poke16(B100_REG_GPIO_RX_DDR, value); return; - case UNIT_TX: _iface->poke16(B100_REG_GPIO_TX_DDR, value); return; - } -} - -void b100_dboard_iface::_set_gpio_out(unit_t unit, boost::uint16_t value){ - switch(unit){ - case UNIT_RX: _iface->poke16(B100_REG_GPIO_RX_IO, value); return; - case UNIT_TX: _iface->poke16(B100_REG_GPIO_TX_IO, value); return; - } -} - -boost::uint16_t b100_dboard_iface::read_gpio(unit_t unit){ - switch(unit){ - case UNIT_RX: return _iface->peek16(B100_REG_GPIO_RX_IO); - case UNIT_TX: return _iface->peek16(B100_REG_GPIO_TX_IO); - default: UHD_THROW_INVALID_CODE_PATH(); - } -} - -void b100_dboard_iface::_set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value){ - //define mapping of unit to atr regs to register address - static const uhd::dict< - unit_t, uhd::dict - > unit_to_atr_to_addr = map_list_of - (UNIT_RX, map_list_of - (ATR_REG_IDLE, B100_REG_ATR_IDLE_RXSIDE) - (ATR_REG_TX_ONLY, B100_REG_ATR_INTX_RXSIDE) - (ATR_REG_RX_ONLY, B100_REG_ATR_INRX_RXSIDE) - (ATR_REG_FULL_DUPLEX, B100_REG_ATR_FULL_RXSIDE) - ) - (UNIT_TX, map_list_of - (ATR_REG_IDLE, B100_REG_ATR_IDLE_TXSIDE) - (ATR_REG_TX_ONLY, B100_REG_ATR_INTX_TXSIDE) - (ATR_REG_RX_ONLY, B100_REG_ATR_INRX_TXSIDE) - (ATR_REG_FULL_DUPLEX, B100_REG_ATR_FULL_TXSIDE) - ) - ; - _iface->poke16(unit_to_atr_to_addr[unit][atr], value); -} - -void b100_dboard_iface::set_gpio_debug(unit_t unit, int which){ - //set this unit to all outputs - this->set_gpio_ddr(unit, 0xffff); - - //calculate the debug selections - boost::uint32_t dbg_sels = 0x0; - int sel = (which == 0)? GPIO_SEL_DEBUG_0 : GPIO_SEL_DEBUG_1; - for(size_t i = 0; i < 16; i++) dbg_sels |= sel << i; - - //set the debug on and which debug selection - switch(unit){ - case UNIT_RX: - _iface->poke16(B100_REG_GPIO_RX_DBG, 0xffff); - _iface->poke16(B100_REG_GPIO_RX_SEL, dbg_sels); - return; - - case UNIT_TX: - _iface->poke16(B100_REG_GPIO_TX_DBG, 0xffff); - _iface->poke16(B100_REG_GPIO_TX_SEL, dbg_sels); - return; - } -} - -/*********************************************************************** - * SPI - **********************************************************************/ -/*! - * Static function to convert a unit type to a spi slave device number. - * \param unit the dboard interface unit type enum - * \return the slave device number - */ -static boost::uint32_t unit_to_otw_spi_dev(dboard_iface::unit_t unit){ - switch(unit){ - case dboard_iface::UNIT_TX: return B100_SPI_SS_TX_DB; - case dboard_iface::UNIT_RX: return B100_SPI_SS_RX_DB; - } - throw std::invalid_argument("unknown unit type"); -} - -void b100_dboard_iface::write_spi( - unit_t unit, - const spi_config_t &config, - boost::uint32_t data, - size_t num_bits -){ - _iface->transact_spi(unit_to_otw_spi_dev(unit), config, data, num_bits, false /*no rb*/); -} - -boost::uint32_t b100_dboard_iface::read_write_spi( - unit_t unit, - const spi_config_t &config, - boost::uint32_t data, - size_t num_bits -){ - return _iface->transact_spi(unit_to_otw_spi_dev(unit), config, data, num_bits, true /*rb*/); -} - -/*********************************************************************** - * I2C - **********************************************************************/ -void b100_dboard_iface::write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){ - return _iface->write_i2c(addr, bytes); -} - -byte_vector_t b100_dboard_iface::read_i2c(boost::uint8_t addr, size_t num_bytes){ - return _iface->read_i2c(addr, num_bytes); -} - -/*********************************************************************** - * Aux DAX/ADC - **********************************************************************/ -void b100_dboard_iface::write_aux_dac(dboard_iface::unit_t, aux_dac_t which, double value){ - //same aux dacs for each unit - static const uhd::dict which_to_aux_dac = map_list_of - (AUX_DAC_A, b100_codec_ctrl::AUX_DAC_A) - (AUX_DAC_B, b100_codec_ctrl::AUX_DAC_B) - (AUX_DAC_C, b100_codec_ctrl::AUX_DAC_C) - (AUX_DAC_D, b100_codec_ctrl::AUX_DAC_D) - ; - _codec->write_aux_dac(which_to_aux_dac[which], value); -} - -double b100_dboard_iface::read_aux_adc(dboard_iface::unit_t unit, aux_adc_t which){ - static const uhd::dict< - unit_t, uhd::dict - > unit_to_which_to_aux_adc = map_list_of - (UNIT_RX, map_list_of - (AUX_ADC_A, b100_codec_ctrl::AUX_ADC_A1) - (AUX_ADC_B, b100_codec_ctrl::AUX_ADC_B1) - ) - (UNIT_TX, map_list_of - (AUX_ADC_A, b100_codec_ctrl::AUX_ADC_A2) - (AUX_ADC_B, b100_codec_ctrl::AUX_ADC_B2) - ) - ; - return _codec->read_aux_adc(unit_to_which_to_aux_adc[unit][which]); -} diff --git a/host/lib/usrp/usrp_b100/dboard_impl.cpp b/host/lib/usrp/usrp_b100/dboard_impl.cpp deleted file mode 100644 index ba3776728..000000000 --- a/host/lib/usrp/usrp_b100/dboard_impl.cpp +++ /dev/null @@ -1,185 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -#include "b100_impl.hpp" -#include "b100_regs.hpp" -#include -#include -#include -#include -#include -#include "usrp_i2c_addr.h" - -using namespace uhd; -using namespace uhd::usrp; - -/*********************************************************************** - * Dboard Initialization - **********************************************************************/ -void b100_impl::dboard_init(void){ - //read the tx and rx dboard eeproms - _rx_db_eeprom.load(*_iface, I2C_ADDR_RX_A); - _tx_db_eeprom.load(*_iface, I2C_ADDR_TX_A); - _gdb_eeprom.load(*_iface, I2C_ADDR_TX_A ^ 5); - - //create a new dboard interface and manager - _dboard_iface = make_b100_dboard_iface( - _iface, _clock_ctrl, _codec_ctrl - ); - _dboard_manager = dboard_manager::make( - _rx_db_eeprom.id, - ((_gdb_eeprom.id == dboard_id_t::none())? _tx_db_eeprom : _gdb_eeprom).id, - _dboard_iface - ); - - //setup the dboard proxies - _rx_dboard_proxy = wax_obj_proxy::make( - boost::bind(&b100_impl::rx_dboard_get, this, _1, _2), - boost::bind(&b100_impl::rx_dboard_set, this, _1, _2) - ); - _tx_dboard_proxy = wax_obj_proxy::make( - boost::bind(&b100_impl::tx_dboard_get, this, _1, _2), - boost::bind(&b100_impl::tx_dboard_set, this, _1, _2) - ); -} - -/*********************************************************************** - * RX Dboard Get - **********************************************************************/ -void b100_impl::rx_dboard_get(const wax::obj &key_, wax::obj &val){ - named_prop_t key = named_prop_t::extract(key_); - - //handle the get request conditioned on the key - switch(key.as()){ - case DBOARD_PROP_NAME: - val = std::string("b100 dboard (rx unit)"); - return; - - case DBOARD_PROP_SUBDEV: - val = _dboard_manager->get_rx_subdev(key.name); - return; - - case DBOARD_PROP_SUBDEV_NAMES: - val = _dboard_manager->get_rx_subdev_names(); - return; - - case DBOARD_PROP_DBOARD_EEPROM: - val = _rx_db_eeprom; - return; - - case DBOARD_PROP_DBOARD_IFACE: - val = _dboard_iface; - return; - - case DBOARD_PROP_CODEC: - val = _rx_codec_proxy->get_link(); - return; - - case DBOARD_PROP_GAIN_GROUP: - val = make_gain_group( - _rx_db_eeprom.id, - _dboard_manager->get_rx_subdev(key.name), - _rx_codec_proxy->get_link(), - GAIN_GROUP_POLICY_RX - ); - return; - - default: UHD_THROW_PROP_GET_ERROR(); - } -} - -/*********************************************************************** - * RX Dboard Set - **********************************************************************/ -void b100_impl::rx_dboard_set(const wax::obj &key, const wax::obj &val){ - switch(key.as()){ - case DBOARD_PROP_DBOARD_EEPROM: - _rx_db_eeprom = val.as(); - _rx_db_eeprom.store(*_iface, I2C_ADDR_RX_A); - return; - - default: UHD_THROW_PROP_SET_ERROR(); - } -} - -/*********************************************************************** - * TX Dboard Get - **********************************************************************/ -void b100_impl::tx_dboard_get(const wax::obj &key_, wax::obj &val){ - named_prop_t key = named_prop_t::extract(key_); - - //handle the get request conditioned on the key - switch(key.as()){ - case DBOARD_PROP_NAME: - val = std::string("b100 dboard (tx unit)"); - return; - - case DBOARD_PROP_SUBDEV: - val = _dboard_manager->get_tx_subdev(key.name); - return; - - case DBOARD_PROP_SUBDEV_NAMES: - val = _dboard_manager->get_tx_subdev_names(); - return; - - case DBOARD_PROP_DBOARD_EEPROM: - val = _tx_db_eeprom; - return; - - case DBOARD_PROP_GBOARD_EEPROM: - val = _gdb_eeprom; - return; - - case DBOARD_PROP_DBOARD_IFACE: - val = _dboard_iface; - return; - - case DBOARD_PROP_CODEC: - val = _tx_codec_proxy->get_link(); - return; - - case DBOARD_PROP_GAIN_GROUP: - val = make_gain_group( - _tx_db_eeprom.id, - _dboard_manager->get_tx_subdev(key.name), - _tx_codec_proxy->get_link(), - GAIN_GROUP_POLICY_TX - ); - return; - - default: UHD_THROW_PROP_GET_ERROR(); - } -} - -/*********************************************************************** - * TX Dboard Set - **********************************************************************/ -void b100_impl::tx_dboard_set(const wax::obj &key, const wax::obj &val){ - switch(key.as()){ - case DBOARD_PROP_DBOARD_EEPROM: - _tx_db_eeprom = val.as(); - _tx_db_eeprom.store(*_iface, I2C_ADDR_TX_A); - return; - - case DBOARD_PROP_GBOARD_EEPROM: - _gdb_eeprom = val.as(); - _gdb_eeprom.store(*_iface, I2C_ADDR_TX_A ^ 5); - return; - - default: UHD_THROW_PROP_SET_ERROR(); - } -} diff --git a/host/lib/usrp/usrp_b100/dsp_impl.cpp b/host/lib/usrp/usrp_b100/dsp_impl.cpp deleted file mode 100644 index c1bf6bedd..000000000 --- a/host/lib/usrp/usrp_b100/dsp_impl.cpp +++ /dev/null @@ -1,189 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -#include "b100_impl.hpp" -#include "b100_regs.hpp" -#include -#include -#include -#include - -#define rint boost::math::iround - -using namespace uhd; -using namespace uhd::usrp; - -static const double MASTER_CLOCK_RATE = 64e6; //TODO get from clock control - -/*********************************************************************** - * RX DDC Initialization - **********************************************************************/ -void b100_impl::rx_ddc_init(void){ - _rx_ddc_proxy = wax_obj_proxy::make( - boost::bind(&b100_impl::rx_ddc_get, this, _1, _2), - boost::bind(&b100_impl::rx_ddc_set, this, _1, _2) - ); - - //initial config and update - rx_ddc_set(DSP_PROP_FREQ_SHIFT, double(0)); - rx_ddc_set(DSP_PROP_HOST_RATE, double(16e6)); -} - -/*********************************************************************** - * RX DDC Get - **********************************************************************/ -void b100_impl::rx_ddc_get(const wax::obj &key_, wax::obj &val){ - named_prop_t key = named_prop_t::extract(key_); - - switch(key.as()){ - case DSP_PROP_NAME: - val = std::string("USRP-B100 RX DSP"); - return; - - case DSP_PROP_OTHERS: - val = prop_names_t(); //empty - return; - - case DSP_PROP_FREQ_SHIFT: - val = _ddc_freq; - return; - - case DSP_PROP_CODEC_RATE: - val = _clock_ctrl->get_fpga_clock_rate(); - return; - - case DSP_PROP_HOST_RATE: - val = _clock_ctrl->get_fpga_clock_rate()/_ddc_decim; - return; - - default: UHD_THROW_PROP_GET_ERROR(); - } -} - -/*********************************************************************** - * RX DDC Set - **********************************************************************/ -void b100_impl::rx_ddc_set(const wax::obj &key_, const wax::obj &val){ - named_prop_t key = named_prop_t::extract(key_); - - switch(key.as()){ - case DSP_PROP_STREAM_CMD: - issue_stream_cmd(val.as()); - return; - - case DSP_PROP_FREQ_SHIFT:{ - double new_freq = val.as(); - _iface->poke32(B100_REG_DSP_RX_FREQ, - dsp_type1::calc_cordic_word_and_update(new_freq, _clock_ctrl->get_fpga_clock_rate()) - ); - _ddc_freq = new_freq; //shadow - } - return; - - case DSP_PROP_HOST_RATE:{ - //set the decimation - _ddc_decim = rint(_clock_ctrl->get_fpga_clock_rate()/val.as()); - _iface->poke32(B100_REG_DSP_RX_DECIM_RATE, dsp_type1::calc_cic_filter_word(_ddc_decim)); - - //set the scaling - static const boost::int16_t default_rx_scale_iq = 1024; - _iface->poke32(B100_REG_DSP_RX_SCALE_IQ, - dsp_type1::calc_iq_scale_word(default_rx_scale_iq, default_rx_scale_iq) - ); - } - return; - - default: UHD_THROW_PROP_SET_ERROR(); - } -} - -/*********************************************************************** - * TX DUC Initialization - **********************************************************************/ -void b100_impl::tx_duc_init(void){ - _tx_duc_proxy = wax_obj_proxy::make( - boost::bind(&b100_impl::tx_duc_get, this, _1, _2), - boost::bind(&b100_impl::tx_duc_set, this, _1, _2) - ); - - //initial config and update - tx_duc_set(DSP_PROP_FREQ_SHIFT, double(0)); - tx_duc_set(DSP_PROP_HOST_RATE, double(16e6)); -} - -/*********************************************************************** - * TX DUC Get - **********************************************************************/ -void b100_impl::tx_duc_get(const wax::obj &key_, wax::obj &val){ - named_prop_t key = named_prop_t::extract(key_); - - switch(key.as()){ - case DSP_PROP_NAME: - val = std::string("USRP-B100 TX DSP"); - return; - - case DSP_PROP_OTHERS: - val = prop_names_t(); //empty - return; - - case DSP_PROP_FREQ_SHIFT: - val = _duc_freq; - return; - - case DSP_PROP_CODEC_RATE: - val = _clock_ctrl->get_fpga_clock_rate(); - return; - - case DSP_PROP_HOST_RATE: - val = _clock_ctrl->get_fpga_clock_rate()/_duc_interp; - return; - - default: UHD_THROW_PROP_GET_ERROR(); - } -} - -/*********************************************************************** - * TX DUC Set - **********************************************************************/ -void b100_impl::tx_duc_set(const wax::obj &key_, const wax::obj &val){ - named_prop_t key = named_prop_t::extract(key_); - - switch(key.as()){ - - case DSP_PROP_FREQ_SHIFT:{ - double new_freq = val.as(); - _iface->poke32(B100_REG_DSP_TX_FREQ, - dsp_type1::calc_cordic_word_and_update(new_freq, _clock_ctrl->get_fpga_clock_rate()) - ); - _duc_freq = new_freq; //shadow - } - return; - - case DSP_PROP_HOST_RATE:{ - _duc_interp = rint(_clock_ctrl->get_fpga_clock_rate()/val.as()); - - //set the interpolation - _iface->poke32(B100_REG_DSP_TX_INTERP_RATE, dsp_type1::calc_cic_filter_word(_duc_interp)); - - //set the scaling - _iface->poke32(B100_REG_DSP_TX_SCALE_IQ, dsp_type1::calc_iq_scale_word(_duc_interp)); - } - return; - - default: UHD_THROW_PROP_SET_ERROR(); - } -} diff --git a/host/lib/usrp/usrp_b100/io_impl.cpp b/host/lib/usrp/usrp_b100/io_impl.cpp deleted file mode 100644 index 3978bea75..000000000 --- a/host/lib/usrp/usrp_b100/io_impl.cpp +++ /dev/null @@ -1,210 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -#include "../../transport/super_recv_packet_handler.hpp" -#include "../../transport/super_send_packet_handler.hpp" -#include "usrp_commands.h" -#include "b100_impl.hpp" -#include "b100_regs.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace uhd; -using namespace uhd::usrp; -using namespace uhd::transport; -namespace asio = boost::asio; - -/*********************************************************************** - * IO Implementation Details - **********************************************************************/ -struct b100_impl::io_impl{ - io_impl(zero_copy_if::sptr data_transport): - data_transport(data_transport) - { - /* NOP */ - } - - ~io_impl(void){ - //drain the rx buffs - //while(data_transport->get_recv_buff().get() != NULL){ - /* NOP */ - //} - } - - zero_copy_if::sptr &data_transport; - - sph::recv_packet_handler recv_handler; - sph::send_packet_handler send_handler; - bool continuous_streaming; -}; - -/*********************************************************************** - * Initialize internals within this file - **********************************************************************/ -void b100_impl::io_init(void){ - _recv_otw_type.width = 16; - _recv_otw_type.shift = 0; - _recv_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; - - _send_otw_type.width = 16; - _send_otw_type.shift = 0; - _send_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; - - _iface->reset_gpif(6); - - //reset state machines - _iface->poke32(B100_REG_CTRL_TX_CLEAR_UNDERRUN, 0); - _iface->poke32(B100_REG_CTRL_RX_CLEAR_OVERRUN, 0); - - _io_impl = UHD_PIMPL_MAKE(io_impl, (_data_transport)); - - //setup rx data path - _iface->poke32(B100_REG_CTRL_RX_NSAMPS_PER_PKT, get_max_recv_samps_per_packet()); - UHD_LOGV(always) << "IO: Using " << get_max_recv_samps_per_packet() << " samples per packet" << std::endl; - _iface->poke32(B100_REG_CTRL_RX_NCHANNELS, 1); - _iface->poke32(B100_REG_CTRL_RX_VRT_HEADER, 0 - | (0x1 << 28) //if data with stream id - | (0x1 << 26) //has trailer - | (0x3 << 22) //integer time other - | (0x1 << 20) //fractional time sample count - ); - _iface->poke32(B100_REG_CTRL_RX_VRT_TRAILER, 0); - - //set the streamid to reset the seq num - _iface->poke32(B100_REG_CTRL_TX_REPORT_SID, 0); - //setup the tx policy - _iface->poke32(B100_REG_CTRL_TX_POLICY, B100_FLAG_CTRL_TX_POLICY_NEXT_PACKET); - - //set the expected packet size in USB frames - _iface->poke32(B100_REG_MISC_RX_LEN, 4); - - update_transport_channel_mapping(); -} - -void b100_impl::update_transport_channel_mapping(void){ - if (_io_impl.get() == NULL) return; //not inited yet - - //set all of the relevant properties on the handler - boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); - _io_impl->recv_handler.resize(_rx_subdev_spec.size()); - _io_impl->recv_handler.set_vrt_unpacker(&vrt::if_hdr_unpack_le); - _io_impl->recv_handler.set_tick_rate(_clock_ctrl->get_fpga_clock_rate()); - _io_impl->recv_handler.set_samp_rate(_rx_ddc_proxy->get_link()[DSP_PROP_HOST_RATE].as()); - for (size_t chan = 0; chan < _io_impl->recv_handler.size(); chan++){ - _io_impl->recv_handler.set_xport_chan_get_buff(chan, boost::bind( - &uhd::transport::zero_copy_if::get_recv_buff, _io_impl->data_transport, _1 - )); - _io_impl->recv_handler.set_overflow_handler(chan, boost::bind( - &b100_impl::handle_overrun, this, chan - )); - } - _io_impl->recv_handler.set_converter(_recv_otw_type); - - //set all of the relevant properties on the handler - boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); - _io_impl->send_handler.resize(_tx_subdev_spec.size()); - _io_impl->send_handler.set_vrt_packer(&vrt::if_hdr_pack_le); - _io_impl->send_handler.set_tick_rate(_clock_ctrl->get_fpga_clock_rate()); - _io_impl->send_handler.set_samp_rate(_tx_duc_proxy->get_link()[DSP_PROP_HOST_RATE].as()); - for (size_t chan = 0; chan < _io_impl->send_handler.size(); chan++){ - _io_impl->send_handler.set_xport_chan_get_buff(chan, boost::bind( - &uhd::transport::zero_copy_if::get_send_buff, _io_impl->data_transport, _1 - )); - } - _io_impl->send_handler.set_converter(_send_otw_type); - _io_impl->send_handler.set_max_samples_per_packet(get_max_send_samps_per_packet()); -} - -/*********************************************************************** - * Data send + helper functions - **********************************************************************/ -size_t b100_impl::get_max_send_samps_per_packet(void) const { - static const size_t hdr_size = 0 - + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) - - sizeof(vrt::if_packet_info_t().cid) //no class id ever used - ; - static const size_t bpp = 2048 - hdr_size; - return bpp / _send_otw_type.get_sample_size(); -} - -size_t b100_impl::send( - const send_buffs_type &buffs, size_t nsamps_per_buff, - const tx_metadata_t &metadata, const io_type_t &io_type, - send_mode_t send_mode, double timeout -){ - return _io_impl->send_handler.send( - buffs, nsamps_per_buff, - metadata, io_type, - send_mode, timeout - ); -} - -/*********************************************************************** - * Data recv + helper functions - **********************************************************************/ - -size_t b100_impl::get_max_recv_samps_per_packet(void) const { - static const size_t hdr_size = 0 - + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) - + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer - - sizeof(vrt::if_packet_info_t().cid) //no class id ever used - ; - size_t bpp = 2048 - hdr_size; //limited by FPGA pkt buffer size - return bpp/_recv_otw_type.get_sample_size(); -} - -size_t b100_impl::recv( - const recv_buffs_type &buffs, size_t nsamps_per_buff, - rx_metadata_t &metadata, const io_type_t &io_type, - recv_mode_t recv_mode, double timeout -){ - return _io_impl->recv_handler.recv( - buffs, nsamps_per_buff, - metadata, io_type, - recv_mode, timeout - ); -} - -void b100_impl::issue_stream_cmd(const stream_cmd_t &stream_cmd) -{ - _io_impl->continuous_streaming = (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS); - _iface->poke32(B100_REG_CTRL_RX_STREAM_CMD, dsp_type1::calc_stream_cmd_word(stream_cmd)); - _iface->poke32(B100_REG_CTRL_RX_TIME_SECS, boost::uint32_t(stream_cmd.time_spec.get_full_secs())); - _iface->poke32(B100_REG_CTRL_RX_TIME_TICKS, stream_cmd.time_spec.get_tick_count(_clock_ctrl->get_fpga_clock_rate())); - - if (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS) { - while(_io_impl->data_transport->get_recv_buff().get() != NULL){ - /* NOP */ - } - } -} - -void b100_impl::handle_overrun(size_t){ - if (_io_impl->continuous_streaming){ - this->issue_stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS); - } -} diff --git a/host/lib/usrp/usrp_b100/mboard_impl.cpp b/host/lib/usrp/usrp_b100/mboard_impl.cpp deleted file mode 100644 index c651ff2a2..000000000 --- a/host/lib/usrp/usrp_b100/mboard_impl.cpp +++ /dev/null @@ -1,246 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -#include "b100_impl.hpp" -#include "usrp_commands.h" -#include "fpga_regs_standard.h" -#include "fpga_regs_common.h" -#include "b100_regs.hpp" -#include "usrp_i2c_addr.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace uhd; -using namespace uhd::usrp; - -static const bool b100_mboard_verbose = true; - -/*********************************************************************** - * Mboard Initialization - **********************************************************************/ -void b100_impl::mboard_init(void) -{ - _mboard_proxy = wax_obj_proxy::make( - boost::bind(&b100_impl::mboard_get, this, _1, _2), - boost::bind(&b100_impl::mboard_set, this, _1, _2)); - - //set the ticks per seconds into the vita time control - _iface->poke32(B100_REG_TIME64_TPS, - boost::uint32_t(_clock_ctrl->get_fpga_clock_rate()) - ); - - //init the clock config - _clock_config = clock_config_t::internal(); - update_clock_config(); -} - -void b100_impl::update_clock_config(void){ - boost::uint32_t pps_flags = 0; - - //translate pps polarity enums - switch(_clock_config.pps_polarity){ - case clock_config_t::PPS_POS: pps_flags |= B100_FLAG_TIME64_PPS_POSEDGE; break; - case clock_config_t::PPS_NEG: pps_flags |= B100_FLAG_TIME64_PPS_NEGEDGE; break; - default: throw uhd::runtime_error("unhandled clock configuration pps polarity"); - } - - //set the pps flags - _iface->poke32(B100_REG_TIME64_FLAGS, pps_flags); - - //clock source ref 10mhz - switch(_clock_config.ref_source){ - case clock_config_t::REF_AUTO: _clock_ctrl->use_auto_ref(); break; - case clock_config_t::REF_INT: _clock_ctrl->use_internal_ref(); break; - case clock_config_t::REF_SMA: _clock_ctrl->use_auto_ref(); break; - default: throw uhd::runtime_error("unhandled clock configuration ref source"); - } -} - -/*********************************************************************** - * Mboard Get - **********************************************************************/ -void b100_impl::mboard_get(const wax::obj &key_, wax::obj &val){ - named_prop_t key = named_prop_t::extract(key_); - - //handle the get request conditioned on the key - switch(key.as()){ - case MBOARD_PROP_NAME: - val = std::string("USRP-B100 mboard"); - return; - - case MBOARD_PROP_OTHERS: - val = prop_names_t(); - return; - - case MBOARD_PROP_RX_DBOARD: - UHD_ASSERT_THROW(key.name == ""); - val = _rx_dboard_proxy->get_link(); - return; - - case MBOARD_PROP_RX_DBOARD_NAMES: - val = prop_names_t(1, ""); //vector of size 1 with empty string - return; - - case MBOARD_PROP_TX_DBOARD: - UHD_ASSERT_THROW(key.name == ""); - val = _tx_dboard_proxy->get_link(); - return; - - case MBOARD_PROP_TX_DBOARD_NAMES: - val = prop_names_t(1, ""); //vector of size 1 with empty string - return; - - case MBOARD_PROP_RX_DSP: - UHD_ASSERT_THROW(key.name == ""); - val = _rx_ddc_proxy->get_link(); - return; - - case MBOARD_PROP_RX_DSP_NAMES: - val = prop_names_t(1, ""); - return; - - case MBOARD_PROP_TX_DSP: - UHD_ASSERT_THROW(key.name == ""); - val = _tx_duc_proxy->get_link(); - return; - - case MBOARD_PROP_TX_DSP_NAMES: - val = prop_names_t(1, ""); - return; - - case MBOARD_PROP_CLOCK_CONFIG: - val = _clock_config; - return; - - case MBOARD_PROP_RX_SUBDEV_SPEC: - val = _rx_subdev_spec; - return; - - case MBOARD_PROP_TX_SUBDEV_SPEC: - val = _tx_subdev_spec; - return; - - case MBOARD_PROP_EEPROM_MAP: - val = _iface->mb_eeprom; - return; - - case MBOARD_PROP_TIME_NOW:while(true){ - uint32_t secs = _iface->peek32(B100_REG_RB_TIME_NOW_SECS); - uint32_t ticks = _iface->peek32(B100_REG_RB_TIME_NOW_TICKS); - if (secs != _iface->peek32(B100_REG_RB_TIME_NOW_SECS)) continue; - val = time_spec_t(secs, ticks, _clock_ctrl->get_fpga_clock_rate()); - return; - } - - case MBOARD_PROP_TIME_PPS: while(true){ - uint32_t secs = _iface->peek32(B100_REG_RB_TIME_PPS_SECS); - uint32_t ticks = _iface->peek32(B100_REG_RB_TIME_PPS_TICKS); - if (secs != _iface->peek32(B100_REG_RB_TIME_PPS_SECS)) continue; - val = time_spec_t(secs, ticks, _clock_ctrl->get_fpga_clock_rate()); - return; - } - - case MBOARD_PROP_CLOCK_RATE: - val = _clock_ctrl->get_fpga_clock_rate(); - return; - - default: UHD_THROW_PROP_GET_ERROR(); - } -} - -/*********************************************************************** - * Mboard Set - **********************************************************************/ -void b100_impl::mboard_set(const wax::obj &key, const wax::obj &val) -{ - if(key.type() == typeid(std::string)) { - if(key.as() == "load_eeprom") { - std::string b100_eeprom_image = val.as(); - UHD_MSG(status) << "B100 EEPROM image: " << b100_eeprom_image << std::endl; - _fx2_ctrl->usrp_load_eeprom(val.as()); - } - return; - } - - //handle the get request conditioned on the key - switch(key.as()){ - case MBOARD_PROP_TIME_NOW: - case MBOARD_PROP_TIME_PPS:{ - time_spec_t time_spec = val.as(); - _iface->poke32(B100_REG_TIME64_TICKS, time_spec.get_tick_count(_clock_ctrl->get_fpga_clock_rate())); - boost::uint32_t imm_flags = (key.as() == MBOARD_PROP_TIME_NOW)? 1 : 0; - _iface->poke32(B100_REG_TIME64_IMM, imm_flags); - _iface->poke32(B100_REG_TIME64_SECS, boost::uint32_t(time_spec.get_full_secs())); - } - return; - - case MBOARD_PROP_RX_SUBDEV_SPEC: - _rx_subdev_spec = val.as(); - verify_rx_subdev_spec(_rx_subdev_spec, _mboard_proxy->get_link()); - UHD_ASSERT_THROW(_rx_subdev_spec.size() == 1); - //set the mux - _iface->poke32(B100_REG_DSP_RX_MUX, dsp_type1::calc_rx_mux_word( - _dboard_manager->get_rx_subdev(_rx_subdev_spec.front().sd_name)[SUBDEV_PROP_CONNECTION].as() - )); - return; - - case MBOARD_PROP_TX_SUBDEV_SPEC: - _tx_subdev_spec = val.as(); - verify_tx_subdev_spec(_tx_subdev_spec, _mboard_proxy->get_link()); - UHD_ASSERT_THROW(_tx_subdev_spec.size() == 1); - //set the mux and set the number of tx channels - _iface->poke32(B100_REG_DSP_TX_MUX, dsp_type1::calc_tx_mux_word( - _dboard_manager->get_tx_subdev(_tx_subdev_spec.front().sd_name)[SUBDEV_PROP_CONNECTION].as() - )); - return; - - case MBOARD_PROP_EEPROM_MAP: - // Step1: commit the map, writing only those values set. - // Step2: readback the entire eeprom map into the iface. - val.as().commit(*_iface, mboard_eeprom_t::MAP_B000); - _iface->mb_eeprom = mboard_eeprom_t(*_iface, mboard_eeprom_t::MAP_B000); - return; - - case MBOARD_PROP_CLOCK_CONFIG: - _clock_config = val.as(); - update_clock_config(); - return; - - case MBOARD_PROP_CLOCK_RATE: - UHD_MSG(warning) - << "You are setting the master clock rate from the API.\n" - << "You may want to pass this into the device address as master_clock_rate=.\n" - << "This way, the clock rate is guaranteed to be initialized first.\n" - << "See the application notes for USRP-B100 for further instructions.\n" - ; - _clock_ctrl->set_fpga_clock_rate(val.as()); - update_transport_channel_mapping(); - return; - - default: UHD_THROW_PROP_SET_ERROR(); - } -} -- cgit v1.2.3 From 3fb011a261c76ec02681a44f35bf4882cf6cd71e Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Wed, 15 Jun 2011 14:11:18 -0700 Subject: b100: update find to match usrp1 style + features --- host/lib/usrp/b100/b100_impl.cpp | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) (limited to 'host/lib/usrp') diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp index 772da8f42..7e733ddd9 100644 --- a/host/lib/usrp/b100/b100_impl.cpp +++ b/host/lib/usrp/b100/b100_impl.cpp @@ -54,20 +54,9 @@ static device_addrs_t b100_find(const device_addr_t &hint) //return an empty list of addresses when type is set to non-b100 if (hint.has_key("type") and hint["type"] != "b100") return b100_addrs; - //extract the firmware path for the b100 - std::string b100_fw_image; - try{ - b100_fw_image = find_image_path( - hint.has_key("fw")? hint["fw"] : "usrp_b100_fw.ihx" - ); - } - catch(...){ - UHD_MSG(warning) << boost::format( - "Could not locate B100 firmware.\n" - "Please install the images package.\n" - ); - return b100_addrs; - } + //Return an empty list of addresses when an address is specified, + //since an address is intended for a different, non-USB, device. + if (hint.has_key("addr")) return b100_addrs; boost::uint16_t vid = hint.has_key("uninit") ? FX2_VENDOR_ID : B100_VENDOR_ID; boost::uint16_t pid = hint.has_key("uninit") ? FX2_PRODUCT_ID : B100_PRODUCT_ID; @@ -80,11 +69,25 @@ static device_addrs_t b100_find(const device_addr_t &hint) //find the usrps and load firmware BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { - try { - fx2_ctrl::make(usb_control::make(handle))->usrp_load_firmware(b100_fw_image); - } catch (...) { - UHD_MSG(status) << "Interface claimed, ignoring device" << std::endl; + //extract the firmware path for the b100 + std::string b100_fw_image; + try{ + b100_fw_image = find_image_path(hint.get("fw", "usrp_b100_fw.ihx")); } + catch(...){ + UHD_MSG(warning) << boost::format( + "Could not locate B100 firmware.\n" + "Please install the images package.\n" + ); + return b100_addrs; + } + UHD_LOG << "the firmware image: " << b100_fw_image << std::endl; + + usb_control::sptr control; + try{control = usb_control::make(handle);} + catch(const uhd::exception &){continue;} //ignore claimed + + fx2_ctrl::make(control)->usrp_load_firmware(b100_fw_image); } //get descriptors again with serial number, but using the initialized VID/PID now since we have firmware -- cgit v1.2.3