diff options
Diffstat (limited to 'host')
-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 */ |