diff options
| author | Nick Foster <nick@nerdnetworks.org> | 2010-08-18 14:49:52 -0700 | 
|---|---|---|
| committer | Nick Foster <nick@nerdnetworks.org> | 2010-08-18 14:49:52 -0700 | 
| commit | 769b565dbdc3fa1bb0891d4dcdbb74695f1dc875 (patch) | |
| tree | 32ec8c11dd8894a2a6c45f002aab204521bd7948 | |
| parent | 2da87fa5082c507d324dce743d63667a6d21fd80 (diff) | |
| parent | 42547a0be8e52758d340fd1eba51a2dd5274a652 (diff) | |
| download | uhd-769b565dbdc3fa1bb0891d4dcdbb74695f1dc875.tar.gz uhd-769b565dbdc3fa1bb0891d4dcdbb74695f1dc875.tar.bz2 uhd-769b565dbdc3fa1bb0891d4dcdbb74695f1dc875.zip | |
Merge branch 'usrp2p_udpfw' of ettus.sourcerepo.com:ettus/uhdpriv into usrp2p
| -rw-r--r-- | firmware/microblaze/Makefile.common | 9 | ||||
| -rw-r--r-- | firmware/microblaze/apps/txrx_uhd.c | 2 | ||||
| -rw-r--r-- | firmware/microblaze/lib/loader_parser.c | 324 | ||||
| -rw-r--r-- | firmware/microblaze/lib/loader_parser.h | 38 | ||||
| -rw-r--r-- | firmware/microblaze/lib/udp_burner_packet.h | 28 | ||||
| -rw-r--r-- | firmware/microblaze/lib/udp_fw_update.h | 71 | ||||
| -rw-r--r-- | firmware/microblaze/usrp2/Makefile.am | 3 | ||||
| -rw-r--r-- | firmware/microblaze/usrp2/udp_fw_update.c (renamed from firmware/microblaze/lib/udp_burner_packet.c) | 28 | ||||
| -rw-r--r-- | firmware/microblaze/usrp2p/Makefile.am | 6 | ||||
| -rw-r--r-- | firmware/microblaze/usrp2p/spi_flash.h | 7 | ||||
| -rw-r--r-- | firmware/microblaze/usrp2p/spi_flash_read.c | 19 | ||||
| -rw-r--r-- | firmware/microblaze/usrp2p/udp_fw_update.c | 106 | ||||
| -rwxr-xr-x | host/utils/usrp2p_fw_update.py | 259 | 
13 files changed, 489 insertions, 411 deletions
| 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/apps/txrx_uhd.c b/firmware/microblaze/apps/txrx_uhd.c index 1c6e0624f..44be2cc9c 100644 --- a/firmware/microblaze/apps/txrx_uhd.c +++ b/firmware/microblaze/apps/txrx_uhd.c @@ -45,6 +45,7 @@  #include <i2c.h>  #include <ethertype.h>  #include <arp_cache.h> +#include "udp_fw_update.h"  /*   * Full duplex Tx and Rx between ethernet and DSP pipelines @@ -509,6 +510,7 @@ main(void)    register_udp_listener(USRP2_UDP_CTRL_PORT, handle_udp_ctrl_packet);    register_udp_listener(USRP2_UDP_DATA_PORT, handle_udp_data_packet); +  register_udp_listener(USRP2_UDP_UPDATE_PORT, handle_udp_fw_update_packet);    // initialize double buffering state machine for ethernet -> DSP Tx diff --git a/firmware/microblaze/lib/loader_parser.c b/firmware/microblaze/lib/loader_parser.c deleted file mode 100644 index 96457a164..000000000 --- a/firmware/microblaze/lib/loader_parser.c +++ /dev/null @@ -1,324 +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 <http://www.gnu.org/licenses/>. - */ - -#include "loader_parser.h" -#include <quadradio/loader_bits.h> -#include <quadradio/flashdir.h> -#include <quadradio/simple_binary_format.h> -#include <spi_flash.h> -#include <nonstdio.h> -//#include <assert.h> -#include <stdlib.h> -#include <stdbool.h> -#include <string.h> -#include "ethernet.h" -#include "qr_settings.h" - -#define min(a,b) ((a) < (b) ? (a) : (b)) - -static spi_flash_async_state_t async_state; - - -static caldiv_eeprom_setter_t _caldiv_set_rev = NULL; -static caldiv_eeprom_setter_t _caldiv_set_ser = NULL; -static caldiv_eeprom_setter_t _caldiv_set_mod = NULL; - -void -register_caldiv_eeprom_setters(caldiv_eeprom_setter_t set_rev, -			       caldiv_eeprom_setter_t set_ser, -			       caldiv_eeprom_setter_t set_mod) -{ -  _caldiv_set_rev = set_rev; -  _caldiv_set_ser = set_ser; -  _caldiv_set_mod = set_mod; -} - - -// big-endian -static uint32_t  -get32(const unsigned char *s) -{ -  return (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; -} - -// big-endian -static unsigned char * -put32(unsigned char *s, uint32_t v) -{ -  s[0] = (v >> 24) & 0xff; -  s[1] = (v >> 16) & 0xff; -  s[2] = (v >>  8) & 0xff; -  s[3] = v & 0xff; -  return s + 4; -} - -static bool -erased_p(uint32_t flash_addr, size_t nbytes) -{ -  unsigned char	buf[64]; - -  size_t n; -  for (size_t i = 0; i < nbytes; i += n, flash_addr += n){ -    n = min(nbytes - i, sizeof(buf)); -    spi_flash_read(flash_addr, n, buf); -    for (size_t j = 0; j < n; j++) -      if (buf[j] != 0xff) -	return false; -  } -  return true; -} - -static bool -erase_flash(uint32_t addr, uint32_t len) -{ -  if (addr % spi_flash_sector_size() != 0) -    return false; - -  if (len % spi_flash_sector_size() != 0) -    return false; - -  spi_flash_async_erase_start(&async_state, addr, len); -  // FIXME? check to see if erase was successful -  return true; -} - -static bool -map_slot(uint32_t slot, uint32_t *slot_start, uint32_t *slot_len, uint32_t *status) -{ -  // This case doesn't require a valid flashdir, and in fact can be used as  -  // part of writing the intial flashdir. -  if (QLD_SLOT_DOM(slot) == QLD_DOM_UNMAPPED){ -    int flash_size = get_flash_size(); -    if (flash_size == 0){ -      *status = QLDS_FAILED;	// Can't find the flash.  most likely a h/w problem. -      return false; -    } -    *slot_start = 0; -    *slot_len = flash_size; -    return true; -  } - -  const struct flashdir *fd = get_flashdir(); -  if (fd == 0) -    return false; - -  uint32_t slot_num = QLD_SLOT_NUM(slot); - -  switch(QLD_SLOT_DOM(slot)){ -  case QLD_DOM_FPGA: -    if (slot_num >= fd->fpga_nslots){ -      *status = QLDS_INVALID_ARG; -      return false; -    } -    *slot_start = fd->slot[slot_num + fd->fpga_slot0].start << spi_flash_log2_sector_size(); -    *slot_len = fd->slot[slot_num + fd->fpga_slot0].len << spi_flash_log2_sector_size(); -    return true; - -  case QLD_DOM_FW: -    if (slot_num >= fd->fw_nslots){ -      *status = QLDS_INVALID_ARG; -      return false; -    } -    *slot_start = fd->slot[slot_num + fd->fw_slot0].start << spi_flash_log2_sector_size(); -    *slot_len = fd->slot[slot_num + fd->fw_slot0].len << spi_flash_log2_sector_size(); -    return true; - -  default: -    *status = QLDS_INVALID_ARG; -    return false; -  } -} - - -static bool -check_flashdir(void) -{ -  return get_flashdir() != 0; -} - -void -loader_parser(const unsigned char *input, size_t ilen, -	      unsigned char *output, size_t max_olen, size_t *actual_olen) -{ -  //assert (max_olen >= 8); -  if (!(max_olen >= 8)) -    abort(); - -  *actual_olen = 0; -  uint32_t status = QLDS_BAD_PKT; - -  uint32_t cmd = get32(input); -  uint32_t nonce = get32(input+4); -  uint32_t slot = 0; -  uint32_t addr = 0; -  uint32_t len = 0; - -  if (ilen < 8){ -    nonce = -1; -    goto done; -  } - -  uint32_t slot_start;		// offset in flash -  uint32_t slot_len;		// length in bytes -   -  if (ilen >= 5 * sizeof(uint32_t)){ -    slot = get32(input+8); -    addr = get32(input+12); -    len = get32(input+16); -  } - -  switch (cmd){ -  case QLD_FLASH_ERASE_START: -    // <QLD_FLASH_ERASE_START> <nonce> <slot> <addr> <len> -    if (ilen != 5 * sizeof(uint32_t)) -      goto done; - -    if (!check_flashdir()){ -      status = QLDS_BAD_FLASHDIR; -      goto done; -    } -    if (!map_slot(slot, &slot_start, &slot_len, &status)) -      goto done; - -    if (QLD_SLOT_DOM(slot) != QLD_DOM_UNMAPPED){ -      addr = slot_start; -      len = slot_len; -    } -    //printf("flash_erase: addr = 0x%x, len=0x%x\n", addr, len); - -    if (0 && erased_p(addr, len)){	// already erased? -      async_state.first = async_state.last = async_state.current = 0; -      goto ok; -    } - -    if (erase_flash(addr, len)) -      goto ok; - -    status = QLDS_FAILED; -    goto done; -     - -  case QLD_FLASH_ERASE_POLL: -    // <QLD_FLASH_ERASE_POLL> <nonce> -    if (ilen != 2 * sizeof(uint32_t)) -      goto done; - -    if (spi_flash_async_erase_poll(&async_state)) -      goto ok; - -    status = QLDS_BUSY; -    goto done; - - -  case QLD_FLASH_WRITE: -    // <QLD_FLASH_WRITE> <nonce> <slot> <addr> <len> <data ...> -    if (ilen < 5 * sizeof(uint32_t)) -      goto done; - -    if (ilen != 5 * sizeof(uint32_t) + len) -      goto done; - -    if (!check_flashdir()){ -      status = QLDS_BAD_FLASHDIR; -      goto done; -    } -    if (!map_slot(slot, &slot_start, &slot_len, &status)) -      goto done; - -    addr += slot_start; -    len = min(len, slot_len); - -    if (spi_flash_program(addr, len, &input[5*sizeof(uint32_t)])) -      goto ok; - -    status = QLDS_FAILED; -    goto done; -     - -  case QLD_FLASH_READ: -  case QLD_MEM_READ: -  case QLD_MEM_WRITE: -  case QLD_GOTO: -    status = QLDS_NOTIMPLEMENTED; -    goto done; - -  case QLD_PING: -    // <QLD_PING> <nonce> -    if (ilen != 2 * sizeof(uint32_t)) -      goto done; -    goto ok; - -#if 0 -  case QLD_EEPROM_SET_XXX: -  // <QLD_EEPROM_SET_XXX> <nonce> <arg> <idlen> <idstr> <data ...> -  { -    uint32_t arg    = get32(input+2*sizeof(uint32_t)); -    uint32_t idlen  = get32(input+3*sizeof(uint32_t)); -    uint8_t *idstr  = (uint8_t*)input+4*sizeof(uint32_t); -    uint8_t *data_p = idstr+idlen; -     -    //handle the ethernet cases -    if (strncmp((char*)idstr, "ip", idlen) == 0){ -        struct ip_addr addr = {get32(data_p)}; -        ethernet_set_ip_addr(arg, addr); -    } -    else if (strncmp((char*)idstr, "mac", idlen) == 0){ -        eth_mac_addr_t addr; -        memcpy(&addr, data_p, sizeof(addr)); -        ethernet_set_mac_addr(arg, &addr); -    } -    //handle the main board eeprom -    else if (strncmp((char*)idstr, "qrrev", idlen) == 0){ -        qr_set_revision(get32(data_p)); -    } -    else if (strncmp((char*)idstr, "qrser", idlen) == 0){ -        qr_set_serial(get32(data_p)); -    } -    else if (strncmp((char*)idstr, "qrmod", idlen) == 0){ -        qr_set_model(get32(data_p)); -    } -    //handle the caldiv eeprom -    else if (strncmp((char*)idstr, "cdrev", idlen) == 0){ -        if (_caldiv_set_rev) _caldiv_set_rev(get32(data_p)); -    } -    else if (strncmp((char*)idstr, "cdser", idlen) == 0){ -        if (_caldiv_set_ser) _caldiv_set_ser(get32(data_p)); -    } -    else if (strncmp((char*)idstr, "cdmod", idlen) == 0){ -        if (_caldiv_set_ser) _caldiv_set_mod(get32(data_p)); -    } -    else { -        goto done; -    } -  } -  goto ok; -#endif - -  default: -    status = QLDS_UNKNOWN_CMD; -    goto done; -  } - - ok: -  status = QLDS_OK; - - done: -  put32(output, nonce); -  put32(output+4, status); -  *actual_olen = 2*sizeof(uint32_t); -} diff --git a/firmware/microblaze/lib/loader_parser.h b/firmware/microblaze/lib/loader_parser.h deleted file mode 100644 index 365317bd7..000000000 --- a/firmware/microblaze/lib/loader_parser.h +++ /dev/null @@ -1,38 +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 <http://www.gnu.org/licenses/>. - */ - -#include <stddef.h> -#include <stdint.h> - -/* - * max_olen must be at least 8 bytes.  1KB is recommended. - */ -void -loader_parser(const unsigned char *input, size_t ilen, -	      unsigned char *output, size_t max_olen, size_t *actual_olen); - -/* - * Major kludge-master altert! - * This function registers functions for setting caldiv eeprom stuff. - * This way, the parser does not depend on the qpn apps at compile time. - */ -typedef void(*caldiv_eeprom_setter_t)(uint32_t); -void register_caldiv_eeprom_setters( -	caldiv_eeprom_setter_t set_rev, -	caldiv_eeprom_setter_t set_ser, -	caldiv_eeprom_setter_t set_mod); diff --git a/firmware/microblaze/lib/udp_burner_packet.h b/firmware/microblaze/lib/udp_burner_packet.h deleted file mode 100644 index 0f4025712..000000000 --- a/firmware/microblaze/lib/udp_burner_packet.h +++ /dev/null @@ -1,28 +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 <http://www.gnu.org/licenses/>. - */ -#ifndef INCLUDED_UDP_BURNER_PACKET_H -#define INCLUDED_UDP_BURNER_PACKET_H - -#include <net/socket_address.h> - -void -handle_udp_burner_packet(struct socket_address src, struct socket_address dst, -			 unsigned char *payload, int payload_len); - - -#endif /* INCLUDED_UDP_BURNER_PACKET_H */ 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 <http://www.gnu.org/licenses/>. + */ + +#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/Makefile.am b/firmware/microblaze/usrp2/Makefile.am index 8da013980..17f7a4744 100644 --- a/firmware/microblaze/usrp2/Makefile.am +++ b/firmware/microblaze/usrp2/Makefile.am @@ -34,7 +34,8 @@ noinst_LIBRARIES = libusrp2.a  libusrp2_a_SOURCES = \  	$(COMMON_SRCS) \  	sd.c \ -	ethernet.c +	ethernet.c \ +  udp_fw_update.c  noinst_PROGRAMS = \  	usrp2_txrx_uhd.elf diff --git a/firmware/microblaze/lib/udp_burner_packet.c b/firmware/microblaze/usrp2/udp_fw_update.c index d86a4cf4a..14eb0b1ee 100644 --- a/firmware/microblaze/lib/udp_burner_packet.c +++ b/firmware/microblaze/usrp2/udp_fw_update.c @@ -1,6 +1,6 @@  /* -*- c++ -*- */  /* - * Copyright 2009 Ettus Research LLC + * 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 @@ -16,23 +16,19 @@   * along with this program.  If not, see <http://www.gnu.org/licenses/>.   */ -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif -#include "udp_burner_packet.h" +//Routines to handle updating the SPI Flash firmware via UDP +  #include "net_common.h" -#include "loader_parser.h" -#include <stdint.h> -#include <compiler.h> +#include "usrp2/fw_common.h"  #include <nonstdio.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) { +  usrp2_fw_update_data_t update_data_out; +  update_data_out.id = USRP2_FW_UPDATE_ID_WAT; -void -handle_udp_burner_packet(struct socket_address src, struct socket_address dst, -			 unsigned char *payload, int payload_len) -{ -  unsigned char reply[128] _AL4; -  size_t actual_reply_len; -  loader_parser(payload, payload_len, reply, sizeof(reply), &actual_reply_len); -  send_udp_pkt(dst.port, src, reply, actual_reply_len); +  send_udp_pkt(USRP2_UDP_UPDATE_PORT, src, &update_data_out, sizeof(update_data_out));  } diff --git a/firmware/microblaze/usrp2p/Makefile.am b/firmware/microblaze/usrp2p/Makefile.am index ddc7006ca..e6bff2dbc 100644 --- a/firmware/microblaze/usrp2p/Makefile.am +++ b/firmware/microblaze/usrp2p/Makefile.am @@ -27,11 +27,12 @@ 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 \  	--change-section-address .vectors.reset+0x8000 +  #	$(MB_OBJCOPY) -O ihex $< $@  # the below would work if objcopy weren't written by apes  #	$(MB_OBJCOPY) -O ihex -w --change-section-address .vectors*+0x8000 $< $@ @@ -49,7 +50,8 @@ libusrp2p_a_SOURCES = \  	spi_flash.c \  	spi_flash_read.c \  	bootloader_utils.c \ -	ethernet.c +	ethernet.c \ +  udp_fw_update.c  noinst_PROGRAMS = \  	usrp2p_txrx_uhd.elf \ diff --git a/firmware/microblaze/usrp2p/spi_flash.h b/firmware/microblaze/usrp2p/spi_flash.h index f65f28477..bbe7b650d 100644 --- a/firmware/microblaze/usrp2p/spi_flash.h +++ b/firmware/microblaze/usrp2p/spi_flash.h @@ -31,6 +31,7 @@  uint32_t spi_flash_rdid(void);	/* Read ID */  uint32_t spi_flash_rdsr(void);	/* Read Status Register */  size_t spi_flash_log2_sector_size(void) __attribute__((pure));  /* either 16 or 18 */ +size_t spi_flash_log2_memory_size(void);  static inline size_t  spi_flash_sector_size(void) @@ -38,6 +39,12 @@ spi_flash_sector_size(void)    return ((size_t) 1) << spi_flash_log2_sector_size();  } +static inline size_t +spi_flash_memory_size(void) +{ +  return ((size_t) 1) << spi_flash_log2_memory_size(); +} +  void spi_flash_read(uint32_t flash_addr,  size_t nbytes, void *buf);  /* diff --git a/firmware/microblaze/usrp2p/spi_flash_read.c b/firmware/microblaze/usrp2p/spi_flash_read.c index 1c65350f7..4682c5fe6 100644 --- a/firmware/microblaze/usrp2p/spi_flash_read.c +++ b/firmware/microblaze/usrp2p/spi_flash_read.c @@ -20,6 +20,8 @@  #include "spi_flash_private.h"  #include <stdlib.h>		// abort +static size_t _spi_flash_log2_memory_size; +  uint32_t   spi_flash_rdid(void)  { @@ -48,9 +50,26 @@ spi_flash_log2_sector_size(void)    };    _spi_flash_log2_sector_size = log2_sector_size[size - 22]; +  _spi_flash_log2_memory_size = size; //while we're at it    return _spi_flash_log2_sector_size;  } +size_t +spi_flash_log2_memory_size(void) +{ +  if (_spi_flash_log2_memory_size != 0) +    return _spi_flash_log2_memory_size; + +  uint32_t id = spi_flash_rdid(); +  int type = (id >> 8) & 0xff; +  int size = id & 0xff; +  if (type != 0x20 || size < 22 || size > 24) +    abort(); + +  _spi_flash_log2_memory_size = size; +  return _spi_flash_log2_memory_size; +} +  void   spi_flash_read(uint32_t flash_addr,  size_t nbytes, void *buf)  { diff --git a/firmware/microblaze/usrp2p/udp_fw_update.c b/firmware/microblaze/usrp2p/udp_fw_update.c new file mode 100644 index 000000000..9242242e7 --- /dev/null +++ b/firmware/microblaze/usrp2p/udp_fw_update.c @@ -0,0 +1,106 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ + +//Routines to handle updating the SPI Flash firmware via UDP + +#include <net_common.h> +#include "usrp2/fw_common.h" +#include "spi.h" +#include "spi_flash.h" +#include <nonstdio.h> +#include <string.h> +#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) { + +  const usrp2_fw_update_data_t *update_data_in = (usrp2_fw_update_data_t *) payload; + +  usrp2_fw_update_data_t update_data_out; +  usrp2_fw_update_id_t update_data_in_id = update_data_in->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(usrp2_fw_update_data_t)){ +      printf("!Error in update packet handler: Expected payload length %d, but got %d\n", +          (int)sizeof(usrp2_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)); +} diff --git a/host/utils/usrp2p_fw_update.py b/host/utils/usrp2p_fw_update.py new file mode 100755 index 000000000..5eff83c07 --- /dev/null +++ b/host/utils/usrp2p_fw_update.py @@ -0,0 +1,259 @@ +#!/usr/bin/env python +# +# 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 <http://www.gnu.org/licenses/>. +# + +# TODO: make it work +# TODO: make it autodetect UHD devices +# TODO: you should probably watch sequence numbers + +import optparse +import math +import os +import re +import struct +import socket +import sys + +######################################################################## +# constants +######################################################################## +UDP_FW_UPDATE_PORT = 49154 +UDP_MAX_XFER_BYTES = 1024 +UDP_TIMEOUT = 3 +UDP_POLL_INTERVAL = 0.10 #in seconds + +USRP2_FW_PROTO_VERSION = 6 + +#from bootloader_utils.h +PROD_FPGA_IMAGE_LOCATION_ADDR = 0x00200000 +PROD_FW_IMAGE_LOCATION_ADDR   = 0x00400000 +SAFE_FW_IMAGE_LOCATION_ADDR   = 0x007F0000 +SAFE_FPGA_IMAGE_LOCATION_ADDR = 0x00000000 +FPGA_IMAGE_SIZE_BYTES         = 2097152 +FW_IMAGE_SIZE_BYTES      = 31744 + +FLASH_DATA_PACKET_SIZE = 256 + +#see fw_common.h +FLASH_ARGS_FMT = '!LLLLL256s' +FLASH_INFO_FMT = '!LLLLL256x' +FLASH_IP_FMT =   '!LLLL260x' + +class update_id_t: +  USRP2_FW_UPDATE_ID_WAT = ord(' ') +  USRP2_FW_UPDATE_ID_OHAI_LOL = ord('a') +  USRP2_FW_UPDATE_ID_OHAI_OMG = ord('A') +  USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL = ord('f') +  USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG = ord('F') +  USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL = ord('e') +  USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG = ord('E') +  USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL = ord('d') +  USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG = ord('D') +  USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG = ord('B') +  USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL = ord('w') +  USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG = ord('W') +  USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL = ord('r') +  USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG = ord('R') +  USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL = ord('s') +  USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG = ord('S') +  USRP2_FW_UPDATE_ID_KTHXBAI = ord('~') + +_seq = -1 +def seq(): +  global _seq +  _seq = _seq+1 +  return _seq  + +######################################################################## +# helper functions +######################################################################## +def unpack_flash_args_fmt(s): +  return struct.unpack(FLASH_ARGS_FMT, s) #(proto_ver, pktid, seq, flash_addr, length, data) + +def unpack_flash_info_fmt(s): +  return struct.unpack(FLASH_INFO_FMT, s) #(proto_ver, pktid, seq, sector_size_bytes, memory_size_bytes) + +def unpack_flash_ip_fmt(s): +  return struct.unpack(FLASH_IP_FMT, s) #(proto_ver, pktid, seq, ip_addr) + +def pack_flash_args_fmt(proto_ver, pktid, seq, flash_addr, length, data): +  return struct.pack(FLASH_ARGS_FMT, proto_ver, pktid, seq, flash_addr, length, data) + +def pack_flash_info_fmt(proto_ver, pktid, seq, sector_size_bytes, memory_size_bytes): +  return struct.pack(FLASH_INFO_FMT, proto_ver, pktid, seq, sector_size_bytes, memory_size_bytes) + +def send_and_recv(pkt, ip): +  update_socket = create_socket() + +  try: +    update_socket.sendto(pkt, (ip, UDP_FW_UPDATE_PORT)) +  except Exception, e:  +    print e +    sys.exit(1) + +  try: +    (recv_pkt, recv_addr) = update_socket.recvfrom(UDP_MAX_XFER_BYTES) +  except Exception, e:  +    print e +    sys.exit(1) + +  if recv_addr != (options.ip, UDP_FW_UPDATE_PORT): +    raise Exception, "Packet received from invalid IP %s, expected %s" % (recv_addr, options.ip) + +  return recv_pkt + +def create_socket(): +  socket.setdefaulttimeout(UDP_TIMEOUT) +  update_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +  return update_socket + +#just here to validate comms +def init_update(ip): +  out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_OHAI_LOL, seq(), 0, 0, "") +  in_pkt = send_and_recv(out_pkt, ip) +  (proto_ver, pktid, rxseq, ip_addr) = unpack_flash_ip_fmt(in_pkt) +  if pktid == update_id_t.USRP2_FW_UPDATE_ID_OHAI_OMG: +    print "USRP2P found." +  else: +    raise Exception, "Invalid reply received from device." + +#  print "Incoming:\n\tVer: %i\n\tID: %c\n\tSeq: %i\n\tIP: %i\n" % (proto_ver, chr(pktid), rxseq, ip_addr) + +def get_flash_info(ip): +  out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL, seq(), 0, 0, "") +  in_pkt = send_and_recv(out_pkt, ip) + +  (proto_ver, pktid, rxseq, sector_size_bytes, memory_size_bytes) = unpack_flash_info_fmt(in_pkt) + +  if pktid != update_id_t.USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG: +    raise Exception, "Invalid reply %c from device." % (chr(pktid)) + + +  return (memory_size_bytes, sector_size_bytes) + +def burn_fw(ip, fw, fpga): +  init_update(ip) +  (flash_size, sector_size) = get_flash_info(ip) + +  print "Flash size: %i\nSector size: %i" % (flash_size, sector_size) + +  if fpga:  +    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) + +  if fw: +    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) + +def write_image(ip, image, addr): +#we split the image into smaller (256B) bits and send them down the wire +  while image: +    out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL, seq(), addr, FLASH_DATA_PACKET_SIZE, image[:FLASH_DATA_PACKET_SIZE]) +    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_WROTE_TEH_FLASHES_OMG: +      raise Exception, "Invalid reply %c from device." % (chr(pktid)) + +    image = image[FLASH_DATA_PACKET_SIZE:] +    addr += FLASH_DATA_PACKET_SIZE + +def verify_image(ip, image, addr): +  readsize = len(image) +  readdata = str() +  while readsize > 0: +    if readsize < FLASH_DATA_PACKET_SIZE: thisreadsize = readsize +    else: thisreadsize = FLASH_DATA_PACKET_SIZE +    out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL, seq(), addr, thisreadsize, "") +    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_KK_READ_TEH_FLASHES_OMG: +      raise Exception, "Invalid reply %c from device." % (chr(pktid)) + +    readdata += data[:thisreadsize] +    readsize -= FLASH_DATA_PACKET_SIZE +    addr += FLASH_DATA_PACKET_SIZE + +  print "Read back %i bytes" % len(readdata) +#  print readdata + +#  for i in range(256, 512): +#    print "out: %i in: %i" % (ord(image[i]), ord(readdata[i])) + +  if readdata != image: +    print "Verify failed. Image did not write correctly." +  else: +    print "Success." + +def erase_image(ip, addr, length): +  #get flash info first +  out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL, seq(), addr, length, "") +  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_ERASING_TEH_FLASHES_OMG: +    raise Exception, "Invalid reply %c from device." % (chr(pktid)) + +  print "Erasing %i bytes at %i" % (length, addr) + +  #now wait for it to finish +  while(1): +    out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_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_IM_DONE_ERASING_OMG: break +    elif pktid != update_id_t.USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG: +      raise Exception, "Invalid reply %c from device." % (chr(pktid)) + +  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='') +    (options, args) = parser.parse_args() + +    return options + +######################################################################## +# main +######################################################################## +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) | 
