diff options
| -rw-r--r-- | host/include/uhd/usrp/multi_usrp.hpp | 44 | ||||
| -rw-r--r-- | host/include/uhd/utils/dirty_tracked.hpp | 132 | ||||
| -rw-r--r-- | host/include/uhd/utils/soft_register.hpp | 449 | ||||
| -rw-r--r-- | host/lib/usrp/multi_usrp.cpp | 116 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_adc_dac_utils.cpp | 68 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_impl.cpp | 143 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_impl.hpp | 52 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_regs.hpp | 109 | 
8 files changed, 900 insertions, 213 deletions
| diff --git a/host/include/uhd/usrp/multi_usrp.hpp b/host/include/uhd/usrp/multi_usrp.hpp index 8d5dc2e5f..cc392542f 100644 --- a/host/include/uhd/usrp/multi_usrp.hpp +++ b/host/include/uhd/usrp/multi_usrp.hpp @@ -29,6 +29,8 @@  #define UHD_USRP_MULTI_USRP_GET_USRP_INFO_API  #define UHD_USRP_MULTI_USRP_NORMALIZED_GAIN  #define UHD_USRP_MULTI_USRP_GPIO_API +#define UHD_USRP_MULTI_USRP_REGISTER_API +#define UHD_USRP_MULTI_USRP_FILTER_API  #include <uhd/config.hpp>  #include <uhd/device.hpp> @@ -975,6 +977,48 @@ public:      virtual boost::uint32_t get_gpio_attr(const std::string &bank, const std::string &attr, const size_t mboard = 0) = 0;      /******************************************************************* +     * Register IO methods +     ******************************************************************/ +    struct register_info_t { +        size_t bitwidth; +        bool readable; +        bool writable; +    }; + +    /*! +     * Enumerate the full paths of all low-level USRP registers accessible to read/write +     * \param mboard the motherboard index 0 to M-1 +     * \return a vector of register paths +     */ +    virtual std::vector<std::string> enumerate_registers(const size_t mboard = 0) = 0; + +    /*! +     * Get more information about a low-level device register +     * \param path the full path to the register +     * \param mboard the motherboard index 0 to M-1 +     * \return the info struct which contains the bitwidth and read-write access information +     */ +    virtual register_info_t get_register_info(const std::string &path, const size_t mboard = 0) = 0; + +    /*! +     * Write a low-level register field for a register in the USRP hardware +     * \param path the full path to the register +     * \param field the identifier of bitfield to be written (all other bits remain unchanged) +     * \param value the value to write to the register field +     * \param mboard the motherboard index 0 to M-1 +     */ +    virtual void write_register(const std::string &path, const boost::uint32_t field, const boost::uint64_t value, const size_t mboard = 0) = 0; + +    /*! +     * Read a low-level register field from a register in the USRP hardware +     * \param path the full path to the register +     * \param field the identifier of bitfield to be read +     * \param mboard the motherboard index 0 to M-1 +     * \return the value of the register field +     */ +    virtual boost::uint64_t read_register(const std::string &path, const boost::uint32_t field, const size_t mboard = 0) = 0; + +    /*******************************************************************       * Filter API methods       ******************************************************************/ diff --git a/host/include/uhd/utils/dirty_tracked.hpp b/host/include/uhd/utils/dirty_tracked.hpp new file mode 100644 index 000000000..d228a9e65 --- /dev/null +++ b/host/include/uhd/utils/dirty_tracked.hpp @@ -0,0 +1,132 @@ +// +// Copyright 2010-2014 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 <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_UTILS_DIRTY_TRACKED_HPP +#define INCLUDED_UHD_UTILS_DIRTY_TRACKED_HPP + +namespace uhd{ +    /*! +     * A class that wraps a data value with a dirty flag +     * When the client uses the assignment operator on this object, the object +     * automatically dirties itself if the assigned type is not equal the underlying +     * data. Data can be cleaned using the mark_clean entry-point. +     * +     * Requirements for data_t +     * - Must have a default constructor +     * - Must have a copy constructor +     * - Must have an assignment operator (=) +     * - Must have an equality operator (==) +     */ +    template<typename data_t> +    class dirty_tracked { +    public: +        /*! +         * Default ctor: Initialize to default value and dirty +         */ +        dirty_tracked() : +            _data(),                //data_t must have a default ctor +            _dirty(true) +        {} + +        /*! +         * Initialize to specified value and dirty +         */ +        dirty_tracked(const data_t& value) : +            _data(value),        //data_t must have a copy ctor +            _dirty(true) +        {} + +        /*! +         * Copy ctor: Assign source to this type +         */ +        dirty_tracked(const dirty_tracked& source) { +            *this = source; +        } + +        /*! +         * Get underlying data +         */ +        inline const data_t& get() const { +            return _data; +        } + +        /*! +         * Has the underlying data changed since the last +         * time it was cleaned? +         */ +        inline bool is_dirty() const { +            return _dirty; +        } + +        /*! +         * Mark the underlying data as clean +         */ +        inline void mark_clean() { +            _dirty = false; +        } + +        /*! +         * Mark the underlying data as dirty +         */ +        inline void force_dirty() { +            _dirty = true; +        } + +        /*! +         * Assignment with data. +         * Store the specified value and mark it as dirty +         * if it is not equal to the underlying data. +         */ +        inline dirty_tracked& operator=(const data_t& value) +        { +            if(!(_data == value)) {    //data_t must have an equality operator +                _dirty = true; +                _data = value;        //data_t must have an assignment operator +            } +            return *this; +        } + +        /*! +         * Assignment with dirty tracked type. +         * Store the specified value from dirty type and mark it as dirty +         * if it is not equal to the underlying data. +         * This exists to optimize out an implicit cast from dirty_tracked +         * type to data type. +         */ +        inline dirty_tracked& operator=(const dirty_tracked& source) { +            if (!(_data == source._data)) { +                _dirty = true; +                _data = source._data; +            } +            return *this; +        } + +        /*! +         * Explicit conversion from this type to data_t +         */ +        inline operator const data_t&() const { +            return get(); +        } + +    private: +        data_t _data; +        bool   _dirty; +    }; + +} //namespace uhd + +#endif /* INCLUDED_UHD_UTILS_DIRTY_TRACKED_HPP */ diff --git a/host/include/uhd/utils/soft_register.hpp b/host/include/uhd/utils/soft_register.hpp index d3537a618..6c712686a 100644 --- a/host/include/uhd/utils/soft_register.hpp +++ b/host/include/uhd/utils/soft_register.hpp @@ -22,15 +22,61 @@  #include <boost/noncopyable.hpp>  #include <uhd/types/wb_iface.hpp>  #include <uhd/exception.hpp> +#include <uhd/utils/dirty_tracked.hpp>  #include <boost/thread/mutex.hpp>  #include <boost/thread/locks.hpp> +#include <boost/unordered_map.hpp> +#include <boost/tokenizer.hpp> +#include <boost/foreach.hpp> +#include <boost/lexical_cast.hpp> + +/*! \file soft_register.hpp + * Utilities to access and index hardware registers. + * + * This file contains three main utilities: + * - A soft_register wrapper class that can manage a soft-copy, + *   do dirty tracking and allow symbolic access to various field + *   of a register. + * - A register map class that can own multiple soft registers that + *   share the same underlying control interface. + * - A register map database that can be used to collect multiple + *   register maps and other databases to create a hierarchy of + *   registers that can be accessed using the UHD register API. + */ + +//================================================================== +// Soft Register Definition +//==================================================================  #define UHD_DEFINE_SOFT_REG_FIELD(name, width, shift) \      static const uhd::soft_reg_field_t name = (((shift & 0xFF) << 8) | (width & 0xFF))  namespace uhd { -/* A register field is defined as a tuple of the mask and the shift. +//TODO: These hints were added to boost 1.53. + +/** \brief hint for the branch prediction */ +inline bool likely(bool expr) +{ +#ifdef __GNUC__ +    return __builtin_expect(expr, true); +#else +    return expr; +#endif +    } + +/** \brief hint for the branch prediction */ +inline bool unlikely(bool expr) +{ +#ifdef __GNUC__ +    return __builtin_expect(expr, false); +#else +    return expr; +#endif +} + +/*! + * A register field is defined as a tuple of the mask and the shift.   * It can be used to make read-modify-write operations more convenient   * For efficiency reasons, it is recommended to always use a constant   * of this type because it will get optimized out by the compiler and @@ -49,33 +95,75 @@ namespace soft_reg_field {      template<typename data_t>      inline size_t mask(const soft_reg_field_t field) { -        return ((static_cast<data_t>(1)<<width(field))-1)<<shift(field); +        static const data_t ONE = static_cast<data_t>(1); +        //Behavior for the left shift operation is undefined in C++ +        //if the shift amount is >= bitwidth of the datatype +        //So we treat that as a special case with a branch predicition hint +        if (likely((sizeof(data_t)*8) != width(field))) +            return ((ONE<<width(field))-ONE)<<shift(field); +        else +            return (0-ONE)<<shift(field);      }  } +class soft_register_base : public boost::noncopyable { +public: +    virtual ~soft_register_base() {} + +    virtual void initialize(wb_iface& iface, bool sync = false) = 0; +    virtual void flush() = 0; +    virtual void refresh() = 0; +    virtual size_t get_bitwidth() = 0; +    virtual bool is_readable() = 0; +    virtual bool is_writable() = 0; + +    /*! +     * Cast the soft_register generic reference to a more specific type +     */ +    template <typename soft_reg_t> +    inline static soft_reg_t& cast(soft_register_base& reg) { +        soft_reg_t* ptr = dynamic_cast<soft_reg_t*>(®); +        if (ptr) { +            return *ptr; +        } else { +            throw uhd::type_error("failed to cast register to specified type"); +        } +    } +}; + +enum soft_reg_flush_mode_t { OPTIMIZED_FLUSH, ALWAYS_FLUSH }; +  /*!   * Soft register object that holds offset, soft-copy and the control iface.   * Methods give convenient field-level access to soft-copy and the ability   * to do read-modify-write operations.   */ -template<typename reg_data_t, bool readable, bool writeable> -class UHD_API soft_register_t : public boost::noncopyable { +template<typename reg_data_t, bool readable, bool writable> +class UHD_API soft_register_t : public soft_register_base {  public: -    typedef boost::shared_ptr< soft_register_t<reg_data_t, readable, writeable> > sptr; +    typedef boost::shared_ptr< soft_register_t<reg_data_t, readable, writable> > sptr; + +    //Reserved field. Represents all bits in the register. +    UHD_DEFINE_SOFT_REG_FIELD(REGISTER, sizeof(reg_data_t)*8, 0);  //[WIDTH-1:0]      /*!       * Generic constructor for all soft_register types       */ -    soft_register_t(wb_iface::wb_addr_type wr_addr, wb_iface::wb_addr_type rd_addr): -        _iface(NULL), _wr_addr(wr_addr), _rd_addr(rd_addr), _soft_copy(0) +    explicit soft_register_t( +            wb_iface::wb_addr_type wr_addr, +            wb_iface::wb_addr_type rd_addr, +            soft_reg_flush_mode_t mode = ALWAYS_FLUSH): +        _iface(NULL), _wr_addr(wr_addr), _rd_addr(rd_addr), _soft_copy(0), _flush_mode(mode)      {}      /*!       * Constructor for read-only, write-only registers and read-write registers       * with rd_addr == wr_addr       */ -    soft_register_t(wb_iface::wb_addr_type addr): -        _iface(NULL), _wr_addr(addr), _rd_addr(addr), _soft_copy(0) +    explicit soft_register_t( +            wb_iface::wb_addr_type addr, +            soft_reg_flush_mode_t mode = ALWAYS_FLUSH): +        _iface(NULL), _wr_addr(addr), _rd_addr(addr), _soft_copy(0), _flush_mode(mode)      {}      /*! @@ -88,7 +176,7 @@ public:          _iface = &iface;          //Synchronize with hardware. For RW register, flush THEN refresh. -        if (sync && writeable) flush(); +        if (sync && writable) flush();          if (sync && readable) refresh();      } @@ -117,15 +205,21 @@ public:       */      inline void flush()      { -        if (writeable && _iface) { -            if (sizeof(reg_data_t) <= 2) { -                _iface->poke16(_wr_addr, static_cast<boost::uint16_t>(_soft_copy)); -            } else if (sizeof(reg_data_t) <= 4) { -                _iface->poke32(_wr_addr, static_cast<boost::uint32_t>(_soft_copy)); -            } else if (sizeof(reg_data_t) <= 8) { -                _iface->poke64(_wr_addr, static_cast<boost::uint64_t>(_soft_copy)); -            } else { -                throw uhd::not_implemented_error("soft_register only supports up to 64 bits."); +        if (writable && _iface) { +            //If optimized flush then poke only if soft copy is dirty +            //If flush mode is ALWAYS, the dirty flag should get optimized +            //out by the compiler because it is never read +            if (_flush_mode == ALWAYS_FLUSH || _soft_copy.is_dirty()) { +                if (get_bitwidth() <= 16) { +                    _iface->poke16(_wr_addr, static_cast<boost::uint16_t>(_soft_copy)); +                } else if (get_bitwidth() <= 32) { +                    _iface->poke32(_wr_addr, static_cast<boost::uint32_t>(_soft_copy)); +                } else if (get_bitwidth() <= 64) { +                    _iface->poke64(_wr_addr, static_cast<boost::uint64_t>(_soft_copy)); +                } else { +                    throw uhd::not_implemented_error("soft_register only supports up to 64 bits."); +                } +                _soft_copy.mark_clean();              }          } else {              throw uhd::not_implemented_error("soft_register is not writable."); @@ -138,15 +232,16 @@ public:      inline void refresh()      {          if (readable && _iface) { -            if (sizeof(reg_data_t) <= 2) { +            if (get_bitwidth() <= 16) {                  _soft_copy = static_cast<reg_data_t>(_iface->peek16(_rd_addr)); -            } else if (sizeof(reg_data_t) <= 4) { +            } else if (get_bitwidth() <= 32) {                  _soft_copy = static_cast<reg_data_t>(_iface->peek32(_rd_addr)); -            } else if (sizeof(reg_data_t) <= 8) { +            } else if (get_bitwidth() <= 64) {                  _soft_copy = static_cast<reg_data_t>(_iface->peek64(_rd_addr));              } else {                  throw uhd::not_implemented_error("soft_register only supports up to 64 bits.");              } +            _soft_copy.mark_clean();          } else {              throw uhd::not_implemented_error("soft_register is not readable.");          } @@ -170,70 +265,101 @@ public:          return get(field);      } +    /*! +     * Get bitwidth for this register +     */ +    inline size_t get_bitwidth() +    { +        static const size_t BITS_IN_BYTE = 8; +        return sizeof(reg_data_t) * BITS_IN_BYTE; +    } + +    /*! +     * Is the register readable? +     */ +    inline bool is_readable() +    { +        return readable; +    } + +    /*! +     * Is the register writable? +     */ +    inline bool is_writable() +    { +        return writable; +    } +  private:      wb_iface*                       _iface;      const wb_iface::wb_addr_type    _wr_addr;      const wb_iface::wb_addr_type    _rd_addr; -    reg_data_t                      _soft_copy; +    dirty_tracked<reg_data_t>       _soft_copy; +    const soft_reg_flush_mode_t     _flush_mode;  };  /*!   * A synchronized soft register object.   * All operations in the synchronized register are serialized.   */ -template<typename reg_data_t, bool readable, bool writeable> -class UHD_API soft_register_sync_t : public soft_register_t<reg_data_t, readable, writeable> { +template<typename reg_data_t, bool readable, bool writable> +class UHD_API soft_register_sync_t : public soft_register_t<reg_data_t, readable, writable> {  public: -    typedef boost::shared_ptr< soft_register_sync_t<reg_data_t, readable, writeable> > sptr; +    typedef boost::shared_ptr< soft_register_sync_t<reg_data_t, readable, writable> > sptr; -    soft_register_sync_t(wb_iface::wb_addr_type wr_addr, wb_iface::wb_addr_type rd_addr): -        soft_register_t<reg_data_t, readable, writeable>(wr_addr, rd_addr), _mutex() +    explicit soft_register_sync_t( +            wb_iface::wb_addr_type wr_addr, +            wb_iface::wb_addr_type rd_addr, +            soft_reg_flush_mode_t mode = ALWAYS_FLUSH): +        soft_register_t<reg_data_t, readable, writable>(wr_addr, rd_addr, mode), _mutex()      {} -    soft_register_sync_t(wb_iface::wb_addr_type addr): -        soft_register_t<reg_data_t, readable, writeable>(addr), _mutex() +    explicit soft_register_sync_t( +            wb_iface::wb_addr_type addr, +            soft_reg_flush_mode_t mode = ALWAYS_FLUSH): +        soft_register_t<reg_data_t, readable, writable>(addr, mode), _mutex()      {}      inline void initialize(wb_iface& iface, bool sync = false)      {          boost::lock_guard<boost::mutex> lock(_mutex); -        soft_register_t<reg_data_t, readable, writeable>::initialize(iface, sync); +        soft_register_t<reg_data_t, readable, writable>::initialize(iface, sync);      }      inline void set(const soft_reg_field_t field, const reg_data_t value)      {          boost::lock_guard<boost::mutex> lock(_mutex); -        soft_register_t<reg_data_t, readable, writeable>::set(field, value); +        soft_register_t<reg_data_t, readable, writable>::set(field, value);      }      inline reg_data_t get(const soft_reg_field_t field)      {          boost::lock_guard<boost::mutex> lock(_mutex); -        return soft_register_t<reg_data_t, readable, writeable>::get(field); +        return soft_register_t<reg_data_t, readable, writable>::get(field);      }      inline void flush()      {          boost::lock_guard<boost::mutex> lock(_mutex); -        soft_register_t<reg_data_t, readable, writeable>::flush(); +        soft_register_t<reg_data_t, readable, writable>::flush();      }      inline void refresh()      {          boost::lock_guard<boost::mutex> lock(_mutex); -        soft_register_t<reg_data_t, readable, writeable>::refresh(); +        soft_register_t<reg_data_t, readable, writable>::refresh();      }      inline void write(const soft_reg_field_t field, const reg_data_t value)      {          boost::lock_guard<boost::mutex> lock(_mutex); -        soft_register_t<reg_data_t, readable, writeable>::write(field, value); +        soft_register_t<reg_data_t, readable, writable>::write(field, value);      }      inline reg_data_t read(const soft_reg_field_t field)      {          boost::lock_guard<boost::mutex> lock(_mutex); -        return soft_register_t<reg_data_t, readable, writeable>::read(field); +        return soft_register_t<reg_data_t, readable, writable>::read(field);      }  private: @@ -306,6 +432,255 @@ typedef soft_register_sync_t<boost::uint64_t, true, true>   soft_reg64_rw_sync_t      reg_obj->write(example_reg_t::FIELD2, 0x1234);    }  */ +} + +//================================================================== +// Soft Register Map and Database Definition +//================================================================== + +namespace uhd { + +class UHD_API soft_regmap_accessor_t { +public: +    typedef boost::shared_ptr<soft_regmap_accessor_t> sptr; + +    virtual ~soft_regmap_accessor_t() {}; +    virtual soft_register_base& lookup(const std::string& path) const = 0; +    virtual std::vector<std::string> enumerate() const = 0; +    virtual const std::string& get_name() const = 0; +}; + +/*! + * A regmap is a collection of registers that share the same + * bus (control iface). A regmap must have an identifier. + * A regmap must manage storage for each register. + * The recommended way to use a regmap is to define individual registers + * within the scope of the regmap and instantiate them in the ragmap. + * Soft register object that holds offset, soft-copy and the control iface. + * Methods give convenient field-level access to soft-copy and the ability + * to do read-modify-write operations. + */ +class UHD_API soft_regmap_t : public soft_regmap_accessor_t, public boost::noncopyable { +public: +    soft_regmap_t(const std::string& name) : _name(name) {} +    virtual ~soft_regmap_t() {}; + +    /*! +     * Get the name of this register map +     */ +    virtual inline const std::string& get_name() const { return _name; } + +    /*! +     * Initialize all registers in this register map using a bus. +     * Optionally synchronize the register with hardware. +     * The order of initialization is the same as the order in +     * which registers were added to the map. +     */ +    void initialize(wb_iface& iface, bool sync = false) { +        boost::lock_guard<boost::mutex> lock(_mutex); +        BOOST_FOREACH(soft_register_base* reg, _reglist) { +            reg->initialize(iface, sync); +        } +    } + +    /*! +     * Flush all registers to hardware. +     * The order of writing is the same as the order in +     * which registers were added to the map. +     */ +    void flush() { +        boost::lock_guard<boost::mutex> lock(_mutex); +        BOOST_FOREACH(soft_register_base* reg, _reglist) { +            reg->flush(); +        } +    } + +    /*! +     * Refresh all register soft-copies from hardware. +     * The order of reading is the same as the order in +     * which registers were added to the map. +     */ +    void refresh() { +        boost::lock_guard<boost::mutex> lock(_mutex); +        BOOST_FOREACH(soft_register_base* reg, _reglist) { +            reg->refresh(); +        } +    } + +    /*! +     * Lookup a register object by name. +     * If a register with "name" is not found, runtime_error is thrown +     */ +    virtual soft_register_base& lookup(const std::string& name) const { +        regmap_t::const_iterator iter = _regmap.find(name); +        if (iter != _regmap.end()) { +            return *(iter->second); +        } else { +            throw uhd::runtime_error("register not found in map: " + name); +        } +    } + +    /*! +     * Enumerate all the registers in this map. +     * Return fully qualified paths. +     */ +    virtual std::vector<std::string> enumerate() const { +        std::vector<std::string> temp; +        BOOST_FOREACH(const regmap_t::value_type& reg, _regmap) { +            temp.push_back(_name + "/" + reg.first); +        } +        return temp; +    } + +protected: +    enum visibility_t { +        PUBLIC,     //Is accessible through the soft_regmap_accessor_t interface +        PRIVATE     //Is NOT accessible through the soft_regmap_accessor_t interface +    }; + +    /*! +     * Add a register to this map with an identifier "name" and visibility +     */ +    inline void add_to_map(soft_register_base& reg, const std::string& name, const visibility_t visible = PRIVATE) { +        boost::lock_guard<boost::mutex> lock(_mutex); +        if (visible == PUBLIC) { +            //Only add to the map if this register is publicly visible +            if (not _regmap.insert(regmap_t::value_type(name, ®)).second) { +                throw uhd::assertion_error("cannot add two registers with the same name to regmap: " + name); +            } +        } +        _reglist.push_back(®); +    } + +private: +    typedef boost::unordered_map<std::string, soft_register_base*> regmap_t; +    typedef std::list<soft_register_base*>                         reglist_t; + +    const std::string   _name; +    regmap_t            _regmap;    //For lookups +    reglist_t           _reglist;   //To maintain order +    boost::mutex        _mutex; +}; + + +/*! + * A regmap database is a collection of regmaps or other regmap databases + * this allows for efficient encapsulation for multiple registers in a hierarchical + * fashion. + * A regmap_db *does not* manage storage for regmaps. It is simply a wrapper. + */ +class UHD_API soft_regmap_db_t : public soft_regmap_accessor_t, public boost::noncopyable { +public: +    typedef boost::shared_ptr<soft_regmap_db_t> sptr; + +    /*! +     * Use the default constructor if this is the top-level DB +     */ +    soft_regmap_db_t() : _name("") {} + +    /*! +     * Use this constructor if this is a nested DB +     */ +    soft_regmap_db_t(const std::string& name) : _name(name) {} + +    /*! +     * Get the name of this register map +     */ +    const std::string& get_name() const { return _name; } + +    /*! +     * Add a regmap to this map with an identifier "name" and visibility +     */ +    void add(soft_regmap_t& regmap) { +        boost::lock_guard<boost::mutex> lock(_mutex); +        _regmaps.push_back(®map); +    } + +    /*! +     * Add a level of regmap_db to this map with an identifier "name" and visibility +     */ +    void add(soft_regmap_db_t& db) { +        boost::lock_guard<boost::mutex> lock(_mutex); +        if (&db == this) { +            throw uhd::assertion_error("cannot add regmap db to itself" + _name); +        } else { +            _regmap_dbs.push_back(&db); +        } +    } + +    /*! +     * Lookup a register by path. +     * A path is defined as a string of "/" separated tokens that scope a register. +     * The leaf (last token) is the name of the register +     * The token immediately before the leaf is the name of the register map +     * If a nested regmap_db is used, the token before the regmap is the db name. +     * For every nested db, the path has an additional token. +     * For example: +     *   radio0/spi_regmap/spi_control_reg +     */ +    soft_register_base& lookup(const std::string& path) const +    { +        //Turn the slash separated path string into tokens +        std::list<std::string> tokens; +        BOOST_FOREACH( +            const std::string& node, +            boost::tokenizer< boost::char_separator<char> >(path, boost::char_separator<char>("/"))) +        { +            tokens.push_back(node); +        } +        if ((tokens.size() > 2 && tokens.front() == _name) ||   //If this is a nested DB +            (tokens.size() > 1 && _name == "")) {               //If this is a top-level DB +            if (_name != "") tokens.pop_front(); +            if (tokens.size() == 2) {                   //2 tokens => regmap/register path +                BOOST_FOREACH(const soft_regmap_accessor_t* regmap, _regmaps) { +                    if (regmap->get_name() == tokens.front()) { +                        return regmap->lookup(tokens.back()); +                    } +                } +                throw uhd::runtime_error("could not find register map: " + path); +            } else if (not _regmap_dbs.empty()) {       //>2 tokens => <1 or more dbs>/regmap/register +                //Reconstruct path from tokens +                std::string newpath; +                BOOST_FOREACH(const std::string& node, tokens) { +                    newpath += ("/" + node); +                } +                //Dispatch path to hierarchical DB +                BOOST_FOREACH(const soft_regmap_accessor_t* db, _regmap_dbs) { +                    try { +                        return db->lookup(newpath.substr(1)); +                    } catch (std::exception& e) { +                        continue; +                    } +                } +            } +        } +        throw uhd::runtime_error("could not find register: " + path); +    } + +    /*! +     * Enumerate the paths of all registers that this DB can access +     */ +    virtual std::vector<std::string> enumerate() const { +        std::vector<std::string> paths; +        BOOST_FOREACH(const soft_regmap_accessor_t* regmap, _regmaps) { +            const std::vector<std::string>& regs = regmap->enumerate(); +            paths.insert(paths.end(), regs.begin(), regs.end()); +        } +        BOOST_FOREACH(const soft_regmap_accessor_t* db, _regmap_dbs) { +            const std::vector<std::string>& regs = db->enumerate(); +            paths.insert(paths.end(), regs.begin(), regs.end()); +        } +        return paths; +    } + +private: +    typedef std::list<soft_regmap_accessor_t*> db_t; + +    const std::string   _name; +    db_t                _regmaps; +    db_t                _regmap_dbs; +    boost::mutex        _mutex; +};  } //namespace uhd diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index 1866255c9..f27d0ca03 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -26,6 +26,7 @@  #include <uhd/usrp/mboard_eeprom.hpp>  #include <uhd/usrp/dboard_eeprom.hpp>  #include <uhd/convert.hpp> +#include <uhd/utils/soft_register.hpp>  #include <boost/assign/list_of.hpp>  #include <boost/thread.hpp>  #include <boost/foreach.hpp> @@ -1371,6 +1372,121 @@ public:          return 0;      } +    void write_register(const std::string &path, const boost::uint32_t field, const boost::uint64_t value, const size_t mboard) +    { +        if (_tree->exists(mb_root(mboard) / "registers")) +        { +            uhd::soft_regmap_accessor_t::sptr accessor = +                _tree->access<uhd::soft_regmap_accessor_t::sptr>(mb_root(mboard) / "registers").get(); +            uhd::soft_register_base& reg = accessor->lookup(path); + +            if (not reg.is_writable()) { +                throw uhd::runtime_error("multi_usrp::write_register - register not writable: " + path); +            } + +            switch (reg.get_bitwidth()) { +            case 16: +                if (reg.is_readable()) +                    uhd::soft_register_base::cast<uhd::soft_reg16_rw_t>(reg).write(field, static_cast<boost::uint16_t>(value)); +                else +                    uhd::soft_register_base::cast<uhd::soft_reg16_wo_t>(reg).write(field, static_cast<boost::uint16_t>(value)); +            break; + +            case 32: +                if (reg.is_readable()) +                    uhd::soft_register_base::cast<uhd::soft_reg32_rw_t>(reg).write(field, static_cast<boost::uint32_t>(value)); +                else +                    uhd::soft_register_base::cast<uhd::soft_reg32_wo_t>(reg).write(field, static_cast<boost::uint32_t>(value)); +            break; + +            case 64: +                if (reg.is_readable()) +                    uhd::soft_register_base::cast<uhd::soft_reg64_rw_t>(reg).write(field, value); +                else +                    uhd::soft_register_base::cast<uhd::soft_reg64_wo_t>(reg).write(field, value); +            break; + +            default: +                throw uhd::assertion_error("multi_usrp::write_register - register has invalid bitwidth"); +            } + +        } else { +            throw uhd::not_implemented_error("multi_usrp::write_register - register IO not supported for this device"); +        } +    } + +    boost::uint64_t read_register(const std::string &path, const boost::uint32_t field, const size_t mboard) +    { +        if (_tree->exists(mb_root(mboard) / "registers")) +        { +            uhd::soft_regmap_accessor_t::sptr accessor = +                _tree->access<uhd::soft_regmap_accessor_t::sptr>(mb_root(mboard) / "registers").get(); +            uhd::soft_register_base& reg = accessor->lookup(path); + +            if (not reg.is_readable()) { +                throw uhd::runtime_error("multi_usrp::read_register - register not readable: " + path); +            } + +            switch (reg.get_bitwidth()) { +            case 16: +                if (reg.is_writable()) +                    return static_cast<boost::uint64_t>(uhd::soft_register_base::cast<uhd::soft_reg16_rw_t>(reg).read(field)); +                else +                    return static_cast<boost::uint64_t>(uhd::soft_register_base::cast<uhd::soft_reg16_ro_t>(reg).read(field)); +            break; + +            case 32: +                if (reg.is_writable()) +                    return static_cast<boost::uint64_t>(uhd::soft_register_base::cast<uhd::soft_reg32_rw_t>(reg).read(field)); +                else +                    return static_cast<boost::uint64_t>(uhd::soft_register_base::cast<uhd::soft_reg32_ro_t>(reg).read(field)); +            break; + +            case 64: +                if (reg.is_writable()) +                    return uhd::soft_register_base::cast<uhd::soft_reg64_rw_t>(reg).read(field); +                else +                    return uhd::soft_register_base::cast<uhd::soft_reg64_ro_t>(reg).read(field); +            break; + +            default: +                throw uhd::assertion_error("multi_usrp::read_register - register has invalid bitwidth: " + path); +            } +        } else { +            throw uhd::not_implemented_error("multi_usrp::read_register - register IO not supported for this device"); +        } +    } + +    std::vector<std::string> enumerate_registers(const size_t mboard) +    { +        if (_tree->exists(mb_root(mboard) / "registers")) +        { +            uhd::soft_regmap_accessor_t::sptr accessor = +                _tree->access<uhd::soft_regmap_accessor_t::sptr>(mb_root(mboard) / "registers").get(); +            return accessor->enumerate(); +        } else { +            return std::vector<std::string>(); +        } +    } + +    register_info_t get_register_info(const std::string &path, const size_t mboard = 0) +    { +        if (_tree->exists(mb_root(mboard) / "registers")) +        { +            uhd::soft_regmap_accessor_t::sptr accessor = +                _tree->access<uhd::soft_regmap_accessor_t::sptr>(mb_root(mboard) / "registers").get(); +            uhd::soft_register_base& reg = accessor->lookup(path); + +            register_info_t info; +            info.bitwidth = reg.get_bitwidth(); +            info.readable = reg.is_readable(); +            info.writable = reg.is_writable(); +            return info; +        } else { +            throw uhd::not_implemented_error("multi_usrp::read_register - register IO not supported for this device"); +        } +    } +  private:      device::sptr _dev;      property_tree::sptr _tree; diff --git a/host/lib/usrp/x300/x300_adc_dac_utils.cpp b/host/lib/usrp/x300/x300_adc_dac_utils.cpp index efc2c8ed1..e08825749 100644 --- a/host/lib/usrp/x300/x300_adc_dac_utils.cpp +++ b/host/lib/usrp/x300/x300_adc_dac_utils.cpp @@ -18,6 +18,8 @@  #include "x300_impl.hpp"  #include <boost/date_time/posix_time/posix_time_io.hpp> +using namespace uhd::usrp::x300; +  /***********************************************************************   * DAC: Reset and synchronization operations   **********************************************************************/ @@ -101,8 +103,8 @@ void x300_impl::self_test_adcs(mboard_members_t& mb, boost::uint32_t ramp_time_m          //Turn on ramp pattern test          perif.adc->set_test_word("ramp", "ramp"); -        perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); -        perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1); +        perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); +        perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1);      }      boost::this_thread::sleep(boost::posix_time::milliseconds(ramp_time_ms)); @@ -110,19 +112,19 @@ void x300_impl::self_test_adcs(mboard_members_t& mb, boost::uint32_t ramp_time_m      std::string status_str;      for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) {          radio_perifs_t &perif = mb.radio_perifs[r]; -        perif.misc_ins->refresh(); +        perif.regmap->misc_ins_reg.refresh();          std::string i_status, q_status; -        if (perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_I_LOCKED)) -            if (perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_I_ERROR)) +        if (perif.regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_LOCKED)) +            if (perif.regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_ERROR))                  i_status = "Bit Errors!";              else                  i_status = "Good";          else              i_status = "Not Locked!"; -        if (perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_Q_LOCKED)) -            if (perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_Q_ERROR)) +        if (perif.regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_LOCKED)) +            if (perif.regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_ERROR))                  q_status = "Bit Errors!";              else                  q_status = "Good"; @@ -193,9 +195,9 @@ void x300_impl::self_cal_adc_capture_delay(mboard_members_t& mb, const size_t ra      while (iter++ < NUM_RETRIES) {          for (boost::uint32_t dly_tap = 0; dly_tap < NUM_DELAY_STEPS; dly_tap++) {              //Apply delay -            perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_VAL, dly_tap); -            perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_STB, 1); -            perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_STB, 0); +            perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_VAL, dly_tap); +            perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 1); +            perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 0);              boost::uint32_t err_code = 0; @@ -204,12 +206,12 @@ void x300_impl::self_cal_adc_capture_delay(mboard_members_t& mb, const size_t ra              perif.adc->set_test_word("ramp", "ones");              //Turn on the pattern checker in the FPGA. It will lock when it sees a zero              //and count deviations from the expected value -            perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); -            perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1); +            perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); +            perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1);              //10ms @ 200MHz = 2 million samples              boost::this_thread::sleep(boost::posix_time::milliseconds(10)); -            if (perif.misc_ins->read(radio_misc_ins_reg::ADC_CHECKER0_I_LOCKED)) { -                err_code += perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER0_I_ERROR); +            if (perif.regmap->misc_ins_reg.read(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_I_LOCKED)) { +                err_code += perif.regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_I_ERROR);              } else {                  err_code += 100;    //Increment error code by 100 to indicate no lock              } @@ -219,12 +221,12 @@ void x300_impl::self_cal_adc_capture_delay(mboard_members_t& mb, const size_t ra              perif.adc->set_test_word("ones", "ramp");              //Turn on the pattern checker in the FPGA. It will lock when it sees a zero              //and count deviations from the expected value -            perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); -            perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1); +            perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); +            perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1);              //10ms @ 200MHz = 2 million samples              boost::this_thread::sleep(boost::posix_time::milliseconds(10)); -            if (perif.misc_ins->read(radio_misc_ins_reg::ADC_CHECKER0_Q_LOCKED)) { -                err_code += perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER0_Q_ERROR); +            if (perif.regmap->misc_ins_reg.read(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_Q_LOCKED)) { +                err_code += perif.regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_Q_ERROR);              } else {                  err_code += 100;    //Increment error code by 100 to indicate no lock              } @@ -258,7 +260,7 @@ void x300_impl::self_cal_adc_capture_delay(mboard_members_t& mb, const size_t ra          }      }      perif.adc->set_test_word("normal", "normal"); -    perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); +    perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0);      if (win_start == -1) {          throw uhd::runtime_error("self_cal_adc_capture_delay: Self calibration failed. Convergence error."); @@ -269,9 +271,9 @@ void x300_impl::self_cal_adc_capture_delay(mboard_members_t& mb, const size_t ra      }      boost::uint32_t ideal_tap = (win_stop + win_start) / 2; -    perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_VAL, ideal_tap); -    perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_STB, 1); -    perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_STB, 0); +    perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_VAL, ideal_tap); +    perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 1); +    perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 0);      if (print_status) {          double tap_delay = (1.0e12 / mb.clock->get_master_clock_rate()) / (2*32); //in ps @@ -300,7 +302,7 @@ double x300_impl::self_cal_adc_xfer_delay(mboard_members_t& mb, bool apply_delay      for (size_t i = 0; i < NUM_DELAY_STEPS; i++) {          //Delay the ADC clock (will set both Ch0 and Ch1 delays)          double delay = mb.clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, delay_incr*i + delay_start); -        wait_for_clk_locked(mb.zpu_ctrl, ZPU_RB_CLK_STATUS_LMK_LOCK, 0.1); +        wait_for_clk_locked(mb, fw_regmap_t::clk_status_reg_t::LMK_LOCK, 0.1);          boost::uint32_t err_code = 0;          for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { @@ -312,12 +314,12 @@ double x300_impl::self_cal_adc_xfer_delay(mboard_members_t& mb, bool apply_delay              mb.radio_perifs[r].adc->set_test_word("ramp", "ones");              //Turn on the pattern checker in the FPGA. It will lock when it sees a zero              //and count deviations from the expected value -            mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); -            mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1); +            mb.radio_perifs[r].regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); +            mb.radio_perifs[r].regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1);              //50ms @ 200MHz = 10 million samples              boost::this_thread::sleep(boost::posix_time::milliseconds(50)); -            if (mb.radio_perifs[r].misc_ins->read(radio_misc_ins_reg::ADC_CHECKER1_I_LOCKED)) { -                err_code += mb.radio_perifs[r].misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_I_ERROR); +            if (mb.radio_perifs[r].regmap->misc_ins_reg.read(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_LOCKED)) { +                err_code += mb.radio_perifs[r].regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_ERROR);              } else {                  err_code += 100;    //Increment error code by 100 to indicate no lock              } @@ -327,12 +329,12 @@ double x300_impl::self_cal_adc_xfer_delay(mboard_members_t& mb, bool apply_delay              mb.radio_perifs[r].adc->set_test_word("ones", "ramp");              //Turn on the pattern checker in the FPGA. It will lock when it sees a zero              //and count deviations from the expected value -            mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); -            mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1); +            mb.radio_perifs[r].regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); +            mb.radio_perifs[r].regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1);              //50ms @ 200MHz = 10 million samples              boost::this_thread::sleep(boost::posix_time::milliseconds(50)); -            if (mb.radio_perifs[r].misc_ins->read(radio_misc_ins_reg::ADC_CHECKER1_Q_LOCKED)) { -                err_code += mb.radio_perifs[r].misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_Q_ERROR); +            if (mb.radio_perifs[r].regmap->misc_ins_reg.read(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_LOCKED)) { +                err_code += mb.radio_perifs[r].regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_ERROR);              } else {                  err_code += 100;    //Increment error code by 100 to indicate no lock              } @@ -392,7 +394,7 @@ double x300_impl::self_cal_adc_xfer_delay(mboard_members_t& mb, bool apply_delay          UHD_MSG(status) << "Validating..." << std::flush;          //Apply delay          win_center = mb.clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, win_center);  //Sets ADC0 and ADC1 -        wait_for_clk_locked(mb.zpu_ctrl, ZPU_RB_CLK_STATUS_LMK_LOCK, 0.1); +        wait_for_clk_locked(mb, fw_regmap_t::clk_status_reg_t::LMK_LOCK, 0.1);          //Validate          self_test_adcs(mb, 2000);      } else { @@ -403,7 +405,7 @@ double x300_impl::self_cal_adc_xfer_delay(mboard_members_t& mb, bool apply_delay      //Teardown      for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) {          mb.radio_perifs[r].adc->set_test_word("normal", "normal"); -        mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); +        mb.radio_perifs[r].regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0);      }      UHD_MSG(status) << (boost::format(" done (FPGA->ADC=%.3fns%s, Window=%.3fns)\n") %          (win_center-fpga_clk_delay) % (cycle_slip?" +cyc":"") % win_length); diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index 5b202aba9..229bf7b23 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -47,6 +47,7 @@ using namespace uhd;  using namespace uhd::usrp;  using namespace uhd::transport;  using namespace uhd::niusrprio; +using namespace uhd::usrp::x300;  namespace asio = boost::asio;  /*********************************************************************** @@ -513,6 +514,9 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)      this->check_fpga_compat(mb_path, mb);      this->check_fw_compat(mb_path, mb.zpu_ctrl); +    mb.fw_regmap = boost::make_shared<fw_regmap_t>(); +    mb.fw_regmap->initialize(*mb.zpu_ctrl.get(), true); +      //store which FPGA image is loaded      mb.loaded_fpga_image = get_fpga_option(mb.zpu_ctrl); @@ -664,7 +668,6 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)      UHD_MSG(status) << "Setup RF frontend clocking..." << std::endl;      //Initialize clock control registers. NOTE: This does not configure the LMK yet. -    initialize_clock_control(mb);      mb.clock = x300_clock_ctrl::make(mb.zpu_spi,          1 /*slaveno*/,          mb.hw_rev, @@ -843,7 +846,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)      // and do the misc mboard sensors      ////////////////////////////////////////////////////////////////////      _tree->create<sensor_value_t>(mb_path / "sensors" / "ref_locked") -        .publish(boost::bind(&x300_impl::get_ref_locked, this, mb.zpu_ctrl)); +        .publish(boost::bind(&x300_impl::get_ref_locked, this, mb));      ////////////////////////////////////////////////////////////////////      // do some post-init tasks @@ -861,6 +864,14 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)      _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(rx_fe_spec);      _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_fe_spec); +    mb.regmap_db = boost::make_shared<uhd::soft_regmap_db_t>(); +    mb.regmap_db->add(*mb.fw_regmap); +    mb.regmap_db->add(*mb.radio_perifs[0].regmap); +    mb.regmap_db->add(*mb.radio_perifs[1].regmap); + +    _tree->create<uhd::soft_regmap_accessor_t::sptr>(mb_path / "registers") +        .set(mb.regmap_db); +      mb.initialization_done = true;  } @@ -871,12 +882,12 @@ x300_impl::~x300_impl(void)          BOOST_FOREACH(mboard_members_t &mb, _mb)          {              //Disable/reset ADC/DAC -            mb.radio_perifs[0].misc_outs->set(radio_misc_outs_reg::ADC_RESET, 1); -            mb.radio_perifs[0].misc_outs->set(radio_misc_outs_reg::DAC_RESET_N, 0); -            mb.radio_perifs[0].misc_outs->set(radio_misc_outs_reg::DAC_ENABLED, 0); -            mb.radio_perifs[0].misc_outs->flush(); -            mb.radio_perifs[1].misc_outs->set(radio_misc_outs_reg::DAC_ENABLED, 0); -            mb.radio_perifs[1].misc_outs->flush(); +            mb.radio_perifs[0].regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1); +            mb.radio_perifs[0].regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0); +            mb.radio_perifs[0].regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 0); +            mb.radio_perifs[0].regmap->misc_outs_reg.flush(); +            mb.radio_perifs[1].regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 0); +            mb.radio_perifs[1].regmap->misc_outs_reg.flush();              //kill the claimer task and unclaim the device              mb.claimer_task.reset(); @@ -914,21 +925,19 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name, con      both_xports_t xport = this->make_transport(mb_i, dest, X300_RADIO_DEST_PREFIX_CTRL, device_addr_t(), ctrl_sid);      perif.ctrl = radio_ctrl_core_3000::make(mb.if_pkt_is_big_endian, xport.recv, xport.send, ctrl_sid, slot_name); -    perif.misc_outs = boost::make_shared<radio_misc_outs_reg>(); -    perif.misc_ins = boost::make_shared<radio_misc_ins_reg>(); -    perif.misc_outs->initialize(*perif.ctrl, true); -    perif.misc_ins->initialize(*perif.ctrl); +    perif.regmap = boost::make_shared<radio_regmap_t>(radio_index); +    perif.regmap->initialize(*perif.ctrl, true);      //Only Radio0 has the ADC/DAC reset bits. Those bits are reserved for Radio1      if (radio_index == 0) { -        perif.misc_outs->set(radio_misc_outs_reg::ADC_RESET, 1); -        perif.misc_outs->set(radio_misc_outs_reg::DAC_RESET_N, 0); -        perif.misc_outs->flush(); -        perif.misc_outs->set(radio_misc_outs_reg::ADC_RESET, 0); -        perif.misc_outs->set(radio_misc_outs_reg::DAC_RESET_N, 1); -        perif.misc_outs->flush(); +        perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1); +        perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0); +        perif.regmap->misc_outs_reg.flush(); +        perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 0); +        perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 1); +        perif.regmap->misc_outs_reg.flush();      } -    perif.misc_outs->write(radio_misc_outs_reg::DAC_ENABLED, 1); +    perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 1);      this->register_loopback_self_test(perif.ctrl); @@ -1346,32 +1355,9 @@ void x300_impl::register_loopback_self_test(wb_iface::sptr iface)   * clock and time control logic   **********************************************************************/ -void x300_impl::update_clock_control(mboard_members_t &mb) -{ -    const size_t reg = mb.clock_control_regs_clock_source -        | (mb.clock_control_regs_pps_select << 2) -        | (mb.clock_control_regs_pps_out_enb << 4) -        | (mb.clock_control_regs_tcxo_enb << 5) -        | (mb.clock_control_regs_gpsdo_pwr << 6) -    ; -    mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_CLOCK_CTRL), reg); -} - -void x300_impl::initialize_clock_control(mboard_members_t &mb) -{ -    //Initialize clock control register soft copies -    mb.clock_control_regs_clock_source = ZPU_SR_CLOCK_CTRL_CLK_SRC_INTERNAL; -    mb.clock_control_regs_pps_select = ZPU_SR_CLOCK_CTRL_PPS_SRC_INTERNAL; -    mb.clock_control_regs_pps_out_enb = 0; -    mb.clock_control_regs_tcxo_enb = 1; -    mb.clock_control_regs_gpsdo_pwr = 1;    //GPSDO power always ON -    this->update_clock_control(mb); -} -  void x300_impl::set_time_source_out(mboard_members_t &mb, const bool enb)  { -    mb.clock_control_regs_pps_out_enb = enb? 1 : 0; -    this->update_clock_control(mb); +    mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::fw_regmap_t::clk_ctrl_reg_t::PPS_OUT_EN, enb?1:0);  }  void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &source) @@ -1382,19 +1368,19 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou      const bool reconfigure_clks = (mb.current_refclk_src != "internal") or (source != "internal");      if (reconfigure_clks) {          //Update the clock MUX on the motherboard to select the requested source -        mb.clock_control_regs_clock_source = 0; -        mb.clock_control_regs_tcxo_enb = 0;          if (source == "internal") { -            mb.clock_control_regs_clock_source = ZPU_SR_CLOCK_CTRL_CLK_SRC_INTERNAL; -            mb.clock_control_regs_tcxo_enb = 1; +            mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::CLK_SOURCE, fw_regmap_t::clk_ctrl_reg_t::SRC_INTERNAL); +            mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::TCXO_EN, 1);          } else if (source == "external") { -            mb.clock_control_regs_clock_source = ZPU_SR_CLOCK_CTRL_CLK_SRC_EXTERNAL; +            mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::CLK_SOURCE, fw_regmap_t::clk_ctrl_reg_t::SRC_EXTERNAL); +            mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::TCXO_EN, 0);          } else if (source == "gpsdo") { -            mb.clock_control_regs_clock_source = ZPU_SR_CLOCK_CTRL_CLK_SRC_GPSDO; +            mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::CLK_SOURCE, fw_regmap_t::clk_ctrl_reg_t::SRC_GPSDO); +            mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::TCXO_EN, 0);          } else {              throw uhd::key_error("update_clock_source: unknown source: " + source);          } -        this->update_clock_control(mb); +        mb.fw_regmap->clock_ctrl_reg.flush();          //Reset the LMK to make sure it re-locks to the new reference          mb.clock->reset_clocks(); @@ -1409,7 +1395,7 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou      //The programming code in x300_clock_ctrl is not compatible with revs <= 4 and may      //lead to locking issues. So, disable the ref-locked check for older (unsupported) boards.      if (mb.hw_rev > 4) { -        if (not wait_for_clk_locked(mb.zpu_ctrl, ZPU_RB_CLK_STATUS_LMK_LOCK, timeout)) { +        if (not wait_for_clk_locked(mb, fw_regmap_t::clk_status_reg_t::LMK_LOCK, timeout)) {              //failed to lock on reference              if (mb.initialization_done) {                  throw uhd::runtime_error((boost::format("Reference Clock PLL failed to lock to %s source.") % source).str()); @@ -1427,7 +1413,7 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou          mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), 0);          //Wait for radio clock PLL to lock -        if (not wait_for_clk_locked(mb.zpu_ctrl, ZPU_RB_CLK_STATUS_RADIO_CLK_LOCK, 0.01)) { +        if (not wait_for_clk_locked(mb, fw_regmap_t::clk_status_reg_t::RADIO_CLK_LOCK, 0.01)) {              throw uhd::runtime_error((boost::format("Reference Clock PLL in FPGA failed to lock to %s source.") % source).str());          } @@ -1436,20 +1422,20 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou          mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), 0);          //Wait for the ADC IDELAYCTRL to be ready -        if (not wait_for_clk_locked(mb.zpu_ctrl, ZPU_RB_CLK_STATUS_IDELAYCTRL_LOCK, 0.01)) { +        if (not wait_for_clk_locked(mb, fw_regmap_t::clk_status_reg_t::IDELAYCTRL_LOCK, 0.01)) {              throw uhd::runtime_error((boost::format("ADC Calibration Clock in FPGA failed to lock to %s source.") % source).str());          }          // Reset ADCs and DACs          for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) {              radio_perifs_t &perif = mb.radio_perifs[r]; -            if (perif.misc_outs && r==0) {  //ADC/DAC reset lines only exist in Radio0 -                perif.misc_outs->set(radio_misc_outs_reg::ADC_RESET, 1); -                perif.misc_outs->set(radio_misc_outs_reg::DAC_RESET_N, 0); -                perif.misc_outs->flush(); -                perif.misc_outs->set(radio_misc_outs_reg::ADC_RESET, 0); -                perif.misc_outs->set(radio_misc_outs_reg::DAC_RESET_N, 1); -                perif.misc_outs->flush(); +            if (perif.regmap && r==0) {  //ADC/DAC reset lines only exist in Radio0 +                perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1); +                perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0); +                perif.regmap->misc_outs_reg.flush(); +                perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 0); +                perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 1); +                perif.regmap->misc_outs_reg.flush();              }              if (perif.adc) perif.adc->reset();              if (perif.dac) perif.dac->reset(); @@ -1463,61 +1449,54 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou  void x300_impl::update_time_source(mboard_members_t &mb, const std::string &source)  {      if (source == "internal") { -        mb.clock_control_regs_pps_select = ZPU_SR_CLOCK_CTRL_PPS_SRC_INTERNAL; +        mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_SELECT, fw_regmap_t::clk_ctrl_reg_t::SRC_INTERNAL);      } else if (source == "external") { -        mb.clock_control_regs_pps_select = ZPU_SR_CLOCK_CTRL_PPS_SRC_EXTERNAL; +        mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_SELECT, fw_regmap_t::clk_ctrl_reg_t::SRC_EXTERNAL);      } else if (source == "gpsdo") { -        mb.clock_control_regs_pps_select = ZPU_SR_CLOCK_CTRL_PPS_SRC_GPSDO; +        mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_SELECT, fw_regmap_t::clk_ctrl_reg_t::SRC_GPSDO);      } else {          throw uhd::key_error("update_time_source: unknown source: " + source);      } -    this->update_clock_control(mb); -      //check for valid pps -    if (!is_pps_present(mb.zpu_ctrl)) +    if (!is_pps_present(mb))      {          // TODO - Implement intelligent PPS detection          /* throw uhd::runtime_error((boost::format("The %d PPS was not detected.  Please check the PPS source and try again.") % source).str()); */      }  } -static bool get_clk_locked(wb_iface::sptr ctrl, boost::uint32_t which) -{ -    return (ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_CLK_STATUS)) & which) != 0; -} - -bool x300_impl::wait_for_clk_locked(wb_iface::sptr ctrl, boost::uint32_t which, double timeout) +bool x300_impl::wait_for_clk_locked(mboard_members_t& mb, boost::uint32_t which, double timeout)  {      boost::system_time timeout_time = boost::get_system_time() + boost::posix_time::milliseconds(timeout * 1000.0);      do { -        if (get_clk_locked(ctrl, which)) +        if (mb.fw_regmap->clock_status_reg.read(which)==1)              return true;          boost::this_thread::sleep(boost::posix_time::milliseconds(1));      } while (boost::get_system_time() < timeout_time);      //Check one last time -    return get_clk_locked(ctrl, which); +    return (mb.fw_regmap->clock_status_reg.read(which)==1);  } -sensor_value_t x300_impl::get_ref_locked(wb_iface::sptr ctrl) +sensor_value_t x300_impl::get_ref_locked(mboard_members_t& mb)  { -    const bool lock = get_clk_locked(ctrl, ZPU_RB_CLK_STATUS_LMK_LOCK) && -                      get_clk_locked(ctrl, ZPU_RB_CLK_STATUS_RADIO_CLK_LOCK) && -                      get_clk_locked(ctrl, ZPU_RB_CLK_STATUS_IDELAYCTRL_LOCK); +    mb.fw_regmap->clock_status_reg.refresh(); +    const bool lock = (mb.fw_regmap->clock_status_reg.get(fw_regmap_t::clk_status_reg_t::LMK_LOCK)==1) && +                      (mb.fw_regmap->clock_status_reg.get(fw_regmap_t::clk_status_reg_t::RADIO_CLK_LOCK)==1) && +                      (mb.fw_regmap->clock_status_reg.get(fw_regmap_t::clk_status_reg_t::IDELAYCTRL_LOCK)==1);      return sensor_value_t("Ref", lock, "locked", "unlocked");  } -bool x300_impl::is_pps_present(wb_iface::sptr ctrl) +bool x300_impl::is_pps_present(mboard_members_t& mb)  {      // The ZPU_RB_CLK_STATUS_PPS_DETECT bit toggles with each rising edge of the PPS.      // We monitor it for up to 1.5 seconds looking for it to toggle. -    boost::uint32_t pps_detect = ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_CLK_STATUS)) & ZPU_RB_CLK_STATUS_PPS_DETECT; +    boost::uint32_t pps_detect = mb.fw_regmap->clock_status_reg.read(fw_regmap_t::clk_status_reg_t::PPS_DETECT);      for (int i = 0; i < 15; i++)      {          boost::this_thread::sleep(boost::posix_time::milliseconds(100)); -        boost::uint32_t clk_status = ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_CLK_STATUS)); -        if (pps_detect != (clk_status & ZPU_RB_CLK_STATUS_PPS_DETECT)) +        if (pps_detect != mb.fw_regmap->clock_status_reg.read(fw_regmap_t::clk_status_reg_t::PPS_DETECT))              return true;      }      return false; diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index 3cec7d5ce..78c497ad9 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -49,7 +49,6 @@  #include <uhd/transport/nirio/niusrprio_session.h>  #include <uhd/transport/vrt_if_packet.hpp>  #include "recv_packet_demuxer_3000.hpp" -#include <uhd/utils/soft_register.hpp>  #include "x300_regs.hpp"  static const std::string X300_FW_FILE_NAME  = "usrp_x300_fw.bin"; @@ -173,39 +172,6 @@ public:  private:      boost::shared_ptr<async_md_type> _async_md; -    class radio_misc_outs_reg : public uhd::soft_reg32_wo_t { -    public: -        UHD_DEFINE_SOFT_REG_FIELD(DAC_ENABLED,          /*width*/ 1, /*shift*/ 0);  //[0] -        UHD_DEFINE_SOFT_REG_FIELD(DAC_RESET_N,          /*width*/ 1, /*shift*/ 1);  //[1] -        UHD_DEFINE_SOFT_REG_FIELD(ADC_RESET,            /*width*/ 1, /*shift*/ 2);  //[2] -        UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_STB,     /*width*/ 1, /*shift*/ 3);  //[3] -        UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_VAL,     /*width*/ 5, /*shift*/ 4);  //[8:4] -        UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER_ENABLED,  /*width*/ 1, /*shift*/ 9);  //[9] - -        radio_misc_outs_reg(): uhd::soft_reg32_wo_t(uhd::usrp::radio::sr_addr(uhd::usrp::radio::MISC_OUTS)) { -            //Initial values -            set(DAC_ENABLED, 0); -            set(DAC_RESET_N, 0); -            set(ADC_RESET, 0); -            set(ADC_DATA_DLY_STB, 0); -            set(ADC_DATA_DLY_VAL, 16); -            set(ADC_CHECKER_ENABLED, 0); -        } -    }; -    class radio_misc_ins_reg : public uhd::soft_reg32_ro_t { -    public: -        UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_Q_LOCKED, /*width*/ 1, /*shift*/ 0);  //[0] -        UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_I_LOCKED, /*width*/ 1, /*shift*/ 1);  //[1] -        UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_Q_LOCKED, /*width*/ 1, /*shift*/ 2);  //[2] -        UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_I_LOCKED, /*width*/ 1, /*shift*/ 3);  //[3] -        UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_Q_ERROR,  /*width*/ 1, /*shift*/ 4);  //[4] -        UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_I_ERROR,  /*width*/ 1, /*shift*/ 5);  //[5] -        UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_Q_ERROR,  /*width*/ 1, /*shift*/ 6);  //[6] -        UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_I_ERROR,  /*width*/ 1, /*shift*/ 7);  //[7] - -        radio_misc_ins_reg(): uhd::soft_reg32_ro_t(uhd::usrp::radio::RB32_MISC_INS) { } -    }; -      //perifs in the radio core      struct radio_perifs_t      { @@ -223,8 +189,7 @@ private:          rx_frontend_core_200::sptr rx_fe;          tx_frontend_core_200::sptr tx_fe;          //Registers -        radio_misc_outs_reg::sptr misc_outs; -        radio_misc_ins_reg::sptr misc_ins; +        uhd::usrp::x300::radio_regmap_t::sptr regmap;      };      //overflow recovery impl @@ -266,18 +231,15 @@ private:          uhd::gps_ctrl::sptr gps;          gpio_core_200::sptr fp_gpio; -        //clock control register bits -        int clock_control_regs_clock_source; -        int clock_control_regs_pps_select; -        int clock_control_regs_pps_out_enb; -        int clock_control_regs_tcxo_enb; -        int clock_control_regs_gpsdo_pwr; +        uhd::usrp::x300::fw_regmap_t::sptr fw_regmap;          //which FPGA image is loaded          std::string loaded_fpga_image;          size_t hw_rev;          std::string current_refclk_src; + +        uhd::soft_regmap_db_t::sptr regmap_db;      };      std::vector<mboard_members_t> _mb; @@ -391,9 +353,9 @@ private:      void update_clock_source(mboard_members_t&, const std::string &);      void update_time_source(mboard_members_t&, const std::string &); -    uhd::sensor_value_t get_ref_locked(uhd::wb_iface::sptr); -    bool wait_for_clk_locked(uhd::wb_iface::sptr, boost::uint32_t which, double timeout); -    bool is_pps_present(uhd::wb_iface::sptr); +    uhd::sensor_value_t get_ref_locked(mboard_members_t& mb); +    bool wait_for_clk_locked(mboard_members_t& mb, boost::uint32_t which, double timeout); +    bool is_pps_present(mboard_members_t& mb);      void set_db_eeprom(uhd::i2c_iface::sptr i2c, const size_t, const uhd::usrp::dboard_eeprom_t &);      void set_mb_eeprom(uhd::i2c_iface::sptr i2c, const uhd::usrp::mboard_eeprom_t &); diff --git a/host/lib/usrp/x300/x300_regs.hpp b/host/lib/usrp/x300/x300_regs.hpp index ac2fcc31e..eba30abb5 100644 --- a/host/lib/usrp/x300/x300_regs.hpp +++ b/host/lib/usrp/x300/x300_regs.hpp @@ -20,6 +20,7 @@  #include <uhd/config.hpp>  #include <stdint.h> +#include <uhd/utils/soft_register.hpp>  namespace uhd { namespace usrp { namespace radio { @@ -83,28 +84,12 @@ localparam ZPU_SR_ETHINT1    = 56;  #define ZPU_SR_SW_RST_RADIO_CLK_PLL     (1<<2)  #define ZPU_SR_SW_RST_ADC_IDELAYCTRL    (1<<3) -//clock controls -#define ZPU_SR_CLOCK_CTRL_CLK_SRC_EXTERNAL 0x00 -#define ZPU_SR_CLOCK_CTRL_CLK_SRC_INTERNAL 0x02 -#define ZPU_SR_CLOCK_CTRL_CLK_SRC_GPSDO    0x03 -#define ZPU_SR_CLOCK_CTRL_PPS_SRC_EXTERNAL 0x00 -#define ZPU_SR_CLOCK_CTRL_PPS_SRC_INTERNAL 0x02 -#define ZPU_SR_CLOCK_CTRL_PPS_SRC_GPSDO    0x03 -  localparam ZPU_RB_SPI        = 2;  localparam ZPU_RB_CLK_STATUS = 3;  localparam ZPU_RB_COMPAT_NUM = 6;  localparam ZPU_RB_ETH_TYPE0  = 4;  localparam ZPU_RB_ETH_TYPE1  = 5; -//clock status -#define ZPU_RB_CLK_STATUS_LMK_STATUS        (0x3 << 0) -#define ZPU_RB_CLK_STATUS_LMK_LOCK          (0x1 << 2) -#define ZPU_RB_CLK_STATUS_LMK_HOLDOVER      (0x1 << 3) -#define ZPU_RB_CLK_STATUS_PPS_DETECT        (0x1 << 4) -#define ZPU_RB_CLK_STATUS_RADIO_CLK_LOCK    (0x1 << 5) -#define ZPU_RB_CLK_STATUS_IDELAYCTRL_LOCK   (0x1 << 6) -  //spi slaves on radio  #define DB_DAC_SEN      (1 << 7)  #define DB_ADC_SEN      (1 << 6) @@ -209,5 +194,97 @@ static const uint32_t PCIE_ZPU_READ_CLOBBER     = 0x80000000;  static const uint32_t PCIE_ZPU_STATUS_BUSY      = 0x1;  static const uint32_t PCIE_ZPU_STATUS_SUSPENDED = 0x80000000; +//------------------------------------------------------------------- +// Register Maps +//------------------------------------------------------------------- +namespace uhd { namespace usrp { namespace x300 { +    class fw_regmap_t : public uhd::soft_regmap_t { +    public: +        typedef boost::shared_ptr<fw_regmap_t> sptr; + +        class clk_ctrl_reg_t : public uhd::soft_reg32_wo_t { +        public: +            UHD_DEFINE_SOFT_REG_FIELD(CLK_SOURCE,   /*width*/ 2, /*shift*/ 0);  //[1:0] +            UHD_DEFINE_SOFT_REG_FIELD(PPS_SELECT,   /*width*/ 2, /*shift*/ 2);  //[3:2] +            UHD_DEFINE_SOFT_REG_FIELD(PPS_OUT_EN,   /*width*/ 1, /*shift*/ 4);  //[4] +            UHD_DEFINE_SOFT_REG_FIELD(TCXO_EN,      /*width*/ 1, /*shift*/ 5);  //[5] +            UHD_DEFINE_SOFT_REG_FIELD(GPSDO_PWR_EN, /*width*/ 1, /*shift*/ 6);  //[6] + +            static const boost::uint32_t SRC_EXTERNAL = 0x0; +            static const boost::uint32_t SRC_INTERNAL = 0x2; +            static const boost::uint32_t SRC_GPSDO    = 0x3; + +            clk_ctrl_reg_t(): uhd::soft_reg32_wo_t(SR_ADDR(SET0_BASE, ZPU_SR_CLOCK_CTRL)) { +                //Initial values +                set(CLK_SOURCE, SRC_INTERNAL); +                set(PPS_SELECT, SRC_INTERNAL); +                set(PPS_OUT_EN, 0); +                set(TCXO_EN, 1); +                set(GPSDO_PWR_EN, 1);   //GPSDO power always ON +            } +        } clock_ctrl_reg; + +        class clk_status_reg_t : public uhd::soft_reg32_ro_t { +        public: +            UHD_DEFINE_SOFT_REG_FIELD(LMK_STATUS,       /*width*/ 2, /*shift*/ 0);  //[1:0] +            UHD_DEFINE_SOFT_REG_FIELD(LMK_LOCK,         /*width*/ 1, /*shift*/ 2);  //[2] +            UHD_DEFINE_SOFT_REG_FIELD(LMK_HOLDOVER,     /*width*/ 1, /*shift*/ 3);  //[3] +            UHD_DEFINE_SOFT_REG_FIELD(PPS_DETECT,       /*width*/ 1, /*shift*/ 4);  //[4] +            UHD_DEFINE_SOFT_REG_FIELD(RADIO_CLK_LOCK,   /*width*/ 1, /*shift*/ 5);  //[5] +            UHD_DEFINE_SOFT_REG_FIELD(IDELAYCTRL_LOCK,  /*width*/ 1, /*shift*/ 6);  //[6] + +            clk_status_reg_t(): uhd::soft_reg32_ro_t(SR_ADDR(SET0_BASE, ZPU_RB_CLK_STATUS)) {} +        } clock_status_reg; + +        fw_regmap_t() : soft_regmap_t("fw_regmap") { +            add_to_map(clock_ctrl_reg, "clock_ctrl_reg", PUBLIC); +            add_to_map(clock_status_reg, "clock_status_reg", PUBLIC); +        } +    }; + +    class radio_regmap_t : public uhd::soft_regmap_t { +    public: +        typedef boost::shared_ptr<radio_regmap_t> sptr; +        class misc_outs_reg_t : public uhd::soft_reg32_wo_t { +        public: +            UHD_DEFINE_SOFT_REG_FIELD(DAC_ENABLED,          /*width*/ 1, /*shift*/ 0);  //[0] +            UHD_DEFINE_SOFT_REG_FIELD(DAC_RESET_N,          /*width*/ 1, /*shift*/ 1);  //[1] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_RESET,            /*width*/ 1, /*shift*/ 2);  //[2] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_STB,     /*width*/ 1, /*shift*/ 3);  //[3] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_VAL,     /*width*/ 5, /*shift*/ 4);  //[8:4] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER_ENABLED,  /*width*/ 1, /*shift*/ 9);  //[9] + +            misc_outs_reg_t(): uhd::soft_reg32_wo_t(uhd::usrp::radio::sr_addr(uhd::usrp::radio::MISC_OUTS)) { +                //Initial values +                set(DAC_ENABLED, 0); +                set(DAC_RESET_N, 0); +                set(ADC_RESET, 0); +                set(ADC_DATA_DLY_STB, 0); +                set(ADC_DATA_DLY_VAL, 16); +                set(ADC_CHECKER_ENABLED, 0); +            } +        } misc_outs_reg; + +        class misc_ins_reg_t : public uhd::soft_reg32_ro_t { +        public: +            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_Q_LOCKED, /*width*/ 1, /*shift*/ 0);  //[0] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_I_LOCKED, /*width*/ 1, /*shift*/ 1);  //[1] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_Q_LOCKED, /*width*/ 1, /*shift*/ 2);  //[2] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_I_LOCKED, /*width*/ 1, /*shift*/ 3);  //[3] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_Q_ERROR,  /*width*/ 1, /*shift*/ 4);  //[4] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_I_ERROR,  /*width*/ 1, /*shift*/ 5);  //[5] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_Q_ERROR,  /*width*/ 1, /*shift*/ 6);  //[6] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_I_ERROR,  /*width*/ 1, /*shift*/ 7);  //[7] + +            misc_ins_reg_t(): uhd::soft_reg32_ro_t(uhd::usrp::radio::RB32_MISC_INS) { } +        } misc_ins_reg; + +        radio_regmap_t(int radio_num) : soft_regmap_t("radio" + boost::lexical_cast<std::string>(radio_num) + "_regmap") { +            add_to_map(misc_outs_reg, "misc_outs_reg", PUBLIC); +            add_to_map(misc_ins_reg, "misc_ins_reg", PUBLIC); +        } +    }; + +}}}  #endif /* INCLUDED_X300_REGS_HPP */ | 
