aboutsummaryrefslogtreecommitdiffstats
path: root/firmware/zpu/usrp2p/bootloader
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/zpu/usrp2p/bootloader')
-rw-r--r--firmware/zpu/usrp2p/bootloader/CMakeLists.txt39
-rw-r--r--firmware/zpu/usrp2p/bootloader/Makefile.am39
-rw-r--r--firmware/zpu/usrp2p/bootloader/fpga_bootloader.c202
-rw-r--r--firmware/zpu/usrp2p/bootloader/fw_bootloader.c50
-rw-r--r--firmware/zpu/usrp2p/bootloader/icap_test.c31
-rw-r--r--firmware/zpu/usrp2p/bootloader/init_bootloader.c120
-rw-r--r--firmware/zpu/usrp2p/bootloader/serial_loader_burner.c49
-rw-r--r--firmware/zpu/usrp2p/bootloader/spi_bootloader.c134
-rw-r--r--firmware/zpu/usrp2p/bootloader/u2p2-rom.ld190
9 files changed, 854 insertions, 0 deletions
diff --git a/firmware/zpu/usrp2p/bootloader/CMakeLists.txt b/firmware/zpu/usrp2p/bootloader/CMakeLists.txt
new file mode 100644
index 000000000..41c86cc9a
--- /dev/null
+++ b/firmware/zpu/usrp2p/bootloader/CMakeLists.txt
@@ -0,0 +1,39 @@
+#
+# 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(FindPythonInterp)
+
+MACRO(GEN_RMI target)
+ GET_FILENAME_COMPONENT(name ${target} NAME_WE)
+ #command to create a rmi from elf
+ ADD_CUSTOM_COMMAND(
+ OUTPUT ${name}.rmi DEPENDS ${name}.bin
+ COMMAND ${PYTHON_EXECUTABLE}
+ ARGS ${CMAKE_SOURCE_DIR}/bin/bin_to_ram_macro_init.py ${name}.bin ${name}.rmi
+ )
+ #add a top level target for output files
+ ADD_CUSTOM_TARGET(
+ ${name}_rmi ALL DEPENDS ${name}.rmi
+ )
+ENDMACRO(GEN_RMI)
+
+########################################################################
+ADD_EXECUTABLE(init_bootloader.elf init_bootloader.c)
+TARGET_LINK_LIBRARIES(init_bootloader.elf libusrp2pfw)
+GEN_OUTPUTS(init_bootloader.elf)
+GEN_RMI(init_bootloader.bin)
diff --git a/firmware/zpu/usrp2p/bootloader/Makefile.am b/firmware/zpu/usrp2p/bootloader/Makefile.am
new file mode 100644
index 000000000..1fc5daf9c
--- /dev/null
+++ b/firmware/zpu/usrp2p/bootloader/Makefile.am
@@ -0,0 +1,39 @@
+#
+# Copyright 2007,2008,2009 Free Software Foundation, Inc.
+#
+# 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 $(top_srcdir)/Makefile.common
+
+ROM_LINKER_SCRIPT = u2p2-rom.ld
+
+# loads into 8K boot ram located at 0x0000_0000
+AM_CFLAGS = $(COMMON_CFLAGS) -I$(top_srcdir)/usrp2p
+AM_LDFLAGS = -Wl,-T,$(ROM_LINKER_SCRIPT) $(COMMON_LFLAGS) -Wl,-defsym -Wl,_STACK_SIZE=1024
+
+EXTRA_DIST = $(ROM_LINKER_SCRIPT)
+
+LDADD = $(top_srcdir)/usrp2p/libusrp2p.a
+
+noinst_PROGRAMS = \
+ init_bootloader.elf
+
+init_bootloader_elf_SOURCES = init_bootloader.c
+
+.bin.rmi:
+ $(top_srcdir)/bin/bin_to_ram_macro_init.py $< $@
+
+_generated_from_elf += \
+ $(noinst_PROGRAMS:.elf=.rmi)
diff --git a/firmware/zpu/usrp2p/bootloader/fpga_bootloader.c b/firmware/zpu/usrp2p/bootloader/fpga_bootloader.c
new file mode 100644
index 000000000..9feff6ecd
--- /dev/null
+++ b/firmware/zpu/usrp2p/bootloader/fpga_bootloader.c
@@ -0,0 +1,202 @@
+/* -*- 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/>.
+ */
+
+/*
+ * This code is bootloader f/w for the slot 0 fpga image. It's job is
+ * to figure out which fpga image should be loaded, and then to load
+ * that image from the SPI flash. (FIXME handle retries, errors,
+ * etc.)
+ *
+ * If the center button is down during boot, it loads firwmare
+ * from 0:0 instead of its normal action.
+ */
+
+#include <stdlib.h>
+#include <hal_io.h>
+#include <nonstdio.h>
+#include <mdelay.h>
+#include <quadradio/flashdir.h>
+#include <xilinx_v5_icap.h>
+#include <bootconfig.h>
+#include <bootconfig_private.h>
+#include <spi_flash.h>
+#include <string.h>
+#include <bootloader_utils.h>
+#include <hal_interrupts.h>
+
+#define VERBOSE 1
+
+#define OUR_FPGA_IMAGE_NUMBER 0 // this code only runs in slot 0
+
+void hal_uart_init(void);
+void spif_init(void);
+void i2c_init(void);
+void bootconfig_init(void);
+
+void pic_interrupt_handler() __attribute__ ((interrupt_handler));
+
+void pic_interrupt_handler()
+{
+ // nop stub
+}
+
+static int
+flash_addr_of_fpga_slot(unsigned int fpga_slot)
+{
+ const struct flashdir *fd = get_flashdir();
+ return fd->slot[fpga_slot + fd->fpga_slot0].start << spi_flash_log2_sector_size();
+}
+
+
+/*
+ * If the first 256 bytes of the image contain the string of bytes,
+ * ff ff ff ff aa 99 55 66, we consider it a likely bitstream.
+ */
+static bool
+looks_like_a_bitstream(unsigned int fpga_slot)
+{
+ unsigned char buf[256];
+ static const unsigned char pattern[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xaa, 0x99, 0x55, 0x66
+ };
+
+ // Read the first 256 bytes of the bitstream
+ spi_flash_read(flash_addr_of_fpga_slot(fpga_slot), sizeof(buf), buf);
+
+ for (int i = 0; i <= sizeof(buf) - sizeof(pattern); i++)
+ if (memcmp(pattern, &buf[i], sizeof(pattern)) == 0)
+ return true;
+
+ return false;
+}
+
+static bool
+plausible_bootconfig(bootconfig_t bc)
+{
+ // Are the fields in range?
+ if (!validate_bootconfig(bc))
+ return false;
+
+ if (!looks_like_a_bitstream(map_fpga_image_number_to_fpga_slot(bc.fpga_image_number)))
+ return false;
+
+ return true;
+}
+
+// Attempt to boot the fpga image specified in next_boot
+static void
+initial_boot_attempt(eeprom_boot_info_t *ee)
+{
+ if (ee->next_boot.fpga_image_number == OUR_FPGA_IMAGE_NUMBER){
+ load_firmware();
+ return;
+ }
+
+ ee->nattempts = 1;
+ _bc_write_eeprom_shadow();
+
+ unsigned int target_slot =
+ map_fpga_image_number_to_fpga_slot(ee->next_boot.fpga_image_number);
+ int flash_addr = flash_addr_of_fpga_slot(target_slot);
+
+ putstr("fpga_bootloader: chaining to ");
+ puthex4(ee->next_boot.fpga_image_number);
+ putchar(':');
+ puthex4(ee->next_boot.firmware_image_number);
+ newline();
+ mdelay(100);
+
+ while (1){
+ icap_reload_fpga(flash_addr);
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ hal_disable_ints(); // In case we got here via jmp 0x0
+ hal_uart_init();
+ i2c_init();
+ bootconfig_init(); // Must come after i2c_init.
+ spif_init(); // Needed for get_flashdir.
+
+ sr_leds->leds = 0xAAAA;
+
+ putstr("\n\n>>> fpga_bootloader <<<\n");
+
+ putstr("\nBOOTSTS ");
+ int bootsts = icap_read_config_reg(rBOOTSTS);
+ puthex32_nl(bootsts);
+ putstr("STAT ");
+ int stat = icap_read_config_reg(rSTAT);
+ puthex32_nl(stat);
+
+ bool fallback =
+ ((bootsts & (BOOTSTS_VALID_0 | BOOTSTS_FALLBACK_0))
+ == (BOOTSTS_VALID_0 | BOOTSTS_FALLBACK_0));
+
+ if (fallback){
+ puts("FALLBACK_0 is set");
+ // FIXME handle fallback condition.
+ }
+
+ const struct flashdir *fd = get_flashdir();
+ if (fd == 0)
+ abort();
+
+ eeprom_boot_info_t *ee = _bc_get_eeprom_shadow();
+
+ if (VERBOSE){
+ putstr("nattempts: ");
+ puthex8_nl(ee->nattempts);
+ }
+
+ mdelay(500); // wait for low-pass on switches
+ putstr("switches: "); puthex32_nl(readback->switches);
+
+ bool center_btn_down = (readback->switches & BTN_CENTER) != 0;
+ if (center_btn_down){
+ putstr("Center button is down!\n");
+ // Force boot of image 0:0
+ ee->next_boot = make_bootconfig(0, 0);
+ }
+
+ // if next_boot is valid, try it
+ if (plausible_bootconfig(ee->next_boot))
+ initial_boot_attempt(ee); // no return
+
+ // if default_boot is valid, try it
+ if (plausible_bootconfig(ee->default_boot)){
+ ee->next_boot = ee->default_boot;
+ initial_boot_attempt(ee); // no return
+ }
+
+ // If we're here, we're in trouble. Try all of them...
+ for (int i = 0; i < 4; i++){
+ bootconfig_t bc = make_bootconfig(i, 0);
+ if (plausible_bootconfig(bc)){
+ ee->next_boot = bc;
+ initial_boot_attempt(ee); // no return
+ }
+ }
+
+ // FIXME, try to find something we can load
+ puts("\n!!! Failed to find a valid FPGA bitstream!\n\n");
+
+ return 0;
+}
diff --git a/firmware/zpu/usrp2p/bootloader/fw_bootloader.c b/firmware/zpu/usrp2p/bootloader/fw_bootloader.c
new file mode 100644
index 000000000..a2c32bf8e
--- /dev/null
+++ b/firmware/zpu/usrp2p/bootloader/fw_bootloader.c
@@ -0,0 +1,50 @@
+/* -*- 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 <memory_map.h>
+#include <nonstdio.h>
+#include <stdlib.h>
+#include <bootconfig.h>
+#include <bootconfig_private.h>
+#include <bootloader_utils.h>
+#include <hal_interrupts.h>
+
+
+void hal_uart_init(void);
+void spif_init(void);
+void i2c_init(void);
+void bootconfig_init(void);
+
+void pic_interrupt_handler() __attribute__ ((interrupt_handler));
+
+void pic_interrupt_handler()
+{
+ // nop stub
+}
+
+int
+main(int argc, char **argv)
+{
+ hal_disable_ints(); // In case we got here via jmp 0x0
+ hal_uart_init();
+ i2c_init();
+ bootconfig_init(); // Must come after i2c_init.
+ spif_init(); // Needed for get_flashdir.
+
+ load_firmware();
+}
diff --git a/firmware/zpu/usrp2p/bootloader/icap_test.c b/firmware/zpu/usrp2p/bootloader/icap_test.c
new file mode 100644
index 000000000..5feb9d014
--- /dev/null
+++ b/firmware/zpu/usrp2p/bootloader/icap_test.c
@@ -0,0 +1,31 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2010 Ettus Research LLC
+ *
+ */
+
+#include <memory_map.h>
+#include <hal_io.h>
+#include <xilinx_s3_icap.h>
+#include <nonstdio.h>
+
+void delay(uint32_t t) {
+ while(t-- != 0) asm("NOP");
+}
+
+
+int main(int argc, char *argv[]) {
+ pic_init();
+ hal_uart_init();
+ puts("\nStarting delay...\n");
+
+ output_regs->leds = 0xFF;
+ delay(4000000);
+ output_regs->leds = 0x00;
+ delay(4000000);
+
+ puts("Rebooting FPGA to 0x00000000\n");
+ icap_reload_fpga((uint32_t)0x00000000);
+
+ return 0;
+}
diff --git a/firmware/zpu/usrp2p/bootloader/init_bootloader.c b/firmware/zpu/usrp2p/bootloader/init_bootloader.c
new file mode 100644
index 000000000..cfa80ffea
--- /dev/null
+++ b/firmware/zpu/usrp2p/bootloader/init_bootloader.c
@@ -0,0 +1,120 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2010 Ettus Research LLC
+ *
+ */
+
+#include <memory_map.h>
+#include <nonstdio.h>
+#include <hal_io.h>
+#include <xilinx_s3_icap.h>
+#include <spi_flash.h>
+#include <spi_flash_private.h>
+//#include <clocks.h>
+#include <ihex.h>
+#include <bootloader_utils.h>
+#include <string.h>
+#include <hal_uart.h>
+#include <i2c.h>
+#include "usrp2/fw_common.h"
+
+//void pic_interrupt_handler() __attribute__ ((interrupt_handler));
+
+void pic_interrupt_handler()
+{
+ // nop stub
+}
+
+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
+
+ ihex_record_t ihex_record;
+ ihex_record.data = ihx;
+
+ while(1) {
+ gets(buf);
+
+ if(!ihex_parse(buf, &ihex_record)) { //RAM data record is valid
+ if(ihex_record.addr >= RAM_BASE) { //it's expecting to see FULLY RELOCATED IHX RECORDS. every address referenced to 0x8000, including vectors.
+ memcpy((void *) (ihex_record.addr), ihex_record.data, ihex_record.length);
+ puts("OK");
+ } else if(ihex_record.type == 1) { //end of record
+ puts("OK");
+ //load main firmware
+ start_program();
+ puts("ERROR: main image returned! Back in IHEX load mode.");
+ } else puts("NOK"); //RAM loads do not support extended segment address records (04) -- upper 16 bits are always "0".
+ } else puts("NOK");
+ }
+}
+
+void delay(uint32_t t) {
+ while(t-- != 0) asm("NOP");
+}
+
+int main(int argc, char *argv[]) {
+ hal_disable_ints(); // In case we got here via jmp 0x0
+ output_regs->leds = 0xFF;
+ delay(5000);
+ output_regs->leds = 0x00;
+ hal_uart_init();
+ spif_init();
+ i2c_init(); //for EEPROM
+ puts("USRP2+ bootloader super ultra ZPU edition\n");
+
+ bool production_image = find_safe_booted_flag();
+ set_safe_booted_flag(0); //haven't booted yet
+
+ if(BUTTON_PUSHED) { //see memory_map.h
+ puts("Starting USRP2+ in safe mode.");
+ if(is_valid_fw_image(SAFE_FW_IMAGE_LOCATION_ADDR)) {
+ set_safe_booted_flag(1); //let the firmware know it's the safe image
+ spi_flash_read(SAFE_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES, (void *)RAM_BASE);
+ start_program();
+ puts("ERROR: return from main program! This should never happen!");
+ 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.");
+ load_ihex();
+ }
+ }
+
+ 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(300); //so serial output can finish
+ icap_reload_fpga(PROD_FPGA_IMAGE_LOCATION_ADDR);
+ }
+ 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);
+ puts("Finished loading. Starting image.");
+ delay(300);
+ start_program();
+ 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.
+ delay(300);
+ icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR);
+ return 1;
+ }
+ puts("No valid production firmware found. Trying safe firmware...");
+ if(is_valid_fw_image(SAFE_FW_IMAGE_LOCATION_ADDR)) {
+ spi_flash_read(SAFE_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES, (void *)RAM_BASE);
+ puts("Finished loading. Starting image.");
+ delay(300);
+ start_program();
+ puts("ERROR: return from main program! This should never happen!");
+ delay(300);
+ 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();
+
+ return 0;
+}
diff --git a/firmware/zpu/usrp2p/bootloader/serial_loader_burner.c b/firmware/zpu/usrp2p/bootloader/serial_loader_burner.c
new file mode 100644
index 000000000..4ac4df454
--- /dev/null
+++ b/firmware/zpu/usrp2p/bootloader/serial_loader_burner.c
@@ -0,0 +1,49 @@
+/* -*- 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 <hal_io.h>
+#include <nonstdio.h>
+#include <mdelay.h>
+#include <gdbstub2.h>
+
+void hal_uart_init(void);
+void spif_init(void);
+
+void pic_interrupt_handler() __attribute__ ((interrupt_handler));
+
+void pic_interrupt_handler()
+{
+ // nop stub
+}
+
+int
+main(int argc, char **argv)
+{
+ hal_uart_init();
+ spif_init();
+
+ sr_leds->leds = 0;
+ mdelay(100);
+ sr_leds->leds = ~0;
+ mdelay(100);
+ sr_leds->leds = 0;
+
+ puts("\n\n>>> stage1: serial_loader_burner <<<");
+
+ gdbstub2_main_loop();
+}
diff --git a/firmware/zpu/usrp2p/bootloader/spi_bootloader.c b/firmware/zpu/usrp2p/bootloader/spi_bootloader.c
new file mode 100644
index 000000000..678e66cf7
--- /dev/null
+++ b/firmware/zpu/usrp2p/bootloader/spi_bootloader.c
@@ -0,0 +1,134 @@
+/* -*- 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 <hal_io.h>
+#include <nonstdio.h>
+#include <mdelay.h>
+#include <spi_flash.h>
+#include <quadradio/flashdir.h>
+#include <quadradio/simple_binary_format.h>
+#include <stdlib.h>
+
+
+void hal_uart_init(void);
+void spif_init(void);
+
+void pic_interrupt_handler() __attribute__ ((interrupt_handler));
+
+void pic_interrupt_handler()
+{
+ // nop stub
+}
+
+static void
+error(int e)
+{
+ putstr("ERR");
+ puthex8(e);
+ newline();
+}
+
+static void
+load(uint32_t flash_addr, uint32_t ram_addr, uint32_t size)
+{
+ spi_flash_read(flash_addr, size, (void *) ram_addr);
+}
+
+static bool
+load_from_slot(const struct flashdir *fd, int fw_slot)
+{
+ putstr("Loading f/w image ");
+ putchar('0' + fw_slot);
+ putstr("... ");
+
+ if (fw_slot >= fd->fw_nslots){
+ error(1);
+ return false;
+ }
+
+ int slot = fw_slot + fd->fw_slot0;
+ if (fd->slot[slot].start == 0 || fd->slot[slot].start == 0xffff
+ || fd->slot[slot].len == 0 || fd->slot[slot].len == 0xffff){
+ error(2);
+ return false;
+ }
+
+ uint32_t sbf_base = fd->slot[slot].start << spi_flash_log2_sector_size();
+ uint32_t sbf_len = fd->slot[slot].len << spi_flash_log2_sector_size();
+ uint32_t sbf_offset = 0;
+
+ struct sbf_header sbf;
+ spi_flash_read(sbf_base, sizeof(struct sbf_header), &sbf);
+ if (sbf.magic != SBF_MAGIC || sbf.nsections > SBF_MAX_SECTIONS){
+ error(3);
+ return false;
+ }
+ sbf_offset += sizeof(struct sbf_header);
+
+ unsigned int i;
+ for (i = 0; i < sbf.nsections; i++){
+ if (sbf_offset + sbf.sec_desc[i].length > sbf_len){
+ error(4);
+ return false;
+ }
+ load(sbf_offset + sbf_base,
+ sbf.sec_desc[i].target_addr,
+ sbf.sec_desc[i].length);
+ sbf_offset += sbf.sec_desc[i].length;
+ }
+ putstr("Done!");
+
+ typedef void (*fptr_t)(void);
+ (*(fptr_t) sbf.entry)(); // almost certainly no return
+
+ return true;
+}
+
+int
+main(int argc, char **argv)
+{
+ hal_uart_init();
+ spif_init();
+
+ sr_leds->leds = 0;
+ mdelay(100);
+ sr_leds->leds = ~0;
+ mdelay(100);
+ sr_leds->leds = 0;
+
+ putstr("\n>>> spi_bootloader <<<\n");
+
+ const struct flashdir *fd = get_flashdir();
+ if (fd == 0)
+ abort();
+
+ while(1){
+ int sw;
+ int fw_slot;
+
+ sw = readback->switches;
+ fw_slot = sw & 0x7;
+
+ if (!load_from_slot(fd, fw_slot)){
+ if (fw_slot != 0){
+ putstr("Falling back to slot 0\n");
+ load_from_slot(fd, 0);
+ }
+ }
+ }
+}
diff --git a/firmware/zpu/usrp2p/bootloader/u2p2-rom.ld b/firmware/zpu/usrp2p/bootloader/u2p2-rom.ld
new file mode 100644
index 000000000..4c9eaa8e5
--- /dev/null
+++ b/firmware/zpu/usrp2p/bootloader/u2p2-rom.ld
@@ -0,0 +1,190 @@
+/*
+ * Same as default, but with bss and stack moved to top 2K of main ram
+ * Copied from qr-rom.ld
+ */
+
+/* Default linker script, for normal executables */
+OUTPUT_FORMAT("elf32-microblaze", "", "")
+/*SEARCH_DIR("/home/eb/build/Xilinx_EDK_GNU_10.1i/mb/release/lin/mb/microblaze-xilinx-elf/lib");*/
+
+
+ENTRY(_start)
+_TEXT_START_ADDR = DEFINED(_TEXT_START_ADDR) ? _TEXT_START_ADDR : 0x50;
+_HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x0;
+_STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : 0x400;
+_BSS_START_ADDR = DEFINED(_BSS_START_ADDR) ? _BSS_START_ADDR : 0xF800;
+SECTIONS
+{
+ .vectors.reset 0x0 : { KEEP (*(.vectors.reset)) } = 0
+ .vectors.sw_exception 0x8 : { KEEP (*(.vectors.sw_exception)) } = 0
+ .vectors.interrupt 0x10 : { KEEP (*(.vectors.interrupt)) } = 0
+ .vectors.debug_sw_break 0x18 : { KEEP (*(.vectors.debug_sw_break)) } = 0
+ .vectors.hw_exception 0x20 : { KEEP (*(.vectors.hw_exception)) } = 0
+ . = _TEXT_START_ADDR;
+ _ftext = .;
+ .text : {
+ *(.text)
+ *(.text.*)
+ *(.gnu.linkonce.t.*)
+ }
+ _etext = .;
+ .init : { KEEP (*(.init)) } =0
+ .fini : { KEEP (*(.fini)) } =0
+ PROVIDE (__CTOR_LIST__ = .);
+ PROVIDE (___CTOR_LIST__ = .);
+ .ctors :
+ {
+ /* gcc uses crtbegin.o to find the start of
+ the constructors, so we make sure it is
+ first. Because this is a wildcard, it
+ doesn't matter if the user does not
+ actually link against crtbegin.o; the
+ linker won't look for a file to match a
+ wildcard. The wildcard also means that it
+ doesn't matter which directory crtbegin.o
+ is in. */
+ KEEP (*crtbegin.o(.ctors))
+ /* We don't want to include the .ctor section from
+ from the crtend.o file until after the sorted ctors.
+ The .ctor section from the crtend file contains the
+ end of ctors marker and it must be last */
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ }
+ PROVIDE (__CTOR_END__ = .);
+ PROVIDE (___CTOR_END__ = .);
+ PROVIDE (__DTOR_LIST__ = .);
+ PROVIDE (___DTOR_LIST__ = .);
+ .dtors :
+ {
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ }
+ PROVIDE (__DTOR_END__ = .);
+ PROVIDE (___DTOR_END__ = .);
+ . = ALIGN(4);
+ _frodata = . ;
+ .rodata : {
+ *(.rodata)
+ *(.rodata.*)
+ *(.gnu.linkonce.r.*)
+ CONSTRUCTORS; /* Is this needed? */
+ }
+ _erodata = .;
+ /* Alignments by 8 to ensure that _SDA2_BASE_ on a word boundary */
+ /* Note that .sdata2 and .sbss2 must be contiguous */
+ . = ALIGN(8);
+ _ssrw = .;
+ .sdata2 : {
+ *(.sdata2)
+ *(.sdata2.*)
+ *(.gnu.linkonce.s2.*)
+ }
+ . = ALIGN(4);
+ .sbss2 : {
+ PROVIDE (__sbss2_start = .);
+ *(.sbss2)
+ *(.sbss2.*)
+ *(.gnu.linkonce.sb2.*)
+ PROVIDE (__sbss2_end = .);
+ }
+ . = ALIGN(8);
+ _essrw = .;
+ _ssrw_size = _essrw - _ssrw;
+ PROVIDE (_SDA2_BASE_ = _ssrw + (_ssrw_size / 2 ));
+ . = ALIGN(4);
+ _fdata = .;
+ .data : {
+ *(.data)
+ *(.gnu.linkonce.d.*)
+ CONSTRUCTORS; /* Is this needed? */
+ }
+ _edata = . ;
+ /* Added to handle pic code */
+ .got : {
+ *(.got)
+ }
+ .got1 : {
+ *(.got1)
+ }
+ .got2 : {
+ *(.got2)
+ }
+ /* Added by Sathya to handle C++ exceptions */
+ .eh_frame : {
+ *(.eh_frame)
+ }
+ .jcr : {
+ *(.jcr)
+ }
+ .gcc_except_table : {
+ *(.gcc_except_table)
+ }
+ /* Alignments by 8 to ensure that _SDA_BASE_ on a word boundary */
+ /* Note that .sdata and .sbss must be contiguous */
+ . = ALIGN(8);
+ _ssro = .;
+ .sdata : {
+ *(.sdata)
+ *(.sdata.*)
+ *(.gnu.linkonce.s.*)
+ }
+ . = ALIGN(4);
+ .sbss : {
+ PROVIDE (__sbss_start = .);
+ *(.sbss)
+ *(.sbss.*)
+ *(.gnu.linkonce.sb.*)
+ PROVIDE (__sbss_end = .);
+ }
+ . = ALIGN(8);
+ _essro = .;
+ _ssro_size = _essro - _ssro;
+ PROVIDE (_SDA_BASE_ = _ssro + (_ssro_size / 2 ));
+ . = _BSS_START_ADDR;
+ . = ALIGN(4);
+ _fbss = .;
+ .bss : {
+ PROVIDE (__bss_start = .);
+ *(.bss)
+ *(.bss.*)
+ *(.gnu.linkonce.b.*)
+ *(COMMON)
+ . = ALIGN(4);
+ PROVIDE (__bss_end = .);
+ }
+ . = ALIGN(4);
+ .heap : {
+ _heap = .;
+ _heap_start = .;
+ . += _HEAP_SIZE;
+ _heap_end = .;
+ }
+ _end = .;
+ . = ALIGN(4);
+ . = 0xFFF0;
+ .stack : {
+ /*
+ _stack_end = .;
+ . += _STACK_SIZE;
+ . = ALIGN(8);
+ _stack = .;
+ _end = .;
+ */
+ _stack_end = .;
+ _stack = .;
+ }
+ .tdata : {
+ *(.tdata)
+ *(.tdata.*)
+ *(.gnu.linkonce.td.*)
+ }
+ .tbss : {
+ *(.tbss)
+ *(.tbss.*)
+ *(.gnu.linkonce.tb.*)
+ }
+}