aboutsummaryrefslogtreecommitdiffstats
path: root/firmware/zpu/usrp2p
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/zpu/usrp2p')
-rw-r--r--firmware/zpu/usrp2p/CMakeLists.txt44
-rw-r--r--firmware/zpu/usrp2p/bootconfig.h61
-rw-r--r--firmware/zpu/usrp2p/bootloader/CMakeLists.txt42
-rw-r--r--firmware/zpu/usrp2p/bootloader/init_bootloader.c123
-rw-r--r--firmware/zpu/usrp2p/bootloader/spi_bootloader.c134
-rw-r--r--firmware/zpu/usrp2p/bootloader/u2p2-rom.ld190
-rw-r--r--firmware/zpu/usrp2p/bootloader/udp_bootloader.c200
-rw-r--r--firmware/zpu/usrp2p/bootloader_utils.c104
-rw-r--r--firmware/zpu/usrp2p/bootloader_utils.h23
-rw-r--r--firmware/zpu/usrp2p/eth_phy.h235
-rw-r--r--firmware/zpu/usrp2p/ethernet.c304
-rw-r--r--firmware/zpu/usrp2p/spi_flash.c205
-rw-r--r--firmware/zpu/usrp2p/spi_flash.h110
-rw-r--r--firmware/zpu/usrp2p/spi_flash_private.h70
-rw-r--r--firmware/zpu/usrp2p/spi_flash_read.c112
-rw-r--r--firmware/zpu/usrp2p/spif.c68
-rw-r--r--firmware/zpu/usrp2p/u2p_init.c30
-rw-r--r--firmware/zpu/usrp2p/u2p_init.h18
-rw-r--r--firmware/zpu/usrp2p/udp_fw_update.c128
-rw-r--r--firmware/zpu/usrp2p/xilinx_s3_icap.c99
-rw-r--r--firmware/zpu/usrp2p/xilinx_s3_icap.h37
21 files changed, 2337 insertions, 0 deletions
diff --git a/firmware/zpu/usrp2p/CMakeLists.txt b/firmware/zpu/usrp2p/CMakeLists.txt
new file mode 100644
index 000000000..976dccb9a
--- /dev/null
+++ b/firmware/zpu/usrp2p/CMakeLists.txt
@@ -0,0 +1,44 @@
+#
+# Copyright 2010-2011 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(${CMAKE_SOURCE_DIR}/lib/CMakeLists.txt)
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+
+ADD_DEFINITIONS(-DUSRP2P)
+
+ADD_LIBRARY(libusrp2pfw STATIC
+ ${COMMON_SRCS}
+ spif.c
+ spi_flash.c
+ spi_flash_read.c
+ bootloader_utils.c
+ ethernet.c
+ xilinx_s3_icap.c
+ udp_fw_update.c
+ u2p_init.c
+)
+
+ADD_SUBDIRECTORY(bootloader)
+
+########################################################################
+SET(GEN_OUTPUTS_BIN_SIZE 0x3fff)
+
+ADD_EXECUTABLE(usrp2p_txrx_uhd.elf ${CMAKE_SOURCE_DIR}/apps/txrx_uhd.c)
+TARGET_LINK_LIBRARIES(usrp2p_txrx_uhd.elf libusrp2pfw)
+GEN_OUTPUTS(usrp2p_txrx_uhd.elf)
+
diff --git a/firmware/zpu/usrp2p/bootconfig.h b/firmware/zpu/usrp2p/bootconfig.h
new file mode 100644
index 000000000..b64834d22
--- /dev/null
+++ b/firmware/zpu/usrp2p/bootconfig.h
@@ -0,0 +1,61 @@
+/* -*- c -*- */
+/*
+ * Copyright 2009-2011 Ettus Research LLC
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio 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, or (at your option)
+ * any later version.
+ *
+ * GNU Radio 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef INCLUDED_BOOTCONFIG_H
+#define INCLUDED_BOOTCONFIG_H
+
+#include <stdbool.h>
+
+typedef struct {
+ unsigned char fpga_image_number;
+ unsigned char firmware_image_number;
+} bootconfig_t;
+
+static inline bootconfig_t
+make_bootconfig(unsigned char fpga_image_number, unsigned char firmware_image_number)
+{
+ bootconfig_t r;
+ r.fpga_image_number = fpga_image_number;
+ r.firmware_image_number = firmware_image_number;
+ return r;
+}
+
+void bootconfig_init(void); /* One time call to initialize */
+
+/*!
+ * \return default boot configuration
+ */
+bootconfig_t bootconfig_get_default(void);
+
+/*!
+ * \brief Set the default boot configuration.
+ */
+bool bootconfig_set_default(bootconfig_t bc);
+
+/*!
+ * \brief attempt to boot the given fpga and software image.
+ *
+ * If successful, this routine does not return.
+ * If it fail for some reason, it returns.
+ */
+void bootconfig_boot(bootconfig_t bc);
+
+#endif /* INCLUDED_BOOTCONFIG_H */
diff --git a/firmware/zpu/usrp2p/bootloader/CMakeLists.txt b/firmware/zpu/usrp2p/bootloader/CMakeLists.txt
new file mode 100644
index 000000000..07f234302
--- /dev/null
+++ b/firmware/zpu/usrp2p/bootloader/CMakeLists.txt
@@ -0,0 +1,42 @@
+#
+# Copyright 2010-2011 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}
+ ${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(bootloader.elf ${CMAKE_SOURCE_DIR}/apps/txrx_uhd.c)
+ADD_DEFINITIONS(-DUSRP2P)
+ADD_DEFINITIONS(-DBOOTLOADER)
+TARGET_LINK_LIBRARIES(bootloader.elf libusrp2pfw)
+SET(GEN_OUTPUTS_BIN_SIZE 0x3fff)
+GEN_OUTPUTS(bootloader.elf)
+GEN_RMI(bootloader.bin)
diff --git a/firmware/zpu/usrp2p/bootloader/init_bootloader.c b/firmware/zpu/usrp2p/bootloader/init_bootloader.c
new file mode 100644
index 000000000..d797edce4
--- /dev/null
+++ b/firmware/zpu/usrp2p/bootloader/init_bootloader.c
@@ -0,0 +1,123 @@
+//
+// Copyright 2010-2011 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 <hal_io.h>
+#include <xilinx_s3_icap.h>
+#include <spi_flash.h>
+#include <spi_flash_private.h>
+#include <mdelay.h>
+#include <ihex.h>
+#include <bootloader_utils.h>
+#include <string.h>
+#include <hal_uart.h>
+#include <i2c.h>
+#include "usrp2/fw_common.h"
+
+#define BUTTON_PUSHED ((router_status->irqs & PIC_BUTTON) ? 0 : 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
+
+ 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.type == 1) { //end of record
+ puts("OK");
+ //load main firmware
+ start_program();
+ puts("ERROR: main image returned! Back in IHEX load mode.");
+ } else {
+ const uint8_t *destination = (uint8_t *)ihex_record.addr + RAM_BASE;
+ memcpy((void *) destination, ihex_record.data, ihex_record.length);
+ puts("OK");
+ }
+ } else puts("NOK");
+ }
+}
+
+int main(int argc, char *argv[]) {
+ hal_disable_ints(); // In case we got here via jmp 0x0
+ output_regs->leds = 0xFF;
+ mdelay(100);
+ 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);
+ mdelay(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.");
+ mdelay(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.
+ mdelay(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.");
+ mdelay(300);
+ start_program();
+ puts("ERROR: return from main program! This should never happen!");
+ mdelay(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/spi_bootloader.c b/firmware/zpu/usrp2p/bootloader/spi_bootloader.c
new file mode 100644
index 000000000..3e66d41cb
--- /dev/null
+++ b/firmware/zpu/usrp2p/bootloader/spi_bootloader.c
@@ -0,0 +1,134 @@
+/* -*- c -*- */
+/*
+ * Copyright 2009-2011 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.*)
+ }
+}
diff --git a/firmware/zpu/usrp2p/bootloader/udp_bootloader.c b/firmware/zpu/usrp2p/bootloader/udp_bootloader.c
new file mode 100644
index 000000000..118de2ae9
--- /dev/null
+++ b/firmware/zpu/usrp2p/bootloader/udp_bootloader.c
@@ -0,0 +1,200 @@
+//
+// Copyright 2010-2011 Ettus Research LLC
+//
+/*
+ * Copyright 2007,2008 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/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <lwip/ip.h>
+#include <lwip/udp.h>
+#include "u2_init.h"
+#include "memory_map.h"
+#include "spi.h"
+#include "hal_io.h"
+#include "pic.h"
+#include <stdbool.h>
+#include "ethernet.h"
+#include "nonstdio.h"
+#include <net/padded_eth_hdr.h>
+#include <net_common.h>
+#include "memcpy_wa.h"
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include "clocks.h"
+#include "usrp2/fw_common.h"
+#include <i2c.h>
+#include <ethertype.h>
+#include <arp_cache.h>
+#include "udp_fw_update.h"
+#include "pkt_ctrl.h"
+#include "banal.h"
+#include <bootloader_utils.h>
+#include <spi_flash.h>
+#include <xilinx_s3_icap.h>
+#include <mdelay.h>
+
+#define BUTTON_PUSHED ((router_status->irqs & PIC_BUTTON) ? 0 : 1)
+
+static void handle_inp_packet(uint32_t *buff, size_t num_lines){
+
+ //test if its an ip recovery packet
+ typedef struct{
+ padded_eth_hdr_t eth_hdr;
+ char code[4];
+ union {
+ struct ip_addr ip_addr;
+ } data;
+ }recovery_packet_t;
+ recovery_packet_t *recovery_packet = (recovery_packet_t *)buff;
+ if (recovery_packet->eth_hdr.ethertype == 0xbeee && strncmp(recovery_packet->code, "addr", 4) == 0){
+ printf("Got ip recovery packet: "); print_ip_addr(&recovery_packet->data.ip_addr); newline();
+ set_ip_addr(&recovery_packet->data.ip_addr);
+ return;
+ }
+
+ //pass it to the slow-path handler
+ handle_eth_packet(buff, num_lines);
+}
+
+
+//------------------------------------------------------------------
+
+/*
+ * Called when eth phy state changes (w/ interrupts disabled)
+ */
+void link_changed_callback(int speed){
+ printf("\neth link changed: speed = %d\n", speed);
+ if (speed != 0){
+ hal_set_leds(LED_RJ45, LED_RJ45);
+ pkt_ctrl_set_routing_mode(PKT_CTRL_ROUTING_MODE_MASTER);
+ send_gratuitous_arp();
+ }
+ else{
+ hal_set_leds(0x0, LED_RJ45);
+ pkt_ctrl_set_routing_mode(PKT_CTRL_ROUTING_MODE_SLAVE);
+ }
+}
+
+static void do_the_bootload_thing(void) {
+
+ 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. I am a brick. Feel free to reprogram me via the UDP burner.");
+ return;
+ //no longer necessary since we can just burn from UDP via the bootloader now
+/*
+ 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 reprogram me via the UDP burner.");
+ return;
+ }
+*/
+ }
+
+ 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);
+ mdelay(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.");
+ mdelay(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.
+ mdelay(300);
+ icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR);
+ return;
+ }
+ 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.");
+ mdelay(300);
+ start_program();
+ puts("ERROR: return from main program! This should never happen!");
+ mdelay(300);
+ icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR);
+ return;
+ }
+ puts("ERROR: no safe firmware image available. I am a brick. Feel free to reprogram me via the UDP burner.");
+}
+
+int
+main(void)
+{
+ u2_init();
+ spif_init();
+
+ set_default_mac_addr();
+ set_default_ip_addr();
+
+ putstr("\nN210 bootloader, UDP edition\n");
+ print_mac_addr(ethernet_mac_addr()); newline();
+ print_ip_addr(get_ip_addr()); newline();
+ printf("FPGA compatibility number: %d\n", USRP2_FPGA_COMPAT_NUM);
+ printf("Firmware compatibility number: %d\n", USRP2_FW_COMPAT_NUM);
+
+ do_the_bootload_thing();
+
+ //if we reach this point, the bootloader has fallen through, so we initalize the UDP handler for updating
+
+ //1) register the addresses into the network stack
+ //TODO: make this a fixed IP address, don't depend on EEPROM
+ register_addrs(ethernet_mac_addr(), get_ip_addr());
+ pkt_ctrl_program_inspector(get_ip_addr(), USRP2_UDP_DSP0_PORT);
+
+ //2) register callbacks for udp ports we service
+ init_udp_listeners();
+ register_udp_listener(USRP2_UDP_UPDATE_PORT, handle_udp_fw_update_packet);
+
+ pkt_ctrl_set_routing_mode(PKT_CTRL_ROUTING_MODE_SLAVE);
+
+ //4) setup ethernet hardware to bring the link up
+ ethernet_register_link_changed_callback(link_changed_callback);
+ ethernet_init();
+
+ while(true){
+
+ size_t num_lines;
+ void *buff = pkt_ctrl_claim_incoming_buffer(&num_lines);
+ if (buff != NULL){
+ handle_inp_packet((uint32_t *)buff, num_lines);
+ pkt_ctrl_release_incoming_buffer();
+ }
+
+ pic_interrupt_handler();
+ }
+}
diff --git a/firmware/zpu/usrp2p/bootloader_utils.c b/firmware/zpu/usrp2p/bootloader_utils.c
new file mode 100644
index 000000000..1efa643b6
--- /dev/null
+++ b/firmware/zpu/usrp2p/bootloader_utils.c
@@ -0,0 +1,104 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2010 Ettus Research LLC
+ *
+ */
+
+//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>
+#include <i2c.h>
+#include <memory_map.h>
+#include <nonstdio.h>
+#include <xilinx_s3_icap.h>
+#include <mdelay.h>
+#include "spi.h"
+
+#define BUTTON_PUSHED ((router_status->irqs & PIC_BUTTON) ? 0 : 1)
+
+int is_valid_fpga_image(uint32_t addr) {
+// printf("is_valid_fpga_image(): starting with addr=%x...\n", addr);
+ 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
+ for(size_t i = 0; i<63; i++) {
+ if(imgbuf[i] == 0xFF) continue;
+ if(imgbuf[i] == 0xAA && imgbuf[i+1] == 0x99) {
+ //printf("is_valid_fpga_image(): found valid FPGA image\n");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int is_valid_fw_image(uint32_t addr) {
+ static const uint8_t fwheader[] = {0x0b, 0x0b, 0x0b, 0x0b}; //just lookin for a jump to anywhere located at the reset vector
+ //printf("is_valid_fw_image(): starting with addr=%x...\n", addr);
+ uint8_t buf[12];
+ spi_flash_read(addr, 4, buf);
+ //printf("is_valid_fw_image(): read ");
+ //for(int i = 0; i < 5; i++) printf("%x ", buf[i]);
+ //printf("\n");
+ return memcmp(buf, fwheader, 4) == 0;
+}
+
+void start_program(void)
+{
+ //ignoring the addr now
+ //all this does is tap that register
+ *((volatile uint32_t *) SR_ADDR_BLDRDONE) = 1;
+}
+
+void do_the_bootload_thing(void) {
+ spif_init(); //initialize SPI flash clock
+
+ 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. Loading safe firmware.");
+ return;
+ }
+
+ 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);
+ mdelay(300); //so serial output can finish
+ icap_reload_fpga(PROD_FPGA_IMAGE_LOCATION_ADDR);
+ }
+ puts("No valid production FPGA image found.\nFalling through to built-in firmware.");
+ return;
+ }
+ 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.");
+ mdelay(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.
+ mdelay(300);
+ icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR);
+ return;
+ }
+ puts("No valid production firmware found. Falling through to built-in 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.");
+ mdelay(300);
+ start_program();
+ puts("ERROR: return from main program! This should never happen!");
+ mdelay(300);
+ icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR);
+ return;
+ }
+ puts("ERROR: no safe firmware image available. Falling through to built-in firmware.");
+ */
+}
diff --git a/firmware/zpu/usrp2p/bootloader_utils.h b/firmware/zpu/usrp2p/bootloader_utils.h
new file mode 100644
index 000000000..d70299b88
--- /dev/null
+++ b/firmware/zpu/usrp2p/bootloader_utils.h
@@ -0,0 +1,23 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2010 Ettus Research LLC
+ *
+ */
+
+#include <stdint.h>
+
+//we're working in bytes and byte addresses so we can run the same code with Flash chips of different sector sizes.
+//it's really 1463736, but rounded up to 1.5MB
+#define FPGA_IMAGE_SIZE_BYTES 1572864
+//16K
+#define FW_IMAGE_SIZE_BYTES 0x3fff
+
+#define SAFE_FPGA_IMAGE_LOCATION_ADDR 0x00000000
+#define SAFE_FW_IMAGE_LOCATION_ADDR 0x003F0000
+#define PROD_FPGA_IMAGE_LOCATION_ADDR 0x00180000
+#define PROD_FW_IMAGE_LOCATION_ADDR 0x00300000
+
+int is_valid_fpga_image(uint32_t addr);
+int is_valid_fw_image(uint32_t addr);
+void start_program(void);
+void do_the_bootload_thing(void);
diff --git a/firmware/zpu/usrp2p/eth_phy.h b/firmware/zpu/usrp2p/eth_phy.h
new file mode 100644
index 000000000..d233e96e8
--- /dev/null
+++ b/firmware/zpu/usrp2p/eth_phy.h
@@ -0,0 +1,235 @@
+/* -*- c -*- */
+/*
+ * Copyright 2007 Free Software Foundation, Inc.
+ * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
+ *
+ * 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/>.
+ */
+
+/* Much of this was extracted from the Linux e1000_hw.h file */
+
+#ifndef INCLUDED_ETH_PHY_H
+#define INCLUDED_ETH_PHY_H
+
+/* PHY 1000 MII Register/Bit Definitions */
+/* PHY Registers defined by IEEE */
+
+#define PHY_CTRL 0x00 /* Control Register */
+#define PHY_STATUS 0x01 /* Status Regiser */
+#define PHY_ID1 0x02 /* Phy Id Reg (word 1) */
+#define PHY_ID2 0x03 /* Phy Id Reg (word 2) */
+#define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */
+#define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */
+#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */
+#define PHY_NEXT_PAGE_TX 0x07 /* Next Page TX */
+#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */
+#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */
+#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */
+#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */
+
+/* PHY 1000 MII Register additions in ET1011C */
+#define PHY_INT_MASK 24
+#define PHY_INT_STATUS 25
+#define PHY_PHY_STATUS 26
+#define PHY_LED2 28
+
+/* Bit definitions for some of the registers above */
+
+/* PHY Control Register (PHY_CTRL) */
+#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */
+#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */
+#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */
+#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */
+#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */
+#define MII_CR_POWER_DOWN 0x0800 /* Power down */
+#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */
+#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */
+#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */
+#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */
+
+/* PHY Status Register (PHY_STATUS) */
+#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */
+#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */
+#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */
+#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */
+#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */
+#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */
+#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */
+#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */
+#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */
+#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */
+#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */
+#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */
+#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */
+#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */
+#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */
+
+/* Autoneg Advertisement Register (PHY_AUTONEG_ADV) */
+#define NWAY_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */
+#define NWAY_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */
+#define NWAY_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */
+#define NWAY_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */
+#define NWAY_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */
+#define NWAY_AR_100T4_CAPS 0x0200 /* 100T4 Capable */
+#define NWAY_AR_PAUSE 0x0400 /* Pause operation desired */
+#define NWAY_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */
+#define NWAY_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */
+#define NWAY_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */
+
+/* Link Partner Ability Register (Base Page) (PHY_LP_ABILITY) */
+#define NWAY_LPAR_SELECTOR_FIELD 0x0000 /* LP protocol selector field */
+#define NWAY_LPAR_10T_HD_CAPS 0x0020 /* LP is 10T Half Duplex Capable */
+#define NWAY_LPAR_10T_FD_CAPS 0x0040 /* LP is 10T Full Duplex Capable */
+#define NWAY_LPAR_100TX_HD_CAPS 0x0080 /* LP is 100TX Half Duplex Capable */
+#define NWAY_LPAR_100TX_FD_CAPS 0x0100 /* LP is 100TX Full Duplex Capable */
+#define NWAY_LPAR_100T4_CAPS 0x0200 /* LP is 100T4 Capable */
+#define NWAY_LPAR_PAUSE 0x0400 /* LP Pause operation desired */
+#define NWAY_LPAR_ASM_DIR 0x0800 /* LP Asymmetric Pause Direction bit */
+#define NWAY_LPAR_REMOTE_FAULT 0x2000 /* LP has detected Remote Fault */
+#define NWAY_LPAR_ACKNOWLEDGE 0x4000 /* LP has rx'd link code word */
+#define NWAY_LPAR_NEXT_PAGE 0x8000 /* Next Page ability supported */
+
+/* Autoneg Expansion Register (PHY_AUTONEG_EXP) */
+#define NWAY_ER_LP_NWAY_CAPS 0x0001 /* LP has Auto Neg Capability */
+#define NWAY_ER_PAGE_RXD 0x0002 /* LP is 10T Half Duplex Capable */
+#define NWAY_ER_NEXT_PAGE_CAPS 0x0004 /* LP is 10T Full Duplex Capable */
+#define NWAY_ER_LP_NEXT_PAGE_CAPS 0x0008 /* LP is 100TX Half Duplex Capable */
+#define NWAY_ER_PAR_DETECT_FAULT 0x0010 /* LP is 100TX Full Duplex Capable */
+
+/* Next Page TX Register (PHY_NEXT_PAGE_TX) */
+#define NPTX_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */
+#define NPTX_TOGGLE 0x0800 /* Toggles between exchanges
+ * of different NP
+ */
+#define NPTX_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg
+ * 0 = cannot comply with msg
+ */
+#define NPTX_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */
+#define NPTX_NEXT_PAGE 0x8000 /* 1 = addition NP will follow
+ * 0 = sending last NP
+ */
+
+/* Link Partner Next Page Register (PHY_LP_NEXT_PAGE) */
+#define LP_RNPR_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */
+#define LP_RNPR_TOGGLE 0x0800 /* Toggles between exchanges
+ * of different NP
+ */
+#define LP_RNPR_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg
+ * 0 = cannot comply with msg
+ */
+#define LP_RNPR_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */
+#define LP_RNPR_ACKNOWLDGE 0x4000 /* 1 = ACK / 0 = NO ACK */
+#define LP_RNPR_NEXT_PAGE 0x8000 /* 1 = addition NP will follow
+ * 0 = sending last NP
+ */
+
+/* 1000BASE-T Control Register (PHY_1000T_CTRL) */
+#define CR_1000T_ASYM_PAUSE 0x0080 /* Advertise asymmetric pause bit */
+#define CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */
+#define CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */
+#define CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port */
+ /* 0=DTE device */
+#define CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master */
+ /* 0=Configure PHY as Slave */
+#define CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value */
+ /* 0=Automatic Master/Slave config */
+#define CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */
+#define CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */
+#define CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */
+#define CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */
+#define CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */
+
+/* 1000BASE-T Status Register (PHY_1000T_STATUS) */
+#define SR_1000T_IDLE_ERROR_CNT 0x00FF /* Num idle errors since last read */
+#define SR_1000T_ASYM_PAUSE_DIR 0x0100 /* LP asymmetric pause direction bit */
+#define SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */
+#define SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */
+#define SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */
+#define SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */
+#define SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local TX is Master, 0=Slave */
+#define SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */
+#define SR_1000T_REMOTE_RX_STATUS_SHIFT 12
+#define SR_1000T_LOCAL_RX_STATUS_SHIFT 13
+#define SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT 5
+#define FFE_IDLE_ERR_COUNT_TIMEOUT_20 20
+#define FFE_IDLE_ERR_COUNT_TIMEOUT_100 100
+
+/* Extended Status Register (PHY_EXT_STATUS) */
+#define IEEE_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */
+#define IEEE_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */
+#define IEEE_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */
+#define IEEE_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */
+
+#define PHY_TX_POLARITY_MASK 0x0100 /* register 10h bit 8 (polarity bit) */
+#define PHY_TX_NORMAL_POLARITY 0 /* register 10h bit 8 (normal polarity) */
+
+#define AUTO_POLARITY_DISABLE 0x0010 /* register 11h bit 4 */
+ /* (0=enable, 1=disable) */
+
+/* PHY Status Register (PHY_PHY_STATUS) */
+#define PHYSTAT_ASYMMETRIC (1 << 0)
+#define PHYSTAT_PAUSE (1 << 1)
+#define PHYSTAT_AUTONEG_EN (1 << 2)
+#define PHYSTAT_COLLISION (1 << 3)
+#define PHYSTAT_RXSTAT (1 << 4)
+#define PHYSTAT_TXSTAT (1 << 5)
+#define PHYSTAT_LINK (1 << 6)
+#define PHYSTAT_DUPLEX (1 << 7)
+#define PHYSTAT_SPEED_MASK ((1 << 8) | (1 << 9))
+#define PHYSTAT_SPEED_1000 (1 << 9)
+#define PHYSTAT_SPEED_100 (1 << 8)
+#define PHYSTAT_SPEED_10 0
+#define PHYSTAT_POLARITY (1 << 10)
+#define PHYSTAT_MDIX (1 << 11)
+#define PHYSTAT_AUTONEG_STAT (1 << 12)
+#define PHYSTAT_STANDBY (1 << 13)
+
+/* Interrupt status, mask and clear regs (PHY_INT_{STATUS,MASK,CLEAR}) */
+#define PHY_INT_ENABLE (1 << 0)
+#define PHY_INT_DOWNSHIFT (1 << 1)
+#define PHY_INT_LINK_STATUS_CHANGE (1 << 2)
+#define PHY_INT_RX_STATUS_CHANGE (1 << 3)
+#define PHY_INT_FIFO_ERROR (1 << 4)
+#define PHY_INT_ERR_CTR_FULL (1 << 5)
+#define PHY_INT_NEXT_PAGE_RX (1 << 6)
+#define PHY_INT_CRC_ERROR (1 << 7)
+#define PHY_INT_AUTONEG_STATUS_CHANGE (1 << 8)
+#define PHY_INT_MDIO_SYNC_LOST (1 << 9)
+#define PHY_INT_TDR_IP_PHONE (1 << 10)
+
+/* PHY LED status register 2 (used for controlling link LED for activity light) */
+#define PHY_LED_TXRX_LSB 12
+#define PHY_LED_LINK_LSB 8
+#define PHY_LED_100_LSB 4
+#define PHY_LED_1000_LSB 0
+
+#define LED_1000 0
+#define LED_100_TX 1
+#define LED_10 2
+#define LED_1000_ON_100_BLINK 3
+#define LED_LINK 4
+#define LED_TX 5
+#define LED_RX 6
+#define LED_ACTIVITY 7
+#define LED_FULLDUPLEX 8
+#define LED_COLLISION 9
+#define LED_LINK_ON_ACTIVITY_BLINK 10
+#define LED_LINK_ON_RX_BLINK 11
+#define LED_FULL_DUPLEX_ON_COLLISION_BLINK 12
+#define LED_BLINK 13
+#define LED_ON 14
+#define LED_OFF 15
+
+
+#endif /* INCLUDED_ETH_PHY_H */
diff --git a/firmware/zpu/usrp2p/ethernet.c b/firmware/zpu/usrp2p/ethernet.c
new file mode 100644
index 000000000..f5bd9f121
--- /dev/null
+++ b/firmware/zpu/usrp2p/ethernet.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright 2011 Ettus Research LLC
+ * Copyright 2007 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/>.
+ */
+
+//Changes for USRP2P: status registers different (ethernet.h)
+
+#include "ethernet.h"
+#include "memory_map.h"
+#include "eth_phy.h"
+#include <eth_mac.h>
+#include <pic.h>
+#include <hal_io.h>
+#include <nonstdio.h>
+#include <stdbool.h>
+#include <i2c.h>
+#include "usrp2/fw_common.h"
+
+#define VERBOSE 0
+
+static ethernet_t ed_state;
+static ethernet_link_changed_callback_t ed_callback = 0;
+
+void
+ethernet_register_link_changed_callback(ethernet_link_changed_callback_t new_callback)
+{
+ ed_callback = new_callback;
+}
+
+
+static void
+ed_set_mac_speed(int speed)
+{
+ printf("Speed set to %d\n",speed);
+ /*
+ switch(speed){
+ case 10:
+ eth_mac->speed = 1;
+ break;
+ case 100:
+ eth_mac->speed = 2;
+ break;
+ case 1000:
+ eth_mac->speed = 4;
+ break;
+ default:
+ break;
+ }
+ */
+}
+
+static void
+ed_link_up(int speed)
+{
+ // putstr("ed_link_up: "); puthex16_nl(speed);
+
+ ed_set_mac_speed(speed);
+
+ //turn on link LED for USRP2P
+ hal_set_leds(LED_RJ45, LED_RJ45);
+
+
+ if (ed_callback) // fire link changed callback
+ (*ed_callback)(speed);
+}
+
+static void
+ed_link_down(void)
+{
+ // putstr("ed_link_down\n");
+
+ //turn off link LED for USRP2P
+ hal_set_leds(0, LED_RJ45);
+
+ if (ed_callback) // fire link changed callback
+ (*ed_callback)(0);
+}
+
+
+static void
+ed_link_speed_change(int speed)
+{
+ ed_link_down();
+ ed_link_up(speed);
+}
+
+static void
+print_flow_control(int flow_control)
+{
+ static const char *flow_control_msg[4] = {
+ "NONE", "WE_TX", "WE_RX", "SYMMETRIC"
+ };
+ putstr("ethernet flow control: ");
+ puts(flow_control_msg[flow_control & 0x3]);
+}
+
+static void
+check_flow_control_resolution(void)
+{
+ static const unsigned char table[16] = {
+ // index = {local_asm, local_pause, partner_asm, partner_pause}
+ FC_NONE, FC_NONE, FC_NONE, FC_NONE,
+ FC_NONE, FC_SYMM, FC_NONE, FC_SYMM,
+ FC_NONE, FC_NONE, FC_NONE, FC_WE_TX,
+ FC_NONE, FC_SYMM, FC_WE_RX, FC_SYMM
+ };
+
+ int us = eth_mac_miim_read(PHY_AUTONEG_ADV);
+ int lp = eth_mac_miim_read(PHY_LP_ABILITY);
+ int index = (((us >> 10) & 0x3) << 2) | ((lp >> 10) & 0x3);
+ ed_state.flow_control = table[index];
+
+ if (1)
+ print_flow_control(ed_state.flow_control);
+}
+
+/*
+ * Read the PHY state register to determine link state and speed
+ */
+static void
+ed_check_phy_state(void)
+{
+ int phystat = eth_mac_miim_read(PHY_PHY_STATUS);
+ eth_link_state_t new_state = LS_UNKNOWN;
+ int new_speed = S_UNKNOWN;
+
+ if (VERBOSE){
+ putstr("PHYSTAT: ");
+ puthex16_nl(phystat);
+ }
+
+ if (phystat & PHYSTAT_LINK){ // link's up
+ if (VERBOSE)
+ puts(" LINK_GOOD");
+
+ new_state = LS_UP;
+ switch (phystat & PHYSTAT_SPEED_MASK){
+ case PHYSTAT_SPEED_10:
+ new_speed = 10;
+ break;
+
+ case PHYSTAT_SPEED_100:
+ new_speed = 100;
+ break;
+
+ case PHYSTAT_SPEED_1000:
+ new_speed = 1000;
+ break;
+
+ default:
+ new_speed = S_UNKNOWN;
+ break;
+ }
+
+ check_flow_control_resolution();
+ }
+ else { // link's down
+ if (VERBOSE)
+ puts(" NOT LINK_GOOD");
+
+ new_state = LS_DOWN;
+ new_speed = S_UNKNOWN;
+ }
+
+ if (new_state != ed_state.link_state){
+ ed_state.link_state = new_state; // remember new state
+ if (new_state == LS_UP)
+ ed_link_up(new_speed);
+ else if (new_state == LS_DOWN)
+ ed_link_down();
+ }
+ else if (new_state == LS_UP && new_speed != ed_state.link_speed){
+ ed_state.link_speed = new_speed; // remember new speed
+ ed_link_speed_change(new_speed);
+ }
+}
+
+/*
+ * This is fired when the ethernet PHY state changes
+ */
+static void
+eth_phy_irq_handler(unsigned irq)
+{
+ ed_check_phy_state();
+ eth_mac_miim_read(PHY_INT_STATUS);
+// eth_mac_miim_write(PHY_INT_CLEAR, ~0); // clear all ints
+}
+
+void
+ethernet_init(void)
+{
+ eth_mac_init(ethernet_mac_addr());
+
+ ed_state.link_state = LS_UNKNOWN;
+ ed_state.link_speed = S_UNKNOWN;
+
+ // initialize MAC registers
+ // eth_mac->tx_hwmark = 0x1e;
+ //eth_mac->tx_lwmark = 0x19;
+
+ //eth_mac->crc_chk_en = 1;
+ //eth_mac->rx_max_length = 2048;
+
+ // configure PAUSE frame stuff
+ //eth_mac->tx_pause_en = 1; // pay attn to pause frames sent to us
+
+ //eth_mac->pause_quanta_set = 38; // a bit more than 1 max frame 16kb/512 + fudge
+ //eth_mac->pause_frame_send_en = 1; // enable sending pause frames
+
+
+ // setup PHY to interrupt on changes
+
+ unsigned mask =
+ (PHY_INT_ENABLE //master interrupt enable
+ | PHY_INT_LINK_STATUS_CHANGE
+ | PHY_INT_RX_STATUS_CHANGE
+ );
+
+ eth_mac_miim_read(PHY_INT_STATUS); //clear interrupts
+ eth_mac_miim_write(PHY_INT_MASK, mask); // enable the ones we want
+
+ //set the LED behavior to activity instead of link
+ unsigned led = (LED_ACTIVITY << PHY_LED_LINK_LSB) | (LED_TX << PHY_LED_TXRX_LSB);
+ eth_mac_miim_write(PHY_LED2, led);
+
+ pic_register_handler(IRQ_PHY, eth_phy_irq_handler);
+
+ // Advertise our flow control configuation.
+ //
+ // We and the link partner each specify two bits in the base page
+ // related to autoconfiguration: NWAY_AR_PAUSE and NWAY_AR_ASM_DIR.
+ // The bits say what a device is "willing" to do, not what may actually
+ // happen as a result of the negotiation. There are 4 cases:
+ //
+ // PAUSE ASM_DIR
+ //
+ // 0 0 I have no flow control capability.
+ //
+ // 1 0 I both assert and respond to flow control.
+ //
+ // 0 1 I assert flow control, but cannot respond. That is,
+ // I want to be able to send PAUSE frames, but will ignore any
+ // you send to me. (This is our configuration.)
+ //
+ // 1 1 I can both assert and respond to flow control AND I am willing
+ // to operate symmetrically OR asymmetrically in EITHER direction.
+ // (We hope the link partner advertises this, otherwise we don't
+ // get what we want.)
+
+ int t = eth_mac_miim_read(PHY_AUTONEG_ADV);
+ t &= ~(NWAY_AR_PAUSE | NWAY_AR_ASM_DIR);
+ t |= NWAY_AR_ASM_DIR;
+
+ // Say we can't to 10BASE-T or 100BASE-TX, half or full duplex
+ t &= ~(NWAY_AR_10T_HD_CAPS | NWAY_AR_10T_FD_CAPS | NWAY_AR_100TX_HD_CAPS | NWAY_AR_100TX_FD_CAPS);
+
+ eth_mac_miim_write(PHY_AUTONEG_ADV, t);
+ int r = eth_mac_miim_read(PHY_AUTONEG_ADV); // DEBUG, read back
+ if (t != r){
+ printf("PHY_AUTONEG_ADV: wrote 0x%x, got 0x%x\n", t, r);
+ }
+
+ // Restart autonegotation.
+ // We want to ensure that we're advertising our PAUSE capabilities.
+ t = eth_mac_miim_read(PHY_CTRL);
+ eth_mac_miim_write(PHY_CTRL, t | MII_CR_RESTART_AUTO_NEG);
+}
+
+int
+ethernet_check_errors(void)
+{
+ // these registers are reset when read
+
+ int r = 0;
+ /*
+ if (eth_mac_read_rmon(0x05) != 0)
+ r |= RME_RX_CRC;
+ if (eth_mac_read_rmon(0x06) != 0)
+ r |= RME_RX_FIFO_FULL;
+ if (eth_mac_read_rmon(0x07) != 0)
+ r |= RME_RX_2SHORT_2LONG;
+
+ if (eth_mac_read_rmon(0x25) != 0)
+ r |= RME_TX_JAM_DROP;
+ if (eth_mac_read_rmon(0x26) != 0)
+ r |= RME_TX_FIFO_UNDER;
+ if (eth_mac_read_rmon(0x27) != 0)
+ r |= RME_TX_FIFO_OVER;
+ */
+ return r;
+}
diff --git a/firmware/zpu/usrp2p/spi_flash.c b/firmware/zpu/usrp2p/spi_flash.c
new file mode 100644
index 000000000..9406f8042
--- /dev/null
+++ b/firmware/zpu/usrp2p/spi_flash.c
@@ -0,0 +1,205 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2009 Free Software Foundation, Inc.
+ * Copyright 2009-2011 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 "spi_flash_private.h"
+//#include <stdlib.h>
+#include <nonstdio.h>
+
+uint32_t
+spi_flash_rdsr(void)
+{
+ return spif_transact(SPI_TXRX, SPI_SS_FLASH, RDSR_CMD << 8, 16, FLAGS) & 0xff;
+}
+
+static void
+spi_flash_write_enable(void)
+{
+// spif_transact(SPI_TXONLY, SPI_SS_FLASH, (WRSR_CMD << 8) | 0x00, 16, FLAGS); //disable write protection bits
+ spif_transact(SPI_TXONLY, SPI_SS_FLASH, WREN_CMD, 8, FLAGS);
+}
+
+bool
+spi_flash_done_p(void)
+{
+ return (spi_flash_rdsr() & SR_WIP) == 0;
+}
+
+void
+spi_flash_wait(void)
+{
+ while (!spi_flash_done_p())
+ ;
+}
+
+void
+spi_flash_erase_sector_start(uint32_t flash_addr)
+{
+ //printf("spi_flash_erase_sector_start: addr = 0x%x\n", flash_addr);
+ if(flash_addr > spi_flash_memory_size())
+ return;
+
+ spi_flash_wait();
+ spi_flash_write_enable();
+ spif_transact(SPI_TXONLY, SPI_SS_FLASH,
+ (SE_CMD << 24) | (flash_addr & 0x00ffffff),
+ 32, FLAGS);
+}
+
+bool
+spi_flash_page_program_start(uint32_t flash_addr, size_t nbytes, const void *buf)
+{
+ if (nbytes == 0 || nbytes > SPI_FLASH_PAGE_SIZE)
+ return false;
+
+ //please to not be writing past the end of the device
+ if ((flash_addr + nbytes) > spi_flash_memory_size())
+ return false;
+
+ uint32_t local_buf[SPI_FLASH_PAGE_SIZE / sizeof(uint32_t)];
+ memset(local_buf, 0xff, sizeof(local_buf)); // init to 0xff (nops when programming)
+ memcpy(local_buf, buf, nbytes);
+
+ spi_flash_wait();
+ spi_flash_write_enable();
+
+ /*
+ * We explicitly control the slave select here (/S), so that we can
+ * do the entire write operation as a single transaction from
+ * device's point of view. (The most our SPI peripheral can transfer
+ * in a single shot is 16 bytes.)
+ */
+ spif_wait();
+
+ spif_regs->ss = 0;
+ spif_regs->ctrl = FLAGS; // ASS is now clear and no chip select is enabled.
+
+ /* write PP_CMD, ADDR2, ADDR1, ADDR0 */
+
+ spif_regs->txrx0 = (PP_CMD << 24) | (flash_addr & 0x00ffffff);
+ spif_regs->ss = SPI_SS_FLASH; // assert chip select
+ spif_regs->ctrl = FLAGS | LEN(4 * 8);
+ spif_regs->ctrl = FLAGS | LEN(4 * 8) | SPI_CTRL_GO_BSY;
+ spif_wait();
+
+ /* send 256 bytes total, 16 at a time */
+ for (size_t i = 0; i < 16; i++){
+ spif_regs->txrx3 = local_buf[i * 4 + 0];
+ spif_regs->txrx2 = local_buf[i * 4 + 1];
+ spif_regs->txrx1 = local_buf[i * 4 + 2];
+ spif_regs->txrx0 = local_buf[i * 4 + 3];
+
+ spif_regs->ctrl = FLAGS | LEN(16 * 8); // xfer 16 bytes
+ spif_regs->ctrl = FLAGS | LEN(16 * 8) | SPI_CTRL_GO_BSY;
+ spif_wait();
+ }
+ spif_regs->ss = 0; // desassert chip select
+
+ return true;
+}
+
+void
+spi_flash_erase(uint32_t flash_addr, size_t nbytes)
+{
+ if (nbytes == 0)
+ return;
+
+ uint32_t first = round_down(flash_addr, spi_flash_sector_size());
+ uint32_t last = round_down(flash_addr + nbytes - 1, spi_flash_sector_size());
+
+ for (uint32_t s = first; s <= last; s += spi_flash_sector_size()){
+ spi_flash_erase_sector_start(s);
+ }
+ spi_flash_wait();
+}
+
+bool
+spi_flash_program(uint32_t flash_addr, size_t nbytes, const void *buf)
+{
+ //uprintf(UART_DEBUG, "\nspi_flash_program: addr = 0x%x, nbytes = %d\n", flash_addr, nbytes);
+
+ const unsigned char *p = (const unsigned char *) buf;
+ size_t n;
+
+ if ((nbytes + flash_addr) > spi_flash_memory_size())
+ return false;
+ if (nbytes == 0)
+ return true;
+
+ uint32_t r = flash_addr % SPI_FLASH_PAGE_SIZE;
+ if (r){ /* do initial non-aligned page */
+ n = min(SPI_FLASH_PAGE_SIZE - r, nbytes);
+ spi_flash_page_program_start(flash_addr, n, p);
+ flash_addr += n;
+ p += n;
+ nbytes -= n;
+ }
+
+ while (nbytes > 0){
+ n = min(SPI_FLASH_PAGE_SIZE, nbytes);
+ spi_flash_page_program_start(flash_addr, n, p);
+ flash_addr += n;
+ p += n;
+ nbytes -= n;
+ }
+
+ spi_flash_wait();
+ return true;
+}
+
+void
+spi_flash_async_erase_start(spi_flash_async_state_t *s,
+ uint32_t flash_addr, size_t nbytes)
+{
+
+ //printf("got command to erase %d bytes at 0x%x\n", nbytes, flash_addr);
+
+ if ((nbytes == 0) || ((flash_addr + nbytes) > spi_flash_memory_size())){
+ s->first = s->last = s->current = 0;
+ return;
+ }
+
+ uint32_t first = round_down(flash_addr, spi_flash_sector_size());
+ uint32_t last = round_down(flash_addr + nbytes - 1, spi_flash_sector_size());
+
+ s->first = first;
+ s->last = last;
+ s->current = first;
+
+ spi_flash_erase_sector_start(s->current);
+}
+
+bool
+spi_flash_async_erase_poll(spi_flash_async_state_t *s)
+{
+ if (!spi_flash_done_p())
+ return false;
+
+ //printf("%d/%d\n", s->current, s->last);
+
+ // The current sector erase has completed. See if we're finished or
+ // if there's more to do.
+
+ if (s->current == s->last) // we're done!
+ return true;
+
+ s->current += spi_flash_sector_size();
+ spi_flash_erase_sector_start(s->current);
+ return false;
+}
+
diff --git a/firmware/zpu/usrp2p/spi_flash.h b/firmware/zpu/usrp2p/spi_flash.h
new file mode 100644
index 000000000..a10533e08
--- /dev/null
+++ b/firmware/zpu/usrp2p/spi_flash.h
@@ -0,0 +1,110 @@
+/* -*- c -*- */
+/*
+ * Copyright 2009 Free Software Foundation, Inc.
+ * Copyright 2009-2011 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_SPI_FLASH_H
+#define INCLUDED_SPI_FLASH_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+
+#define SPI_FLASH_PAGE_SIZE 256
+#define SPI_SS_FLASH 1
+
+
+uint32_t spi_flash_rdid(void); /* Read ID */
+uint32_t spi_flash_rdsr(void); /* Read Status Register */
+
+size_t spi_flash_log2_memory_size(void);
+size_t spi_flash_log2_sector_size(void);
+size_t spi_flash_sector_size(void);
+size_t spi_flash_memory_size(void);
+
+void spi_flash_read(uint32_t flash_addr, size_t nbytes, void *buf);
+
+/*
+ * Erase all sectors that fall within the interval [flash_addr, flash_addr + nbytes).
+ * Erasing sets the memory to ones.
+ */
+void spi_flash_erase(uint32_t flash_addr, size_t nbytes);
+
+/*
+ * Program the flash.
+ * The area must have been erased prior to programming.
+ */
+bool spi_flash_program(uint32_t flash_addr, size_t nbytes, const void *buf);
+
+/*
+ * --- asynchronous routines ---
+ */
+
+/*
+ * Is the erasing or programming done?
+ */
+bool spi_flash_done_p(void);
+
+/*
+ * Wait for erasing or programming to complete
+ */
+void spi_flash_wait(void);
+
+/*
+ * Start the erase process on a single sector.
+ * (It takes between 1 and 3 seconds to erase a 64KB sector)
+ */
+void spi_flash_erase_sector_start(uint32_t flash_addr);
+
+/*
+ * Start the programming process within a single page.
+ * nbytes must be between 1 and 256.
+ * (It takes between 1.4 and 5 ms to program a page -> 640 ms for 64KB)
+ */
+bool spi_flash_page_program_start(uint32_t flash_addr, size_t nbytes, const void *buf);
+
+
+/*
+ * --- high-level async erase ---
+ */
+
+typedef struct {
+ uint32_t first;
+ uint32_t last;
+ uint32_t current;
+} spi_flash_async_state_t;
+
+/*
+ * Start to erase all sectors that fall within the interval [flash_addr, flash_addr + nbytes).
+ * Erasing sets the memory to ones.
+ *
+ * Initializes s and begins the process. Call spi_flash_async_erase_poll
+ * to test for completion and advance state machine.
+ */
+void spi_flash_async_erase_start(spi_flash_async_state_t *s,
+ uint32_t flash_addr, size_t nbytes);
+
+/*
+ * Poll for aysnc flash erase completion.
+ * Returns true when the erase has completed.
+ * (This should be called at something >= 4 Hz. It takes 1 to 3 seconds to
+ * erase each 64KB sector).
+ */
+bool spi_flash_async_erase_poll(spi_flash_async_state_t *s);
+
+
+#endif /* INCLUDED_SPI_FLASH_H */
diff --git a/firmware/zpu/usrp2p/spi_flash_private.h b/firmware/zpu/usrp2p/spi_flash_private.h
new file mode 100644
index 000000000..6bf06fda8
--- /dev/null
+++ b/firmware/zpu/usrp2p/spi_flash_private.h
@@ -0,0 +1,70 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2009 Free Software Foundation, Inc.
+ * Copyright 2009-2011 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_SPI_FLASH_PRIVATE_H
+#define INCLUDED_SPI_FLASH_PRIVATE_H
+
+#include "spi_flash.h"
+#include "spi.h"
+#include "memory_map.h"
+#include <string.h>
+
+
+/* M25P64 et al. */
+
+#define WREN_CMD 0x06 // write enable
+#define WRDI_CMD 0x04 // write disable
+#define RDID_CMD 0x9f // read identification
+#define RDSR_CMD 0x05 // read status register
+#define WRSR_CMD 0x01 // write status register
+#define READ_CMD 0x03
+#define FAST_READ_CMD 0x0b
+#define PP_CMD 0x02 // page program (256 bytes)
+#define SE_CMD 0xd8 // sector erase (64KB)
+#define BE_CMD 0xc7 // bulk erase (all)
+#define RES_CMD 0xab // read electronic sig (deprecated)
+
+/* Status register bits */
+
+#define SR_SRWD 0x80
+#define SR_BP2 0x10 // block protect bit 2
+#define SR_BP1 0x08 // block protect bit 1
+#define SR_BP0 0x04 // block protect bit 0
+#define SR_WEL 0x02 // Write Enable Latch
+#define SR_WIP 0x01 // Write in Progress. Set if busy w/ program or erase cycle.
+
+
+#define FLAGS (SPIF_PUSH_FALL | SPIF_LATCH_RISE)
+
+#define LEN(x) ((x) & SPI_CTRL_CHAR_LEN_MASK)
+
+
+static inline uint32_t
+min(uint32_t a, uint32_t b)
+{
+ return a < b ? a : b;
+}
+
+static inline uint32_t
+round_down(uint32_t x, uint32_t power_of_2)
+{
+ return x & -power_of_2;
+}
+
+#endif /* INCLUDED_SPI_FLASH_PRIVATE_H */
diff --git a/firmware/zpu/usrp2p/spi_flash_read.c b/firmware/zpu/usrp2p/spi_flash_read.c
new file mode 100644
index 000000000..fffc2a671
--- /dev/null
+++ b/firmware/zpu/usrp2p/spi_flash_read.c
@@ -0,0 +1,112 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2009 Free Software Foundation, Inc.
+ * Copyright 2009-2011 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 "spi_flash_private.h"
+#include <stdlib.h> // abort
+#include <nonstdio.h>
+
+uint32_t
+spi_flash_rdid(void)
+{
+ return spif_transact(SPI_TXRX, SPI_SS_FLASH, RDID_CMD << 24, 32, FLAGS) & 0xffffff;
+}
+
+size_t spi_flash_log2_memory_size(void)
+{
+ static size_t _spi_flash_log2_memory_size = 0;
+ if (_spi_flash_log2_memory_size == 0){
+ uint32_t id = spi_flash_rdid();
+ uint8_t type = (id >> 8) & 0xff;
+ uint8_t size = (id >> 0) & 0xff;
+ if (type != 0x20) abort();
+ _spi_flash_log2_memory_size = size;
+ }
+ if (_spi_flash_log2_memory_size < 22 ||
+ _spi_flash_log2_memory_size > 24 ) abort();
+ return _spi_flash_log2_memory_size;
+}
+
+size_t spi_flash_log2_sector_size(void)
+{
+ static unsigned char log2_sector_size[3] = {
+ 16, /* M25P32 */
+ 16, /* M25P64 */
+ 18, /* M25P128 */
+ };
+ return log2_sector_size[spi_flash_log2_memory_size() - 22];
+}
+
+size_t spi_flash_sector_size(void)
+{
+ return ((size_t) 1) << spi_flash_log2_sector_size();
+}
+
+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)
+{
+ /*
+ * We explicitly control the slave select here (/S), so that we can
+ * do the entire read operation as a single transaction from
+ * device's point of view. (The most our SPI peripheral can transfer
+ * in a single shot is 16 bytes.)
+ */
+ spif_wait();
+
+ spif_regs->ss = 0;
+ spif_regs->ctrl = FLAGS; // ASS is now clear and no chip select is enabled.
+
+ /*
+ * Do the 5 byte instruction tranfer:
+ * FAST_READ_CMD, ADDR2, ADDR1, ADDR0, DUMMY
+ */
+ spif_regs->txrx1 = FAST_READ_CMD;
+ spif_regs->txrx0 = ((flash_addr & 0x00ffffff) << 8);
+ spif_regs->ss = SPI_SS_FLASH; // assert chip select
+ spif_regs->ctrl = FLAGS | LEN(5 * 8);
+ spif_regs->ctrl = FLAGS | LEN(5 * 8) | SPI_CTRL_GO_BSY;
+ spif_wait();
+
+ /*
+ * Read up to 16 bytes at a time until done
+ */
+ unsigned char *dst = (unsigned char *) buf;
+ size_t m;
+ for (size_t n = 0; n < nbytes; n += m){
+
+ spif_regs->ctrl = FLAGS | LEN(16 * 8); // xfer 16 bytes
+ spif_regs->ctrl = FLAGS | LEN(16 * 8) | SPI_CTRL_GO_BSY;
+ spif_wait();
+
+ uint32_t w[4];
+ w[0] = spif_regs->txrx3; // txrx3 has first bits in it
+ w[1] = spif_regs->txrx2;
+ w[2] = spif_regs->txrx1;
+ w[3] = spif_regs->txrx0;
+ unsigned char *src = (unsigned char *) &w[0];
+ m = min(nbytes - n, 16);
+ for (size_t i = 0; i < m; i++)
+ *(dst++) = src[i];
+ }
+ spif_regs->ss = 0; // deassert chip select
+}
diff --git a/firmware/zpu/usrp2p/spif.c b/firmware/zpu/usrp2p/spif.c
new file mode 100644
index 000000000..91da73155
--- /dev/null
+++ b/firmware/zpu/usrp2p/spif.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2007,2008,2009 Free Software Foundation, Inc.
+ * Copyright 2009-2011 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/>.
+ */
+
+/*
+ * Code for the Flash SPI bus
+ */
+
+#include "spi.h"
+#include "memory_map.h"
+
+void
+spif_init(void)
+{
+ /*
+ * f_sclk = f_wb / ((div + 1) * 2)
+ */
+ spif_regs->div = 1; // 0 = Div by 2 (31.25 MHz); 1 = Div-by-4 (15.625 MHz)
+
+ // run dummy transaction to work around invalid initial clock state
+ spif_transact(SPI_TXONLY, 0, 0, 8, SPIF_PUSH_FALL | SPIF_LATCH_RISE);
+}
+
+inline void
+spif_wait(void)
+{
+ while (spif_regs->ctrl & SPI_CTRL_GO_BSY)
+ ;
+}
+
+uint32_t
+spif_transact(bool readback_, int slave, uint32_t data, int length, uint32_t flags)
+{
+ flags &= (SPI_CTRL_TXNEG | SPI_CTRL_RXNEG);
+ int ctrl = SPI_CTRL_ASS | (SPI_CTRL_CHAR_LEN_MASK & length) | flags;
+
+ spif_wait();
+
+ // Data we will send
+ spif_regs->txrx0 = data;
+
+ // Run it -- write once and rewrite with GO set
+ spif_regs->ctrl = ctrl;
+ // Tell it which SPI slave device to access
+ spif_regs->ss = slave & 0xff;
+ spif_regs->ctrl = ctrl | SPI_CTRL_GO_BSY;
+
+ if(readback_) {
+ spif_wait();
+ return spif_regs->txrx0;
+ }
+ else
+ return 0;
+}
diff --git a/firmware/zpu/usrp2p/u2p_init.c b/firmware/zpu/usrp2p/u2p_init.c
new file mode 100644
index 000000000..381987ae6
--- /dev/null
+++ b/firmware/zpu/usrp2p/u2p_init.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2011 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 "u2p_init.h"
+#include "i2c.h"
+#include "ethernet.h"
+
+void u2p_init(void){
+ //we do this to see if we should set a default ip addr or not
+ bool safe_fw = find_safe_booted_flag();
+ set_safe_booted_flag(0);
+ if (safe_fw) {
+ set_default_ip_addr();
+ set_default_mac_addr();
+ }
+}
diff --git a/firmware/zpu/usrp2p/u2p_init.h b/firmware/zpu/usrp2p/u2p_init.h
new file mode 100644
index 000000000..b0dc20f1f
--- /dev/null
+++ b/firmware/zpu/usrp2p/u2p_init.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2011 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/>.
+ */
+
+void u2p_init(void);
diff --git a/firmware/zpu/usrp2p/udp_fw_update.c b/firmware/zpu/usrp2p/udp_fw_update.c
new file mode 100644
index 000000000..793011651
--- /dev/null
+++ b/firmware/zpu/usrp2p/udp_fw_update.c
@@ -0,0 +1,128 @@
+/* -*- 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"
+#include "xilinx_s3_icap.h"
+#include "i2c.h"
+
+uint16_t get_hw_rev(void) {
+ uint16_t tmp;
+ eeprom_read(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_REV, &tmp, sizeof(tmp));
+ return tmp;
+}
+
+spi_flash_async_state_t spi_flash_async_state;
+
+//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;
+ }
+
+ 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));
+ //this is to stop streaming for the folks who think updating while streaming is a good idea
+ sr_rx_ctrl0->cmd = 1 << 31 | 1 << 28; //no samples now
+ sr_rx_ctrl0->time_secs = 0;
+ sr_rx_ctrl0->time_ticks = 0; //latch the command
+ sr_rx_ctrl1->cmd = 1 << 31 | 1 << 28; //no samples now
+ sr_rx_ctrl1->time_secs = 0;
+ sr_rx_ctrl1->time_ticks = 0; //latch the command
+ sr_tx_ctrl->cyc_per_up = 0;
+ 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_I_CAN_HAS_HW_REV_LOL: //get the hardware revision of the platform for validation checking
+ update_data_out.data.hw_rev = (uint32_t) get_hw_rev();
+ update_data_out.id = USRP2_FW_UPDATE_ID_HERES_TEH_HW_REV_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
+ icap_reload_fpga(0);
+ 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/firmware/zpu/usrp2p/xilinx_s3_icap.c b/firmware/zpu/usrp2p/xilinx_s3_icap.c
new file mode 100644
index 000000000..8995aa23d
--- /dev/null
+++ b/firmware/zpu/usrp2p/xilinx_s3_icap.c
@@ -0,0 +1,99 @@
+/* -*- c -*- */
+/*
+ * Copyright 2009-2011 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);
+}
diff --git a/firmware/zpu/usrp2p/xilinx_s3_icap.h b/firmware/zpu/usrp2p/xilinx_s3_icap.h
new file mode 100644
index 000000000..d4238eee9
--- /dev/null
+++ b/firmware/zpu/usrp2p/xilinx_s3_icap.h
@@ -0,0 +1,37 @@
+/* -*- c -*- */
+/*
+ * Copyright 2009-2011 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_XILINX_S3_ICAP_H
+#define INCLUDED_XILINX_S3_ICAP_H
+
+#include <stdint.h>
+
+
+void wr_icap(uint8_t x);
+uint8_t rd_icap(void);
+
+//int icap_read_config_reg(int regno);
+
+/*
+ * Attempt to reload the fpga from \p flash_address.
+ * Shouldn't return, but might.
+ */
+void icap_reload_fpga(uint32_t flash_address);
+
+
+#endif /* INCLUDED_XILINX_S3_ICAP_H */