diff options
| author | Philip Balister <philip@opensdr.com> | 2011-02-14 22:01:53 -0800 | 
|---|---|---|
| committer | Philip Balister <philip@opensdr.com> | 2011-02-14 22:01:53 -0800 | 
| commit | f157f0ba340b5bee85965881b25a3861a099b7bb (patch) | |
| tree | 19bf9c0bcff6e2884438db09ca3ff74128b1f0ba | |
| parent | c9b3cb60e1559a5ea583bbef184a3c76ccb51a21 (diff) | |
| parent | 85a1575d83505d4d2770e5754bee207181ba0f8a (diff) | |
| download | uhd-f157f0ba340b5bee85965881b25a3861a099b7bb.tar.gz uhd-f157f0ba340b5bee85965881b25a3861a099b7bb.tar.bz2 uhd-f157f0ba340b5bee85965881b25a3861a099b7bb.zip | |
Merge branch 'next' of ettus.sourcerepo.com:ettus/uhdpriv into next
| -rw-r--r-- | host/docs/images.rst | 2 | ||||
| -rw-r--r-- | host/include/uhd/types/ref_vector.hpp | 40 | ||||
| -rw-r--r-- | host/include/uhd/usrp/multi_usrp.hpp | 6 | ||||
| -rw-r--r-- | host/lib/types/types.cpp | 15 | ||||
| -rw-r--r-- | host/lib/usrp/multi_usrp.cpp | 8 | ||||
| -rw-r--r-- | host/lib/usrp/usrp1/io_impl.cpp | 138 | ||||
| -rw-r--r-- | host/lib/usrp/usrp2/io_impl.cpp | 43 | ||||
| -rwxr-xr-x | host/utils/usrp_n2xx_net_burner.py | 53 | 
8 files changed, 215 insertions, 90 deletions
| diff --git a/host/docs/images.rst b/host/docs/images.rst index 612a00aa5..f5be88a65 100644 --- a/host/docs/images.rst +++ b/host/docs/images.rst @@ -12,6 +12,8 @@ The methods of loading images into the device varies among devices:  * **USRP1:** The host code will automatically load the firmware and FPGA at runtime.  * **USRP2:** The user must manually write the images onto the USRP2 SD card. +* **USRP-N Series:** The user must manually transfer the images over ethernet. +* **USRP-E Series:** The host code will automatically load the FPGA at runtime.  ------------------------------------------------------------------------  Pre-built images diff --git a/host/include/uhd/types/ref_vector.hpp b/host/include/uhd/types/ref_vector.hpp index efd4b8f89..0ae301647 100644 --- a/host/include/uhd/types/ref_vector.hpp +++ b/host/include/uhd/types/ref_vector.hpp @@ -29,30 +29,54 @@ namespace uhd{   */  template <typename T> class ref_vector{  public: -    //! Create a reference vector from a pointer and size -    template <typename Ptr> ref_vector(Ptr *ptr, size_t size = 1): -        _mem(T(ptr)), _size(size) +    /*! +     * Create a reference vector of size 1 from a pointer. +     * Therefore: rv[0] == ptr and rv.size() == 1 +     * \param ptr a pointer to a chunk of memory +     */ +    template <typename Ptr> ref_vector(Ptr *ptr): +        _ptr(T(ptr)), _mem(_mem_t(&_ptr)), _size(1)      {          /* NOP */      } -    //! Create a reference vector from a std::vector container -    template <typename Range> ref_vector(const Range &range): -        _mem(T(range.front())), _size(range.size()) +    /*! +     * Create a reference vector from a std::vector container. +     * Therefore: rv[n] == vec[n] and rv.size() == vec.size() +     * \param range a const reference to an std::vector +     */ +    template <typename Vector> ref_vector(const Vector &vec): +        _ptr(T()), _mem(_mem_t(&vec.front())), _size(vec.size())      {          /* NOP */      } +    /*! +     * Create a reference vector from a pointer and a length +     * Therefore: rv[n] == mem[n] and rv.size() == len +     * \param mem a pointer to an array of pointers +     * \param len the length of the array of pointers +     */ +    ref_vector(const T *mem, size_t len): +        _ptr(T()), _mem(_mem_t(mem)), _size(len) +    { +        /* NOP */ +    } + +    //! Index operator gets the value of rv[index]      const T &operator[](size_t index) const{ -        return (&_mem)[index]; +        return _mem[index];      } +    //! The number of elements in this container      size_t size(void) const{          return _size;      }  private: -    const T      _mem; +    const T      _ptr; +    typedef T*   _mem_t; +    const _mem_t _mem;      const size_t _size;  }; diff --git a/host/include/uhd/usrp/multi_usrp.hpp b/host/include/uhd/usrp/multi_usrp.hpp index c77b5d6d2..60b757f50 100644 --- a/host/include/uhd/usrp/multi_usrp.hpp +++ b/host/include/uhd/usrp/multi_usrp.hpp @@ -141,15 +141,17 @@ public:      /*!       * Get the current time in the usrp time registers. +     * \param mboard which motherboard to query       * \return a timespec representing current usrp time       */ -    virtual time_spec_t get_time_now(void) = 0; +    virtual time_spec_t get_time_now(size_t mboard = 0) = 0;      /*!       * Get the time when the last pps pulse occured. +     * \param mboard which motherboard to query       * \return a timespec representing the last pps       */ -    virtual time_spec_t get_time_last_pps(void) = 0; +    virtual time_spec_t get_time_last_pps(size_t mboard = 0) = 0;      /*!       * Sets the time registers on the usrp immediately. diff --git a/host/lib/types/types.cpp b/host/lib/types/types.cpp index bf308a0b3..7c65d2997 100644 --- a/host/lib/types/types.cpp +++ b/host/lib/types/types.cpp @@ -76,17 +76,16 @@ static std::vector<size_t> get_tid_size_table(void){      return table;  } -static size_t tid_to_size(io_type_t::tid_t tid){ -    static const std::vector<size_t> size_table(get_tid_size_table()); -    return size_table[size_t(tid) & 0x7f]; -} +static const std::vector<size_t> tid_size_table(get_tid_size_table()); -io_type_t::io_type_t(tid_t tid) -: size(tid_to_size(tid)), tid(tid){ +io_type_t::io_type_t(tid_t tid): +    size(tid_size_table[size_t(tid) & 0x7f]), tid(tid) +{      /* NOP */  } -io_type_t::io_type_t(size_t size) -: size(size), tid(CUSTOM_TYPE){ +io_type_t::io_type_t(size_t size): +    size(size), tid(CUSTOM_TYPE) +{      /* NOP */  } diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index 817d7b085..4bdb2bf2e 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -128,12 +128,12 @@ public:          return _mboard(mboard)[MBOARD_PROP_NAME].as<std::string>();      } -    time_spec_t get_time_now(void){ -        return _mboard(0)[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); +    time_spec_t get_time_now(size_t mboard = 0){ +        return _mboard(mboard)[MBOARD_PROP_TIME_NOW].as<time_spec_t>();      } -    time_spec_t get_time_last_pps(void){ -        return _mboard(0)[MBOARD_PROP_TIME_PPS].as<time_spec_t>(); +    time_spec_t get_time_last_pps(size_t mboard = 0){ +        return _mboard(mboard)[MBOARD_PROP_TIME_PPS].as<time_spec_t>();      }      void set_time_now(const time_spec_t &time_spec, size_t mboard){ diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp index 88cbab073..61d5fa872 100644 --- a/host/lib/usrp/usrp1/io_impl.cpp +++ b/host/lib/usrp/usrp1/io_impl.cpp @@ -37,21 +37,64 @@ static const size_t alignment_padding = 512;  /***********************************************************************   * Helper struct to associate an offset with a buffer   **********************************************************************/ -class offset_send_buffer{ -public: -    typedef boost::shared_ptr<offset_send_buffer> sptr; +struct offset_send_buffer{ +    offset_send_buffer(void){ +        /* NOP */ +    } -    static sptr make(managed_send_buffer::sptr buff, size_t offset = 0){ -        return sptr(new offset_send_buffer(buff, offset)); +    offset_send_buffer(managed_send_buffer::sptr buff, size_t offset = 0): +        buff(buff), offset(offset) +    { +        /* NOP */      }      //member variables      managed_send_buffer::sptr buff;      size_t offset; /* in bytes */ +}; + +/*********************************************************************** + * Reusable managed send buffer to handle aligned commits + **********************************************************************/ +class offset_managed_send_buffer : public managed_send_buffer{ +public: +    typedef boost::function<void(offset_send_buffer&, offset_send_buffer&, size_t)> commit_cb_type; +    offset_managed_send_buffer(const commit_cb_type &commit_cb): +        _expired(true), _commit_cb(commit_cb) +    { +        /* NOP */ +    } + +    bool expired(void){return _expired;} + +    void commit(size_t size){ +        if (_expired) return; +        this->_commit_cb(_curr_buff, _next_buff, size); +        _expired = true; +    } + +    sptr get_new( +        offset_send_buffer &curr_buff, +        offset_send_buffer &next_buff +    ){ +        _expired = false; +        _curr_buff = curr_buff; +        _next_buff = next_buff; +        return sptr(this, &offset_managed_send_buffer::fake_deleter); +    }  private: -    offset_send_buffer(managed_send_buffer::sptr buff, size_t offset): -        buff(buff), offset(offset){/* NOP */} +    static void fake_deleter(void */*obj*/){ +        //dont do anything and assume the bastard committed it +        //static_cast<offset_managed_send_buffer *>(obj)->commit(0); +    } + +    void  *get_buff(void) const{return _curr_buff.buff->cast<char *>() + _curr_buff.offset;} +    size_t get_size(void) const{return _curr_buff.buff->size()         - _curr_buff.offset;} + +    bool _expired; +    offset_send_buffer _curr_buff, _next_buff; +    commit_cb_type _commit_cb;  };  /*********************************************************************** @@ -62,10 +105,11 @@ struct usrp1_impl::io_impl{          data_transport(data_transport),          underflow_poll_samp_count(0),          overflow_poll_samp_count(0), -        curr_buff_committed(true), -        curr_buff(offset_send_buffer::make(data_transport->get_send_buff())) +        curr_buff(offset_send_buffer(data_transport->get_send_buff())), +        omsb(boost::bind(&usrp1_impl::io_impl::commit_send_buff, this, _1, _2, _3))      { -        /* NOP */ +        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);      }      ~io_impl(void){ @@ -74,6 +118,13 @@ 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; @@ -86,11 +137,16 @@ struct usrp1_impl::io_impl{      //all of this to ensure only aligned lengths are committed      //NOTE: you must commit before getting a new buffer      //since the vrt packet handler obeys this, we are ok -    bool curr_buff_committed; -    offset_send_buffer::sptr curr_buff; -    void commit_send_buff(offset_send_buffer::sptr, offset_send_buffer::sptr, size_t); +    offset_send_buffer curr_buff; +    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 &, double); +    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; +    }  };  /*! @@ -99,12 +155,12 @@ struct usrp1_impl::io_impl{   * Commit the current buffer at multiples of alignment.   */  void usrp1_impl::io_impl::commit_send_buff( -    offset_send_buffer::sptr curr, -    offset_send_buffer::sptr next, +    offset_send_buffer &curr, +    offset_send_buffer &next,      size_t num_bytes  ){      //total number of bytes now in the current buffer -    size_t bytes_in_curr_buffer = curr->offset + num_bytes; +    size_t bytes_in_curr_buffer = curr.offset + num_bytes;      //calculate how many to commit and remainder      size_t num_bytes_remaining = bytes_in_curr_buffer % alignment_padding; @@ -112,17 +168,16 @@ void usrp1_impl::io_impl::commit_send_buff(      //copy the remainder into the next buffer      std::memcpy( -        next->buff->cast<char *>() + next->offset, -        curr->buff->cast<char *>() + num_bytes_to_commit, +        next.buff->cast<char *>() + next.offset, +        curr.buff->cast<char *>() + num_bytes_to_commit,          num_bytes_remaining      );      //update the offset into the next buffer -    next->offset += num_bytes_remaining; +    next.offset += num_bytes_remaining;      //commit the current buffer -    curr->buff->commit(num_bytes_to_commit); -    curr_buff_committed = true; +    curr.buff->commit(num_bytes_to_commit);  }  /*! @@ -130,14 +185,14 @@ void usrp1_impl::io_impl::commit_send_buff(   */  void usrp1_impl::io_impl::flush_send_buff(void){      //calculate the number of bytes to alignment -    size_t bytes_to_pad = (-1*curr_buff->offset)%alignment_padding; +    size_t bytes_to_pad = (-1*curr_buff.offset)%alignment_padding;      //send at least alignment_padding to guarantee zeros are sent      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, 0.1)){ +    if (this->get_send_buffs(buffs)){          std::memset(buffs[0]->cast<void *>(), 0, bytes_to_pad);          buffs[0]->commit(bytes_to_pad);      } @@ -148,25 +203,19 @@ void usrp1_impl::io_impl::flush_send_buff(void){   * 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, double timeout +    vrt_packet_handler::managed_send_buffs_t &buffs  ){ -    UHD_ASSERT_THROW(curr_buff_committed and buffs.size() == 1); +    UHD_ASSERT_THROW(omsb.expired() and buffs.size() == 1);      //try to get a new managed buffer with timeout -    offset_send_buffer::sptr next_buff(offset_send_buffer::make(data_transport->get_send_buff(timeout))); -    if (not next_buff->buff.get()) return false; /* propagate timeout here */ - -    //calculate the buffer pointer and size given the offset -    //references to the buffers are held in the bound function -    buffs[0] = managed_send_buffer::make_safe( -        curr_buff->buff->cast<char *>() + curr_buff->offset, -        curr_buff->buff->size()         - curr_buff->offset, -        boost::bind(&usrp1_impl::io_impl::commit_send_buff, this, curr_buff, next_buff, _1) -    ); +    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);      //store the next buffer for the next call      curr_buff = next_buff; -    curr_buff_committed = false;      return true;  } @@ -226,6 +275,7 @@ size_t usrp1_impl::send(  ){      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 @@ -233,7 +283,7 @@ size_t usrp1_impl::send(          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, -        boost::bind(&usrp1_impl::io_impl::get_send_buffs, _io_impl.get(), _1, timeout), +        _io_impl->get_send_buffs_fcn,          get_max_send_samps_per_packet(),          0,                                         //vrt header offset          _tx_subdev_spec.size()                     //num channels @@ -281,15 +331,6 @@ static void usrp1_bs_vrt_unpacker(      if_packet_info.has_tlr = false;  } -static bool get_recv_buffs( -    zero_copy_if::sptr zc_if, double timeout, -    vrt_packet_handler::managed_recv_buffs_t &buffs -){ -    UHD_ASSERT_THROW(buffs.size() == 1); -    buffs[0] = zc_if->get_recv_buff(timeout); -    return buffs[0].get() != NULL; -} -  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() @@ -302,6 +343,7 @@ size_t usrp1_impl::recv(      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 @@ -309,7 +351,7 @@ size_t usrp1_impl::recv(          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, -        boost::bind(&get_recv_buffs, _data_transport, timeout, _1), +        _io_impl->get_recv_buffs_fcn,          &vrt_packet_handler::handle_overflow_nop,          0,                                         //vrt header offset          _rx_subdev_spec.size()                     //num channels diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index d09ce1871..67b52db71 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -24,9 +24,7 @@  #include <boost/format.hpp>  #include <boost/bind.hpp>  #include <boost/thread.hpp> -#include <boost/date_time/posix_time/posix_time_types.hpp>  #include <iostream> -#include <list>  using namespace uhd;  using namespace uhd::usrp; @@ -369,6 +367,19 @@ static UHD_INLINE bool handle_msg_packet(      return true;  } +class alignment_indexes{ +public: +    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; +}; +  UHD_INLINE bool usrp2_impl::io_impl::get_recv_buffs(      vrt_packet_handler::managed_recv_buffs_t &buffs  ){ @@ -383,14 +394,13 @@ UHD_INLINE bool usrp2_impl::io_impl::get_recv_buffs(      //-------------------- begin alignment logic ---------------------//      boost::system_time exit_time = boost::get_system_time() + to_time_dur(recv_timeout);      managed_recv_buffer::sptr buff_tmp; -    std::list<size_t> _all_indexes, indexes_to_do; -    for (size_t i = 0; i < buffs.size(); i++) _all_indexes.push_back(i); +    alignment_indexes indexes_to_do;      bool clear, msg;      time_spec_t expected_time;      //respond to a clear by starting from scratch      got_clear: -    indexes_to_do = _all_indexes; +    indexes_to_do.reset(buffs.size());      clear = false;      //do an initial pop to load an initial sequence id @@ -401,10 +411,10 @@ UHD_INLINE bool usrp2_impl::io_impl::get_recv_buffs(      if (clear) goto got_clear;      buffs[index] = buff_tmp;      if (msg) return handle_msg_packet(buffs, index); -    indexes_to_do.pop_front(); +    indexes_to_do.remove(index);      //get an aligned set of elements from the buffers: -    while(indexes_to_do.size() != 0){ +    while(not indexes_to_do.empty()){          //pop an element off for this index          index = indexes_to_do.front(); @@ -419,25 +429,22 @@ UHD_INLINE bool usrp2_impl::io_impl::get_recv_buffs(          //if the sequence id matches:          //  remove this index from the list and continue          if (this_time == expected_time){ -            indexes_to_do.pop_front(); -            continue; -        } - -        //if the sequence id is older: -        //  continue with the same index to try again -        else if (this_time < expected_time){ -            continue; +            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{ +        else if (this_time > expected_time){              expected_time = this_time; -            indexes_to_do = _all_indexes; +            indexes_to_do.reset(buffs.size());              indexes_to_do.remove(index); -            continue;          } + +        //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 -----------------------// diff --git a/host/utils/usrp_n2xx_net_burner.py b/host/utils/usrp_n2xx_net_burner.py index f52a2cbc1..db94d50a4 100755 --- a/host/utils/usrp_n2xx_net_burner.py +++ b/host/utils/usrp_n2xx_net_burner.py @@ -27,6 +27,7 @@ import re  import struct  import socket  import sys +import os.path  ########################################################################  # constants @@ -258,6 +259,32 @@ def verify_image(ip, image, addr):    else:      print "Success." +def read_flash(ip, image, size, addr): +  print "Reading image" +  readsize = size +  readdata = str() +  while readsize > 0: +    if readsize < FLASH_DATA_PACKET_SIZE: thisreadsize = readsize +    else: thisreadsize = FLASH_DATA_PACKET_SIZE +    out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL, seq(), addr, thisreadsize, "") +    in_pkt = send_and_recv(out_pkt, ip) +     +    (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) + +    if pktid != update_id_t.USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG: +      raise Exception, "Invalid reply %c from device." % (chr(pktid)) + +    readdata += data[:thisreadsize] +    readsize -= FLASH_DATA_PACKET_SIZE +    addr += FLASH_DATA_PACKET_SIZE + +  print "Read back %i bytes" % len(readdata) +   +  #write to disk +  f = open(image, 'w') +  f.write(readdata) +  f.close() +      def reset_usrp(ip):      out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL, seq(), 0, 0, "")      in_pkt = send_and_recv(out_pkt, ip) @@ -299,6 +326,7 @@ def get_options():      parser.add_option("--fw",   type="string",                 help="firmware image path (optional)", default='')      parser.add_option("--fpga", type="string",                 help="fpga image path (optional)",     default='')      parser.add_option("--reset", action="store_true",          help="reset the device after writing", default=False) +    parser.add_option("--read", action="store_true",           help="read to file instead of write from file", default=False)      parser.add_option("--overwrite-safe", action="store_true", help="never ever use this option", default=False)      (options, args) = parser.parse_args() @@ -313,11 +341,32 @@ if __name__=='__main__':      if not options.fpga and not options.fw and not options.reset: raise Exception, 'Must specify either a firmware image or FPGA image, and/or reset.' -    if options.overwrite_safe: +    if options.overwrite_safe and not options.read:          print("Are you REALLY, REALLY sure you want to overwrite the safe image? This is ALMOST ALWAYS a terrible idea.")          print("If your image is faulty, your USRP2+ will become a brick until reprogrammed via JTAG.")          response = raw_input("""Type "yes" to continue, or anything else to quit: """)          if response != "yes":              sys.exit(0) -    burn_fw(ip=options.ip, fw=options.fw, fpga=options.fpga, reset=options.reset, safe=options.overwrite_safe) +    if options.read is True: +        if options.fw: +            file = options.fw +            if os.path.isfile(file): +                response = raw_input("File already exists -- overwrite? (y/n) ") +                if response != "y": +                    sys.exit(0) +            size = FW_IMAGE_SIZE_BYTES +            addr = SAFE_FW_IMAGE_LOCATION_ADDR if options.overwrite_safe else PROD_FW_IMAGE_LOCATION_ADDR +            read_flash(options.ip, file, size, addr) +        if options.fpga:              +            file = options.fpga +            if os.path.isfile(file): +                response = raw_input("File already exists -- overwrite? (y/n) ") +                if response != "y": +                    sys.exit(0) +            size = FPGA_IMAGE_SIZE_BYTES +            addr = SAFE_FPGA_IMAGE_LOCATION_ADDR if options.overwrite_safe else PROD_FPGA_IMAGE_LOCATION_ADDR +            read_flash(options.ip, file, size, addr) +             +    else:     +        burn_fw(ip=options.ip, fw=options.fw, fpga=options.fpga, reset=options.reset, safe=options.overwrite_safe) | 
