diff options
Diffstat (limited to 'host/lib/rfnoc/blockdef_xml_impl.cpp')
-rw-r--r-- | host/lib/rfnoc/blockdef_xml_impl.cpp | 442 |
1 files changed, 442 insertions, 0 deletions
diff --git a/host/lib/rfnoc/blockdef_xml_impl.cpp b/host/lib/rfnoc/blockdef_xml_impl.cpp new file mode 100644 index 000000000..5ff69d512 --- /dev/null +++ b/host/lib/rfnoc/blockdef_xml_impl.cpp @@ -0,0 +1,442 @@ +// +// Copyright 2014-2015 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/>. +// + +#include <uhd/exception.hpp> +#include <uhd/rfnoc/constants.hpp> +#include <uhd/rfnoc/blockdef.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/paths.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/filesystem/operations.hpp> +#include <boost/property_tree/ptree.hpp> +#include <boost/property_tree/xml_parser.hpp> +#include <cstdlib> + +using namespace uhd; +using namespace uhd::rfnoc; +namespace fs = boost::filesystem; +namespace pt = boost::property_tree; + +static const fs::path XML_BLOCKS_SUBDIR("blocks"); +static const fs::path XML_COMPONENTS_SUBDIR("components"); +static const fs::path XML_EXTENSION(".xml"); + + +/**************************************************************************** + * port_t stuff + ****************************************************************************/ +const device_addr_t blockdef::port_t::PORT_ARGS( + "name," + "type," + "vlen=0," + "pkt_size=0," + "optional=0," + "bursty=0," + "port," +); + +blockdef::port_t::port_t() +{ + // This guarantees that we can access these keys + // even if they were never initialized: + BOOST_FOREACH(const std::string &key, PORT_ARGS.keys()) { + set(key, PORT_ARGS[key]); + } +} + +bool blockdef::port_t::is_variable(const std::string &key) const +{ + const std::string &val = get(key); + return (val[0] == '$'); +} + +bool blockdef::port_t::is_keyword(const std::string &key) const +{ + const std::string &val = get(key); + return (val[0] == '%'); +} + +bool blockdef::port_t::is_valid() const +{ + // Check we have all the keys: + BOOST_FOREACH(const std::string &key, PORT_ARGS.keys()) { + if (not has_key(key)) { + return false; + } + } + + // Twelve of the clock, all seems well + return true; +} + +std::string blockdef::port_t::to_string() const +{ + std::string result; + BOOST_FOREACH(const std::string &key, PORT_ARGS.keys()) { + if (has_key(key)) { + result += str(boost::format("%s=%s,") % key % get(key)); + } + } + + return result; +} + +/**************************************************************************** + * arg_t stuff + ****************************************************************************/ +const device_addr_t blockdef::arg_t::ARG_ARGS( + // List all tags/args an <arg> can have here: + "name," + "type," + "value," + "check," + "check_message," + "action," + "port=0," +); + +const std::set<std::string> blockdef::arg_t::VALID_TYPES = boost::assign::list_of + // List all tags/args a <type> can have here: + ("string") + ("int") + ("int_vector") + ("double") +; + +blockdef::arg_t::arg_t() +{ + // This guarantees that we can access these keys + // even if they were never initialized: + BOOST_FOREACH(const std::string &key, ARG_ARGS.keys()) { + set(key, ARG_ARGS[key]); + } +} + +bool blockdef::arg_t::is_valid() const +{ + // 1. Check we have all the keys: + BOOST_FOREACH(const std::string &key, ARG_ARGS.keys()) { + if (not has_key(key)) { + return false; + } + } + + // 2. Check arg type is valid + if (not get("type").empty() and not VALID_TYPES.count(get("type"))) { + return false; + } + + // Twelve of the clock, all seems well + return true; +} + +std::string blockdef::arg_t::to_string() const +{ + std::string result; + BOOST_FOREACH(const std::string &key, ARG_ARGS.keys()) { + if (has_key(key)) { + result += str(boost::format("%s=%s,") % key % get(key)); + } + } + + return result; +} + +/**************************************************************************** + * blockdef_impl stuff + ****************************************************************************/ +class blockdef_xml_impl : public blockdef +{ +public: + enum xml_repr_t { + DESCRIBES_BLOCK, + DESCRIBES_COMPONENT + }; + + //! Returns a list of base paths for the XML files. + // It is assumed that block definitions are in a subdir with name + // XML_BLOCKS_SUBDIR and component definitions in a subdir with name + // XML_COMPONENTS_SUBDIR + static std::vector<boost::filesystem::path> get_xml_paths() + { + std::vector<boost::filesystem::path> paths; + + // Path from environment variable + if (std::getenv(XML_PATH_ENV.c_str()) != NULL) { + paths.push_back(boost::filesystem::path(std::getenv(XML_PATH_ENV.c_str()))); + } + + // Finally, the default path + const boost::filesystem::path pkg_path = uhd::get_pkg_path(); + paths.push_back(pkg_path / XML_DEFAULT_PATH); + + return paths; + } + + //! Matches a NoC ID through substring matching + static bool match_noc_id(const std::string &lhs_, boost::uint64_t rhs_) + { + // Sanitize input: Make both values strings with all uppercase + // characters and no leading 0x. Check inputs are valid. + std::string lhs = boost::to_upper_copy(lhs_); + std::string rhs = str(boost::format("%016X") % rhs_); + if (lhs.size() > 2 and lhs[0] == '0' and lhs[1] == 'X') { + lhs = lhs.substr(2); + } + UHD_ASSERT_THROW(rhs.size() == 16); + if (lhs.size() < 4 or lhs.size() > 16) { + throw uhd::value_error(str(boost::format( + "%s is not a valid NoC ID (must be hexadecimal, min 4 and max 16 characters)" + ) % lhs_)); + } + + // OK, all good now. Next, we try and match the substring lhs in rhs: + return (rhs.find(lhs) == 0); + } + + //! Open the file at filename and see if it's a block definition for the given NoC ID + static bool has_noc_id(boost::uint64_t noc_id, const fs::path &filename) + { + pt::ptree propt; + try { + read_xml(filename.string(), propt); + BOOST_FOREACH(pt::ptree::value_type &v, propt.get_child("nocblock.ids")) { + if (v.first == "id" and match_noc_id(v.second.data(), noc_id)) { + return true; + } + } + } catch (std::exception &e) { + UHD_MSG(warning) << "has_noc_id(): caught exception " << e.what() << std::endl; + return false; + } + return false; + } + + blockdef_xml_impl(const fs::path &filename, boost::uint64_t noc_id, xml_repr_t type=DESCRIBES_BLOCK) : + _type(type), + _noc_id(noc_id) + { + //UHD_MSG(status) << "Reading XML file: " << filename.string().c_str() << std::endl; + read_xml(filename.string(), _pt); + try { + // Check key is valid + get_key(); + // Check name is valid + get_name(); + // Check there's at least one port + ports_t in = get_input_ports(); + ports_t out = get_output_ports(); + if (in.empty() and out.empty()) { + throw uhd::runtime_error("Block does not define inputs or outputs."); + } + // Check args are valid + get_args(); + // TODO any more checks? + } catch (const std::exception &e) { + throw uhd::runtime_error(str( + boost::format("Invalid block definition in %s: %s") + % filename.string() % e.what() + )); + } + } + + bool is_block() const + { + return _type == DESCRIBES_BLOCK; + } + + bool is_component() const + { + return _type == DESCRIBES_COMPONENT; + } + + std::string get_key() const + { + try { + return _pt.get<std::string>("nocblock.key"); + } catch (const pt::ptree_bad_path &) { + return _pt.get<std::string>("nocblock.blockname"); + } + } + + std::string get_name() const + { + return _pt.get<std::string>("nocblock.blockname"); + } + + boost::uint64_t noc_id() const + { + return _noc_id; + } + + ports_t get_input_ports() + { + return _get_ports("sink"); + } + + ports_t get_output_ports() + { + return _get_ports("source"); + } + + ports_t _get_ports(const std::string &port_type) + { + std::set<size_t> port_numbers; + size_t n_ports = 0; + ports_t ports; + BOOST_FOREACH(pt::ptree::value_type &v, _pt.get_child("nocblock.ports")) { + if (v.first != port_type) continue; + // Now we have the correct sink or source node: + port_t port; + BOOST_FOREACH(const std::string &key, port_t::PORT_ARGS.keys()) { + port[key] = v.second.get(key, port_t::PORT_ARGS[key]); + } + // We have to be extra-careful with the port numbers: + if (port["port"].empty()) { + port["port"] = boost::lexical_cast<std::string>(n_ports); + } + size_t new_port_number; + try { + new_port_number = boost::lexical_cast<size_t>(port["port"]); + } catch (const boost::bad_lexical_cast &e) { + throw uhd::value_error(str( + boost::format("Invalid port number '%s' on port '%s'") + % port["port"] % port["name"] + )); + } + if (port_numbers.count(new_port_number) or new_port_number > MAX_NUM_PORTS) { + throw uhd::value_error(str( + boost::format("Port '%s' has invalid port number %d!") + % port["name"] % new_port_number + )); + } + port_numbers.insert(new_port_number); + n_ports++; + ports.push_back(port); + } + return ports; + } + + std::vector<size_t> get_all_port_numbers() + { + std::set<size_t> set_ports; + BOOST_FOREACH(const port_t &port, get_input_ports()) { + set_ports.insert(boost::lexical_cast<size_t>(port["port"])); + } + BOOST_FOREACH(const port_t &port, get_output_ports()) { + set_ports.insert(boost::lexical_cast<size_t>(port["port"])); + } + return std::vector<size_t>(set_ports.begin(), set_ports.end()); + } + + + blockdef::args_t get_args() + { + args_t args; + bool is_valid = true; + pt::ptree def; + BOOST_FOREACH(pt::ptree::value_type &v, _pt.get_child("nocblock.args", def)) { + arg_t arg; + if (v.first != "arg") continue; + BOOST_FOREACH(const std::string &key, arg_t::ARG_ARGS.keys()) { + arg[key] = v.second.get(key, arg_t::ARG_ARGS[key]); + } + if (arg["type"].empty()) { + arg["type"] = "string"; + } + if (not arg.is_valid()) { + UHD_MSG(warning) << boost::format("Found invalid argument: %s") % arg.to_string() << std::endl; + is_valid = false; + } + args.push_back(arg); + } + if (not is_valid) { + throw uhd::runtime_error(str( + boost::format("Found invalid arguments for block %s.") + % get_name() + )); + } + return args; + } + + registers_t get_settings_registers() + { + return _get_regs("setreg"); + } + + registers_t get_readback_registers() + { + return _get_regs("readback"); + } + + registers_t _get_regs(const std::string ®_type) + { + registers_t registers; + pt::ptree def; + BOOST_FOREACH(pt::ptree::value_type &v, _pt.get_child("nocblock.registers", def)) { + if (v.first != reg_type) continue; + registers[v.second.get<std::string>("name")] = + boost::lexical_cast<size_t>(v.second.get<size_t>("address")); + } + return registers; + } + + +private: + + //! Tells us if is this for a NoC block, or a component. + const xml_repr_t _type; + //! The NoC-ID as reported (there may be several valid NoC IDs, this is the one used) + const boost::uint64_t _noc_id; + + //! This is a boost property tree, not the same as + // our property tree. + pt::ptree _pt; + +}; + +blockdef::sptr blockdef::make_from_noc_id(boost::uint64_t noc_id) +{ + std::vector<fs::path> paths = blockdef_xml_impl::get_xml_paths(); + // Iterate over all paths + BOOST_FOREACH(const fs::path &base_path, paths) { + fs::path this_path = base_path / XML_BLOCKS_SUBDIR; + if (not fs::exists(this_path) or not fs::is_directory(this_path)) { + continue; + } + // Iterate over all .xml files + fs::directory_iterator end_itr; + for (fs::directory_iterator i(this_path); i != end_itr; ++i) { + if (not fs::exists(*i) or fs::is_directory(*i) or fs::is_empty(*i)) { + continue; + } + if (i->path().filename().extension() != XML_EXTENSION) { + continue; + } + if (blockdef_xml_impl::has_noc_id(noc_id, i->path())) { + return blockdef::sptr(new blockdef_xml_impl(i->path(), noc_id)); + } + } + } + + return blockdef::sptr(); +} +// vim: sw=4 et: |