diff options
author | Lane Kolbly <lane.kolbly@ni.com> | 2020-03-03 15:56:21 -0600 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2021-01-11 13:18:22 -0600 |
commit | 92cb632ce1e443b1b3cffdc314cab57943b39f8c (patch) | |
tree | 63b58d93d38e5cbf5b580e531d6956cbae8226a5 /host | |
parent | 53ef97460eea713a013244d88a4b08bf95f4a790 (diff) | |
download | uhd-92cb632ce1e443b1b3cffdc314cab57943b39f8c.tar.gz uhd-92cb632ce1e443b1b3cffdc314cab57943b39f8c.tar.bz2 uhd-92cb632ce1e443b1b3cffdc314cab57943b39f8c.zip |
uhd_image_loader: Consume .lvbitx files
.lvbitx files contain the device tree and the FPGA bitstream, so this
change allows a user to download a .lvbitx to their device instead of
having to juggle separate .bin and .dts files.
If a .lvbitx file does not contain a .dts section for any reason,
download will still succeed, and a warning will be printed that no dts
file was found. This behaviour was chosen (over erroring) to mimic the
existing behaviour when a .bin file is found but there is no
corresponding .dts file.
Diffstat (limited to 'host')
-rw-r--r-- | host/lib/usrp/mpmd/mpmd_image_loader.cpp | 354 |
1 files changed, 237 insertions, 117 deletions
diff --git a/host/lib/usrp/mpmd/mpmd_image_loader.cpp b/host/lib/usrp/mpmd/mpmd_image_loader.cpp index 06d9c53a2..497fa14d1 100644 --- a/host/lib/usrp/mpmd/mpmd_image_loader.cpp +++ b/host/lib/usrp/mpmd/mpmd_image_loader.cpp @@ -16,12 +16,18 @@ #include <uhd/utils/static.hpp> #include <uhdlib/utils/prefs.hpp> #include <boost/algorithm/string.hpp> +#include <boost/archive/iterators/binary_from_base64.hpp> +#include <boost/archive/iterators/transform_width.hpp> #include <boost/filesystem/convenience.hpp> +#include <boost/optional.hpp> +#include <boost/property_tree/xml_parser.hpp> +#include <cctype> #include <fstream> #include <iterator> #include <sstream> #include <streambuf> #include <string> +#include <vector> using namespace uhd; @@ -98,24 +104,201 @@ uhd::usrp::component_file_t generate_component(const std::string& id, return component_file; } -/* - * Function to be registered with uhd_image_loader - */ -static bool mpmd_image_loader(const image_loader::image_loader_args_t& image_loader_args) +boost::optional<std::vector<uint8_t>> parse_dts_from_lvbitx( + const boost::property_tree::ptree& pt) { - // See if any MPM devices with the given args are found - device_addr_t find_hint = prefs::get_usrp_args(image_loader_args.args); - find_hint.set("find_all", "1"); // We need to find all devices - device_addrs_t devs = mpmd_find(find_hint); + std::string dts; + try { + dts = pt.get<std::string>("Bitfile.Project.CompilationResultsTree." + "CompilationResults.deviceTreeOverlay"); + } catch (boost::property_tree::ptree_error&) { + UHD_LOG_WARNING( + "MPMD IMAGE LOADER", "Could not find DTS in .lvbitx file, not including it"); + return boost::none; + } - if (devs.size() != 1) { - // TODO: Do we want to handle multiple devices here? - // Or force uhd_image_loader to only feed us a single device? - return false; + if (dts.size() % 2 != 0) { + throw uhd::runtime_error( + "The deviceTreeOverlay is corrupt in the specified .lvbitx file"); } - // Grab the first device_addr and add the option to skip initialization - device_addr_t dev_addr(devs[0]); + + std::vector<uint8_t> text; + for (size_t i = 0; i < dts.size() / 2; i++) { + const char s[3] = {dts[i * 2], dts[i * 2 + 1], '\0'}; + const uint8_t value = static_cast<uint8_t>(strtoul(s, nullptr, 16)); + text.push_back(value); + } + + return text; +} + +std::vector<uint8_t> parse_bitstream_from_lvbitx(const boost::property_tree::ptree& pt) +{ + std::string encoded_bitstream = pt.get<std::string>("Bitfile.Bitstream"); + + // Strip the whitespace + encoded_bitstream.erase(std::remove_if(encoded_bitstream.begin(), + encoded_bitstream.end(), + [](char c) { return std::isspace(c); }), + encoded_bitstream.end()); + + // Base64-decode the result + namespace bai = boost::archive::iterators; + using base64_dec = bai::transform_width<bai::binary_from_base64<char*>, 8, 6>; + + std::vector<uint8_t> bitstream(base64_dec(encoded_bitstream.data()), + base64_dec(encoded_bitstream.data() + encoded_bitstream.length())); + + // Remove null bytes that were formed from the padding + const size_t pad_count = + std::count(encoded_bitstream.begin(), encoded_bitstream.end(), '='); + bitstream.erase(bitstream.end() - pad_count, bitstream.end()); + + return bitstream; +} + +static std::string get_fpga_path( + const image_loader::image_loader_args_t& image_loader_args, + device_addr_t dev_addr, + uhd::property_tree::sptr tree) +{ + // If the user provided a path to an fpga image, use that + if (not image_loader_args.fpga_path.empty()) { + if (boost::filesystem::exists(image_loader_args.fpga_path)) { + return image_loader_args.fpga_path; + } else { + throw uhd::runtime_error( + "FPGA file provided does not exist: " + image_loader_args.fpga_path); + } + } + // Otherwise, we need to generate one + else { + /* + * The user can specify an FPGA type (HG, XG, AA), rather than a + * filename. If the user does not specify one, this will default to + * the type currently on the device. If this cannot be determined, + * then the user is forced to specify a filename. + */ + const auto fpga_type = [image_loader_args, tree]() -> std::string { + // If the user didn't provide a type, use the type of currently + // loaded image on the device + if (image_loader_args.args.has_key("fpga")) { + return image_loader_args.args.get("fpga"); + } else if (tree->exists("/mboards/0/components/fpga")) { + // Pull the FPGA info from the property tree + // The getter should return a vector of a single + // component_file_t, so grab the metadata from that + auto fpga_metadata = tree->access<uhd::usrp::component_files_t>( + "/mboards/0/components/fpga") + .get()[0] + .metadata; + return fpga_metadata.get("type", ""); + } + return ""; + }(); // generate_fpga_type lambda function + UHD_LOG_TRACE("MPMD IMAGE LOADER", "FPGA type: " << fpga_type); + + if (!dev_addr.has_key("product")) { + throw uhd::runtime_error("Found a device but could not " + "auto-generate an image filename."); + } else if (fpga_type == "") { + return find_image_path( + "usrp_" + boost::algorithm::to_lower_copy(dev_addr["product"]) + "_fpga.bit"); + } else { + return find_image_path( + "usrp_" + boost::algorithm::to_lower_copy(dev_addr["product"]) + "_fpga_" + fpga_type + ".bit"); + } + } +} + +static uhd::usrp::component_files_t lvbitx_to_component_files( + std::string fpga_path, bool delay_reload) +{ + uhd::usrp::component_files_t all_component_files; + + boost::property_tree::ptree pt; + try { + boost::property_tree::xml_parser::read_xml(fpga_path, pt); + } catch (const boost::property_tree::xml_parser::xml_parser_error& ex) { + throw uhd::runtime_error( + std::string("Got error parsing lvbitx file: ") + ex.what()); + } + + const auto bitstream = parse_bitstream_from_lvbitx(pt); + + uhd::dict<std::string, std::string> fpga_metadata; + fpga_metadata.set("filename", "usrp_x410_fpga_LV.bin"); + fpga_metadata.set("reset", delay_reload ? "false" : "true"); + + uhd::usrp::component_file_t comp_fpga = + generate_component("fpga", bitstream, fpga_metadata); + all_component_files.push_back(comp_fpga); + + const auto maybe_dts = parse_dts_from_lvbitx(pt); + if (maybe_dts) { + const auto dts = maybe_dts.get(); + + uhd::dict<std::string, std::string> dts_metadata; + dts_metadata.set("filename", "usrp_x410_fpga_LV.dts"); + dts_metadata.set("reset", delay_reload ? "false" : "true"); + + uhd::usrp::component_file_t comp_dts = + generate_component("dts", dts, dts_metadata); + all_component_files.push_back(comp_dts); + } + + return all_component_files; +} + +static uhd::usrp::component_files_t bin_dts_to_component_files( + std::string fpga_path, bool delay_reload) +{ + uhd::usrp::component_files_t all_component_files; + + uhd::usrp::component_file_t comp_fpga = generate_component("fpga", fpga_path); + + // If we want to delay the image reloading, explicitly turn off the + // component reset flag + if (delay_reload) { + comp_fpga.metadata.set("reset", "false"); + } + all_component_files.push_back(comp_fpga); + // DTS component struct + // First, we need to determine the name + const std::string base_name = + boost::filesystem::change_extension(fpga_path, "").string(); + if (base_name == fpga_path) { + const std::string err_msg( + "Can't cut extension from FPGA filename... " + fpga_path); + throw uhd::runtime_error(err_msg); + } + UHD_LOG_TRACE("MPMD IMAGE LOADER", "base_name = " << base_name); + const std::string dts_path = base_name + ".dts"; + // Then try to generate it + try { + uhd::usrp::component_file_t comp_dts = generate_component("dts", dts_path); + // If we want to delay the image reloading, explicitly turn off the + // component reset flag + if (delay_reload) { + comp_dts.metadata.set("reset", "false"); + } + all_component_files.push_back(comp_dts); + UHD_LOG_TRACE("MPMD IMAGE LOADER", "FPGA and DTS images read from file."); + } catch (const uhd::runtime_error& ex) { + // If we can't find the DTS file, that's fine, continue without it + UHD_LOG_WARNING("MPMD IMAGE LOADER", ex.what()); + UHD_LOG_TRACE("MPMD IMAGE LOADER", "FPGA images read from file."); + } + + return all_component_files; +} + +static void mpmd_send_fpga_to_device( + const image_loader::image_loader_args_t& image_loader_args, device_addr_t dev_addr) +{ + // Skip initializing the device dev_addr["skip_init"] = "1"; + // Make the device uhd::device::sptr usrp = uhd::device::make(dev_addr, uhd::device::USRP); uhd::property_tree::sptr tree = usrp->get_tree(); @@ -135,118 +318,55 @@ static bool mpmd_image_loader(const image_loader::image_loader_args_t& image_loa comp_fpga_stub.metadata["just_reload"] = "true"; all_component_files.push_back(comp_fpga_stub); + } else if (not image_loader_args.id.empty() + && not image_loader_args.component.empty()) { + uhd::usrp::component_file_t component = generate_component(image_loader_args.id, + image_loader_args.component, + image_loader_args.metadata); + all_component_files.push_back(component); } else { - // check if a component was provided - if (not image_loader_args.id.empty() && not image_loader_args.component.empty()) { - uhd::usrp::component_file_t component = - generate_component(image_loader_args.id, - image_loader_args.component, - image_loader_args.metadata); - all_component_files.push_back(component); + // Determine if we need to delay the reload of fpga/dts components. + const bool delay_reload = image_loader_args.delay_reload; + UHD_LOG_TRACE( + "MPMD IMAGE LOADER", "Delay reload?: " << (delay_reload ? "Yes" : "No")); + + // FPGA component struct + const auto fpga_path = get_fpga_path(image_loader_args, dev_addr, tree); + UHD_LOG_TRACE("MPMD IMAGE LOADER", "FPGA path: " << fpga_path); + + // If the fpga_path is a lvbitx file, parse it as such + if (boost::filesystem::extension(fpga_path) == ".lvbitx") { + all_component_files = lvbitx_to_component_files(fpga_path, delay_reload); } else { - // Determine if we need to delay the reload of fpga/dts components. - bool delay_reload = image_loader_args.delay_reload; - UHD_LOG_TRACE( - "MPMD IMAGE LOADER", "Delay reload?: " << (delay_reload ? "Yes" : "No")); - - // FPGA component struct - const auto fpga_path = [image_loader_args, dev_addr, tree]() -> std::string { - // If the user provided a path to an fpga image, use that - if (not image_loader_args.fpga_path.empty()) { - if (boost::filesystem::exists(image_loader_args.fpga_path)) { - return image_loader_args.fpga_path; - } else { - throw uhd::runtime_error( - str(boost::format("FPGA file provided does not exist: %s") - % image_loader_args.fpga_path)); - } - } - // Otherwise, we need to generate one - else { - /* - * The user can specify an FPGA type (HG, XG, AA), rather than a - * filename. If the user does not specify one, this will default to - * the type currently on the device. If this cannot be determined, - * then the user is forced to specify a filename. - */ - const auto fpga_type = [image_loader_args, tree]() -> std::string { - // If the user didn't provide a type, use the type of currently - // loaded image on the device - if (image_loader_args.args.has_key("fpga")) { - return image_loader_args.args.get("fpga"); - } else if (tree->exists("/mboards/0/components/fpga")) { - // Pull the FPGA info from the property tree - // The getter should return a vector of a single - // component_file_t, so grab the metadata from that - auto fpga_metadata = - tree->access<uhd::usrp::component_files_t>( - "/mboards/0/components/fpga") - .get()[0] - .metadata; - return fpga_metadata.get("type", ""); - // TODO: Do we want to pull the type from the filename if its - // not available in the metadata directly? - } - return ""; - }(); // generate_fpga_type lambda function - UHD_LOG_TRACE("MPMD IMAGE LOADER", "FPGA type: " << fpga_type); - - if (!dev_addr.has_key("product")) { - throw uhd::runtime_error("Found a device but could not " - "auto-generate an image filename."); - } else if (fpga_type == "") { - return find_image_path(str( - boost::format("usrp_%s_fpga.bit") - % (boost::algorithm::to_lower_copy(dev_addr["product"])))); - } else { - return find_image_path( - str(boost::format("usrp_%s_fpga_%s.bit") - % (boost::algorithm::to_lower_copy(dev_addr["product"])) - % fpga_type)); - } - } - }(); // generate_fpga_path lambda function - UHD_LOG_TRACE("MPMD IMAGE LOADER", "FPGA path: " << fpga_path); - uhd::usrp::component_file_t comp_fpga = generate_component("fpga", fpga_path); - // If we want to delay the image reloading, explicitly turn off the - // component reset flag - if (delay_reload) { - comp_fpga.metadata.set("reset", "false"); - } - all_component_files.push_back(comp_fpga); - // DTS component struct - // First, we need to determine the name - const std::string base_name = - boost::filesystem::change_extension(fpga_path, "").string(); - if (base_name == fpga_path) { - const std::string err_msg( - "Can't cut extension from FPGA filename... " + fpga_path); - throw uhd::runtime_error(err_msg); - } - const std::string dts_path = base_name + ".dts"; - // Then try to generate it - try { - uhd::usrp::component_file_t comp_dts = - generate_component("dts", dts_path); - // If we want to delay the image reloading, explicitly turn off the - // component reset flag - if (delay_reload) { - comp_dts.metadata.set("reset", "false"); - } - all_component_files.push_back(comp_dts); - UHD_LOG_TRACE("MPMD IMAGE LOADER", "FPGA and DTS images read from file."); - } catch (const uhd::runtime_error& ex) { - // If we can't find the DTS file, that's fine, continue without it - UHD_LOG_WARNING("MPMD IMAGE LOADER", ex.what()); - UHD_LOG_TRACE("MPMD IMAGE LOADER", "FPGA images read from file."); - } + all_component_files = bin_dts_to_component_files(fpga_path, delay_reload); } } + // Call RPC to update the component UHD_LOG_INFO("MPMD IMAGE LOADER", "Starting update. This may take a while."); tree->access<uhd::usrp::component_files_t>("/mboards/0/components/fpga") .set(all_component_files); UHD_LOG_INFO("MPMD IMAGE LOADER", "Update component function succeeded."); +} + +/* + * Function to be registered with uhd_image_loader + */ +static bool mpmd_image_loader(const image_loader::image_loader_args_t& image_loader_args) +{ + // See if any MPM devices with the given args are found + device_addr_t find_hint = prefs::get_usrp_args(image_loader_args.args); + find_hint.set("find_all", "1"); // We need to find all devices + device_addrs_t devs = mpmd_find(find_hint); + + if (devs.size() != 1) { + UHD_LOG_ERROR("MPMD IMAGE LOADER", "mpmd_image_loader only supports a single device."); + return false; + } + // Grab the first device_addr + device_addr_t dev_addr(devs[0]); + + mpmd_send_fpga_to_device(image_loader_args, dev_addr); return true; } |