aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul David <paul.david@ettus.com>2017-03-14 19:38:41 -0400
committerMartin Braun <martin.braun@ettus.com>2017-06-30 10:50:37 -0700
commit55ce8d93dbe5cd77d64ec8274f6b6d05027cc76f (patch)
treebfc02813c4e9e5f562e1f8288cbb3b77dccd1cfe
parentc857a9e2e1b8b959827b6a35863430d988100368 (diff)
downloaduhd-55ce8d93dbe5cd77d64ec8274f6b6d05027cc76f.tar.gz
uhd-55ce8d93dbe5cd77d64ec8274f6b6d05027cc76f.tar.bz2
uhd-55ce8d93dbe5cd77d64ec8274f6b6d05027cc76f.zip
X3xx: Added image loader support for reading FPGA images
-rw-r--r--host/include/uhd/image_loader.hpp4
-rw-r--r--host/lib/usrp/x300/x300_image_loader.cpp236
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;
}