From 9d10efa5f0f81e1a971e92b28ba7f38e0384fdab Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Tue, 17 Aug 2010 18:35:11 -0700 Subject: UDP firmware update support for USRP2P. The hooks are in there for USRP2, but without CPLD changes it won't support it. Added an app host/utils/usrp2p_fw_update.py to write to USRP2P over the wire. Lots of TODOs in that file. Caveat -- fw_common.h, bootloader_utils.h, and the .py app MUST ALL AGREE! --- firmware/microblaze/usrp2p/udp_fw_update.c | 116 +++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 firmware/microblaze/usrp2p/udp_fw_update.c (limited to 'firmware/microblaze/usrp2p/udp_fw_update.c') diff --git a/firmware/microblaze/usrp2p/udp_fw_update.c b/firmware/microblaze/usrp2p/udp_fw_update.c new file mode 100644 index 000000000..6b860007a --- /dev/null +++ b/firmware/microblaze/usrp2p/udp_fw_update.c @@ -0,0 +1,116 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 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 . + */ + +//Routines to handle updating the SPI Flash firmware via UDP + +#include +#include "usrp2/fw_common.h" +#include "spi.h" +#include "spi_flash.h" +#include "udp_fw_update.h" +#include +#include +#include "ethernet.h" + +//Firmware update packet handler +void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst, + unsigned char *payload, int payload_len) { +//okay, we can do this ANY NUMBER of ways. we have up to ~1500 bytes to play with -- we don't handle +//fragmentation, so limit it to id; + + //ensure that the protocol versions match + if (payload_len >= sizeof(uint32_t) && update_data_in->proto_ver != USRP2_FW_COMPAT_NUM){ + printf("!Error in update packet handler: Expected compatibility number %d, but got %d\n", + USRP2_FW_COMPAT_NUM, update_data_in->proto_ver + ); + update_data_in_id = USRP2_FW_UPDATE_ID_OHAI_LOL; //so we can respond + } + + //ensure that this is not a short packet + if (payload_len < sizeof(udp_fw_update_data_t)){ + printf("!Error in update packet handler: Expected payload length %d, but got %d\n", + (int)sizeof(udp_fw_update_data_t), payload_len + ); + update_data_in_id = USRP2_FW_UPDATE_ID_WAT; + } + + spi_flash_async_state_t spi_flash_async_state; + + switch(update_data_in_id) { + case USRP2_FW_UPDATE_ID_OHAI_LOL: //why hello there you handsome devil + update_data_out.id = USRP2_FW_UPDATE_ID_OHAI_OMG; + memcpy(&update_data_out.data.ip_addr, (void *)get_ip_addr(), sizeof(struct ip_addr)); + break; + + case USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL: //query sector size, memory size so the host can mind the boundaries + update_data_out.data.flash_info_args.sector_size_bytes = spi_flash_sector_size(); + update_data_out.data.flash_info_args.memory_size_bytes = spi_flash_memory_size(); + update_data_out.id = USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG; + break; + + case USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL: //out with the old + spi_flash_async_erase_start(&spi_flash_async_state, update_data_in->data.flash_args.flash_addr, update_data_in->data.flash_args.length); + update_data_out.id = USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG; + break; + + case USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL: + //poll for done, set something in the reply packet + //spi_flash_async_erase_poll() also advances the state machine, so you should call it reasonably often to get things done quicker + if(spi_flash_async_erase_poll(&spi_flash_async_state)) update_data_out.id = USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG; + else update_data_out.id = USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG; + break; + + case USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL: //and in with the new + //spi_flash_program() goes pretty quick compared to page erases, so we don't bother polling -- it'll come back in some milliseconds + //if it doesn't come back fast enough, we'll just write smaller packets at a time until it does + spi_flash_program(update_data_in->data.flash_args.flash_addr, update_data_in->data.flash_args.length, update_data_in->data.flash_args.data); + update_data_out.id = USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG; + break; + + case USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL: //for verify + spi_flash_read(update_data_in->data.flash_args.flash_addr, update_data_in->data.flash_args.length, update_data_out.data.flash_args.data); + update_data_out.id = USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG; + break; + + case USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL: //for if we ever get the ICAP working + //should reset via icap_reload_fpga(uint32_t flash_address); + update_data_out.id = USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG; + //you should note that if you get a reply packet to this the reset has obviously failed + break; + +// case USRP2_FW_UPDATE_ID_KTHXBAI: //see ya +// break; + + default: //uhhhh + update_data_out.id = USRP2_FW_UPDATE_ID_WAT; + } + send_udp_pkt(USRP2_UDP_UPDATE_PORT, src, &update_data_out, sizeof(update_data_out)); +} -- cgit v1.2.3 From 42547a0be8e52758d340fd1eba51a2dd5274a652 Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Wed, 18 Aug 2010 14:38:50 -0700 Subject: Moved UDP firmware update stuff out of fw_common.h into udp_fw_update.h. This stuff should only go back into fw_common.h if we decide to integrate firmware update into the UHD code. Right now it's a separate Python script. Also moved udp_fw_update.h to lib/ because it's the same between USRP2 and USRP2P. --- firmware/microblaze/Makefile.common | 9 +++- firmware/microblaze/lib/udp_fw_update.h | 71 ++++++++++++++++++++++++++++++ firmware/microblaze/usrp2/udp_fw_update.c | 4 +- firmware/microblaze/usrp2/udp_fw_update.h | 22 --------- firmware/microblaze/usrp2p/Makefile.am | 2 +- firmware/microblaze/usrp2p/udp_fw_update.c | 20 +++------ firmware/microblaze/usrp2p/udp_fw_update.h | 22 --------- host/lib/usrp/usrp2/fw_common.h | 48 -------------------- host/utils/usrp2p_fw_update.py | 2 +- 9 files changed, 87 insertions(+), 113 deletions(-) create mode 100644 firmware/microblaze/lib/udp_fw_update.h delete mode 100644 firmware/microblaze/usrp2/udp_fw_update.h delete mode 100644 firmware/microblaze/usrp2p/udp_fw_update.h (limited to 'firmware/microblaze/usrp2p/udp_fw_update.c') diff --git a/firmware/microblaze/Makefile.common b/firmware/microblaze/Makefile.common index ceb6a553a..4e726edab 100644 --- a/firmware/microblaze/Makefile.common +++ b/firmware/microblaze/Makefile.common @@ -59,8 +59,13 @@ COMMON_LFLAGS = \ ######################################################################## # Common stuff for building top level microblaze images ######################################################################## +#we use COMMON_IHX_ARGS to relocate the reset and interrupt vectors to +#just below the start of code. upon creating the BIN, any leading padding +#is thrown out, so the .bin file is valid for uploading to USRP2P. this +#does not affect USRP2 because the USRP2 already starts at 0x0000, and +#because the relocate_args are not defined for USRP2's Makefile.am. .elf.bin: - $(MB_OBJCOPY) -O binary $< $@ + $(MB_OBJCOPY) -O binary $(RELOCATE_ARGS) $< $@ .elf.dump: $(MB_OBJDUMP) -DSC $< > $@ @@ -69,7 +74,7 @@ COMMON_LFLAGS = \ $(HEXDUMP) -v -e'1/1 "%.2X\n"' $< > $@ .elf.ihx: - $(MB_OBJCOPY) -O ihex $(COMMON_IHX_ARGS) $< $@ + $(MB_OBJCOPY) -O ihex $(RELOCATE_ARGS) $< $@ _generated_from_elf = \ $(noinst_PROGRAMS:.elf=.map) \ diff --git a/firmware/microblaze/lib/udp_fw_update.h b/firmware/microblaze/lib/udp_fw_update.h new file mode 100644 index 000000000..d25525bd2 --- /dev/null +++ b/firmware/microblaze/lib/udp_fw_update.h @@ -0,0 +1,71 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 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 . + */ + +#include "net_common.h" + +#define USRP2_UDP_UPDATE_PORT 49154 + +typedef enum { + USRP2_FW_UPDATE_ID_WAT = ' ', + + USRP2_FW_UPDATE_ID_OHAI_LOL = 'a', + USRP2_FW_UPDATE_ID_OHAI_OMG = 'A', + + USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL = 'f', + USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG = 'F', + + USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL = 'e', + USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG = 'E', + + USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL = 'd', + USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG = 'D', + USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG = 'B', + + USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL = 'w', + USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG = 'W', + + USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL = 'r', + USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG = 'R', + + USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL = 's', + USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG = 'S', + + USRP2_FW_UPDATE_ID_KTHXBAI = '~' + +} usrp2_fw_update_id_t; + +typedef struct { + uint32_t proto_ver; + uint32_t id; + uint32_t seq; + union { + uint32_t ip_addr; + struct { + uint32_t flash_addr; + uint32_t length; + uint8_t data[256]; + } flash_args; + struct { + uint32_t sector_size_bytes; + uint32_t memory_size_bytes; + } flash_info_args; + } data; +} usrp2_fw_update_data_t; + +void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst, + unsigned char *payload, int payload_len); diff --git a/firmware/microblaze/usrp2/udp_fw_update.c b/firmware/microblaze/usrp2/udp_fw_update.c index bd62aed85..14eb0b1ee 100644 --- a/firmware/microblaze/usrp2/udp_fw_update.c +++ b/firmware/microblaze/usrp2/udp_fw_update.c @@ -20,14 +20,14 @@ #include "net_common.h" #include "usrp2/fw_common.h" -#include "udp_fw_update.h" #include +#include "udp_fw_update.h" //Firmware update packet handler void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst, unsigned char *payload, int payload_len) { - udp_fw_update_data_t update_data_out; + usrp2_fw_update_data_t update_data_out; update_data_out.id = USRP2_FW_UPDATE_ID_WAT; send_udp_pkt(USRP2_UDP_UPDATE_PORT, src, &update_data_out, sizeof(update_data_out)); diff --git a/firmware/microblaze/usrp2/udp_fw_update.h b/firmware/microblaze/usrp2/udp_fw_update.h deleted file mode 100644 index dcf44bc42..000000000 --- a/firmware/microblaze/usrp2/udp_fw_update.h +++ /dev/null @@ -1,22 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2010 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 . - */ - -#include "net_common.h" - -void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst, - unsigned char *payload, int payload_len); diff --git a/firmware/microblaze/usrp2p/Makefile.am b/firmware/microblaze/usrp2p/Makefile.am index c9ed3b99b..e6bff2dbc 100644 --- a/firmware/microblaze/usrp2p/Makefile.am +++ b/firmware/microblaze/usrp2p/Makefile.am @@ -27,7 +27,7 @@ AM_LDFLAGS = \ -Wl,-defsym -Wl,_STACK_SIZE=3072 #all of this here is to relocate the hardware vectors to somewhere normal. -COMMON_IHX_ARGS = \ +RELOCATE_ARGS = \ --change-section-address .vectors.sw_exception+0x8000 \ --change-section-address .vectors.hw_exception+0x8000 \ --change-section-address .vectors.interrupt+0x8000 \ diff --git a/firmware/microblaze/usrp2p/udp_fw_update.c b/firmware/microblaze/usrp2p/udp_fw_update.c index 6b860007a..9242242e7 100644 --- a/firmware/microblaze/usrp2p/udp_fw_update.c +++ b/firmware/microblaze/usrp2p/udp_fw_update.c @@ -22,28 +22,18 @@ #include "usrp2/fw_common.h" #include "spi.h" #include "spi_flash.h" -#include "udp_fw_update.h" #include #include #include "ethernet.h" +#include "udp_fw_update.h" //Firmware update packet handler void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst, unsigned char *payload, int payload_len) { -//okay, we can do this ANY NUMBER of ways. we have up to ~1500 bytes to play with -- we don't handle -//fragmentation, so limit it to id; //ensure that the protocol versions match @@ -55,9 +45,9 @@ void handle_udp_fw_update_packet(struct socket_address src, struct socket_addres } //ensure that this is not a short packet - if (payload_len < sizeof(udp_fw_update_data_t)){ + if (payload_len < sizeof(usrp2_fw_update_data_t)){ printf("!Error in update packet handler: Expected payload length %d, but got %d\n", - (int)sizeof(udp_fw_update_data_t), payload_len + (int)sizeof(usrp2_fw_update_data_t), payload_len ); update_data_in_id = USRP2_FW_UPDATE_ID_WAT; } diff --git a/firmware/microblaze/usrp2p/udp_fw_update.h b/firmware/microblaze/usrp2p/udp_fw_update.h deleted file mode 100644 index dcf44bc42..000000000 --- a/firmware/microblaze/usrp2p/udp_fw_update.h +++ /dev/null @@ -1,22 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2010 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 . - */ - -#include "net_common.h" - -void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst, - unsigned char *payload, int payload_len); diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index 320ed7d77..4d3b62d6b 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -43,7 +43,6 @@ extern "C" { // Dynamic and/or private ports: 49152-65535 #define USRP2_UDP_CTRL_PORT 49152 #define USRP2_UDP_DATA_PORT 49153 -#define USRP2_UDP_UPDATE_PORT 49154 //for firmware upgrade commands //////////////////////////////////////////////////////////////////////// // I2C addresses @@ -94,35 +93,6 @@ typedef enum{ } usrp2_ctrl_id_t; -typedef enum { - USRP2_FW_UPDATE_ID_WAT = ' ', - - USRP2_FW_UPDATE_ID_OHAI_LOL = 'a', - USRP2_FW_UPDATE_ID_OHAI_OMG = 'A', - - USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL = 'f', - USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG = 'F', - - USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL = 'e', - USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG = 'E', - - USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL = 'd', - USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG = 'D', - USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG = 'B', - - USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL = 'w', - USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG = 'W', - - USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL = 'r', - USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG = 'R', - - USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL = 's', - USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG = 'S', - - USRP2_FW_UPDATE_ID_KTHXBAI = '~' - -} usrp2_fw_update_id_t; - typedef enum{ USRP2_DIR_RX = 'r', USRP2_DIR_TX = 't' @@ -167,24 +137,6 @@ typedef struct{ } data; } usrp2_ctrl_data_t; -typedef struct { - __stdint(uint32_t) proto_ver; - __stdint(uint32_t) id; - __stdint(uint32_t) seq; - union { - __stdint(uint32_t) ip_addr; - struct { - __stdint(uint32_t) flash_addr; - __stdint(uint32_t) length; - __stdint(uint8_t) data[256]; - } flash_args; - struct { - __stdint(uint32_t) sector_size_bytes; - __stdint(uint32_t) memory_size_bytes; - } flash_info_args; - } data; -} udp_fw_update_data_t; - #undef __stdint #ifdef __cplusplus } diff --git a/host/utils/usrp2p_fw_update.py b/host/utils/usrp2p_fw_update.py index b15542933..5eff83c07 100755 --- a/host/utils/usrp2p_fw_update.py +++ b/host/utils/usrp2p_fw_update.py @@ -36,7 +36,7 @@ UDP_MAX_XFER_BYTES = 1024 UDP_TIMEOUT = 3 UDP_POLL_INTERVAL = 0.10 #in seconds -USRP2_FW_PROTO_VERSION = 5 +USRP2_FW_PROTO_VERSION = 6 #from bootloader_utils.h PROD_FPGA_IMAGE_LOCATION_ADDR = 0x00200000 -- cgit v1.2.3 From c205cd329b37f24060ff20c23bc91249969502a1 Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Thu, 7 Oct 2010 18:09:53 -0700 Subject: U2P: Bootloader/ICAP updates. 2-stage bootloader works. Uses EEPROM for state info. --- firmware/microblaze/lib/xilinx_s3_icap.c | 101 --------------------- firmware/microblaze/usrp2p/Makefile.am | 1 + firmware/microblaze/usrp2p/bootloader/Makefile.am | 2 +- .../microblaze/usrp2p/bootloader/init_bootloader.c | 53 ++++++++--- firmware/microblaze/usrp2p/bootloader_utils.c | 18 ++-- firmware/microblaze/usrp2p/udp_fw_update.c | 2 + firmware/microblaze/usrp2p/xilinx_s3_icap.c | 99 ++++++++++++++++++++ host/utils/usrp2p_fw_update.py | 59 +++++++++--- 8 files changed, 197 insertions(+), 138 deletions(-) delete mode 100644 firmware/microblaze/lib/xilinx_s3_icap.c create mode 100644 firmware/microblaze/usrp2p/xilinx_s3_icap.c (limited to 'firmware/microblaze/usrp2p/udp_fw_update.c') diff --git a/firmware/microblaze/lib/xilinx_s3_icap.c b/firmware/microblaze/lib/xilinx_s3_icap.c deleted file mode 100644 index 8aa7fd297..000000000 --- a/firmware/microblaze/lib/xilinx_s3_icap.c +++ /dev/null @@ -1,101 +0,0 @@ -/* -*- c -*- */ -/* - * Copyright 2009 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 . - */ - - -/* Changes required to work for the Spartan-3A series: - * The ICAP interface on the 3A is 8 bits wide, instead of 32. - * Everything is Xilinx standard LSB-first. - * The operations are all different. - * Commands are 16 bits long, presented to the ICAP interface 8 bits at a time. -*/ - -#include -#include -#include //for READ_CMD - - -/* bit swap end-for-end */ -static unsigned char -swap8(unsigned char x) -{ - unsigned char r = 0; - r |= (x >> 7) & 0x01; - r |= (x >> 5) & 0x02; - r |= (x >> 3) & 0x04; - r |= (x >> 1) & 0x08; - - r |= (x << 1) & 0x10; - r |= (x << 3) & 0x20; - r |= (x << 5) & 0x40; - r |= (x << 7) & 0x80; - - return r; -} - -void -wr_icap(uint8_t x) -{ - uint8_t t = swap8(x); - - icap_regs->icap = t; //DEBUG: does not swap bits -} - -uint8_t -rd_icap(void) -{ - return swap8(icap_regs->icap); -} - - -void -icap_reload_fpga(uint32_t flash_address) -//this DOES NOT WORK right now. reboot is not getting executed correctly. -{ - union { - uint32_t i; - uint8_t c[4]; - } t; - t.i = flash_address; - - //note! t.c[0] MUST contain the byte-wide read command for the flash device used. - //for the 25P64, and most other flash devices, this is 0x03. - t.c[0] = READ_CMD; //legacy command, use FAST_READ_CMD 0x0B after testing - - //TODO: look up the watchdog timer, ensure it won't fire too soon - - //UG332 p279 -// wr_icap(0xff); -// wr_icap(0xff); //dummy word, probably unnecessary - wr_icap(0xAA); - wr_icap(0x99); //sync word - wr_icap(0x32); - wr_icap(0x61); //Type 1 write General 1 (1 word) - wr_icap(t.c[2]); //bits 15-8 - wr_icap(t.c[3]); //bits 7-0 - wr_icap(0x32); - wr_icap(0x81); //Type 1 write General 2 (1 word) - wr_icap(t.c[0]); //C0-C8, the byte-wide read command - wr_icap(t.c[1]); //Upper 8 bits of 24-bit address - wr_icap(0x30); - wr_icap(0xA1); //Type 1 write CMD (1 word) - wr_icap(0x00); - wr_icap(0x0E); //REBOOT command - wr_icap(0x20); - wr_icap(0x00); //Type 1 NOP - -} diff --git a/firmware/microblaze/usrp2p/Makefile.am b/firmware/microblaze/usrp2p/Makefile.am index e6bff2dbc..0d0b60437 100644 --- a/firmware/microblaze/usrp2p/Makefile.am +++ b/firmware/microblaze/usrp2p/Makefile.am @@ -51,6 +51,7 @@ libusrp2p_a_SOURCES = \ spi_flash_read.c \ bootloader_utils.c \ ethernet.c \ + xilinx_s3_icap.c \ udp_fw_update.c noinst_PROGRAMS = \ diff --git a/firmware/microblaze/usrp2p/bootloader/Makefile.am b/firmware/microblaze/usrp2p/bootloader/Makefile.am index 1fc5daf9c..eb72d937d 100644 --- a/firmware/microblaze/usrp2p/bootloader/Makefile.am +++ b/firmware/microblaze/usrp2p/bootloader/Makefile.am @@ -30,7 +30,7 @@ LDADD = $(top_srcdir)/usrp2p/libusrp2p.a noinst_PROGRAMS = \ init_bootloader.elf -init_bootloader_elf_SOURCES = init_bootloader.c +init_bootloader_elf_SOURCES = init_bootloader.c i2c_sync.c .bin.rmi: $(top_srcdir)/bin/bin_to_ram_macro_init.py $< $@ diff --git a/firmware/microblaze/usrp2p/bootloader/init_bootloader.c b/firmware/microblaze/usrp2p/bootloader/init_bootloader.c index 15d719d73..8b513002d 100644 --- a/firmware/microblaze/usrp2p/bootloader/init_bootloader.c +++ b/firmware/microblaze/usrp2p/bootloader/init_bootloader.c @@ -15,6 +15,12 @@ #include #include #include +#include "i2c_sync.h" + +#define SAFE_FLAG_LOCATION 247 + +bool find_safe_booted_flag(void); +void set_safe_booted_flag(bool flag); void pic_interrupt_handler() __attribute__ ((interrupt_handler)); @@ -23,6 +29,19 @@ void pic_interrupt_handler() // nop stub } +bool find_safe_booted_flag(void) { + unsigned char flag_byte; + i2c_read(SAFE_FLAG_LOCATION, &flag_byte, 1); + + return (flag_byte != 0x5E); +} + +void set_safe_booted_flag(bool flag) { + unsigned char flag_byte = flag ? 0x5E : 0xDC; + i2c_write(SAFE_FLAG_LOCATION, &flag_byte, 1); +} + + void load_ihex(void) { //simple IHEX parser to load proper records into RAM. loads program when it receives end of record. char buf[128]; //input data buffer uint8_t ihx[32]; //ihex data buffer @@ -51,19 +70,18 @@ void delay(uint32_t t) { while(t-- != 0) asm("NOP"); } -//let's clean up this logic. state machine? no, you only have to go through it once. - -//don't need else cases since all these are terminal cases - int main(int argc, char *argv[]) { hal_disable_ints(); // In case we got here via jmp 0x0 output_regs->leds = 0xFF; delay(500000); output_regs->leds = 0x00; - hal_uart_init(); + hal_uart_init(); spif_init(); -// i2c_init(); //for EEPROM + i2c_init(); //for EEPROM puts("USRP2+ bootloader\n"); + + bool production_image = find_safe_booted_flag(); + if(production_image) set_safe_booted_flag(0); //we're the production image, so we clear the flag for the next boot if(BUTTON_PUSHED) { //see memory_map.h puts("Starting USRP2+ in safe mode."); @@ -71,26 +89,31 @@ int main(int argc, char *argv[]) { spi_flash_read(SAFE_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES, (void *)RAM_BASE); start_program(RAM_BASE); puts("ERROR: return from main program! This should never happen!"); - //icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR); + icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR); } else { puts("ERROR: no safe firmware image available. I am a brick. Feel free to load IHEX to RAM."); + //puts("ERROR: no safe firmware image available. I am a brick."); load_ihex(); } } - puts("Checking for valid production FPGA image..."); - if(is_valid_fpga_image(PROD_FPGA_IMAGE_LOCATION_ADDR)) { - puts("Valid production FPGA image found. Attempting to boot."); - //icap_reload_fpga(PROD_FPGA_IMAGE_LOCATION_ADDR); + if(!production_image) { + puts("Checking for valid production FPGA image..."); + if(is_valid_fpga_image(PROD_FPGA_IMAGE_LOCATION_ADDR)) { + puts("Valid production FPGA image found. Attempting to boot."); + set_safe_booted_flag(1); + delay(10000); + icap_reload_fpga(PROD_FPGA_IMAGE_LOCATION_ADDR); + } + puts("No valid production FPGA image found.\nAttempting to load production firmware..."); } - puts("No valid production FPGA image found.\nAttempting to load production firmware..."); if(is_valid_fw_image(PROD_FW_IMAGE_LOCATION_ADDR)) { puts("Valid production firmware found. Loading..."); spi_flash_read(PROD_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES, (void *)RAM_BASE); start_program(RAM_BASE); puts("ERROR: Return from main program! This should never happen!"); //if this happens, though, the safest thing to do is reboot the whole FPGA and start over. - //icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR); + icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR); return 1; } puts("No valid production firmware found. Trying safe firmware..."); @@ -98,8 +121,8 @@ int main(int argc, char *argv[]) { spi_flash_read(SAFE_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES, (void *)RAM_BASE); start_program(RAM_BASE); puts("ERROR: return from main program! This should never happen!"); - //icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR); - return 1; //_exit will trap in loop + icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR); + return 1; } puts("ERROR: no safe firmware image available. I am a brick. Feel free to load IHEX to RAM."); load_ihex(); diff --git a/firmware/microblaze/usrp2p/bootloader_utils.c b/firmware/microblaze/usrp2p/bootloader_utils.c index 00893db02..fadd225bb 100644 --- a/firmware/microblaze/usrp2p/bootloader_utils.c +++ b/firmware/microblaze/usrp2p/bootloader_utils.c @@ -5,16 +5,23 @@ */ //contains routines for loading programs from Flash. depends on Flash libraries. +//also contains routines for reading / writing EEPROM flags for the bootloader +#include #include #include #include - int is_valid_fpga_image(uint32_t addr) { - static const uint8_t fpgaheader[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0x99}; //AA 99 is the standard Xilinx sync sequence, and it's always prefixed with 0xFF padding - uint8_t buf[10]; - spi_flash_read(addr, 6, buf); - return memcmp(buf, fpgaheader, 6) == 0; + uint8_t imgbuf[64]; + spi_flash_read(addr, 64, imgbuf); + //we're just looking for leading 0xFF padding, followed by the sync bytes 0xAA 0x99 + int i = 0; + for(i; i<63; i++) { + if(imgbuf[i] == 0xFF) continue; + if(imgbuf[i] == 0xAA && imgbuf[i+1] == 0x99) return 1; + } + + return 0; } int is_valid_fw_image(uint32_t addr) { @@ -30,4 +37,3 @@ void start_program(uint32_t addr) typedef void (*fptr_t)(void); (*(fptr_t) 0x00000000)(); // most likely no return } - diff --git a/firmware/microblaze/usrp2p/udp_fw_update.c b/firmware/microblaze/usrp2p/udp_fw_update.c index 9242242e7..55c206b1b 100644 --- a/firmware/microblaze/usrp2p/udp_fw_update.c +++ b/firmware/microblaze/usrp2p/udp_fw_update.c @@ -26,6 +26,7 @@ #include #include "ethernet.h" #include "udp_fw_update.h" +#include "xilinx_s3_icap.h" //Firmware update packet handler void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst, @@ -94,6 +95,7 @@ void handle_udp_fw_update_packet(struct socket_address src, struct socket_addres //should reset via icap_reload_fpga(uint32_t flash_address); update_data_out.id = USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG; //you should note that if you get a reply packet to this the reset has obviously failed + icap_reload_fpga(0); break; // case USRP2_FW_UPDATE_ID_KTHXBAI: //see ya diff --git a/firmware/microblaze/usrp2p/xilinx_s3_icap.c b/firmware/microblaze/usrp2p/xilinx_s3_icap.c new file mode 100644 index 000000000..50c85231c --- /dev/null +++ b/firmware/microblaze/usrp2p/xilinx_s3_icap.c @@ -0,0 +1,99 @@ +/* -*- c -*- */ +/* + * Copyright 2009 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 . + */ + + +/* Changes required to work for the Spartan-3A series: + * The ICAP interface on the 3A is 8 bits wide, instead of 32. + * Everything is Xilinx standard LSBit-first. + * The operations are all different. + * Commands are 16 bits long, presented to the ICAP interface 8 bits at a time. +*/ + +#include +#include +#include //for READ_CMD + + +/* bit swap end-for-end */ +static inline unsigned char +swap8(unsigned char x) +{ + unsigned char r = 0; + r |= (x >> 7) & 0x01; + r |= (x >> 5) & 0x02; + r |= (x >> 3) & 0x04; + r |= (x >> 1) & 0x08; + + r |= (x << 1) & 0x10; + r |= (x << 3) & 0x20; + r |= (x << 5) & 0x40; + r |= (x << 7) & 0x80; + + return r; +} + +void +wr_icap(uint8_t x) +{ + icap_regs->icap = swap8(x); +} + +uint8_t +rd_icap(void) +{ + return swap8(icap_regs->icap); +} + + +void +icap_reload_fpga(uint32_t flash_address) +{ + union { + uint32_t i; + uint8_t c[4]; + } t; + t.i = flash_address; + + //note! t.c[0] MUST contain the byte-wide read command for the flash device used. + //for the 25P64, and most other flash devices, this is 0x03. + t.c[0] = FAST_READ_CMD; + + //TODO: look up the watchdog timer, ensure it won't fire too soon + + //UG332 p279 + wr_icap(0xff); + wr_icap(0xff); //dummy word, probably unnecessary + wr_icap(0xAA); + wr_icap(0x99); //sync word + wr_icap(0x32); + wr_icap(0x61); //Type 1 write General 1 (1 word) + wr_icap(t.c[2]); //bits 15-8 + wr_icap(t.c[3]); //bits 7-0 + wr_icap(0x32); + wr_icap(0x81); //Type 1 write General 2 (1 word) + wr_icap(t.c[0]); //C0-C8, the byte-wide read command + wr_icap(t.c[1]); //Upper 8 bits of 24-bit address + wr_icap(0x30); + wr_icap(0xA1); //Type 1 write CMD (1 word) + wr_icap(0x00); + wr_icap(0x0E); //REBOOT command + wr_icap(0x20); + wr_icap(0x00); //Type 1 NOP + wr_icap(0x20); + wr_icap(0x00); +} diff --git a/host/utils/usrp2p_fw_update.py b/host/utils/usrp2p_fw_update.py index 1cd735796..0c07c398d 100755 --- a/host/utils/usrp2p_fw_update.py +++ b/host/utils/usrp2p_fw_update.py @@ -146,25 +146,38 @@ def get_flash_info(ip): return (memory_size_bytes, sector_size_bytes) -def burn_fw(ip, fw, fpga): +def burn_fw(ip, fw, fpga, reset, safe): init_update(ip) (flash_size, sector_size) = get_flash_info(ip) print "Flash size: %i\nSector size: %i" % (flash_size, sector_size) - if fpga: + if fpga: + if safe: + image_location = SAFE_FPGA_IMAGE_LOCATION_ADDR + else: + image_location = PROD_FPGA_IMAGE_LOCATION_ADDR + fpga_file = open(fpga, 'rb') fpga_image = fpga_file.read() - erase_image(ip, SAFE_FPGA_IMAGE_LOCATION_ADDR, FPGA_IMAGE_SIZE_BYTES) - write_image(ip, fpga_image, SAFE_FPGA_IMAGE_LOCATION_ADDR) #TODO: DO NOT WRITE SAFE IMAGE! this is only here because the bootloader isn't finished yet - verify_image(ip, fpga_image, SAFE_FPGA_IMAGE_LOCATION_ADDR) + erase_image(ip, image_location, FPGA_IMAGE_SIZE_BYTES) + write_image(ip, fpga_image, image_location) + verify_image(ip, fpga_image, image_location) if fw: + if safe: + image_location = SAFE_FW_IMAGE_LOCATION_ADDR + else: + image_location = PROD_FW_IMAGE_LOCATION_ADDR + fw_file = open(fw, 'rb') fw_image = fw_file.read() - erase_image(ip, PROD_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES) - write_image(ip, fw_image, PROD_FW_IMAGE_LOCATION_ADDR) - verify_image(ip, fw_image, PROD_FW_IMAGE_LOCATION_ADDR) + erase_image(ip, image_location, FW_IMAGE_SIZE_BYTES) + write_image(ip, fw_image, image_location) + verify_image(ip, fw_image, image_location) + + if reset: + reset_usrp(ip) def write_image(ip, image, addr): #we split the image into smaller (256B) bits and send them down the wire @@ -208,6 +221,14 @@ def verify_image(ip, image, addr): print "Verify failed. Image did not write correctly." else: print "Success." + +def reset_usrp(ip): + out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL, seq(), 0, 0, "") + in_pkt = send_and_recv(out_pkt, ip) + + (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) + if pktid == update_id_t.USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG: + raise Exception, "Device failed to reset." def erase_image(ip, addr, length): #get flash info first @@ -234,17 +255,17 @@ def erase_image(ip, addr, length): print "\tFinished." -#def verify_image(ip, image, addr): - ######################################################################## # command line options ######################################################################## def get_options(): parser = optparse.OptionParser() - parser.add_option("--ip", type="string", help="USRP2P firmware address", default='') - parser.add_option("--fw", type="string", help="firmware image path (optional)", default='') - parser.add_option("--fpga", type="string", help="fpga image path (optional)", default='') + parser.add_option("--ip", type="string", help="USRP2P firmware address", default='') + parser.add_option("--fw", type="string", help="firmware image path (optional)", default='') + parser.add_option("--fpga", type="string", help="fpga image path (optional)", default='') + parser.add_option("--reset", action="store_true", help="reset the device after writing", default=False) + parser.add_option("--overwrite-safe", action="store_true", help="never ever use this option", default=False) (options, args) = parser.parse_args() return options @@ -256,5 +277,13 @@ if __name__=='__main__': options = get_options() if not options.ip: raise Exception, 'no ip address specified' - if not options.fpga and not options.fw: raise Exception, 'Must specify either a firmware image or FPGA image.' - burn_fw(ip=options.ip, fw=options.fw, fpga=options.fpga) + if not options.fpga and not options.fw and not options.reset: raise Exception, 'Must specify either a firmware image or FPGA image, and/or reset.' + + if options.overwrite_safe: + print("Are you REALLY, REALLY sure you want to overwrite the safe image? This is ALMOST ALWAYS a terrible idea.") + print("If your image is faulty, your USRP2+ will become a brick until reprogrammed via JTAG.") + response = raw_input("""Type "yes" to continue, or anything else to quit: """) + if response != "yes": + sys.exit(0) + + burn_fw(ip=options.ip, fw=options.fw, fpga=options.fpga, reset=options.reset, safe=options.overwrite_safe) -- cgit v1.2.3