aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--host/include/uhd/usrp/multi_usrp.hpp42
-rw-r--r--host/include/uhd/utils/dirty_tracked.hpp132
-rw-r--r--host/include/uhd/utils/soft_register.hpp425
-rw-r--r--host/lib/usrp/multi_usrp.cpp116
-rw-r--r--host/lib/usrp/x300/x300_adc_dac_utils.cpp68
-rw-r--r--host/lib/usrp/x300/x300_impl.cpp143
-rw-r--r--host/lib/usrp/x300/x300_impl.hpp52
-rw-r--r--host/lib/usrp/x300/x300_regs.hpp109
8 files changed, 875 insertions, 212 deletions
diff --git a/host/include/uhd/usrp/multi_usrp.hpp b/host/include/uhd/usrp/multi_usrp.hpp
index 8d5dc2e5f..80501e489 100644
--- a/host/include/uhd/usrp/multi_usrp.hpp
+++ b/host/include/uhd/usrp/multi_usrp.hpp
@@ -975,6 +975,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 register 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..8be80fb1f
--- /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:
+ bool _dirty;
+ data_t _data;
+ };
+
+} //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..63dccf06a 100644
--- a/host/include/uhd/utils/soft_register.hpp
+++ b/host/include/uhd/utils/soft_register.hpp
@@ -22,14 +22,45 @@
#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>
+
+//==================================================================
+// 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 {
+//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
@@ -49,33 +80,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 shit 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*>(&reg);
+ 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 +161,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 +190,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 +217,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 +250,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 +417,248 @@ 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.
+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, &reg)).second) {
+ throw uhd::assertion_error("cannot add two registers with the same name to regmap: " + name);
+ }
+ }
+ _reglist.push_back(&reg);
+ }
+
+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(&regmap);
+ }
+
+ /*!
+ * 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 */