From d49ca2cf0fca954fc033cf4ddf3cd3502cc39548 Mon Sep 17 00:00:00 2001 From: Ashish Chaudhari Date: Tue, 4 Aug 2015 16:09:40 -0500 Subject: uhd: Added APIs to multi_usrp to read/write device registers - Added regmap object to soft_register library - Added a regmap_db object to hold multiple regmaps/dbs - Multiple soft_register enhancements --- host/include/uhd/usrp/multi_usrp.hpp | 29 +++ host/include/uhd/utils/soft_register.hpp | 359 ++++++++++++++++++++++++++++--- host/lib/usrp/multi_usrp.cpp | 90 ++++++++ 3 files changed, 454 insertions(+), 24 deletions(-) diff --git a/host/include/uhd/usrp/multi_usrp.hpp b/host/include/uhd/usrp/multi_usrp.hpp index 8d5dc2e5f..0c1e73895 100644 --- a/host/include/uhd/usrp/multi_usrp.hpp +++ b/host/include/uhd/usrp/multi_usrp.hpp @@ -974,6 +974,35 @@ 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 + ******************************************************************/ + + /*! + * 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; + + /*! + * 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 enumerate_registers(const size_t mboard = 0) = 0; + /******************************************************************* * Filter API methods ******************************************************************/ diff --git a/host/include/uhd/utils/soft_register.hpp b/host/include/uhd/utils/soft_register.hpp index d3537a618..f28b2aa2a 100644 --- a/host/include/uhd/utils/soft_register.hpp +++ b/host/include/uhd/utils/soft_register.hpp @@ -24,6 +24,15 @@ #include #include #include +#include +#include +#include +#include +#include + +//================================================================== +// 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)) @@ -49,19 +58,54 @@ namespace soft_reg_field { template inline size_t mask(const soft_reg_field_t field) { - return ((static_cast(1)<(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 (boost::lockfree::detail::likely((sizeof(data_t)*8) != width(field))) + return ((ONE< + inline static soft_reg_t& cast(soft_register_base& reg) { + soft_reg_t* ptr = dynamic_cast(®); + if (ptr) { + return *ptr; + } else { + throw uhd::type_error("failed to cast register to specified type"); + } + } +}; + /*! * 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 -class UHD_API soft_register_t : public boost::noncopyable { +template +class UHD_API soft_register_t : public soft_register_base { public: - typedef boost::shared_ptr< soft_register_t > sptr; + typedef boost::shared_ptr< soft_register_t > 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 @@ -88,7 +132,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,12 +161,12 @@ public: */ inline void flush() { - if (writeable && _iface) { - if (sizeof(reg_data_t) <= 2) { + if (writable && _iface) { + if (get_bitwidth() <= 16) { _iface->poke16(_wr_addr, static_cast(_soft_copy)); - } else if (sizeof(reg_data_t) <= 4) { + } else if (get_bitwidth() <= 32) { _iface->poke32(_wr_addr, static_cast(_soft_copy)); - } else if (sizeof(reg_data_t) <= 8) { + } else if (get_bitwidth() <= 64) { _iface->poke64(_wr_addr, static_cast(_soft_copy)); } else { throw uhd::not_implemented_error("soft_register only supports up to 64 bits."); @@ -138,11 +182,11 @@ public: inline void refresh() { if (readable && _iface) { - if (sizeof(reg_data_t) <= 2) { + if (get_bitwidth() <= 16) { _soft_copy = static_cast(_iface->peek16(_rd_addr)); - } else if (sizeof(reg_data_t) <= 4) { + } else if (get_bitwidth() <= 32) { _soft_copy = static_cast(_iface->peek32(_rd_addr)); - } else if (sizeof(reg_data_t) <= 8) { + } else if (get_bitwidth() <= 64) { _soft_copy = static_cast(_iface->peek64(_rd_addr)); } else { throw uhd::not_implemented_error("soft_register only supports up to 64 bits."); @@ -170,6 +214,31 @@ 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; @@ -181,59 +250,59 @@ private: * A synchronized soft register object. * All operations in the synchronized register are serialized. */ -template -class UHD_API soft_register_sync_t : public soft_register_t { +template +class UHD_API soft_register_sync_t : public soft_register_t { public: - typedef boost::shared_ptr< soft_register_sync_t > sptr; + typedef boost::shared_ptr< soft_register_sync_t > sptr; soft_register_sync_t(wb_iface::wb_addr_type wr_addr, wb_iface::wb_addr_type rd_addr): - soft_register_t(wr_addr, rd_addr), _mutex() + soft_register_t(wr_addr, rd_addr), _mutex() {} soft_register_sync_t(wb_iface::wb_addr_type addr): - soft_register_t(addr), _mutex() + soft_register_t(addr), _mutex() {} inline void initialize(wb_iface& iface, bool sync = false) { boost::lock_guard lock(_mutex); - soft_register_t::initialize(iface, sync); + soft_register_t::initialize(iface, sync); } inline void set(const soft_reg_field_t field, const reg_data_t value) { boost::lock_guard lock(_mutex); - soft_register_t::set(field, value); + soft_register_t::set(field, value); } inline reg_data_t get(const soft_reg_field_t field) { boost::lock_guard lock(_mutex); - return soft_register_t::get(field); + return soft_register_t::get(field); } inline void flush() { boost::lock_guard lock(_mutex); - soft_register_t::flush(); + soft_register_t::flush(); } inline void refresh() { boost::lock_guard lock(_mutex); - soft_register_t::refresh(); + soft_register_t::refresh(); } inline void write(const soft_reg_field_t field, const reg_data_t value) { boost::lock_guard lock(_mutex); - soft_register_t::write(field, value); + soft_register_t::write(field, value); } inline reg_data_t read(const soft_reg_field_t field) { boost::lock_guard lock(_mutex); - return soft_register_t::read(field); + return soft_register_t::read(field); } private: @@ -306,6 +375,248 @@ typedef soft_register_sync_t 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 sptr; + + virtual ~soft_regmap_accessor_t() {}; + virtual soft_register_base& lookup(const std::string& path) const = 0; + virtual std::vector 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 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 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 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 enumerate() const { + std::vector 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 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 regmap_t; + typedef std::list 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 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 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 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 tokens; + BOOST_FOREACH( + const std::string& node, + boost::tokenizer< boost::char_separator >(path, boost::char_separator("/"))) + { + 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 enumerate() const { + std::vector paths; + BOOST_FOREACH(const soft_regmap_accessor_t* regmap, _regmaps) { + const std::vector& regs = regmap->enumerate(); + paths.insert(paths.end(), regs.begin(), regs.end()); + } + BOOST_FOREACH(const soft_regmap_accessor_t* db, _regmap_dbs) { + const std::vector& regs = db->enumerate(); + paths.insert(paths.end(), regs.begin(), regs.end()); + } + return paths; + } + +private: + typedef std::list 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..285799674 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -1371,6 +1372,95 @@ 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(mb_root(mboard) / "registers").get(); + uhd::soft_register_base& reg = accessor->lookup(path); + + switch (reg.get_bitwidth()) { + case 16: + if (reg.is_readable()) + uhd::soft_register_base::cast(reg).write(field, static_cast(value)); + else + uhd::soft_register_base::cast(reg).write(field, static_cast(value)); + break; + + case 32: + if (reg.is_readable()) + uhd::soft_register_base::cast(reg).write(field, static_cast(value)); + else + uhd::soft_register_base::cast(reg).write(field, static_cast(value)); + break; + + case 64: + if (reg.is_readable()) + uhd::soft_register_base::cast(reg).write(field, value); + else + uhd::soft_register_base::cast(reg).write(field, value); + break; + + default: + throw uhd::assertion_error("register has invalid bitwidth"); + } + + } else { + throw uhd::not_implemented_error("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(mb_root(mboard) / "registers").get(); + uhd::soft_register_base& reg = accessor->lookup(path); + + switch (reg.get_bitwidth()) { + case 16: + if (reg.is_writable()) + return static_cast(uhd::soft_register_base::cast(reg).read(field)); + else + return static_cast(uhd::soft_register_base::cast(reg).read(field)); + break; + + case 32: + if (reg.is_writable()) + return static_cast(uhd::soft_register_base::cast(reg).read(field)); + else + return static_cast(uhd::soft_register_base::cast(reg).read(field)); + break; + + case 64: + if (reg.is_writable()) + return uhd::soft_register_base::cast(reg).read(field); + else + return uhd::soft_register_base::cast(reg).read(field); + break; + + default: + throw uhd::assertion_error("register has invalid bitwidth: " + path); + } + } else { + throw uhd::not_implemented_error("register IO not supported for this device"); + } + } + + std::vector enumerate_registers(const size_t mboard) + { + if (_tree->exists(mb_root(mboard) / "registers")) + { + uhd::soft_regmap_accessor_t::sptr accessor = + _tree->access(mb_root(mboard) / "registers").get(); + return accessor->enumerate(); + } else { + return std::vector(); + } + } + private: device::sptr _dev; property_tree::sptr _tree; -- cgit v1.2.3