summaryrefslogtreecommitdiffstats
path: root/firmware/microblaze/usrp2p
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/microblaze/usrp2p')
-rw-r--r--firmware/microblaze/usrp2p/Makefile.am1
-rw-r--r--firmware/microblaze/usrp2p/bootloader/Makefile.am2
-rw-r--r--firmware/microblaze/usrp2p/bootloader/init_bootloader.c53
-rw-r--r--firmware/microblaze/usrp2p/bootloader_utils.c18
-rw-r--r--firmware/microblaze/usrp2p/udp_fw_update.c2
-rw-r--r--firmware/microblaze/usrp2p/xilinx_s3_icap.c99
6 files changed, 153 insertions, 22 deletions
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 <bootloader_utils.h>
#include <string.h>
#include <hal_uart.h>
+#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 <stdbool.h>
#include <string.h>
#include <bootloader_utils.h>
#include <spi_flash.h>
-
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 <string.h>
#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 <http://www.gnu.org/licenses/>.
+ */
+
+
+/* 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 <xilinx_s3_icap.h>
+#include <memory_map.h>
+#include <spi_flash_private.h> //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);
+}