diff options
-rw-r--r-- | host/include/uhd/image_loader.hpp | 4 | ||||
-rw-r--r-- | host/lib/usrp/x300/x300_image_loader.cpp | 236 |
2 files changed, 222 insertions, 18 deletions
diff --git a/host/include/uhd/image_loader.hpp b/host/include/uhd/image_loader.hpp index 4ebac288e..6ecb1b794 100644 --- a/host/include/uhd/image_loader.hpp +++ b/host/include/uhd/image_loader.hpp @@ -1,5 +1,5 @@ // -// Copyright 2014-2015 Ettus Research LLC +// Copyright 2014-2017 Ettus Research // // 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 @@ -36,8 +36,10 @@ public: uhd::device_addr_t args; bool load_firmware; bool load_fpga; + bool download; std::string firmware_path; std::string fpga_path; + std::string out_path; } image_loader_args_t; //! Signature of an image loading function diff --git a/host/lib/usrp/x300/x300_image_loader.cpp b/host/lib/usrp/x300/x300_image_loader.cpp index e8c2a1329..f5564d2ce 100644 --- a/host/lib/usrp/x300/x300_image_loader.cpp +++ b/host/lib/usrp/x300/x300_image_loader.cpp @@ -1,5 +1,5 @@ // -// Copyright 2015 Ettus Research LLC +// Copyright 2015-2017 Ettus Research // // 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 @@ -58,6 +58,15 @@ using namespace uhd::transport; #define FPGA_LOAD_TIMEOUT 15 /* + * Bitstream header pattern + */ +static const uint8_t X300_FPGA_BIT_HEADER[] = +{ + 0x00, 0x09, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x00, 0x00, 0x01, 0x61, 0x00 +}; + +/* * Packet structure */ typedef struct { @@ -79,17 +88,20 @@ typedef struct { bool ethernet; bool configure; // Reload FPGA after burning to flash (Ethernet only) bool verify; // Device will verify the download along the way (Ethernet only) + bool download; // Host will read the FPGA image on the device to a file bool lvbitx; uhd::device_addr_t dev_addr; std::string ip_addr; std::string fpga_type; std::string resource; std::string filepath; + std::string outpath; std::string rpc_port; - uint32_t size; - udp_simple::sptr xport; + udp_simple::sptr write_xport; + udp_simple::sptr read_xport; + uint32_t size; + uint8_t data_in[udp_simple::mtu]; std::vector<char> bitstream; // .bin image extracted from .lvbitx file - uint8_t data_in[udp_simple::mtu]; } x300_session_t; /* @@ -157,7 +169,8 @@ static void x300_validate_image(x300_session_t &session){ static void x300_setup_session(x300_session_t &session, const device_addr_t &args, - const std::string &filepath){ + const std::string &filepath, + const std::string &outpath){ device_addrs_t devs = x300_find(args); if(devs.size() == 0){ session.found = false; @@ -188,9 +201,12 @@ static void x300_setup_session(x300_session_t &session, if(session.ethernet){ session.ip_addr = session.dev_addr["addr"]; session.configure = args.has_key("configure"); - session.xport = udp_simple::make_connected(session.ip_addr, - BOOST_STRINGIZE(X300_FPGA_PROG_UDP_PORT)); + session.write_xport = udp_simple::make_connected(session.ip_addr, + BOOST_STRINGIZE(X300_FPGA_PROG_UDP_PORT)); + session.read_xport = udp_simple::make_connected(session.ip_addr, + BOOST_STRINGIZE(X300_FPGA_READ_UDP_PORT)); session.verify = args.has_key("verify"); + session.download = args.has_key("download"); } else{ session.resource = session.dev_addr["resource"]; @@ -213,6 +229,23 @@ static void x300_setup_session(x300_session_t &session, } else session.filepath = filepath; + /* + * The user can specify an output image path, or UHD will use the + * system temporary path by default + */ + if(outpath == ""){ + if(!session.dev_addr.has_key("product") or session.fpga_type == ""){ + throw uhd::runtime_error("Found a device but could not auto-generate an image filename."); + } + std::string filename = str(boost::format("usrp_%s_fpga_%s") + % (to_lower_copy(session.dev_addr["product"])) + % session.fpga_type); + + session.outpath = get_tmp_path() + "/" + filename; + } else { + session.outpath = outpath; + } + // Validate image x300_validate_image(session); } @@ -240,7 +273,7 @@ static UHD_INLINE void x300_bitswap(uint8_t *num){ *num = ((*num & 0xF0) >> 4) | ((*num & 0x0F) << 4); *num = ((*num & 0xCC) >> 2) | ((*num & 0x33) << 2); *num = ((*num & 0xAA) >> 1) | ((*num & 0x55) << 1); -} +} static void x300_ethernet_load(x300_session_t &session){ @@ -250,7 +283,7 @@ static void x300_ethernet_load(x300_session_t &session){ // Initialize write session uint32_t flags = X300_FPGA_PROG_FLAGS_ACK | X300_FPGA_PROG_FLAGS_INIT; - size_t len = x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in); + size_t len = x300_send_and_recv(session.write_xport, flags, &pkt_out, session.data_in); if(x300_recv_ok(pkt_in, len)){ std::cout << "-- Initializing FPGA loading..." << std::flush; } @@ -312,7 +345,7 @@ static void x300_ethernet_load(x300_session_t &session){ pkt_out.data16[k] = htonx<uint16_t>(pkt_out.data16[k]); } - len = x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in); + len = x300_send_and_recv(session.write_xport, flags, &pkt_out, session.data_in); if(len == 0){ if(!session.lvbitx) image.close(); throw uhd::runtime_error("Timed out waiting for reply from device."); @@ -339,7 +372,7 @@ static void x300_ethernet_load(x300_session_t &session){ pkt_out.sector = pkt_out.index = pkt_out.size = 0; memset(pkt_out.data8, 0, X300_PACKET_SIZE_BYTES); std::cout << "-- Finalizing image load..." << std::flush; - len = x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in); + len = x300_send_and_recv(session.write_xport, flags, &pkt_out, session.data_in); if(len == 0){ std::cout << "failed." << std::endl; throw uhd::runtime_error("Timed out waiting for reply from device."); @@ -353,7 +386,7 @@ static void x300_ethernet_load(x300_session_t &session){ // Save new FPGA image (if option set) if(session.configure){ flags = (X300_FPGA_PROG_CONFIGURE | X300_FPGA_PROG_FLAGS_ACK); - x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in); + x300_send_and_recv(session.write_xport, flags, &pkt_out, session.data_in); std::cout << "-- Saving image onto device..." << std::flush; if(len == 0){ std::cout << "failed." << std::endl; @@ -368,6 +401,164 @@ static void x300_ethernet_load(x300_session_t &session){ std::cout << str(boost::format("Power-cycle the USRP %s to use the new image.") % session.dev_addr.get("product", "")) << std::endl; } +static void x300_ethernet_read(x300_session_t &session){ + + // UDP receive buffer + x300_fpga_update_data_t pkt_out; + memset(pkt_out.data8, 0, X300_PACKET_SIZE_BYTES); + + x300_fpga_update_data_t *pkt_in = reinterpret_cast<x300_fpga_update_data_t*>(session.data_in); + + // Initialize read session + uint32_t flags = X300_FPGA_READ_FLAGS_ACK | X300_FPGA_READ_FLAGS_INIT; + size_t len = x300_send_and_recv(session.read_xport, flags, &pkt_out, session.data_in); + if(x300_recv_ok(pkt_in, len)){ + std::cout << "-- Initializing FPGA reading..." << std::flush; + } + else if(len == 0){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Timed out waiting for reply from device."); + } + else{ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Device reported an error during initialization."); + } + + std::cout << "successful." << std::endl; + + // Read the first packet + // Acknowledge receipt of the FPGA image data + flags = X300_FPGA_READ_FLAGS_ACK; + + // Set the initial burn location + pkt_out.sector = htonx<uint32_t>(X300_FPGA_SECTOR_START); + pkt_out.index = 0; + pkt_out.size = htonx<uint32_t>(X300_PACKET_SIZE_BYTES / 2); + + len = x300_send_and_recv(session.read_xport, flags, &pkt_out, session.data_in); + if(len == 0){ + throw uhd::runtime_error("Timed out waiting for reply from device."); + } + else if((ntohl(pkt_in->flags) & X300_FPGA_READ_FLAGS_ERROR)){ + throw uhd::runtime_error("Device reported an error."); + } + + // Data must be bitswapped and byteswapped + for(size_t k = 0; k < X300_PACKET_SIZE_BYTES; k++){ + x300_bitswap(&pkt_in->data8[k]); + } + for(size_t k = 0; k < (X300_PACKET_SIZE_BYTES/2); k++){ + pkt_in->data16[k] = htonx<uint16_t>(pkt_in->data16[k]); + } + + // Assume the largest size first + size_t image_size = X300_FPGA_BIT_SIZE_BYTES; + size_t sectors = (image_size / X300_FLASH_SECTOR_SIZE); + std::string extension(".bit"); + + // Check for the beginning header sequence to determine + // the total amount of data (.bit vs .bin) on the flash + // The .bit file format includes header information not part of a .bin + for (size_t i = 0; i < sizeof(X300_FPGA_BIT_HEADER); i++) + { + if (pkt_in->data8[i] != X300_FPGA_BIT_HEADER[i]) + { + std::cout << "-- No *.bit header detected, FPGA image is a raw stream (*.bin)!" << std::endl; + image_size = X300_FPGA_BIN_SIZE_BYTES; + sectors = (image_size / X300_FLASH_SECTOR_SIZE); + extension = std::string(".bin"); + break; + } + } + + session.outpath += extension; + std::ofstream image(session.outpath.c_str(), std::ios::binary); + std::cout << boost::format("-- Output FPGA file: %s\n") + % session.outpath; + + // Write the first packet + image.write((char*)pkt_in->data8, X300_PACKET_SIZE_BYTES); + + // Each sector + size_t pkt_count = X300_PACKET_SIZE_BYTES; + for(size_t i = 0; i < image_size; i += X300_FLASH_SECTOR_SIZE){ + + // Once we determine the image size, print the progress percentage + std::cout << boost::format("\r-- Reading %s FPGA image: %d%% (%d/%d sectors)") + % session.fpga_type + % (int(double(i) / double(image_size) * 100.0)) + % (i / X300_FLASH_SECTOR_SIZE) + % sectors + << std::flush; + + // Each packet + while (pkt_count < image_size and pkt_count < (i + X300_FLASH_SECTOR_SIZE)) + { + // Set burn location + pkt_out.sector = htonx<uint32_t>(X300_FPGA_SECTOR_START + (i/X300_FLASH_SECTOR_SIZE)); + pkt_out.index = htonx<uint32_t>((pkt_count % X300_FLASH_SECTOR_SIZE) / 2); + + len = x300_send_and_recv(session.read_xport, flags, &pkt_out, session.data_in); + if(len == 0){ + image.close(); + throw uhd::runtime_error("Timed out waiting for reply from device."); + } + else if((ntohl(pkt_in->flags) & X300_FPGA_READ_FLAGS_ERROR)){ + image.close(); + throw uhd::runtime_error("Device reported an error."); + } + + // Data must be bitswapped and byteswapped + for(size_t k = 0; k < X300_PACKET_SIZE_BYTES; k++){ + x300_bitswap(&pkt_in->data8[k]); + } + for(size_t k = 0; k < (X300_PACKET_SIZE_BYTES/2); k++){ + pkt_in->data16[k] = htonx<uint16_t>(pkt_in->data16[k]); + } + + // Calculate the number of bytes to write + // If this is the last packet, get rid of the extra zero padding + // due to packet size + size_t nbytes = X300_PACKET_SIZE_BYTES; + if (pkt_count > (image_size - X300_PACKET_SIZE_BYTES)) + { + nbytes = (image_size - pkt_count); + } + + // Write the incoming piece of the image to a file + image.write((char*)pkt_in->data8, nbytes); + + // Increment the data count + pkt_count += X300_PACKET_SIZE_BYTES; + } + + pkt_count = i + X300_FLASH_SECTOR_SIZE; + } + + std::cout << boost::format("\r-- Reading %s FPGA image: 100%% (%d/%d sectors)") + % session.fpga_type + % sectors + % sectors + << std::endl; + + // Cleanup + image.close(); + flags = (X300_FPGA_READ_FLAGS_CLEANUP | X300_FPGA_READ_FLAGS_ACK); + pkt_out.sector = pkt_out.index = pkt_out.size = 0; + memset(pkt_out.data8, 0, X300_PACKET_SIZE_BYTES); + std::cout << "-- Finalizing image read for verification..." << std::flush; + len = x300_send_and_recv(session.read_xport, flags, &pkt_out, session.data_in); + if(len == 0){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Timed out waiting for reply from device."); + } + else if((ntohl(pkt_in->flags) & X300_FPGA_READ_FLAGS_ERROR)){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Device reported an error during cleanup."); + } + else std::cout << "successful image read." << std::endl; +} + static void x300_pcie_load(x300_session_t &session){ std::cout << boost::format("\r-- Loading %s FPGA image (this will take 5-10 minutes)...") @@ -389,13 +580,15 @@ static void x300_pcie_load(x300_session_t &session){ static bool x300_image_loader(const image_loader::image_loader_args_t &image_loader_args){ // See if any X3x0 with the given args is found device_addrs_t devs = x300_find(image_loader_args.args); - if(devs.size() == 0 or !image_loader_args.load_fpga) return false; + + if (devs.size() == 0) return false; x300_session_t session; x300_setup_session(session, image_loader_args.args, - image_loader_args.fpga_path - ); + image_loader_args.fpga_path, + image_loader_args.out_path); + if(!session.found) return false; std::cout << boost::format("Unit: USRP %s (%s, %s)\nFPGA Image: %s\n") @@ -404,8 +597,17 @@ static bool x300_image_loader(const image_loader::image_loader_args_t &image_loa % session.dev_addr[session.ethernet ? "addr" : "resource"] % session.filepath; - if(session.ethernet) x300_ethernet_load(session); - else x300_pcie_load(session); + // Download the FPGA image to a file + if(image_loader_args.download) { + std::cout << "Attempting to download the FPGA image ..." << std::endl; + x300_ethernet_read(session); + } + + if (not image_loader_args.load_fpga) return true; + + if (session.ethernet) x300_ethernet_load(session); + else x300_pcie_load(session); + return true; } |