summaryrefslogtreecommitdiffstats
path: root/firmware/microblaze/usrp2p
diff options
context:
space:
mode:
authorNick Foster <nick@nerdnetworks.org>2010-07-26 15:33:30 -0700
committerNick Foster <nick@nerdnetworks.org>2010-07-26 15:33:30 -0700
commitf72ad02f5ec370f45995ecd2b3e8b322e9a7e4dc (patch)
tree069b728b6e44a5db8607a8c5815e3a2d50a114ac /firmware/microblaze/usrp2p
parentf86c25317b457b280c697fc47905c79bdbbc0c93 (diff)
downloaduhd-f72ad02f5ec370f45995ecd2b3e8b322e9a7e4dc.tar.gz
uhd-f72ad02f5ec370f45995ecd2b3e8b322e9a7e4dc.tar.bz2
uhd-f72ad02f5ec370f45995ecd2b3e8b322e9a7e4dc.zip
New branch with firmware dir from USRP2P branch.
Diffstat (limited to 'firmware/microblaze/usrp2p')
-rw-r--r--firmware/microblaze/usrp2p/.gitignore9
-rw-r--r--firmware/microblaze/usrp2p/Makefile.am66
-rw-r--r--firmware/microblaze/usrp2p/bootconfig.h61
-rw-r--r--firmware/microblaze/usrp2p/bootloader/.gitignore11
-rw-r--r--firmware/microblaze/usrp2p/bootloader/Makefile.am39
-rw-r--r--firmware/microblaze/usrp2p/bootloader/fpga_bootloader.c202
-rw-r--r--firmware/microblaze/usrp2p/bootloader/fw_bootloader.c50
-rw-r--r--firmware/microblaze/usrp2p/bootloader/icap_test.c31
-rw-r--r--firmware/microblaze/usrp2p/bootloader/init_bootloader.c108
-rw-r--r--firmware/microblaze/usrp2p/bootloader/serial_loader_burner.c49
-rw-r--r--firmware/microblaze/usrp2p/bootloader/spi_bootloader.c134
-rw-r--r--firmware/microblaze/usrp2p/bootloader/u2p2-rom.ld190
-rw-r--r--firmware/microblaze/usrp2p/bootloader_utils.c33
-rw-r--r--firmware/microblaze/usrp2p/bootloader_utils.h21
-rw-r--r--firmware/microblaze/usrp2p/clocks.c259
-rw-r--r--firmware/microblaze/usrp2p/memory_map.h838
-rw-r--r--firmware/microblaze/usrp2p/spi_flash.c194
-rw-r--r--firmware/microblaze/usrp2p/spi_flash.h112
-rw-r--r--firmware/microblaze/usrp2p/spi_flash_private.h70
-rw-r--r--firmware/microblaze/usrp2p/spi_flash_read.c100
-rw-r--r--firmware/microblaze/usrp2p/spif.c68
-rw-r--r--firmware/microblaze/usrp2p/xilinx_s3_icap.h37
22 files changed, 2682 insertions, 0 deletions
diff --git a/firmware/microblaze/usrp2p/.gitignore b/firmware/microblaze/usrp2p/.gitignore
new file mode 100644
index 000000000..18f715618
--- /dev/null
+++ b/firmware/microblaze/usrp2p/.gitignore
@@ -0,0 +1,9 @@
+/Makefile
+/Makefile.in
+/*.a
+/*.bin
+/*.dump
+/*.ihx
+/*.elf
+/*.rom
+/*.map
diff --git a/firmware/microblaze/usrp2p/Makefile.am b/firmware/microblaze/usrp2p/Makefile.am
new file mode 100644
index 000000000..eff544294
--- /dev/null
+++ b/firmware/microblaze/usrp2p/Makefile.am
@@ -0,0 +1,66 @@
+#
+# 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 $(top_srcdir)/Makefile.common
+
+AM_CFLAGS = \
+ $(COMMON_CFLAGS)
+
+AM_LDFLAGS = \
+ $(COMMON_LFLAGS) \
+ libusrp2p.a \
+ -Wl,-defsym -Wl,_TEXT_START_ADDR=0x8050 \
+ -Wl,-defsym -Wl,_STACK_SIZE=3072
+
+#all of this here is to relocate the hardware vectors to somewhere normal.
+COMMON_IHX_ARGS = \
+ --change-section-address .vectors.sw_exception+0x8000 \
+ --change-section-address .vectors.hw_exception+0x8000 \
+ --change-section-address .vectors.interrupt+0x8000 \
+ --change-section-address .vectors.reset+0x8000
+# $(MB_OBJCOPY) -O ihex $< $@
+# the below would work if objcopy weren't written by apes
+# $(MB_OBJCOPY) -O ihex -w --change-section-address .vectors*+0x8000 $< $@
+# using the below will throw away the interrupt vectors when they get relocated below 0x0000.
+# $(MB_OBJCOPY) -O ihex --change-addresses -0x8000 $< $@
+
+########################################################################
+# USRP2P specific library and programs
+########################################################################
+noinst_LIBRARIES = libusrp2p.a
+
+libusrp2p_a_SOURCES = \
+ $(COMMON_SRCS) \
+ clocks.c \
+ spif.c \
+ spi_flash.c \
+ spi_flash_read.c \
+ bootloader_utils.c
+
+noinst_PROGRAMS = \
+ usrp2p_txrx_uhd.elf \
+ usrp2p_blinkenlights.elf \
+ usrp2p_uart_flash_loader.elf
+
+usrp2p_txrx_uhd_elf_SOURCES = \
+ $(top_srcdir)/apps/txrx_uhd.c
+
+usrp2p_blinkenlights_elf_SOURCES = \
+ $(top_srcdir)/apps/blinkenlights.c
+
+usrp2p_uart_flash_loader_elf_SOURCES = \
+ $(top_srcdir)/apps/uart_flash_loader.c
diff --git a/firmware/microblaze/usrp2p/bootconfig.h b/firmware/microblaze/usrp2p/bootconfig.h
new file mode 100644
index 000000000..35c2726ed
--- /dev/null
+++ b/firmware/microblaze/usrp2p/bootconfig.h
@@ -0,0 +1,61 @@
+/* -*- c -*- */
+/*
+ * Copyright 2009 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/microblaze/usrp2p/bootloader/.gitignore b/firmware/microblaze/usrp2p/bootloader/.gitignore
new file mode 100644
index 000000000..17b0f82f3
--- /dev/null
+++ b/firmware/microblaze/usrp2p/bootloader/.gitignore
@@ -0,0 +1,11 @@
+/*.ihx
+/*.rmi
+/*_rom
+/*.elf
+/*.bin
+/*.dump
+/*.log
+/*.rom
+/*.map
+/Makefile
+/Makefile.in
diff --git a/firmware/microblaze/usrp2p/bootloader/Makefile.am b/firmware/microblaze/usrp2p/bootloader/Makefile.am
new file mode 100644
index 000000000..1fc5daf9c
--- /dev/null
+++ b/firmware/microblaze/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/microblaze/usrp2p/bootloader/fpga_bootloader.c b/firmware/microblaze/usrp2p/bootloader/fpga_bootloader.c
new file mode 100644
index 000000000..9feff6ecd
--- /dev/null
+++ b/firmware/microblaze/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/microblaze/usrp2p/bootloader/fw_bootloader.c b/firmware/microblaze/usrp2p/bootloader/fw_bootloader.c
new file mode 100644
index 000000000..a2c32bf8e
--- /dev/null
+++ b/firmware/microblaze/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/microblaze/usrp2p/bootloader/icap_test.c b/firmware/microblaze/usrp2p/bootloader/icap_test.c
new file mode 100644
index 000000000..5feb9d014
--- /dev/null
+++ b/firmware/microblaze/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/microblaze/usrp2p/bootloader/init_bootloader.c b/firmware/microblaze/usrp2p/bootloader/init_bootloader.c
new file mode 100644
index 000000000..15d719d73
--- /dev/null
+++ b/firmware/microblaze/usrp2p/bootloader/init_bootloader.c
@@ -0,0 +1,108 @@
+/* -*- 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>
+
+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(RAM_BASE);
+ 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");
+}
+
+//let's clean up this logic. state machine? no, you only have to go through it once.
+
+//don't need else cases since all these are terminal cases
+
+int main(int argc, char *argv[]) {
+ hal_disable_ints(); // In case we got here via jmp 0x0
+ output_regs->leds = 0xFF;
+ delay(500000);
+ output_regs->leds = 0x00;
+ hal_uart_init();
+ spif_init();
+// i2c_init(); //for EEPROM
+ puts("USRP2+ bootloader\n");
+
+ if(BUTTON_PUSHED) { //see memory_map.h
+ puts("Starting USRP2+ in safe mode.");
+ 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);
+ start_program(RAM_BASE);
+ 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();
+ }
+ }
+
+ puts("Checking for valid production FPGA image...");
+ if(is_valid_fpga_image(PROD_FPGA_IMAGE_LOCATION_ADDR)) {
+ puts("Valid production FPGA image found. Attempting to boot.");
+ //icap_reload_fpga(PROD_FPGA_IMAGE_LOCATION_ADDR);
+ }
+ puts("No valid production FPGA image found.\nAttempting to load production firmware...");
+ if(is_valid_fw_image(PROD_FW_IMAGE_LOCATION_ADDR)) {
+ puts("Valid production firmware found. Loading...");
+ spi_flash_read(PROD_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES, (void *)RAM_BASE);
+ start_program(RAM_BASE);
+ puts("ERROR: Return from main program! This should never happen!");
+ //if this happens, though, the safest thing to do is reboot the whole FPGA and start over.
+ //icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR);
+ 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);
+ start_program(RAM_BASE);
+ puts("ERROR: return from main program! This should never happen!");
+ //icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR);
+ return 1; //_exit will trap in loop
+ }
+ 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/microblaze/usrp2p/bootloader/serial_loader_burner.c b/firmware/microblaze/usrp2p/bootloader/serial_loader_burner.c
new file mode 100644
index 000000000..4ac4df454
--- /dev/null
+++ b/firmware/microblaze/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/microblaze/usrp2p/bootloader/spi_bootloader.c b/firmware/microblaze/usrp2p/bootloader/spi_bootloader.c
new file mode 100644
index 000000000..678e66cf7
--- /dev/null
+++ b/firmware/microblaze/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/microblaze/usrp2p/bootloader/u2p2-rom.ld b/firmware/microblaze/usrp2p/bootloader/u2p2-rom.ld
new file mode 100644
index 000000000..4c9eaa8e5
--- /dev/null
+++ b/firmware/microblaze/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/microblaze/usrp2p/bootloader_utils.c b/firmware/microblaze/usrp2p/bootloader_utils.c
new file mode 100644
index 000000000..00893db02
--- /dev/null
+++ b/firmware/microblaze/usrp2p/bootloader_utils.c
@@ -0,0 +1,33 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2010 Ettus Research LLC
+ *
+ */
+
+//contains routines for loading programs from Flash. depends on Flash libraries.
+#include <string.h>
+#include <bootloader_utils.h>
+#include <spi_flash.h>
+
+
+int is_valid_fpga_image(uint32_t addr) {
+ static const uint8_t fpgaheader[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0x99}; //AA 99 is the standard Xilinx sync sequence, and it's always prefixed with 0xFF padding
+ uint8_t buf[10];
+ spi_flash_read(addr, 6, buf);
+ return memcmp(buf, fpgaheader, 6) == 0;
+}
+
+int is_valid_fw_image(uint32_t addr) {
+ static const uint8_t fwheader[] = {0xB0, 0x00, 0x00, 0x00, 0xB8, 0x08}; //just lookin for a jump to anywhere located at the reset vector
+ uint8_t buf[12];
+ spi_flash_read(addr, 6, buf);
+ return memcmp(buf, fwheader, 6) == 0;
+}
+
+void start_program(uint32_t addr)
+{
+ memcpy(0x00000000, addr+0x00000000, 36); //copy the whole vector table, with the reset vector, into boot RAM
+ typedef void (*fptr_t)(void);
+ (*(fptr_t) 0x00000000)(); // most likely no return
+}
+
diff --git a/firmware/microblaze/usrp2p/bootloader_utils.h b/firmware/microblaze/usrp2p/bootloader_utils.h
new file mode 100644
index 000000000..c72128f43
--- /dev/null
+++ b/firmware/microblaze/usrp2p/bootloader_utils.h
@@ -0,0 +1,21 @@
+/* -*- 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.
+#define FPGA_IMAGE_SIZE_BYTES 2097152
+//instead of 32K, we write 31K because we're using the top 1K for stack space!
+#define FW_IMAGE_SIZE_BYTES 31744
+
+#define SAFE_FPGA_IMAGE_LOCATION_ADDR 0x00000000
+#define SAFE_FW_IMAGE_LOCATION_ADDR 0x007F0000
+#define PROD_FPGA_IMAGE_LOCATION_ADDR 0x00200000
+#define PROD_FW_IMAGE_LOCATION_ADDR 0x00400000
+
+int is_valid_fpga_image(uint32_t addr);
+int is_valid_fw_image(uint32_t addr);
+void start_program(uint32_t addr);
diff --git a/firmware/microblaze/usrp2p/clocks.c b/firmware/microblaze/usrp2p/clocks.c
new file mode 100644
index 000000000..3488ec468
--- /dev/null
+++ b/firmware/microblaze/usrp2p/clocks.c
@@ -0,0 +1,259 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 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 <clocks.h>
+
+#include "memory_map.h"
+#include "ad9510.h"
+#include "spi.h"
+#include "u2_init.h"
+#include "nonstdio.h"
+
+//USRP2PLUS clocks:
+//Clock 0: testclk
+//Clock 1: FPGA clk
+//Clock 2: ADC clk
+//Clock 3: DAC clk
+//Clock 4: SER clk
+//Clock 5: TX dboard clk
+//Clock 6: EXP clk
+//Clock 7: RX dboard clk
+
+//TODO: should have enough brains to init the FPGA clock for USRP2+. all others are suspect.
+//note that without EEPROM support u2_hw_rev_major is going to be incorrect.
+
+void
+clocks_init(void)
+{
+ // Set up basic clocking functions in AD9510
+ ad9510_write_reg(0x45, 0x01); // CLK2 drives distribution
+
+ clocks_enable_fpga_clk(true, 1);
+
+ spi_wait();
+
+ // Set up PLL for 10 MHz reference
+ // Reg 4, A counter, Don't Care
+ ad9510_write_reg(0x05, 0x00); // Reg 5, B counter MSBs, 0
+ ad9510_write_reg(0x06, 0x05); // Reg 6, B counter LSBs, 5
+ // Reg 7, Loss of reference detect, doesn't work yet, 0
+ ad9510_write_reg(0x5A, 0x01); // Update Regs
+
+ // Primary clock configuration
+ clocks_mimo_config(MC_WE_DONT_LOCK);
+
+ // Set up other clocks
+ //clocks_enable_test_clk(false, 0);
+ //clocks_enable_tx_dboard(false, 0);
+ //clocks_enable_rx_dboard(false, 0);
+ clocks_enable_eth_phyclk(false, 0); //PHY clk is separate now (u2r4, u2p)
+
+ // Enable clock to ADCs and DACs
+ //clocks_enable_dac_clk(true, 1);
+ //clocks_enable_adc_clk(true, 1);
+}
+
+
+void
+clocks_mimo_config(int flags)
+{
+ if (flags & _MC_WE_LOCK){
+ // Reg 8, Charge pump on, dig lock det, positive PFD, 47
+ ad9510_write_reg(0x08, 0x47);
+ }
+ else {
+ // Reg 8, Charge pump off, dig lock det, positive PFD
+ ad9510_write_reg(0x08, 0x00);
+ }
+
+ // Reg 9, Charge pump current, 0x40=3mA, 0x00=650uA
+ ad9510_write_reg(0x09, 0x00);
+ // Reg A, Prescaler of 2, everything normal 04
+ ad9510_write_reg(0x0A, 0x04);
+ // Reg B, R Div MSBs, 0
+ ad9510_write_reg(0x0B, 0x00);
+ // Reg C, R Div LSBs, 1
+ ad9510_write_reg(0x0C, 0x01);
+ // Reg D, Antibacklash, Digital lock det, 0
+
+ ad9510_write_reg(0x5A, 0x01); // Update Regs
+
+ spi_wait();
+
+ // Allow for clock switchover
+
+ if (flags & _MC_WE_LOCK){ // WE LOCK
+ if (flags & _MC_MIMO_CLK_INPUT) {
+ // Turn on ref output and choose the MIMO connector
+ output_regs->clk_ctrl = 0x15;
+ }
+ else {
+ // turn on ref output and choose the SMA
+ output_regs->clk_ctrl = 0x1C;
+ }
+ }
+ else { // WE DONT LOCK
+ // Disable both ext clk inputs
+ output_regs->clk_ctrl = 0x10;
+ }
+
+ // Do we drive a clock onto the MIMO connector?
+ if (flags & MC_PROVIDE_CLK_TO_MIMO)
+ clocks_enable_clkexp_out(true,10);
+ else
+ clocks_enable_clkexp_out(false,0);
+}
+
+bool
+clocks_lock_detect()
+{
+ if(pic_regs->pending & PIC_CLKSTATUS)
+ return true;
+ return false;
+}
+
+int inline
+clocks_gen_div(int divisor)
+{
+ int L,H;
+ L = (divisor>>1)-1;
+ H = divisor-L-2;
+ return (L<<4)|H;
+}
+
+#define CLOCK_OUT_EN 0x08
+#define CLOCK_OUT_DIS_CMOS 0x01
+#define CLOCK_OUT_DIS_PECL 0x02
+#define CLOCK_DIV_DIS 0x80
+#define CLOCK_DIV_EN 0x00
+
+#define CLOCK_MODE_PECL 1
+#define CLOCK_MODE_LVDS 2
+#define CLOCK_MODE_CMOS 3
+
+//CHANGED: set to PECL for default behavior
+void
+clocks_enable_XXX_clk(bool enable, int divisor, int reg_en, int reg_div, int mode)
+{
+ int enable_word, div_word, div_en_word;
+
+ switch(mode) {
+ case CLOCK_MODE_LVDS :
+ enable_word = enable ? 0x02 : 0x03;
+ break;
+ case CLOCK_MODE_CMOS :
+ enable_word = enable ? 0x08 : 0x09;
+ break;
+ case CLOCK_MODE_PECL :
+ default:
+ enable_word = enable ? 0x08 : 0x0A;
+ break;
+ }
+ if(enable && (divisor>1)) {
+ div_word = clocks_gen_div(divisor);
+ div_en_word = CLOCK_DIV_EN;
+ }
+ else {
+ div_word = 0;
+ div_en_word = CLOCK_DIV_DIS;
+ }
+
+ ad9510_write_reg(reg_en,enable_word); // Output en/dis
+ ad9510_write_reg(reg_div,div_word); // Set divisor
+ ad9510_write_reg(reg_div+1,div_en_word); // Enable or Bypass Divider
+ ad9510_write_reg(0x5A, 0x01); // Update Regs
+}
+
+// Clock 0
+/*void
+clocks_enable_test_clk(bool enable, int divisor)
+{
+ clocks_enable_XXX_clk(enable,divisor,0x3C,0x48,CLOCK_MODE_PECL);
+}*/
+
+// Clock 1
+void
+clocks_enable_fpga_clk(bool enable, int divisor)
+{
+ clocks_enable_XXX_clk(enable,divisor,0x3D,0x4A,CLOCK_MODE_PECL);
+}
+
+// Clock 2 on Rev 3, Clock 5 on Rev 4, Clock 6 on USRP2+
+void
+clocks_enable_clkexp_out(bool enable, int divisor)
+{
+ if(u2_hw_rev_major == 3)
+ clocks_enable_XXX_clk(enable,divisor,0x3E,0x4C,CLOCK_MODE_PECL);
+ else if(u2_hw_rev_major == 4) {
+ ad9510_write_reg(0x34,0x00); // Turn on fine delay
+ ad9510_write_reg(0x35,0x00); // Set Full Scale to nearly 10ns
+ ad9510_write_reg(0x36,0x1c); // Set fine delay. 0x20 is midscale
+ clocks_enable_XXX_clk(enable,divisor,0x41,0x52,CLOCK_MODE_LVDS);
+ }
+ else if(u2_hw_rev_major == 10) {
+ ad9510_write_reg(0x34, 0x00);
+ ad9510_write_reg(0x35, 0x00);
+ ad9510_write_reg(0x36, 0x1C);
+ clocks_enable_XXX_clk(enable, divisor, 0x42, 0x52, CLOCK_MODE_LVDS);
+ }
+ else
+ putstr("ERR (clocks_enable_clkexp_out): Invalid hw rev, don't know what to do!\n");
+}
+
+// Clock 5 on Rev 3, none (was 2) on Rev 4, none on USRP2+
+void
+clocks_enable_eth_phyclk(bool enable, int divisor)
+{
+ if(u2_hw_rev_major == 3)
+ clocks_enable_XXX_clk(enable,divisor,0x41,0x52,CLOCK_MODE_LVDS);
+ else if(u2_hw_rev_major == 4)
+ clocks_enable_XXX_clk(enable,divisor,0x3E,0x4C,CLOCK_MODE_PECL);
+ else
+ putstr("(clocks_enable_eth_phyclk): no eth PHY clock or invalid hw rev\n"); //not really an error
+}
+
+// Clock 3
+/*void
+clocks_enable_dac_clk(bool enable, int divisor)
+{
+ clocks_enable_XXX_clk(enable,divisor,0x3F,0x4E,CLOCK_MODE_PECL);
+}*/
+
+// Clock 4
+/*void
+clocks_enable_adc_clk(bool enable, int divisor)
+{
+ clocks_enable_XXX_clk(enable,divisor,0x40,0x50,CLOCK_MODE_LVDS);
+}*/
+
+// Clock 6
+/*void
+clocks_enable_tx_dboard(bool enable, int divisor)
+{
+ clocks_enable_XXX_clk(enable,divisor,0x42,0x54,CLOCK_MODE_CMOS);
+}*/
+
+// Clock 7
+/*void
+clocks_enable_rx_dboard(bool enable, int divisor)
+{
+ clocks_enable_XXX_clk(enable,divisor,0x43,0x56,CLOCK_MODE_CMOS);
+}*/
diff --git a/firmware/microblaze/usrp2p/memory_map.h b/firmware/microblaze/usrp2p/memory_map.h
new file mode 100644
index 000000000..fc0094e67
--- /dev/null
+++ b/firmware/microblaze/usrp2p/memory_map.h
@@ -0,0 +1,838 @@
+/* -*- c -*- */
+/*
+ * 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/>.
+ */
+
+/* Overall Memory Map
+ * 0000-FFFF 64K RAM space
+ *
+ * 0000-1FFF 8K Boot RAM
+ * 2000-5FFF 16K Buffer pool
+ * 6000-7FFF 8K Peripherals
+ * 8000-FFFF 32K Main System RAM
+
+
+From u2plus_core.v:
+wb_1master #(.decode_w(8),
+.s0_addr(8'b0000_0000),.s0_mask(8'b1110_0000), // 0-8K, Boot RAM
+.s1_addr(8'b0100_0000),.s1_mask(8'b1100_0000), // 16K-32K, Buffer Pool
+.s2_addr(8'b0011_0000),.s2_mask(8'b1111_1111), // SPI 0x3000
+.s3_addr(8'b0011_0001),.s3_mask(8'b1111_1111), // I2C 0x3100
+.s4_addr(8'b0011_0010),.s4_mask(8'b1111_1111), // GPIO 0x3200
+.s5_addr(8'b0011_0011),.s5_mask(8'b1111_1111), // Readback 0x3300
+.s6_addr(8'b0011_0100),.s6_mask(8'b1111_1111), // Ethernet MAC 0x3400
+.s7_addr(8'b0010_0000),.s7_mask(8'b1111_0000), // 8-12K, Settings Bus (only uses 1K) 0x2000-0x2FFF
+.s8_addr(8'b0011_0101),.s8_mask(8'b1111_1111), // PIC 0x3500
+.s9_addr(8'b0011_0110),.s9_mask(8'b1111_1111), // Unused 0x3600
+.sa_addr(8'b0011_0111),.sa_mask(8'b1111_1111), // UART 0x3700
+.sb_addr(8'b0011_1000),.sb_mask(8'b1111_1111), // ATR 0x3800
+.sc_addr(8'b0011_1001),.sc_mask(8'b1111_1111), // Unused 0x3900
+.sd_addr(8'b0011_1010),.sd_mask(8'b1111_1111), // ICAP 0x3A00
+.se_addr(8'b0011_1011),.se_mask(8'b1111_1111), // SPI Flash 0x3B00
+.sf_addr(8'b1000_0000),.sf_mask(8'b1000_0000), // 32-64K, Main RAM 0x8000-0xFFFF
+ .dw(dw),.aw(aw),.sw(sw)) wb_1master
+
+ */
+
+
+#ifndef INCLUDED_MEMORY_MAP_H
+#define INCLUDED_MEMORY_MAP_H
+
+#include <stdint.h>
+
+
+#define MASTER_CLK_RATE 100000000 // 100 MHz
+
+
+////////////////////////////////////////////////////////////////
+//
+// Memory map for embedded wishbone bus
+//
+////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////
+// Boot RAM, Slave 0
+
+#define BOOTRAM_BASE 0x0000
+
+
+////////////////////////////////////////////////////////////////
+// Buffer Pool RAM, Slave 1
+//
+// The buffers themselves are located in Slave 1, Buffer Pool RAM.
+// The status registers are in Slave 5, Buffer Pool Status.
+// The control register is in Slave 7, Settings Bus.
+
+#define BUFFER_POOL_RAM_BASE 0x4000
+
+#define NBUFFERS 8
+#define BP_NLINES 0x0200 // number of 32-bit lines in a buffer
+#define BP_LAST_LINE (BP_NLINES - 1) // last line in a buffer
+
+#define buffer_pool_ram \
+ ((uint32_t *) BUFFER_POOL_RAM_BASE)
+
+#define buffer_ram(n) (&buffer_pool_ram[(n) * BP_NLINES])
+
+
+/////////////////////////////////////////////////////
+// SPI Core, Slave 2. See core docs for more info
+#define SPI_BASE 0x3000 // Base address (16-bit) is base peripheral addr
+
+typedef struct {
+ volatile uint32_t txrx0;
+ volatile uint32_t txrx1;
+ volatile uint32_t txrx2;
+ volatile uint32_t txrx3;
+ volatile uint32_t ctrl;
+ volatile uint32_t div;
+ volatile uint32_t ss;
+} spi_regs_t;
+
+#define spi_regs ((spi_regs_t *) SPI_BASE)
+
+
+// Masks for controlling different peripherals
+#define SPI_SS_AD9510 1
+#define SPI_SS_AD9777 2
+#define SPI_SS_RX_DAC 4
+#define SPI_SS_RX_ADC 8
+#define SPI_SS_RX_DB 16
+#define SPI_SS_TX_DAC 32
+#define SPI_SS_TX_ADC 64
+#define SPI_SS_TX_DB 128
+#define SPI_SS_ADS62P44 256
+
+// Masks for different parts of CTRL reg
+#define SPI_CTRL_ASS (1<<13)
+#define SPI_CTRL_IE (1<<12)
+#define SPI_CTRL_LSB (1<<11)
+#define SPI_CTRL_TXNEG (1<<10)
+#define SPI_CTRL_RXNEG (1<< 9)
+#define SPI_CTRL_GO_BSY (1<< 8)
+#define SPI_CTRL_CHAR_LEN_MASK 0x7F
+
+////////////////////////////////////////////////
+// I2C, Slave 3
+// See Wishbone I2C-Master Core Specification.
+
+#define I2C_BASE 0x3100
+
+typedef struct {
+ volatile uint32_t prescaler_lo; // r/w
+ volatile uint32_t prescaler_hi; // r/w
+ volatile uint32_t ctrl; // r/w
+ volatile uint32_t data; // wr = transmit reg; rd = receive reg
+ volatile uint32_t cmd_status; // wr = command reg; rd = status reg
+} i2c_regs_t;
+
+#define i2c_regs ((i2c_regs_t *) I2C_BASE)
+
+#define I2C_CTRL_EN (1 << 7) // core enable
+#define I2C_CTRL_IE (1 << 6) // interrupt enable
+
+//
+// STA, STO, RD, WR, and IACK bits are cleared automatically
+//
+#define I2C_CMD_START (1 << 7) // generate (repeated) start condition
+#define I2C_CMD_STOP (1 << 6) // generate stop condition
+#define I2C_CMD_RD (1 << 5) // read from slave
+#define I2C_CMD_WR (1 << 4) // write to slave
+#define I2C_CMD_NACK (1 << 3) // when a rcvr, send ACK (ACK=0) or NACK (ACK=1)
+#define I2C_CMD_RSVD_2 (1 << 2) // reserved
+#define I2C_CMD_RSVD_1 (1 << 1) // reserved
+#define I2C_CMD_IACK (1 << 0) // set to clear pending interrupt
+
+#define I2C_ST_RXACK (1 << 7) // Received acknowledgement from slave (1 = NAK, 0 = ACK)
+#define I2C_ST_BUSY (1 << 6) // 1 after START signal detected; 0 after STOP signal detected
+#define I2C_ST_AL (1 << 5) // Arbitration lost. 1 when core lost arbitration
+#define I2C_ST_RSVD_4 (1 << 4) // reserved
+#define I2C_ST_RSVD_3 (1 << 3) // reserved
+#define I2C_ST_RSVD_2 (1 << 2) // reserved
+#define I2C_ST_TIP (1 << 1) // Transfer-in-progress
+#define I2C_ST_IP (1 << 0) // Interrupt pending
+
+
+////////////////////////////////////////////////
+// GPIO, Slave 4
+//
+// These go to the daughterboard i/o pins
+
+#define GPIO_BASE 0x3200
+
+typedef struct {
+ volatile uint32_t io; // tx data in high 16, rx in low 16
+ volatile uint32_t ddr; // 32 bits, 1 means output. tx in high 16, rx in low 16
+ volatile uint32_t tx_sel; // 16 2-bit fields select which source goes to TX DB
+ volatile uint32_t rx_sel; // 16 2-bit fields select which source goes to RX DB
+} gpio_regs_t;
+
+// each 2-bit sel field is layed out this way
+#define GPIO_SEL_SW 0 // if pin is an output, set by software in the io reg
+#define GPIO_SEL_ATR 1 // if pin is an output, set by ATR logic
+#define GPIO_SEL_DEBUG_0 2 // if pin is an output, debug lines from FPGA fabric
+#define GPIO_SEL_DEBUG_1 3 // if pin is an output, debug lines from FPGA fabric
+
+#define gpio_base ((gpio_regs_t *) GPIO_BASE)
+
+///////////////////////////////////////////////////
+// Buffer Pool Status, Slave 5
+//
+// The buffers themselves are located in Slave 1, Buffer Pool RAM.
+// The status registers are in Slave 5, Buffer Pool Status.
+// The control register is in Slave 7, Settings Bus.
+
+#define BUFFER_POOL_STATUS_BASE 0x3300
+
+typedef struct {
+ volatile uint32_t last_line[NBUFFERS]; // last line xfer'd in buffer
+ volatile uint32_t status; // error and done flags
+ volatile uint32_t hw_config; // see below
+ volatile uint32_t dummy[3];
+ volatile uint32_t irqs;
+ volatile uint32_t pri_enc_bp_status;
+ volatile uint32_t cycle_count;
+} buffer_pool_status_t;
+
+#define buffer_pool_status ((buffer_pool_status_t *) BUFFER_POOL_STATUS_BASE)
+
+#define BUTTON_PUSHED ((buffer_pool_status->irqs & PIC_BUTTON) ? 0 : 1)
+
+/*
+ * Buffer n's xfer is done.
+ * Clear this bit by issuing bp_clear_buf(n)
+ */
+#define BPS_DONE(n) (0x00000001 << (n))
+#define BPS_DONE_0 BPS_DONE(0)
+#define BPS_DONE_1 BPS_DONE(1)
+#define BPS_DONE_2 BPS_DONE(2)
+#define BPS_DONE_3 BPS_DONE(3)
+#define BPS_DONE_4 BPS_DONE(4)
+#define BPS_DONE_5 BPS_DONE(5)
+#define BPS_DONE_6 BPS_DONE(6)
+#define BPS_DONE_7 BPS_DONE(7)
+
+/*
+ * Buffer n's xfer had an error.
+ * Clear this bit by issuing bp_clear_buf(n)
+ */
+#define BPS_ERROR(n) (0x00000100 << (n))
+#define BPS_ERROR_0 BPS_ERROR(0)
+#define BPS_ERROR_1 BPS_ERROR(1)
+#define BPS_ERROR_2 BPS_ERROR(2)
+#define BPS_ERROR_3 BPS_ERROR(3)
+#define BPS_ERROR_4 BPS_ERROR(4)
+#define BPS_ERROR_5 BPS_ERROR(5)
+#define BPS_ERROR_6 BPS_ERROR(6)
+#define BPS_ERROR_7 BPS_ERROR(7)
+
+/*
+ * Buffer n is idle. A buffer is idle if it's not
+ * DONE, ERROR, or processing a transaction. If it's
+ * IDLE, it's safe to start a new transaction.
+ *
+ * Clear this bit by starting a xfer with
+ * bp_send_from_buf or bp_receive_to_buf.
+ */
+#define BPS_IDLE(n) (0x00010000 << (n))
+#define BPS_IDLE_0 BPS_IDLE(0)
+#define BPS_IDLE_1 BPS_IDLE(1)
+#define BPS_IDLE_2 BPS_IDLE(2)
+#define BPS_IDLE_3 BPS_IDLE(3)
+#define BPS_IDLE_4 BPS_IDLE(4)
+#define BPS_IDLE_5 BPS_IDLE(5)
+#define BPS_IDLE_6 BPS_IDLE(6)
+#define BPS_IDLE_7 BPS_IDLE(7)
+
+/*
+ * Buffer n has a "slow path" packet in it.
+ * This bit is orthogonal to the bits above and indicates that
+ * the FPGA ethernet rx protocol engine has identified this packet
+ * as one requiring firmware intervention.
+ */
+#define BPS_SLOWPATH(n) (0x01000000 << (n))
+#define BPS_SLOWPATH_0 BPS_SLOWPATH(0)
+#define BPS_SLOWPATH_1 BPS_SLOWPATH(1)
+#define BPS_SLOWPATH_2 BPS_SLOWPATH(2)
+#define BPS_SLOWPATH_3 BPS_SLOWPATH(3)
+#define BPS_SLOWPATH_4 BPS_SLOWPATH(4)
+#define BPS_SLOWPATH_5 BPS_SLOWPATH(5)
+#define BPS_SLOWPATH_6 BPS_SLOWPATH(6)
+#define BPS_SLOWPATH_7 BPS_SLOWPATH(7)
+
+
+#define BPS_DONE_ALL 0x000000ff // mask of all dones
+#define BPS_ERROR_ALL 0x0000ff00 // mask of all errors
+#define BPS_IDLE_ALL 0x00ff0000 // mask of all idles
+#define BPS_SLOWPATH_ALL 0xff000000 // mask of all slowpaths
+
+// The hw_config register
+
+#define HWC_SIMULATION 0x80000000
+#define HWC_WB_CLK_DIV_MASK 0x0000000f
+
+/*!
+ * \brief return non-zero if we're running under the simulator
+ */
+inline static int
+hwconfig_simulation_p(void)
+{
+ return buffer_pool_status->hw_config & HWC_SIMULATION;
+}
+
+/*!
+ * \brief Return Wishbone Clock divisor.
+ * The processor runs at the Wishbone Clock rate which is MASTER_CLK_RATE / divisor.
+ */
+inline static int
+hwconfig_wishbone_divisor(void)
+{
+ return buffer_pool_status->hw_config & HWC_WB_CLK_DIV_MASK;
+}
+
+///////////////////////////////////////////////////
+// Ethernet Core, Slave 6
+
+#define ETH_BASE 0x3400
+
+#include "eth_mac_regs.h"
+
+#define eth_mac ((eth_mac_regs_t *) ETH_BASE)
+
+////////////////////////////////////////////////////
+// Settings Bus, Slave #7, Not Byte Addressable!
+//
+// Output-only from processor point-of-view.
+// 1KB of address space (== 256 32-bit write-only regs)
+
+
+#define MISC_OUTPUT_BASE 0x2000
+#define TX_PROTOCOL_ENGINE_BASE 0x2080
+#define RX_PROTOCOL_ENGINE_BASE 0x20C0
+#define BUFFER_POOL_CTRL_BASE 0x2100
+#define LAST_SETTING_REG 0x23FC // last valid setting register
+
+#define SR_MISC 0
+#define SR_TX_PROT_ENG 32
+#define SR_RX_PROT_ENG 48
+#define SR_BUFFER_POOL_CTRL 64
+#define SR_UDP_SM 96
+#define SR_TX_DSP 208
+#define SR_TX_CTRL 224
+#define SR_RX_DSP 160
+#define SR_RX_CTRL 176
+#define SR_TIME64 192
+#define SR_SIMTIMER 198
+#define SR_LAST 255
+
+#define _SR_ADDR(sr) (MISC_OUTPUT_BASE + (sr) * sizeof(uint32_t))
+
+// --- buffer pool control regs ---
+
+typedef struct {
+ volatile uint32_t ctrl;
+} buffer_pool_ctrl_t;
+
+// buffer pool ports
+
+#define PORT_SERDES 0 // serial/deserializer
+#define PORT_DSP 1 // DSP tx or rx pipeline
+#define PORT_ETH 2 // ethernet tx or rx
+#define PORT_RAM 3 // RAM tx or rx
+
+// the buffer pool ctrl register fields
+
+#define BPC_BUFFER(n) (((n) & 0xf) << 28)
+#define BPC_BUFFER_MASK BPC_BUFFER(~0)
+#define BPC_BUFFER_0 BPC_BUFFER(0)
+#define BPC_BUFFER_1 BPC_BUFFER(1)
+#define BPC_BUFFER_2 BPC_BUFFER(2)
+#define BPC_BUFFER_3 BPC_BUFFER(3)
+#define BPC_BUFFER_4 BPC_BUFFER(4)
+#define BPC_BUFFER_5 BPC_BUFFER(5)
+#define BPC_BUFFER_6 BPC_BUFFER(6)
+#define BPC_BUFFER_7 BPC_BUFFER(7)
+#define BPC_BUFFER_NIL BPC_BUFFER(0x8) // disable
+
+#define BPC_PORT(n) (((n) & 0x7) << 25)
+#define BPC_PORT_MASK BPC_PORT(~0)
+#define BPC_PORT_SERDES BPC_PORT(PORT_SERDES)
+#define BPC_PORT_DSP BPC_PORT(PORT_DSP)
+#define BPC_PORT_ETH BPC_PORT(PORT_ETH)
+#define BPC_PORT_RAM BPC_PORT(PORT_RAM)
+#define BPC_PORT_NIL BPC_PORT(0x4) // disable
+
+#define BPC_CLR (1 << 24) // mutually excl commands
+#define BPC_READ (1 << 23)
+#define BPC_WRITE (1 << 22)
+
+#define BPC_STEP(step) (((step) & 0xf) << 18)
+#define BPC_STEP_MASK BPC_STEP(~0)
+#define BPC_LAST_LINE(line) (((line) & 0x1ff) << 9)
+#define BPC_LAST_LINE_MASK BPC_LAST_LINE(~0)
+#define BPC_FIRST_LINE(line) (((line) & 0x1ff) << 0)
+#define BPC_FIRST_LINE_MASK BPC_FIRST_LINE(~0)
+
+#define buffer_pool_ctrl ((buffer_pool_ctrl_t *) BUFFER_POOL_CTRL_BASE)
+
+// --- misc outputs ---
+
+typedef struct {
+ volatile uint32_t clk_ctrl;
+ volatile uint32_t serdes_ctrl;
+ volatile uint32_t adc_ctrl;
+ volatile uint32_t leds;
+ volatile uint32_t phy_ctrl; // LSB is reset line to eth phy
+ volatile uint32_t debug_mux_ctrl;
+ volatile uint32_t ram_page; // FIXME should go somewhere else...
+ volatile uint32_t flush_icache; // Flush the icache
+ volatile uint32_t led_src; // HW or SW control for LEDs
+} output_regs_t;
+
+#define SERDES_ENABLE 8
+#define SERDES_PRBSEN 4
+#define SERDES_LOOPEN 2
+#define SERDES_RXEN 1
+
+#define ADC_CTRL_ON 0x0F
+#define ADC_CTRL_OFF 0x00
+
+// crazy order that matches the labels on the case
+
+#define LED_A (1 << 4)
+#define LED_B (1 << 1)
+#define LED_C (1 << 3)
+#define LED_D (1 << 0)
+#define LED_E (1 << 2)
+// LED_F // controlled by CPLD
+#define LED_RJ45 (1 << 5)
+
+#define output_regs ((output_regs_t *) MISC_OUTPUT_BASE)
+
+// --- udp tx regs ---
+
+typedef struct {
+ // Bits 19:16 are control info; bits 15:0 are data (see below)
+ // First two words are unused.
+ volatile uint32_t _nope[2];
+ //--- ethernet header - 14 bytes---
+ volatile struct{
+ uint32_t mac_dst_0_1; //word 2
+ uint32_t mac_dst_2_3;
+ uint32_t mac_dst_4_5;
+ uint32_t mac_src_0_1;
+ uint32_t mac_src_2_3;
+ uint32_t mac_src_4_5;
+ uint32_t ether_type; //word 8
+ } eth_hdr;
+ //--- ip header - 20 bytes ---
+ volatile struct{
+ uint32_t ver_ihl_tos; //word 9
+ uint32_t total_length;
+ uint32_t identification;
+ uint32_t flags_frag_off;
+ uint32_t ttl_proto;
+ uint32_t checksum;
+ uint32_t src_addr_high;
+ uint32_t src_addr_low;
+ uint32_t dst_addr_high;
+ uint32_t dst_addr_low; //word 18
+ } ip_hdr;
+ //--- udp header - 8 bytes ---
+ volatile struct{
+ uint32_t src_port; //word 19
+ uint32_t dst_port;
+ uint32_t length;
+ uint32_t checksum; //word 22
+ } udp_hdr;
+ volatile uint32_t _pad[32-23];
+} sr_udp_sm_t;
+
+// control bits (all expect UDP_SM_LAST_WORD are mutually exclusive)
+
+// This is the last word of the header
+#define UDP_SM_LAST_WORD (1 << 19)
+
+// Insert IP header checksum here. Data is the xor of 16'hFFFF and
+// the values written into regs 9-13 and 15-18.
+#define UDP_SM_INS_IP_HDR_CHKSUM (1 << 18)
+
+// Insert IP Length here (data ignored)
+#define UDP_SM_INS_IP_LEN (1 << 17)
+
+// Insert UDP Length here (data ignore)
+#define UDP_SM_INS_UDP_LEN (1 << 16)
+
+#define sr_udp_sm ((sr_udp_sm_t *) _SR_ADDR(SR_UDP_SM))
+
+// --- dsp tx regs ---
+
+#define MIN_CIC_INTERP 1
+#define MAX_CIC_INTERP 128
+
+typedef struct {
+ volatile uint32_t num_chan;
+ volatile uint32_t clear_state; // clears out state machine, fifos,
+} sr_tx_ctrl_t;
+
+#define sr_tx_ctrl ((sr_tx_ctrl_t *) _SR_ADDR(SR_TX_CTRL))
+
+typedef struct {
+ volatile int32_t freq;
+ volatile uint32_t scale_iq; // {scale_i,scale_q}
+ volatile uint32_t interp_rate;
+ volatile uint32_t _padding0; // padding for the tx_mux
+ // NOT freq, scale, interp
+ /*!
+ * \brief output mux configuration.
+ *
+ * <pre>
+ * 3 2 1
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-------------------------------+-------+-------+-------+-------+
+ * | | DAC1 | DAC0 |
+ * +-------------------------------+-------+-------+-------+-------+
+ *
+ * There are N DUCs (1 now) with complex inputs and outputs.
+ * There are two DACs.
+ *
+ * Each 4-bit DACx field specifies the source for the DAC
+ * Each subfield is coded like this:
+ *
+ * 3 2 1 0
+ * +-------+
+ * | N |
+ * +-------+
+ *
+ * N specifies which DUC output is connected to this DAC.
+ *
+ * N which interp output
+ * --- -------------------
+ * 0 DUC 0 I
+ * 1 DUC 0 Q
+ * 2 DUC 1 I
+ * 3 DUC 1 Q
+ * F All Zeros
+ *
+ * The default value is 0x10
+ * </pre>
+ */
+ volatile uint32_t tx_mux;
+
+} dsp_tx_regs_t;
+
+#define dsp_tx_regs ((dsp_tx_regs_t *) _SR_ADDR(SR_TX_DSP))
+
+// --- VITA RX CTRL regs ---
+typedef struct {
+ // The following 3 are logically a single command register.
+ // They are clocked into the underlying fifo when time_ticks is written.
+ volatile uint32_t cmd; // {now, chain, num_samples(30)
+ volatile uint32_t time_secs;
+ volatile uint32_t time_ticks;
+
+ volatile uint32_t clear_overrun; // write anything to clear overrun
+ volatile uint32_t vrt_header; // word 0 of packet. FPGA fills in packet counter
+ volatile uint32_t vrt_stream_id; // word 1 of packet.
+ volatile uint32_t vrt_trailer;
+ volatile uint32_t nsamples_per_pkt;
+ volatile uint32_t nchannels; // 1 in basic case, up to 4 for vector sources
+ volatile uint32_t pad[7]; // Make each structure 16 elements long
+} sr_rx_ctrl_t;
+
+#define sr_rx_ctrl ((sr_rx_ctrl_t *) _SR_ADDR(SR_RX_CTRL))
+
+// --- dsp rx regs ---
+#define MIN_CIC_DECIM 1
+#define MAX_CIC_DECIM 128
+
+typedef struct {
+ volatile int32_t freq;
+ volatile uint32_t scale_iq; // {scale_i,scale_q}
+ volatile uint32_t decim_rate;
+ volatile uint32_t dcoffset_i; // Bit 31 high sets fixed offset mode, using lower 14 bits,
+ // otherwise it is automatic
+ volatile uint32_t dcoffset_q; // Bit 31 high sets fixed offset mode, using lower 14 bits
+
+ /*!
+ * \brief input mux configuration.
+ *
+ * This determines which ADC (or constant zero) is connected to
+ * each DDC input. There are N DDCs (1 now). Each has two inputs.
+ *
+ * <pre>
+ * Mux value:
+ *
+ * 3 2 1
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * | |Q0 |I0 |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ *
+ * Each 2-bit I field is either 00 (A/D A), 01 (A/D B) or 1X (const zero)
+ * Each 2-bit Q field is either 00 (A/D A), 01 (A/D B) or 1X (const zero)
+ *
+ * The default value is 0x4
+ * </pre>
+ */
+ volatile uint32_t rx_mux; // called adc_mux in dsp_core_rx.v
+
+ /*!
+ * \brief Streaming GPIO configuration
+ *
+ * This determines whether the LSBs of I and Q samples come from the DSP
+ * pipeline or from the io_rx GPIO pins. To stream GPIO, one must first
+ * set the GPIO data direction register to have io_rx[15] and/or io_rx[14]
+ * configured as inputs. The GPIO pins will be sampled at the time the
+ * remainder of the DSP sample is strobed into the RX sample FIFO. There
+ * will be a decimation-dependent fixed time offset between the GPIO
+ * sample stream and the associated RF samples.
+ *
+ * 3 2 1
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * | MBZ |Q|I|
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ *
+ * I 0=LSB comes from DSP pipeline (default)
+ * 1=LSB comes from io_rx[15]
+ *
+ * Q 0=LSB comes from DSP pipeline (default)
+ * 1=LSB comes from io_rx[14]
+ */
+ volatile uint32_t gpio_stream_enable;
+
+} dsp_rx_regs_t;
+
+#define dsp_rx_regs ((dsp_rx_regs_t *) _SR_ADDR(SR_RX_DSP))
+
+// ----------------------------------------------------------------
+// VITA49 64 bit time (write only)
+ /*!
+ * \brief Time 64 flags
+ *
+ * <pre>
+ *
+ * 3 2 1
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-----------------------------------------------------------+-+-+
+ * | |S|P|
+ * +-----------------------------------------------------------+-+-+
+ *
+ * P - PPS edge selection (0=negedge, 1=posedge, default=0)
+ * S - Source (0=sma, 1=mimo, 0=default)
+ *
+ * </pre>
+ */
+typedef struct {
+ volatile uint32_t secs; // value to set absolute secs to on next PPS
+ volatile uint32_t ticks; // value to set absolute ticks to on next PPS
+ volatile uint32_t flags; // flags - see chart above
+ volatile uint32_t imm; // set immediate (0=latch on next pps, 1=latch immediate, default=0)
+} sr_time64_t;
+
+#define sr_time64 ((sr_time64_t *) _SR_ADDR(SR_TIME64))
+
+
+/*
+ * --- ethernet tx protocol engine regs (write only) ---
+ *
+ * These registers control the transmit portion of the ethernet
+ * protocol engine (out of USRP2). The protocol engine handles fifo
+ * status and sequence number insertion in outgoing packets, and
+ * automagically generates status packets when required to inform the
+ * host of changes in fifo availability.
+ *
+ * All outgoing packets have their fifo_status field set to the number
+ * of 32-bit lines of fifo available in the ethernet Rx fifo (see
+ * usrp2_eth_packet.h). Seqno's are set if FIXME, else 0.
+ *
+ * FIXME clean this up once we know how it's supposed to behave.
+ */
+
+typedef struct {
+ volatile uint32_t flags; // not yet fully defined (channel?)
+ volatile uint32_t mac_dst0123; // 4 bytes of destination mac addr
+ volatile uint32_t mac_dst45src01; // 2 bytes of dest mac addr; 2 bytes of src mac addr
+ volatile uint32_t mac_src2345; // 4 bytes of destination mac addr
+ volatile uint32_t seqno; // Write to init seqno. It autoincs on match
+} tx_proto_engine_regs_t;
+
+#define tx_proto_engine ((tx_proto_engine_regs_t *) TX_PROTOCOL_ENGINE_BASE)
+
+/*
+ * --- ethernet rx protocol engine regs (write only) ---
+ *
+ * These registers control the receive portion of the ethernet
+ * protocol engine (into USRP2). The protocol engine offloads common
+ * packet inspection operations so that firmware has less to do on
+ * "fast path" packets.
+ *
+ * The registers define conditions which must be matched for a packet
+ * to be considered a "fast path" packet. If a received packet
+ * matches the src and dst mac address, ethertype, flags field, and
+ * expected seqno number it is considered a "fast path" packet, and
+ * the expected seqno is updated. If the packet fails to satisfy any
+ * of the above conditions it's a "slow path" packet, and the
+ * corresponding SLOWPATH flag will be set buffer_status register.
+ */
+
+typedef struct {
+ volatile uint32_t flags; // not yet fully defined (channel?)
+ volatile uint32_t mac_dst0123; // 4 bytes of destination mac addr
+ volatile uint32_t mac_dst45src01; // 2 bytes of dest mac addr; 2 bytes of src mac addr
+ volatile uint32_t mac_src2345; // 4 bytes of destination mac addr
+ volatile uint32_t ethertype_pad; // ethertype in high 16-bits
+} rx_proto_engine_regs_t;
+
+#define rx_proto_engine ((rx_proto_engine_regs_t *) RX_PROTOCOL_ENGINE_BASE)
+
+
+
+///////////////////////////////////////////////////
+// Simple Programmable Interrupt Controller, Slave 8
+
+#define PIC_BASE 0x3500
+
+// Interrupt request lines
+// Bit numbers (LSB == 0) that correpond to interrupts into PIC
+
+#define IRQ_BUFFER 0 // buffer manager
+#define IRQ_ONETIME 1
+#define IRQ_SPI 2
+#define IRQ_I2C 3
+#define IRQ_PHY 4 // ethernet PHY
+#define IRQ_UNDERRUN 5
+#define IRQ_OVERRUN 6
+#define IRQ_PPS 7 // pulse per second
+#define IRQ_UART_RX 8
+#define IRQ_UART_TX 9
+#define IRQ_SERDES 10
+#define IRQ_CLKSTATUS 11
+#define IRQ_PERIODIC 12
+#define IRQ_BUTTON 13
+
+#define IRQ_TO_MASK(x) (1 << (x))
+
+#define PIC_BUFFER_INT IRQ_TO_MASK(IRQ_BUFFER)
+#define PIC_ONETIME_INT IRQ_TO_MASK(IRQ_ONETIME)
+#define PIC_SPI_INT IRQ_TO_MASK(IRQ_SPI)
+#define PIC_I2C_INT IRQ_TO_MASK(IRQ_I2C)
+#define PIC_PHY_INT IRQ_TO_MASK(IRQ_PHY)
+#define PIC_UNDERRUN_INT IRQ_TO_MASK(IRQ_UNDERRUN)
+#define PIC_OVERRUN_INT IRQ_TO_MASK(IRQ_OVERRUN)
+#define PIC_PPS_INT IRQ_TO_MASK(IRQ_PPS)
+#define PIC_UART_RX_INT IRQ_TO_MASK(IRQ_UART_RX)
+#define PIC_UART_TX_INT IRQ_TO_MASK(IRQ_UART_TX)
+#define PIC_SERDES IRQ_TO_MASK(IRQ_SERDES)
+#define PIC_CLKSTATUS IRQ_TO_MASK(IRQ_CLKSTATUS)
+#define PIC_BUTTON IRQ_TO_MASK(IRQ_BUTTON)
+
+typedef struct {
+ volatile uint32_t edge_enable; // mask: 1 -> edge triggered, 0 -> level
+ volatile uint32_t polarity; // mask: 1 -> rising edge
+ volatile uint32_t mask; // mask: 1 -> disabled
+ volatile uint32_t pending; // mask: 1 -> pending; write 1's to clear pending ints
+} pic_regs_t;
+
+#define pic_regs ((pic_regs_t *) PIC_BASE)
+
+// ----------------------------------------------------------------
+// WB_CLK_RATE is the time base for this
+typedef struct {
+ volatile uint32_t onetime; // Number of wb clk cycles till the onetime interrupt
+ volatile uint32_t periodic; // Repeat rate of periodic interrupt
+} sr_simple_timer_t;
+
+#define sr_simple_timer ((sr_simple_timer_t *) _SR_ADDR(SR_SIMTIMER))
+
+///////////////////////////////////////////////////
+// UNUSED, Slave 9
+
+///////////////////////////////////////////////////
+// UART, Slave 10
+
+#define UART_BASE 0x3700
+
+typedef struct {
+ // All elements are 8 bits except for clkdiv (16), but we use uint32 to make
+ // the hardware for decoding easier
+ volatile uint32_t clkdiv; // Set to 50e6 divided by baud rate (no x16 factor)
+ volatile uint32_t txlevel; // Number of spaces in the FIFO for writes
+ volatile uint32_t rxlevel; // Number of available elements in the FIFO for reads
+ volatile uint32_t txchar; // Write characters to be sent here
+ volatile uint32_t rxchar; // Read received characters here
+} uart_regs_t;
+
+#define uart_regs ((uart_regs_t *) UART_BASE)
+
+///////////////////////////////////////////////////
+// ATR Controller, Slave 11
+
+#define ATR_BASE 0x3800
+
+typedef struct {
+ volatile uint32_t v[16];
+} atr_regs_t;
+
+#define ATR_IDLE 0x0 // indicies into v
+#define ATR_TX 0x1
+#define ATR_RX 0x2
+#define ATR_FULL 0x3
+
+#define atr_regs ((atr_regs_t *) ATR_BASE)
+
+///////////////////////////////////////////////////
+// UNUSED, Slave 12
+
+///////////////////////////////////////////////////
+// ICAP, Slave 13
+
+#define ICAP_BASE 0x3A00
+typedef struct {
+ uint32_t icap; //only the lower 8 bits matter
+} icap_regs_t;
+
+#define icap_regs ((icap_regs_t *) ICAP_BASE)
+
+///////////////////////////////////////////////////
+// SPI Flash interface, Slave 14
+// Control register definitions are the same as SPI, so use SPI_CTRL_ASS, etc.
+// Peripheral mask not needed since bus is dedicated (CE held low)
+
+#define SPIF_BASE 0x3B00
+typedef struct {
+ volatile uint32_t txrx0;
+ volatile uint32_t txrx1;
+ volatile uint32_t txrx2;
+ volatile uint32_t txrx3;
+ volatile uint32_t ctrl;
+ volatile uint32_t div;
+ volatile uint32_t ss;
+} spif_regs_t;
+
+#define spif_regs ((spif_regs_t *) SPIF_BASE)
+
+////////////////////////////////////////////////////////////////
+// Main RAM, Slave 15
+
+#define RAM_BASE 0x8000
+
+
+
+///////////////////////////////////////////////////
+#endif
+
diff --git a/firmware/microblaze/usrp2p/spi_flash.c b/firmware/microblaze/usrp2p/spi_flash.c
new file mode 100644
index 000000000..09b74a513
--- /dev/null
+++ b/firmware/microblaze/usrp2p/spi_flash.c
@@ -0,0 +1,194 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2009 Free Software Foundation, Inc.
+ * 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 "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)
+{
+ //uprintf(UART_DEBUG, "spi_flash_erase_sector_start: addr = 0x%x\n", flash_addr);
+
+ 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;
+
+ 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 == 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)
+{
+ if (nbytes == 0){
+ 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/microblaze/usrp2p/spi_flash.h b/firmware/microblaze/usrp2p/spi_flash.h
new file mode 100644
index 000000000..f65f28477
--- /dev/null
+++ b/firmware/microblaze/usrp2p/spi_flash.h
@@ -0,0 +1,112 @@
+/* -*- c -*- */
+/*
+ * Copyright 2009 Free Software Foundation, Inc.
+ * Copyright 2009 Ettus Research LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef INCLUDED_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_sector_size(void) __attribute__((pure)); /* either 16 or 18 */
+
+static inline size_t
+spi_flash_sector_size(void)
+{
+ return ((size_t) 1) << spi_flash_log2_sector_size();
+}
+
+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/microblaze/usrp2p/spi_flash_private.h b/firmware/microblaze/usrp2p/spi_flash_private.h
new file mode 100644
index 000000000..9a1b8d3e3
--- /dev/null
+++ b/firmware/microblaze/usrp2p/spi_flash_private.h
@@ -0,0 +1,70 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2009 Free Software Foundation, Inc.
+ * Copyright 2009 Ettus Research LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef INCLUDED_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/microblaze/usrp2p/spi_flash_read.c b/firmware/microblaze/usrp2p/spi_flash_read.c
new file mode 100644
index 000000000..1c65350f7
--- /dev/null
+++ b/firmware/microblaze/usrp2p/spi_flash_read.c
@@ -0,0 +1,100 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2009 Free Software Foundation, Inc.
+ * 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 "spi_flash_private.h"
+#include <stdlib.h> // abort
+
+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_sector_size(void)
+{
+ static size_t _spi_flash_log2_sector_size;
+
+ if (_spi_flash_log2_sector_size != 0)
+ return _spi_flash_log2_sector_size;
+
+
+ uint32_t id = spi_flash_rdid();
+ int type = (id >> 8) & 0xff;
+ int size = id & 0xff;
+ if (type != 0x20 || size < 22 || size > 24)
+ abort();
+
+ static unsigned char log2_sector_size[3] = {
+ 16, /* M25P32 */
+ 16, /* M25P64 */
+ 18, /* M25P128 */
+ };
+
+ _spi_flash_log2_sector_size = log2_sector_size[size - 22];
+ return _spi_flash_log2_sector_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, dst += 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[i] = src[i];
+ }
+ spif_regs->ss = 0; // deassert chip select
+}
diff --git a/firmware/microblaze/usrp2p/spif.c b/firmware/microblaze/usrp2p/spif.c
new file mode 100644
index 000000000..1c1a348f4
--- /dev/null
+++ b/firmware/microblaze/usrp2p/spif.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2007,2008,2009 Free Software Foundation, Inc.
+ * 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/>.
+ */
+
+/*
+ * 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/microblaze/usrp2p/xilinx_s3_icap.h b/firmware/microblaze/usrp2p/xilinx_s3_icap.h
new file mode 100644
index 000000000..7b7e9eccc
--- /dev/null
+++ b/firmware/microblaze/usrp2p/xilinx_s3_icap.h
@@ -0,0 +1,37 @@
+/* -*- c -*- */
+/*
+ * Copyright 2009 Ettus Research LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef INCLUDED_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 */