summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--firmware/microblaze/Makefile.am5
-rw-r--r--firmware/microblaze/Makefile.common9
-rw-r--r--firmware/microblaze/apps/blinkenlights.c26
-rw-r--r--firmware/microblaze/apps/flash_test.c67
-rw-r--r--firmware/microblaze/apps/hardware_testbed.c47
-rw-r--r--firmware/microblaze/apps/txrx_uhd.c24
-rw-r--r--firmware/microblaze/apps/uart_flash_loader.c169
-rwxr-xr-xfirmware/microblaze/bin/bin_to_mif.py29
-rwxr-xr-xfirmware/microblaze/bin/bin_to_ram_macro_init.py48
-rwxr-xr-xfirmware/microblaze/bin/elf_to_sbf142
-rw-r--r--firmware/microblaze/bin/sbf.py134
-rwxr-xr-xfirmware/microblaze/bin/serial_loader363
-rwxr-xr-xfirmware/microblaze/bin/uart_ihex_flash_loader.py138
-rwxr-xr-xfirmware/microblaze/bin/uart_ihex_ram_loader.py70
-rwxr-xr-xfirmware/microblaze/bootstrap1
-rw-r--r--firmware/microblaze/configure.ac2
-rw-r--r--firmware/microblaze/lib/Makefile.inc1
-rw-r--r--firmware/microblaze/lib/bootconfig.c101
-rw-r--r--firmware/microblaze/lib/gdbstub2.c506
-rw-r--r--firmware/microblaze/lib/gdbstub2.h25
-rw-r--r--firmware/microblaze/lib/hal_io.c92
-rw-r--r--firmware/microblaze/lib/hal_io.h5
-rw-r--r--firmware/microblaze/lib/hal_uart.c94
-rw-r--r--firmware/microblaze/lib/hal_uart.h50
-rw-r--r--firmware/microblaze/lib/ihex.c57
-rw-r--r--firmware/microblaze/lib/ihex.h18
-rw-r--r--firmware/microblaze/lib/udp_fw_update.h71
-rw-r--r--firmware/microblaze/lib/xilinx_s3_icap.c101
-rw-r--r--firmware/microblaze/usrp2/Makefile.am3
-rw-r--r--firmware/microblaze/usrp2/udp_fw_update.c34
-rw-r--r--firmware/microblaze/usrp2p/.gitignore9
-rw-r--r--firmware/microblaze/usrp2p/Makefile.am68
-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/eth_phy.h235
-rw-r--r--firmware/microblaze/usrp2p/ethernet.c399
-rw-r--r--firmware/microblaze/usrp2p/memory_map.h843
-rw-r--r--firmware/microblaze/usrp2p/spi_flash.c194
-rw-r--r--firmware/microblaze/usrp2p/spi_flash.h119
-rw-r--r--firmware/microblaze/usrp2p/spi_flash_private.h70
-rw-r--r--firmware/microblaze/usrp2p/spi_flash_read.c119
-rw-r--r--firmware/microblaze/usrp2p/spif.c68
-rw-r--r--firmware/microblaze/usrp2p/udp_fw_update.c106
-rw-r--r--firmware/microblaze/usrp2p/xilinx_s3_icap.h37
-rw-r--r--host/lib/gain_group.cpp149
-rw-r--r--host/lib/ic_reg_maps/CMakeLists.txt5
-rwxr-xr-xhost/lib/ic_reg_maps/gen_ads62p44_regs.py124
-rw-r--r--host/lib/usrp/usrp2/CMakeLists.txt3
-rw-r--r--host/lib/usrp/usrp2/clock_ctrl.cpp103
-rw-r--r--host/lib/usrp/usrp2/codec_ctrl.cpp51
-rw-r--r--host/lib/usrp/usrp2/codec_ctrl.hpp21
-rw-r--r--host/lib/usrp/usrp2/codec_impl.cpp77
-rw-r--r--host/lib/usrp/usrp2/dboard_iface.cpp30
-rw-r--r--host/lib/usrp/usrp2/dsp_impl.cpp12
-rw-r--r--host/lib/usrp/usrp2/fw_common.h11
-rw-r--r--host/lib/usrp/usrp2/gps_ctrl.cpp207
-rw-r--r--host/lib/usrp/usrp2/gps_ctrl.hpp53
-rw-r--r--host/lib/usrp/usrp2/io_impl.cpp2
-rw-r--r--host/lib/usrp/usrp2/mboard_impl.cpp58
-rw-r--r--host/lib/usrp/usrp2/serdes_ctrl.cpp4
-rw-r--r--host/lib/usrp/usrp2/usrp2_clk_regs.hpp63
-rw-r--r--host/lib/usrp/usrp2/usrp2_iface.cpp71
-rw-r--r--host/lib/usrp/usrp2/usrp2_iface.hpp25
-rw-r--r--host/lib/usrp/usrp2/usrp2_impl.hpp5
-rw-r--r--host/lib/usrp/usrp2/usrp2_regs.cpp99
-rw-r--r--host/lib/usrp/usrp2/usrp2_regs.hpp232
-rwxr-xr-xhost/utils/usrp2p_fw_update.py259
77 files changed, 7058 insertions, 234 deletions
diff --git a/firmware/microblaze/Makefile.am b/firmware/microblaze/Makefile.am
index 6316b31a2..52fa649c2 100644
--- a/firmware/microblaze/Makefile.am
+++ b/firmware/microblaze/Makefile.am
@@ -23,4 +23,7 @@ EXTRA_DIST = \
u2_flash_tool
SUBDIRS = \
- usrp2
+ usrp2 \
+ usrp2p \
+ usrp2p/bootloader
+
diff --git a/firmware/microblaze/Makefile.common b/firmware/microblaze/Makefile.common
index ceb6a553a..4e726edab 100644
--- a/firmware/microblaze/Makefile.common
+++ b/firmware/microblaze/Makefile.common
@@ -59,8 +59,13 @@ COMMON_LFLAGS = \
########################################################################
# Common stuff for building top level microblaze images
########################################################################
+#we use COMMON_IHX_ARGS to relocate the reset and interrupt vectors to
+#just below the start of code. upon creating the BIN, any leading padding
+#is thrown out, so the .bin file is valid for uploading to USRP2P. this
+#does not affect USRP2 because the USRP2 already starts at 0x0000, and
+#because the relocate_args are not defined for USRP2's Makefile.am.
.elf.bin:
- $(MB_OBJCOPY) -O binary $< $@
+ $(MB_OBJCOPY) -O binary $(RELOCATE_ARGS) $< $@
.elf.dump:
$(MB_OBJDUMP) -DSC $< > $@
@@ -69,7 +74,7 @@ COMMON_LFLAGS = \
$(HEXDUMP) -v -e'1/1 "%.2X\n"' $< > $@
.elf.ihx:
- $(MB_OBJCOPY) -O ihex $(COMMON_IHX_ARGS) $< $@
+ $(MB_OBJCOPY) -O ihex $(RELOCATE_ARGS) $< $@
_generated_from_elf = \
$(noinst_PROGRAMS:.elf=.map) \
diff --git a/firmware/microblaze/apps/blinkenlights.c b/firmware/microblaze/apps/blinkenlights.c
new file mode 100644
index 000000000..4cebe5c9d
--- /dev/null
+++ b/firmware/microblaze/apps/blinkenlights.c
@@ -0,0 +1,26 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2010 Ettus Research LLC
+ *
+ */
+
+#include "memory_map.h"
+#include <nonstdio.h>
+
+int main(int argc, char *argv[]) {
+
+ uint32_t c = 0;
+ uint8_t i = 0;
+
+ while(1) {
+ //delay(5000000);
+ for(c=0;c<5000000;c++) asm("NOP");
+ output_regs->leds = (i++ % 2) ? 0xFF : 0x00; //blink everything on that register
+ }
+
+ return 0;
+}
+
+//void delay(uint32_t t) {
+// while(t-- != 0) asm("NOP");
+//}
diff --git a/firmware/microblaze/apps/flash_test.c b/firmware/microblaze/apps/flash_test.c
new file mode 100644
index 000000000..5b4569030
--- /dev/null
+++ b/firmware/microblaze/apps/flash_test.c
@@ -0,0 +1,67 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2010 Ettus Research LLC
+ *
+ */
+
+#include <memory_map.h>
+#include <hal_io.h>
+#include <hal_uart.h>
+#include <xilinx_s3_icap.h>
+#include <nonstdio.h>
+#include <spi_flash.h>
+#include <spi.h>
+#include <clocks.h>
+#include <string.h>
+
+//just a test to write to SPI flash and retrieve the same values.
+//uses the MOBFLEET SPI flash library
+
+void delay(uint32_t t) {
+ while(t-- != 0) asm("NOP");
+}
+
+int main(int argc, char *argv[]) {
+ uint16_t i, t;
+ uint8_t buf[260];
+ const uint8_t testdata[] = {0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C};
+
+ hal_disable_ints(); // In case we got here via jmp 0x0
+// spi_init();
+ hal_uart_init();
+// clocks_init(); //set up AD9510, enable FPGA clock @ 1x divisor
+
+ puts("SPI Flash test\n");
+ puts("Initializing SPI\n");
+
+ spif_init();
+ delay(800000);
+ puts("Erasing sector 1\n");
+ spi_flash_erase(0x00010000, 256);
+ delay(800000);
+ puts("Reading back data\n");
+ spi_flash_read(0x00010000, 256, buf);
+ delay(800000);
+
+ t=1;
+ for(i=4; i<250; i++) {
+ if(buf[i] != 0xFF) t=0;
+ }
+
+ if(!t) puts("Data was not initialized to 0xFF. Unsuccessful erase or read\n");
+ else puts("Data initialized to 0xFF, erase confirmed\n");
+
+ puts("Writing test buffer\n");
+ spi_flash_program(0x00010000, 16, testdata);
+ //memset(buf, 0, 256);
+
+ delay(800000);
+ puts("Wrote data, reading back\n");
+
+ spi_flash_read(0x00010000, 16, buf);
+
+ if(memcmp(testdata, buf, 16)) puts("Data is not the same between read and write. Unsuccessful write or read\n");
+ else puts("Successful write! Flash write correct\n");
+
+ return 0;
+}
diff --git a/firmware/microblaze/apps/hardware_testbed.c b/firmware/microblaze/apps/hardware_testbed.c
new file mode 100644
index 000000000..e68e68ff7
--- /dev/null
+++ b/firmware/microblaze/apps/hardware_testbed.c
@@ -0,0 +1,47 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2010 Ettus Research LLC
+ *
+ */
+
+#include <memory_map.h>
+#include <nonstdio.h>
+#include <hal_io.h>
+#include <xilinx_s3_icap.h>
+#include <spi_flash.h>
+//#include <spi_flash_private.h>
+#include <clocks.h>
+#include <ihex.h>
+#include <bootloader_utils.h>
+#include <string.h>
+#include <hal_uart.h>
+#include <spi.h>
+
+//so this is just an evolving file used to set up and test different bits of hardware (EEPROM, clock chip, A/D, D/A, PHY)
+void delay(uint32_t t) {
+ while(t-- != 0) asm("NOP");
+}
+
+int main(int argc, char *argv[]) {
+
+ hal_disable_ints();
+ hal_uart_init();
+ spi_init();
+
+ puts("Hardware testbed. Init clocks...");
+
+ clocks_init();
+
+ //now, hopefully, we should be running at 100MHz instead of 50MHz, meaning our UART is twice as fast and we're talking at 230400.
+
+ while(1) {
+ delay(500000);
+ puts("Eat at Joe's.");
+ }
+
+
+
+
+
+ return 0;
+}
diff --git a/firmware/microblaze/apps/txrx_uhd.c b/firmware/microblaze/apps/txrx_uhd.c
index 1dd6e80ac..44be2cc9c 100644
--- a/firmware/microblaze/apps/txrx_uhd.c
+++ b/firmware/microblaze/apps/txrx_uhd.c
@@ -45,6 +45,7 @@
#include <i2c.h>
#include <ethertype.h>
#include <arp_cache.h>
+#include "udp_fw_update.h"
/*
* Full duplex Tx and Rx between ethernet and DSP pipelines
@@ -346,6 +347,28 @@ void handle_udp_ctrl_packet(
send_udp_pkt(USRP2_UDP_CTRL_PORT, src, &ctrl_data_out, sizeof(ctrl_data_out));
break;
+ case USRP2_CTRL_ID_SO_LIKE_CAN_YOU_READ_THIS_UART_BRO:{
+ //executes a readline()-style read, up to num_bytes long, up to and including newline
+ int num_bytes = ctrl_data_in->data.uart_args.bytes;
+ if(num_bytes > 20) num_bytes = 20;
+ num_bytes = fngets_timeout(ctrl_data_in->data.uart_args.dev, (char *) ctrl_data_out.data.uart_args.data, num_bytes);
+ ctrl_data_out.id = USRP2_CTRL_ID_I_HELLA_READ_THAT_UART_DUDE;
+ ctrl_data_out.data.uart_args.bytes = num_bytes;
+ break;
+ }
+
+ case USRP2_CTRL_ID_HEY_WRITE_THIS_UART_FOR_ME_BRO:{
+ int num_bytes = ctrl_data_in->data.uart_args.bytes;
+ if(num_bytes > 20) num_bytes = 20;
+ //before we write to the UART, we flush the receive buffer
+ //this assumes that we're interested in the reply
+ hal_uart_rx_flush(ctrl_data_in->data.uart_args.dev);
+ fnputstr(ctrl_data_in->data.uart_args.dev, (char *) ctrl_data_in->data.uart_args.data, num_bytes);
+ ctrl_data_out.id = USRP2_CTRL_ID_MAN_I_TOTALLY_WROTE_THAT_UART_DUDE;
+ ctrl_data_out.data.uart_args.bytes = num_bytes;
+ break;
+ }
+
default:
ctrl_data_out.id = USRP2_CTRL_ID_HUH_WHAT;
send_udp_pkt(USRP2_UDP_CTRL_PORT, src, &ctrl_data_out, sizeof(ctrl_data_out));
@@ -487,6 +510,7 @@ main(void)
register_udp_listener(USRP2_UDP_CTRL_PORT, handle_udp_ctrl_packet);
register_udp_listener(USRP2_UDP_DATA_PORT, handle_udp_data_packet);
+ register_udp_listener(USRP2_UDP_UPDATE_PORT, handle_udp_fw_update_packet);
// initialize double buffering state machine for ethernet -> DSP Tx
diff --git a/firmware/microblaze/apps/uart_flash_loader.c b/firmware/microblaze/apps/uart_flash_loader.c
new file mode 100644
index 000000000..d67b84677
--- /dev/null
+++ b/firmware/microblaze/apps/uart_flash_loader.c
@@ -0,0 +1,169 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2010 Ettus Research LLC
+ *
+ */
+
+//#include <stdio.h>
+#include <stdlib.h>
+#include <memory_map.h>
+#include <nonstdio.h>
+#include <hal_io.h>
+#include <hal_uart.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>
+
+//uses UART to load files to Flash in Intel HEX 16-bit format.
+//this is one example of a "safe" firmware image to be loaded by a bootloader into main RAM.
+//this CANNOT write to main RAM, since it is resident there.
+
+//Sector 0-31: Safe FPGA bootloader image
+//Sector 32-63: Production bootloader image
+//Sector 64: Production firmware image
+//Sector 127: Safe firmware image
+
+
+void uart_flash_loader(void) {
+
+ char buf[256]; //input data buffer
+ uint8_t ihx[32]; //ihex data buffer
+ uint32_t slot_offset = PROD_FW_IMAGE_LOCATION_ADDR; //initial slot offset to program to.
+ uint32_t extended_addr = 0x00000000; //extended Intel hex segment address
+
+ size_t sector_size = spi_flash_log2_sector_size();
+ ihex_record_t ihex_record;
+ ihex_record.data = ihx;
+ int i;
+
+
+ //not gonna win a turing prize for my C text parsing
+ while(1) {
+ gets(buf);
+ if(!strncmp(buf, "!SECTORSIZE", 7)) { //return the sector size in log format
+ putstr("OK ");
+ puthex8((uint32_t) sector_size); //err, this should probably be decimal for human readability. we do have itoa now...
+ putstr("\n");
+ }
+ else if(!strncmp(buf, "!SETADDR", 7)) { //set start address for programming
+ slot_offset = atol(&buf[8]);
+ puts("OK");
+// puthex32(slot_offset);
+// putstr("\n");
+ }
+ else if(!strncmp(buf, "!ERASE", 6)) { //erase a sector
+ uint32_t sector = atol(&buf[6]);
+ uint32_t size = 2 << (sector_size-1);
+ uint32_t addr = sector << sector_size;
+
+ spi_flash_erase(addr, size); //we DO NOT implement write protection here. it is up to the HOST PROGRAM to not issue an ERASE unless it means it.
+ //unfortunately the Flash cannot write-protect the segments that really matter, so we only use global write-protect
+ //as a means of avoiding accidental writes from runaway code / garbage on the SPI bus.
+ puts("OK");
+ }
+//can't exactly run firmware if you're already executing out of main RAM
+/* else if(!strncmp(buf, "!RUNSFW", 7)) {
+ if(is_valid_fw_image(SAFE_FW_IMAGE_LOCATION_ADDR)) {
+ puts("OK");
+ spi_flash_read(SAFE_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES, (void *)RAM_BASE);
+ start_program(RAM_BASE);
+ } else {
+ puts("NOK");
+ }
+ }
+ else if(!strncmp(buf, "!RUNPFW", 7)) {
+ if(is_valid_fw_image(PROD_FW_IMAGE_LOCATION_ADDR)) {
+ puts("OK");
+ spi_flash_read(PROD_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES-1, (void *)RAM_BASE);
+ start_program(RAM_BASE);
+ } else {
+ puts("NOK");
+ }
+ }
+*/
+ else if(!strncmp(buf, "!RUNPFPGA", 8)) {
+ if(is_valid_fpga_image(PROD_FPGA_IMAGE_LOCATION_ADDR)) {
+ puts("OK");
+ //icap_reload_fpga(PROD_FPGA_IMAGE_LOCATION_ADDR);
+ } else {
+ puts("NOK");
+ }
+ }
+ else if(!strncmp(buf, "!RUNSFPGA", 8)) {
+ if(is_valid_fpga_image(SAFE_FPGA_IMAGE_LOCATION_ADDR)) {
+ puts("OK");
+ //icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR);
+ } else {
+ puts("NOK");
+ }
+ }
+ else if(!strncmp(buf, "!READ", 5)) {
+ uint32_t addr = atol(&buf[5]);
+ spi_flash_read(addr, 16, ihx);
+ for(i=0; i < 16; i++) {
+ puthex8(ihx[i]);
+ }
+ putstr("\n");
+ }
+
+ else if(!ihex_parse(buf, &ihex_record)) { //last, try to see if the input was a valid IHEX line
+ switch (ihex_record.type) {
+ case 0:
+ spi_flash_program(ihex_record.addr + slot_offset + extended_addr, ihex_record.length, ihex_record.data);
+ puts("OK");
+ break;
+ case 1:
+ //here we would expect a CRC checking or something else to take place. for now we do nothing.
+ //well, we set the extended segment addr back to 0
+ extended_addr = 0;
+ puts("DONE");
+ break;
+ case 4:
+ //set the upper 16 bits of the address
+ extended_addr = ((ihex_record.data[0] << 8) + ihex_record.data[1]) << 16;
+ puts("OK");
+ break;
+ default:
+ puts("NOK");
+ }
+ }
+ else puts("NOK");
+ } //while(1)
+}
+
+void delay(uint32_t t) {
+ while(t-- != 0) asm("NOP");
+}
+
+int main(int argc, char *argv[]) {
+ uint8_t buf[32];
+ int i = 0;
+
+ hal_disable_ints(); // In case we got here via jmp 0x0
+
+// delay(10000000);
+
+ //before anything else you might want to blinkenlights just to indicate code startup to the user.
+
+ hal_uart_init();
+ spif_init();
+// i2c_init(); //for EEPROM
+ puts("USRP2+ UART firmware loader");
+
+// puts("Debug: loading production image, 10 bytes.");
+
+// spi_flash_read(PROD_FW_IMAGE_LOCATION_ADDR, 10, buf);
+// for(i=0; i < 10; i++) {
+// puthex8(buf[i]);
+// }
+
+ uart_flash_loader();
+
+ //shouldn't get here. should reboot.
+ puts("shit happened.\n");
+
+ return 0;
+}
diff --git a/firmware/microblaze/bin/bin_to_mif.py b/firmware/microblaze/bin/bin_to_mif.py
new file mode 100755
index 000000000..cefce4e92
--- /dev/null
+++ b/firmware/microblaze/bin/bin_to_mif.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+
+import struct
+import sys
+
+hextab = ('0000', '0001', '0010', '0011',
+ '0100', '0101', '0110', '0111',
+ '1000', '1001', '1010', '1011',
+ '1100', '1101', '1110', '1111')
+
+def w_to_binary_ascii(w):
+ return ''.join([hextab[(w >> 4*i) & 0xf] for i in range(7,-1,-1)])
+
+def bin_to_mif(bin_input_file, mif_output_file):
+ ifile = open(bin_input_file, 'rb')
+ ofile = open(mif_output_file, 'w')
+ idata = ifile.read()
+ fmt = ">%dI" % ((len(idata) / 4),)
+ words = struct.unpack(fmt, idata)
+ for w in words:
+ ofile.write(w_to_binary_ascii(w))
+ ofile.write('\n')
+
+if __name__ == '__main__':
+ if len(sys.argv) != 3:
+ sys.stderr.write("usage: bin_to_mif bin_input_file mif_output_file\n")
+ raise SystemExit, 1
+
+ bin_to_mif(sys.argv[1], sys.argv[2])
diff --git a/firmware/microblaze/bin/bin_to_ram_macro_init.py b/firmware/microblaze/bin/bin_to_ram_macro_init.py
new file mode 100755
index 000000000..65cf2dbdf
--- /dev/null
+++ b/firmware/microblaze/bin/bin_to_ram_macro_init.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+
+import struct
+import sys
+
+def do_8_words(ofile, which_ram, row, words):
+ ofile.write("defparam bootram.RAM%d.INIT_%02X=256'h" % (which_ram, row))
+ ofile.write("%08x_%08x_%08x_%08x_%08x_%08x_%08x_%08x;\n" % (
+ words[7], words[6], words[5], words[4], words[3], words[2], words[1], words[0]))
+
+def bin_to_ram_macro_init(bin_input_file, ram_init_output_file):
+ ifile = open(bin_input_file, 'rb')
+ ofile = open(ram_init_output_file, 'w')
+ idata = ifile.read()
+ fmt = ">%dI" % ((len(idata) / 4),)
+ words = struct.unpack(fmt, idata)
+
+ # pad to a multiple of 8 words
+ r = len(words) % 8
+ if r != 0:
+ words += (8 - r) * (0,)
+
+ if len(words) > 2048:
+ sys.stderr.write("bin_to_macro_init: error: input file %s is > 8KiB\n" % (bin_input_file,))
+ sys.exit(1)
+
+ # first 2KB
+ for i in range(0, min(512, len(words)), 8):
+ do_8_words(ofile, 0, i/8, words[i:i+8])
+
+ # second 2KB
+ for i in range(512, min(1024, len(words)), 8):
+ do_8_words(ofile, 1, (i/8) % 64, words[i:i+8])
+
+ # third 2KB
+ for i in range(1024, min(1536, len(words)), 8):
+ do_8_words(ofile, 2, (i/8) % 64, words[i:i+8])
+
+ # last 2KB
+ for i in range(1536, len(words), 8):
+ do_8_words(ofile, 3, (i/8) % 64, words[i:i+8])
+
+if __name__ == '__main__':
+ if len(sys.argv) != 3:
+ sys.stderr.write("usage: bin_to_ram_macro_init bin_input_file ram_init_output_file\n")
+ sys.exit(1)
+
+ bin_to_ram_macro_init(sys.argv[1], sys.argv[2])
diff --git a/firmware/microblaze/bin/elf_to_sbf b/firmware/microblaze/bin/elf_to_sbf
new file mode 100755
index 000000000..d1be10c0d
--- /dev/null
+++ b/firmware/microblaze/bin/elf_to_sbf
@@ -0,0 +1,142 @@
+#!/usr/bin/env python
+#
+# Copyright 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/>.
+#
+
+import sys
+import struct
+from optparse import OptionParser
+from pprint import pprint
+
+import sbf
+
+# see /usr/include/elf.h for the various magic values
+
+
+_ehdr_fmt = ">16sHH5I6H"
+_ehdr_fmt_size = struct.calcsize(_ehdr_fmt)
+_phdr_fmt = ">8I"
+_phdr_fmt_size = struct.calcsize(_phdr_fmt)
+
+class elf32_ehdr(object):
+ def __init__(self, s):
+ (self.ident, self.type, self.machine, self.version, self.entry,
+ self.phoff, self.shoff, self.flags, self.ehsize,
+ self.phentsize, self.phnum, self.shentsize, self.shnum,
+ self.shstrndx) = struct.unpack(_ehdr_fmt, s)
+
+class elf32_phdr(object):
+ def __init__(self, s):
+ (self.type, self.offset, self.vaddr, self.paddr,
+ self.filesz, self.memsz,
+ self.flags, self.align) = struct.unpack(_phdr_fmt, s)
+
+ def __repr__(self):
+ return "<elf32_phdr %s offset=%d paddr=0x%x, filesz=%d>" % (
+ p_type_str(self.type), self.offset, self.paddr, self.filesz)
+
+
+def p_type_str(t):
+ if t <= 8:
+ return ('NULL', 'LOAD', 'DYNAMIC', 'INTERP', 'NOTE', 'SHLIB', 'PHDR', 'TLS', 'NUM')[t]
+ return "0x%x" % (t,)
+
+
+
+def _get_ehdr(f):
+ if len(f) < _ehdr_fmt_size:
+ return False
+ ehdr = elf32_ehdr(f[0:_ehdr_fmt_size])
+ return ehdr
+
+
+def elf32_big_endian_exec_p(f):
+ ehdr = _get_ehdr(f)
+ if not ehdr:
+ return False
+
+ #pprint(ehdr, sys.stderr)
+ e_ident = ehdr.ident
+ if not e_ident.startswith('\177ELF'):
+ return False
+ if (ord(e_ident[4]) != 1 # EI_CLASS == CLASS32
+ or ord(e_ident[5]) != 2 # EI_DATA == DATA2MSB
+ or ord(e_ident[6]) != 1 # EI_VERSION == EV_CURRENT
+ ):
+ return False
+
+ if ehdr.type != 2: # e_type == ET_EXEC
+ return False
+
+ return True
+
+
+
+# return (entry, (phdr, ...))
+
+def get_elf32_prog_headers(f):
+ ehdr = _get_ehdr(f)
+ entry = ehdr.entry
+ phoff = ehdr.phoff
+ phentsize = ehdr.phentsize
+ phnum = ehdr.phnum
+
+ def extract(i):
+ start = phoff + i * phentsize
+ end = start + phentsize
+ return f[start:end]
+
+ return (entry, [elf32_phdr(extract(i)) for i in range(phnum)])
+
+
+def main():
+ usage = "%prog: [options] elf_file"
+ parser = OptionParser()
+ parser.add_option("-o", "--output", default=None,
+ help="specify output filename [default=stdout]")
+ (options, args) = parser.parse_args()
+ if len(args) != 1:
+ parser.print_help()
+ sys.exit(1)
+
+ elf_file = open(args[0], 'rb')
+
+ elf_contents = elf_file.read()
+ if not elf32_big_endian_exec_p(elf_contents):
+ sys.stderr.write("%s: not a big-endian 32-bit ELF executable\n" % (args[0],))
+ sys.exit(1)
+
+ if options.output is None:
+ sbf_file = sys.stdout
+ else:
+ sbf_file = open(options.output, 'wb')
+
+ (entry, phdrs) = get_elf32_prog_headers(elf_contents)
+ #pprint(phdrs, sys.stderr)
+
+ def phdr_to_sec_desc(phdr):
+ target_addr = phdr.paddr
+ data = elf_contents[phdr.offset:phdr.offset+phdr.filesz]
+ #print >>sys.stderr, "pdhr_to_sec_desc:", (target_addr, data)
+ return sbf.sec_desc(target_addr, data)
+
+ sections = map(phdr_to_sec_desc, phdrs)
+ # pprint(sections, sys.stderr)
+ sbf.write_sbf(sbf_file, sbf.header(entry, sections))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/firmware/microblaze/bin/sbf.py b/firmware/microblaze/bin/sbf.py
new file mode 100644
index 000000000..8e2c868a5
--- /dev/null
+++ b/firmware/microblaze/bin/sbf.py
@@ -0,0 +1,134 @@
+#
+# Copyright 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/>.
+#
+
+_SBF_MAGIC = 'SBF!'
+_SBF_DONT_EXECUTE = 0x1
+_SBF_MAX_SECTIONS = 14
+_SBF_HEADER_LEN = 128
+
+import struct
+import sys
+from pprint import pprint
+
+def dump_data(f, offset, data):
+ L = len(data) // 4
+ for i in range(L):
+ f.write('%08x: %08x\n' % (offset + 4 * i, struct.unpack('>I', data[4*i:4*(i+1)])[0]))
+ remainder = len(data) - L * 4
+ if remainder != 0:
+ f.write('%08x: ' % (offset + L*4,))
+ i = 0
+ while i < remainder:
+ f.write('%02x' % ((ord(data[L*4 + i]),)))
+ i += 1
+ f.write('\n')
+
+
+
+class sec_desc(object):
+ def __init__(self, target_addr, data):
+ self.target_addr = target_addr
+ self.data = data
+
+ def __repr__(self):
+ #print >>sys.stderr, "target_addr:", self.target_addr
+ #print >>sys.stderr, "data:", self.data
+ return "<sec_desc target_addr=0x%x len=%d>" % (
+ self.target_addr, len(self.data))
+
+
+class header(object):
+ def __init__(self, entry, sections):
+ self.entry = entry
+ self.section = sections
+
+ def dump(self, f):
+ if self.entry == _SBF_DONT_EXECUTE:
+ f.write("Entry: DONT_EXECUTE\n")
+ else:
+ f.write("Entry: 0x%x\n" % (self.entry,))
+ for i in range(len(self.section)):
+ s = self.section[i]
+ f.write("Section[%d]: target_addr = 0x%x length = %d\n" % (i,
+ s.target_addr,
+ len(s.data)))
+ dump_data(f, s.target_addr, s.data)
+
+ #
+ # Returns an iterator. Each yield returns (target_addr, data)
+ #
+ def iterator(self, max_piece=512):
+ for s in self.section:
+ offset = 0
+ L = len(s.data)
+ while offset < L:
+ n = min(max_piece, L - offset)
+ yield (s.target_addr + offset,
+ s.data[offset:offset+n])
+ offset += n
+
+
+
+def read_sbf(input_file):
+ """Parse an SBF file"""
+
+ f = input_file.read(_SBF_HEADER_LEN)
+ #if len(f) < _SBF_HEADER_LEN or not f.startswith(_SBF_MAGIC):
+ #raise ValueError, '%s: not an SBF file' % (input_file.name,)
+
+ def extract(i):
+ start = 16+8*i
+ stop = start+8
+ return struct.unpack('>2I', f[start:stop])
+
+ def get_data(ss):
+ L = ss[1]
+ s = input_file.read(L)
+ #if len(s) != L:
+ #raise ValueError, '%s: file is too short' % (input_file.name(),)
+ return s
+
+ (magic, entry, nsections, reserved) = struct.unpack('>4s3I', f[0:16])
+ assert nsections <= _SBF_MAX_SECTIONS
+ descs = [extract(i) for i in range(nsections)]
+ #pprint(descs, sys.stderr)
+ data = map(get_data, descs)
+ secs = map(lambda ss, data: sec_desc(ss[0], data), descs, data)
+ return header(entry, secs)
+
+
+def write_sbf(output_file, sbf_header):
+ assert(len(sbf_header.section) <= _SBF_MAX_SECTIONS)
+ sbf_header.nsections = len(sbf_header.section)
+ f = output_file
+
+ # write the file header
+ f.write(struct.pack('>4s3I', _SBF_MAGIC, sbf_header.entry, sbf_header.nsections, 0))
+
+ # write the section headers
+ for i in range(sbf_header.nsections):
+ f.write(struct.pack('>2I',
+ sbf_header.section[i].target_addr,
+ len(sbf_header.section[i].data)))
+ for i in range(_SBF_MAX_SECTIONS - sbf_header.nsections):
+ f.write(struct.pack('>2I', 0, 0))
+
+ # write the section data
+ for i in range(sbf_header.nsections):
+ f.write(sbf_header.section[i].data)
+
+ return True
diff --git a/firmware/microblaze/bin/serial_loader b/firmware/microblaze/bin/serial_loader
new file mode 100755
index 000000000..9bd5aada7
--- /dev/null
+++ b/firmware/microblaze/bin/serial_loader
@@ -0,0 +1,363 @@
+#!/usr/bin/env python
+#
+# Copyright 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/>.
+#
+
+import termios
+import tty
+import os
+import sys
+import threading
+import Queue
+from optparse import OptionParser
+import time
+
+import sbf
+
+GDB_ESCAPE = chr(0x7d)
+
+# Indexes for termios list.
+IFLAG = 0
+OFLAG = 1
+CFLAG = 2
+LFLAG = 3
+ISPEED = 4
+OSPEED = 5
+CC = 6
+
+class terminal(object):
+ def __init__(self, device, speed_bits):
+ fd = os.open(device, os.O_RDWR)
+ if not os.isatty(fd):
+ raise ValueError(device + " is not a tty")
+
+ self.read_file = os.fdopen(fd, "rb", 0)
+ self.write_file = os.fdopen(os.dup(fd), "wb", 0)
+ self.old_attrs = termios.tcgetattr(self.write_file.fileno())
+ #print "old_attrs: ", self.old_attrs
+ attrs = list(self.old_attrs) # copy of attributes
+ attrs[ISPEED] = speed_bits # set input and output speed
+ attrs[OSPEED] = speed_bits
+ termios.tcsetattr(self.write_file.fileno(), termios.TCSAFLUSH, attrs)
+ tty.setraw(self.write_file.fileno()) # enable raw mode
+ attrs = termios.tcgetattr(self.write_file.fileno())
+ attrs[CC][termios.VMIN] = 1 # minimim of 1 char
+ attrs[CC][termios.VTIME] = 1 # wait no longer than 1/10
+ termios.tcsetattr(self.write_file.fileno(), termios.TCSAFLUSH, attrs)
+
+ def __del__(self):
+ termios.tcsetattr(self.write_file.fileno(), termios.TCSAFLUSH, self.old_attrs)
+ self.read_file.close()
+ self.write_file.close()
+
+ def read(self, n):
+ """Read at most n bytes from tty"""
+ return self.read_file.read(n)
+
+ def write(self, str):
+ """Write str to tty."""
+ return self.write_file.write(str)
+
+
+def hexnibble(i):
+ return "0123456789abcdef"[i & 0xf]
+
+def build_pkt(payload):
+ s = ['$']
+ checksum = 0
+
+ for p in payload:
+ if p in ('$', '#', GDB_ESCAPE):
+ s.append(GDB_ESCAPE)
+ s.append(p)
+ checksum += ord(p)
+
+ checksum &= 0xff
+ s.append('#')
+ s.append(hexnibble(checksum >> 4))
+ s.append(hexnibble(checksum))
+
+ return ''.join(s)
+
+
+def build_memory_read_pkt(addr, len):
+ return build_pkt(('m%x,%x' % (addr, len)))
+
+def build_memory_write_hex_pkt(addr, s):
+ hexdata = ''.join(["%02x" % (ord(c),) for c in s])
+ return build_pkt(('M%x,%x:' % (addr, len(s))) + hexdata)
+
+def build_memory_write_pkt(addr, s):
+ return build_pkt(('X%x,%x:' % (addr, len(s))) + s)
+
+def build_goto_pkt(addr):
+ return build_pkt(('c%x' % (addr,)))
+
+
+def get_packet(f):
+ """Return a valid packet, or None on EOF or timeout"""
+ LOOKING_FOR_DOLLAR = 0
+ LOOKING_FOR_HASH = 1
+ CSUM1 = 2
+ CSUM2 = 3
+
+ fd = f.fileno()
+
+ state = LOOKING_FOR_DOLLAR
+ buf = []
+
+ while True:
+ ch = os.read(fd, 1)
+ sys.stdout.write(ch)
+ if len(ch) == 0:
+ print("Returning None")
+ return(None)
+
+ if state == LOOKING_FOR_DOLLAR:
+ if ch == '$':
+ buf = []
+ state = LOOKING_FOR_HASH
+ elif ch == '#':
+ state = LOOKING_FOR_DOLLAR
+
+ elif state == LOOKING_FOR_HASH:
+ if ch == '$':
+ state = LOOKING_FOR_DOLLAR
+ elif ch == '#':
+ state = CSUM1
+ else:
+ if ch == GDB_ESCAPE:
+ ch = getc()
+ buf.append(ch)
+
+ elif state == CSUM1:
+ chksum1 = ch
+ state = CSUM2
+
+ elif state == CSUM2:
+ chksum2 = ch
+ r = ''.join(buf)
+ if chksum1 == '.' and chksum2 == '.':
+ return r
+
+ expected_checksum = int(chksum1 + chksum2, 16)
+ checksum = 0
+ for c in buf:
+ checksum += ord(c)
+
+ checksum &= 0xff
+ if checksum == expected_checksum:
+ return r
+
+ state = LOOKING_FOR_DOLLAR
+
+ else:
+ raise ValueError( "Invalid state")
+
+
+class packet_reader_thread(threading.Thread):
+ def __init__(self, tty_in, q):
+ threading.Thread.__init__(self)
+ self.setDaemon(1)
+ self.tty_in = tty_in
+ self.q = q
+ self._keep_running = True
+ self.start()
+
+ def run(self):
+ while self._keep_running == True:
+ p = get_packet(self.tty_in)
+ if p is not None:
+ self.q.put(('pkt', p))
+
+
+def _make_tr_table():
+ table = []
+ for c in range(256):
+ if c < ord(' ') or c > ord('~'):
+ table.append('.')
+ else:
+ table.append(chr(c))
+ return ''.join(table)
+
+
+class controller(object):
+ def __init__(self, tty):
+ self.tty = tty
+ self.q = Queue.Queue(0)
+ self.timers = {}
+ self.next_tid = 1
+ self.current_tid = 0
+ self.ntimeouts = 0
+ self.packet_reader = packet_reader_thread(tty.read_file, self.q)
+ self.state = None
+ self.debug = False
+ self.tt = _make_tr_table()
+
+ self.done = False
+ self.addr = None
+ self.bits = None
+
+ def shutdown(self):
+ self.packet_reader._keep_running = False
+
+ def start_timeout(self, timeout_in_secs):
+ def callback(tid):
+ if self.timers.has_key(tid):
+ del self.timers[tid]
+ self.q.put(('timeout', tid))
+ self.next_tid += 1
+ tid = self.next_tid
+ timer = threading.Timer(timeout_in_secs, callback, (tid,))
+ self.timers[tid] = timer
+ timer.start()
+ return tid
+
+ def cancel_timeout(self, tid):
+ if self.timers.has_key(tid):
+ self.timers[tid].cancel()
+ del self.timers[tid]
+
+ def send_packet(self, pkt):
+ if self.debug:
+ if len(pkt) > 64:
+ s = pkt[0:64] + '...'
+ else:
+ s = pkt
+ sys.stdout.write('-> ' + s.translate(self.tt) + '\n')
+ self.tty.write(pkt);
+
+ def send_packet_start_timeout(self, pkt, secs):
+ self.send_packet(pkt)
+ self.current_tid = self.start_timeout(secs)
+
+ def upload_code(self, sbf):
+ MAX_PIECE = 512 # biggest piece to send
+ MWRITE_TIMEOUT = 0.1
+
+ IDLE = 0
+ WAIT_FOR_ACK = 1
+ UPLOAD_DONE = 2
+ DONE = 3
+ FAILED = 4
+
+ self.done = False
+ it = sbf.iterator(MAX_PIECE)
+ entry_addr = sbf.entry
+
+ def get_next_bits():
+ try:
+ (self.addr, self.bits) = it.next()
+ except StopIteration:
+ self.done = True
+
+ def is_done():
+ return self.done
+
+ def send_piece():
+ pkt = build_memory_write_pkt(self.addr, self.bits)
+ #pkt = build_memory_write_hex_pkt(self.addr, self.bits)
+ self.send_packet_start_timeout(pkt, MWRITE_TIMEOUT)
+ state = WAIT_FOR_ACK
+
+ def advance():
+ get_next_bits()
+ if is_done():
+ self.state = DONE
+ self.send_packet(build_goto_pkt(entry_addr))
+
+ else:
+ self.ntimeouts = 0
+ send_piece()
+
+ get_next_bits()
+ if is_done(): # empty file
+ return True
+
+ send_piece() # initial transition
+
+ while 1:
+ (event, value) = self.q.get()
+
+ if event == 'timeout' and value == self.current_tid:
+ self.ntimeouts += 1
+ if self.ntimeouts >= 5:
+ return False # say we failed
+ send_piece() # resend
+
+
+ elif event == 'pkt':
+ if value == 'OK':
+ self.cancel_timeout(self.current_tid)
+ advance()
+ if self.state == DONE:
+ return True
+ else:
+ print("Error returned from firmware: " + value)
+ return False
+
+ else:
+ print("Unknown event:", (event, value))
+
+def main():
+ usage="%prog: [options] filename"
+ parser = OptionParser(usage=usage)
+ parser.add_option("-t", "--tty", type="string", default="/dev/ttyS0",
+ help="select serial port [default=%default]")
+
+ (options, args) = parser.parse_args()
+ if len(args) != 1:
+ parser.print_help()
+ raise SystemExit(1)
+
+
+ filename = args[0]
+ f = open(filename, "rb")
+ try:
+ # Try to open the file as an SBF file
+ sbf_header = sbf.read_sbf(f)
+ except:
+ # If that fails, build an SBF from the binary, assuming default
+ # load address and entry point
+ f.seek(0)
+ t = f.read()
+ if t.startswith('\177ELF'):
+ sys.stderr.write("Can't load an ELF file. Please use an SBF file instead.\n")
+ raise SystemExit( 1)
+ sbf_header = sbf.header(0x8000, [sbf.sec_desc(0x8000, t)])
+
+
+ tty = terminal(options.tty, termios.B115200)
+ ctrl = controller(tty)
+ ok = ctrl.upload_code(sbf_header)
+
+ if ok:
+ print("OK")
+ try:
+ raw_input("Press Enter to exit: ")
+ except KeyboardInterrupt:
+ pass
+ ctrl.shutdown()
+ time.sleep(0.2)
+
+ else:
+ print("Upload failed")
+ ctrl.shutdown()
+
+
+
+if __name__ == "__main__":
+ main()
diff --git a/firmware/microblaze/bin/uart_ihex_flash_loader.py b/firmware/microblaze/bin/uart_ihex_flash_loader.py
new file mode 100755
index 000000000..5a3300f34
--- /dev/null
+++ b/firmware/microblaze/bin/uart_ihex_flash_loader.py
@@ -0,0 +1,138 @@
+#!/usr/bin/python
+
+import serial
+from optparse import OptionParser
+import os, sys
+
+#TODO: pull everything but parser out of main() and put it in a separate function we can call from another script. lets us automate loading RAM+FLASH to produce a fully-loaded image.
+#TODO: make it fail gracefully -- if it gets a NOK or times out, do at least one retry.
+#TODO: put hooks in (eventually) to allow verifying a firmware image so the user can more safely update the "safe" image
+#TODO: how about a progress indicator? FPGA images take FOREVER. you can use wc -l to get the number of lines, or do it with file i/o.
+
+def main():
+ usage="%prog: [options] filename"
+ parser = OptionParser(usage=usage)
+ parser.add_option("-t", "--tty", type="string", default="/dev/ttyUSB0",
+ help="select serial port [default=%default]")
+ parser.add_option("-b", "--baudrate", type=int, default=115200,
+ help="set baudrate [default=%default]")
+ parser.add_option("-F", "--write-safe-firmware", action="store_const", const=1, dest="image",
+ help="write to safe firmware image")
+ parser.add_option("-f", "--write-production-firmware", action="store_const", const=2, dest="image",
+ help="write to production firmware image")
+ parser.add_option("-P", "--write-safe-fpga", action="store_const", const=3, dest="image",
+ help="write to safe FPGA image")
+ parser.add_option("-p", "--write-production-fpga", action="store_const", const=4, dest="image",
+ help="write to production FPGA image")
+
+ (options, args) = parser.parse_args()
+
+ if(options.image is None):
+ print("At least one of -f, -F, -p, -P must be specified.\n")
+ parser.print_help()
+ raise SystemExit(1)
+
+ if len(args) != 1:
+ parser.print_help()
+ raise SystemExit(1)
+
+ if(options.image == 3):
+ print "Are you *really* sure you want to write to the failsafe FPGA image? If you mess this up your USRP2+ will become a brick. Press 'y' to continue, any other key to abort."
+ if(raw_input().rstrip() is not "y"):
+ print "Good choice."
+ raise SystemExit(0)
+
+ elif(options.image == 1):
+ print "Are you *really* sure you want to write to the failsafe firmware image? If you mess this up your USRP2+ will only be able to be reprogrammed via the UART RAM loader.\nPress 'y' to continue, any other key to abort."
+ if(raw_input().rstrip() is not "y"):
+ print "Good choice."
+ raise SystemExit(0)
+
+ filename = args[0]
+ f = open(filename, "r")
+
+ #now we start doing things...
+ if(os.path.exists(options.tty) is False):
+ sys.stderr.write("No serial port found at %s\n" % options.tty)
+ raise SystemExit(1)
+
+ try:
+ ser = serial.Serial(port=options.tty, timeout=1, baudrate=options.baudrate, bytesize=8, parity=serial.PARITY_NONE, stopbits=1, rtscts=0, xonxoff=0)
+ except serial.SerialException:
+ sys.stderr.write("Unable to open serial port\n")
+ raise SystemExit(1)
+
+ ser.open()
+
+#test to see if a valid USRP2+ in flash load mode is connected
+ ser.write("garbage\n")
+ ser.readline()
+ ser.write("!SECTORSIZE\n")
+ reply = ser.readline().rstrip()
+ if("NOK" in reply):
+ sys.stderr.write("Error writing to USRP2+. Try again.\n")
+ raise SystemExit(1)
+ elif("OK" in reply):
+ sectorsize = int(reply[3:5], 16)
+ print("USRP2+ found with sector size %i. Erasing old image." % 2**sectorsize)
+ else:
+ sys.stderr.write("Invalid response or no USRP2+ connected.\n")
+ raise SystemExit(1)
+
+ if(options.image == 1):
+ sectors = range(127, 128)
+ runcmd = "!RUNSFD\n"
+ elif(options.image == 2):
+ sectors = range(64, 65)
+ runcmd = "!RUNPFD\n"
+ elif(options.image == 3):
+ sectors = range(0,32)
+ runcmd = "!RUNSFPGA\n"
+ elif(options.image == 4):
+ sectors = range(32,64)
+ runcmd = "!RUNPFPGA\n"
+
+ writeaddr = sectors[0] << sectorsize
+ if(options.image < 3):
+ writeaddr -= 0x8000 #i know this is awkward, but we subtract 0x8000 from the address for firmware loads. the reason we do this is that the IHX files are located at 0x8000 (RAM_BASE), and
+ #doing this here allows us to use the same IHX files for RAM load as for Flash load, without relocating in objcopy or relinking with another ld script.
+ #this limits us to writing above 32K for our firmware images. since the safe FPGA image located at 0x00000000 takes up 2MB of space this isn't really a worry.
+ #FPGA images (.mcs) always start at 0x00000000 so they don't need this relocation.
+
+ for sector in sectors:
+ print "Erasing sector %i" % sector
+ ser.write("!ERASE %i\n" % sector)
+ reply = ser.readline()
+ if("NOK" in reply):
+ sys.stderr.write("Error erasing sector %i" % sector)
+ raise SystemExit(1)
+
+ print "Setting start address to %i" % writeaddr
+ ser.write("!SETADDR %i\n" % writeaddr)
+ if("NOK" in reply):
+ sys.stderr.write("Error setting address\n")
+ raise SystemExit(1)
+ else:
+ print reply
+
+
+ for line in f:
+ ser.write(line.rstrip()+'\n')
+ reply = ser.readline()
+ if("NOK" in reply): #TODO: simplify this logic, use (reply.rstrip() is "NOK")
+ print("Received NOK reply during data write")
+ raise SystemExit(1)
+ elif("DONE" in reply):
+ print("Finished writing program. Loading...\n")
+ ser.write(runcmd)
+ elif("OK" not in reply):
+ print("Received invalid reply %s during data write" % reply)
+ raise SystemExit(1)
+ else:
+ print reply.rstrip() + '\t' + line.rstrip()
+
+ print ser.readline()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/firmware/microblaze/bin/uart_ihex_ram_loader.py b/firmware/microblaze/bin/uart_ihex_ram_loader.py
new file mode 100755
index 000000000..c90fbe1d8
--- /dev/null
+++ b/firmware/microblaze/bin/uart_ihex_ram_loader.py
@@ -0,0 +1,70 @@
+#!/usr/bin/python
+
+import serial
+from optparse import OptionParser
+import os, sys
+
+def main():
+ usage="%prog: [options] filename"
+ parser = OptionParser(usage=usage)
+ parser.add_option("-t", "--tty", type="string", default="/dev/ttyUSB0",
+ help="select serial port [default=%default]")
+ parser.add_option("-b", "--baudrate", type=int, default=115200,
+ help="set baudrate [default=%default]")
+
+ (options, args) = parser.parse_args()
+ if len(args) != 1:
+ parser.print_help()
+ raise SystemExit(1)
+
+ filename = args[0]
+ f = open(filename, "r")
+
+ #all we have to do is load the IHX file and attempt to spit it out to the serial port.
+ if(os.path.exists(options.tty) is False):
+ sys.stderr.write("No serial port found at %s\n" % options.tty)
+ raise SystemExit(1)
+
+ try:
+ ser = serial.Serial(port=options.tty, timeout=1, baudrate=options.baudrate, bytesize=8, parity=serial.PARITY_NONE, stopbits=1, rtscts=0, xonxoff=0)
+ except serial.SerialException:
+ sys.stderr.write("Unable to open serial port\n")
+ raise SystemExit(1)
+
+ ser.open()
+
+#test to see if a valid USRP2+ in RAM load mode is connected
+
+ ser.write("WOOOOO\n");
+ reply = ser.readline()
+ if("NOK" not in reply):
+ sys.stderr.write("Valid USRP2+ not connected or no response received\n")
+ raise SystemExit(1)
+ else:
+ print("USRP2+ found.")
+
+ for line in f:
+ ser.write(line.rstrip() + '\n')
+ reply = ser.readline()
+ if("NOK" in reply): #blocks to wait for response
+ print("Received NOK reply from USRP2+")
+ raise SystemExit(1)
+ elif("OK" not in reply):
+ print("Received invalid reply!")
+ raise SystemExit(1)
+# else:
+# print("OK received")
+
+ print "USRP2+ RAM programmed.\nLoading program."
+
+ #at this point it should have sent the end line of the file, which starts the program!
+ #we'll just act like a dumb terminal now
+# ser.timeout = 0
+# try:
+# while 1:
+# print ser.readline()
+# except KeyboardInterrupt:
+# raise SystemExit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/firmware/microblaze/bootstrap b/firmware/microblaze/bootstrap
index 5786c1624..26987b0ec 100755
--- a/firmware/microblaze/bootstrap
+++ b/firmware/microblaze/bootstrap
@@ -18,6 +18,7 @@
rm -rf *.cache
rm -rf libusrp2/.deps
+rm -rf libusrp2p/.deps
aclocal
autoconf
diff --git a/firmware/microblaze/configure.ac b/firmware/microblaze/configure.ac
index e27bcb557..f6986f2dd 100644
--- a/firmware/microblaze/configure.ac
+++ b/firmware/microblaze/configure.ac
@@ -47,6 +47,8 @@ AC_PATH_PROG([HEXDUMP], [hexdump])
##################################################
AC_CONFIG_FILES([ \
Makefile \
+ usrp2p/bootloader/Makefile \
usrp2/Makefile \
+ usrp2p/Makefile \
])
AC_OUTPUT
diff --git a/firmware/microblaze/lib/Makefile.inc b/firmware/microblaze/lib/Makefile.inc
index a576d1ccb..ae123f531 100644
--- a/firmware/microblaze/lib/Makefile.inc
+++ b/firmware/microblaze/lib/Makefile.inc
@@ -41,6 +41,7 @@ COMMON_SRCS = \
$(top_srcdir)/lib/print_rmon_regs.c \
$(top_srcdir)/lib/print_buffer.c \
$(top_srcdir)/lib/printf.c \
+ $(top_srcdir)/lib/ihex.c \
$(top_srcdir)/lib/spi.c \
$(top_srcdir)/lib/net_common.c \
$(top_srcdir)/lib/arp_cache.c \
diff --git a/firmware/microblaze/lib/bootconfig.c b/firmware/microblaze/lib/bootconfig.c
new file mode 100644
index 000000000..93adc05c2
--- /dev/null
+++ b/firmware/microblaze/lib/bootconfig.c
@@ -0,0 +1,101 @@
+/* -*- 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/>.
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "bootconfig.h"
+#include "bootconfig_private.h"
+#include <stdint.h>
+#include <stddef.h>
+#include <i2c.h>
+#include <quadradio/i2c_addr.h>
+#include <mdelay.h>
+#include <xilinx_v5_icap.h>
+#include <nonstdio.h>
+
+eeprom_boot_info_t eeprom_shadow;
+
+static eeprom_boot_info_t eeprom_default = {
+ .magic = EEPROM_BOOT_INFO_MAGIC,
+ .nattempts = 1,
+ .next_boot.fpga_image_number = 0,
+ .next_boot.firmware_image_number = 0,
+ .default_boot.fpga_image_number = 0,
+ .default_boot.firmware_image_number = 0
+};
+
+eeprom_boot_info_t *
+_bc_get_eeprom_shadow(void)
+{
+ return &eeprom_shadow;
+}
+
+
+bool
+_bc_write_eeprom_shadow(void)
+{
+ return eeprom_write(I2C_ADDR_MBOARD, BOOT_INFO_OFFSET, &eeprom_shadow, sizeof(eeprom_shadow));
+}
+
+void
+bootconfig_init(void)
+{
+ if (!eeprom_read(I2C_ADDR_MBOARD, BOOT_INFO_OFFSET, &eeprom_shadow, sizeof(eeprom_shadow))
+ || eeprom_shadow.magic != EEPROM_BOOT_INFO_MAGIC){
+ eeprom_shadow = eeprom_default;
+ _bc_write_eeprom_shadow();
+ }
+}
+
+bootconfig_t
+bootconfig_get_default(void)
+{
+ return eeprom_shadow.default_boot;
+}
+
+bool
+bootconfig_set_default(bootconfig_t bc)
+{
+ if (!validate_bootconfig(bc))
+ return false;
+
+ eeprom_shadow.default_boot = bc;
+ eeprom_shadow.next_boot = bc;
+ return _bc_write_eeprom_shadow();
+}
+
+void
+bootconfig_boot(bootconfig_t bc)
+{
+ if (!validate_bootconfig(bc))
+ return;
+
+ eeprom_shadow.next_boot = bc;
+ eeprom_shadow.nattempts = 1;
+ _bc_write_eeprom_shadow();
+
+ if (1){
+ puts("\nbootconfig: chaining to FPGA slot 0 bootloader");
+ mdelay(100);
+ }
+
+ while (1){
+ // Reload fpga with code from SPI flash address 0x0.
+ icap_reload_fpga(0x00000000);
+ }
+}
diff --git a/firmware/microblaze/lib/gdbstub2.c b/firmware/microblaze/lib/gdbstub2.c
new file mode 100644
index 000000000..4c63dfce2
--- /dev/null
+++ b/firmware/microblaze/lib/gdbstub2.c
@@ -0,0 +1,506 @@
+/* -*- 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/>.
+ */
+
+/*
+ * Implement a eensy weensy part of the GDB Remote Serial Protocol
+ *
+ * See Appendix D of the GDB manual
+ *
+ * m<addr>,<length> -- read <length> bytes of memory starting at <addr>
+ * Reply:
+ * XX... XX... is memory contents in hex
+ * ENN ENN NN is a hex error number
+ *
+ * M<addr>,<length>:XX... -- write memory, data in hex
+ * Reply:
+ * OK for success
+ * ENN for an error. NN is a hex error number
+ *
+ * X<addr>,<length>:XX... -- write memory, data in binary
+ * Reply:
+ * OK for success
+ * ENN for an error. NN is a hex error number
+ *
+ * c<addr> -- continue. <addr> is the address to resume (goto).
+ * Reply: <none>
+ *
+ * \x80 New Format...
+ */
+
+#include "gdbstub2.h"
+#include "loader_parser.h"
+#include "hal_uart.h"
+#include <stdbool.h>
+#include <stddef.h>
+
+#define MAX_PACKET 1024
+
+/*
+ * Get raw character from serial port, no echo.
+ */
+static inline int
+gdb_getc(void)
+{
+ return hal_uart_getc();
+}
+
+/*
+ * Put character to serial port. Raw output.
+ */
+static inline void
+gdb_putc(int ch)
+{
+ hal_uart_putc(ch);
+}
+
+// ------------------------------------------------------------------------
+
+#define GDB_ESCAPE 0x7d
+
+static unsigned char hex_table[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+};
+
+static int
+put_hex8_checksum(int ch, int checksum)
+{
+ unsigned char t = hex_table[(ch >> 4) & 0xf];
+ checksum += t;
+ gdb_putc(t);
+
+ t = hex_table[ch & 0xf];
+ checksum += t;
+ gdb_putc(t);
+ return checksum;
+}
+
+static void
+put_hex8(int ch)
+{
+ put_hex8_checksum(ch, 0);
+}
+
+static bool
+hex4_to_bin(int ch, int *value)
+{
+ if ('0' <= ch && ch <= '9'){
+ *value = ch - '0';
+ return true;
+ }
+ if ('a' <= ch && ch <= 'f'){
+ *value = ch - 'a' + 10;
+ return true;
+ }
+ if ('A' <= ch && ch <= 'F'){
+ *value = ch - 'A' + 10;
+ return true;
+ }
+ *value = 0;
+ return false;
+}
+
+static bool
+hex8_to_bin(const unsigned char *s, int *value)
+{
+ int v0, v1;
+ if (hex4_to_bin(s[0], &v0) && hex4_to_bin(s[1], &v1)){
+ *value = (v0 << 4) | v1;
+ return true;
+ }
+ return false;
+}
+
+static bool
+hex_to_bin_array(unsigned char *binary_data, const unsigned char *hex_data, size_t nbytes)
+{
+ for (size_t i = 0; i < nbytes; i++){
+ int t;
+ if (!hex8_to_bin(&hex_data[2*i], &t))
+ return false;
+ binary_data[i] = t;
+ }
+ return true;
+}
+
+static bool
+needs_escaping(int ch)
+{
+ return ch == '$' || ch == '#' || ch == GDB_ESCAPE;
+}
+
+/*
+ * \brief Wait for a packet.
+ * \param[out] pkt_buf gets the received packet payload.
+ * \param[in] max_size is the maximum number of bytes to write into \p pkt_buf.
+ * \param[out] actual_size is the number of bytes written to \p pkt_buf.
+ *
+ * \returns true iff the payload fits and the checksum is OK.
+ *
+ * Packets have this format:
+ *
+ * $<packet-data>#<checksum>
+ *
+ * Where <packet-data> is anything and <checksum> is a two byte hex
+ * checksum. In <packet-data> '$', '#' and 0x7d are escaped with 0x7d.
+ * The checksum is computed as the modulo 256 sum of all characters
+ * btween the leading '$' and the trailing '#' (an 8-bit unsigned
+ * checksum).
+ */
+static bool
+get_packet(unsigned char *pkt_buf, size_t max_size, size_t *actual_size)
+{
+ typedef enum states {
+ LOOKING_FOR_DOLLAR,
+ LOOKING_FOR_HASH,
+ CSUM1,
+ CSUM2,
+ } state_t;
+
+ *actual_size = 0;
+ unsigned char csum[2] = {0, 0};
+ state_t state = LOOKING_FOR_DOLLAR;
+ size_t pi = 0;
+
+ while (1){
+ int ch = gdb_getc();
+
+ switch (state){
+ case LOOKING_FOR_DOLLAR:
+ if (ch == '$'){
+ pi = 0;
+ state = LOOKING_FOR_HASH;
+ }
+ else if (ch == '#'){ // most likely missed the $
+ return false;
+ }
+ break;
+
+ case LOOKING_FOR_HASH:
+ if (ch == '$'){
+ return false;
+ }
+ else if (ch == '#'){
+ state = CSUM1;
+ }
+ else {
+ if (pi >= max_size) // payload too big
+ return false;
+
+ if (ch == GDB_ESCAPE)
+ ch = gdb_getc();
+
+ pkt_buf[pi++] = ch;
+ }
+ break;
+
+ case CSUM1:
+ csum[0] = ch;
+ state = CSUM2;
+ break;
+
+ case CSUM2:
+ csum[1] = ch;
+ *actual_size = pi;
+
+ // accept .. as a correct checksum
+ if (csum[0] == '.' && csum[1] == '.')
+ return true;
+
+ int expected_checksum;
+ if (!hex8_to_bin(csum, &expected_checksum))
+ return false;
+
+ int checksum = 0;
+ for (size_t i = 0; i < pi; i++)
+ checksum += pkt_buf[i];
+
+ checksum &= 0xff;
+ return checksum == expected_checksum;
+ }
+ }
+}
+
+static void
+put_packet_trailer(int checksum)
+{
+ gdb_putc('#');
+ put_hex8(checksum & 0xff);
+ gdb_putc('\r');
+ gdb_putc('\n');
+}
+
+static void
+put_packet(const unsigned char *pkt_buf, size_t size)
+{
+ gdb_putc('$');
+
+ int checksum = 0;
+ for (size_t i = 0; i < size; i++){
+ int ch = pkt_buf[i];
+ if (needs_escaping(ch))
+ gdb_putc(GDB_ESCAPE);
+ gdb_putc(ch);
+ checksum += ch;
+ }
+ put_packet_trailer(checksum);
+}
+
+/*!
+ * Read a hex number
+ *
+ * \param[inout] bufptr - pointer to pointer to buffer (updated on return)
+ * \param[in] end - one past end of valid data in buf
+ * \param[out] value - the parsed value
+ *
+ * \returns true iff a valid hex number was read from bufptr
+ */
+static bool
+parse_number(const unsigned char **bufptr, const unsigned char *end, unsigned int *value)
+{
+ const unsigned char *buf = *bufptr;
+ unsigned int v = 0;
+ bool valid = false;
+ int nibble;
+
+ while (buf < end && hex4_to_bin(*buf, &nibble)){
+ valid = true;
+ v = (v << 4) | nibble;
+ buf++;
+ }
+
+ *value = v;
+ *bufptr = buf;
+ return valid;
+}
+
+static bool
+parse_char(const unsigned char **bufptr, const unsigned char *end, unsigned char *ch)
+{
+ const unsigned char *buf = *bufptr;
+ if (buf < end){
+ *ch = *buf++;
+ *bufptr = buf;
+ return true;
+ }
+ return false;
+}
+
+static bool
+expect_char(const unsigned char **bufptr, const unsigned char *end, unsigned char expected)
+{
+ unsigned char ch;
+ return parse_char(bufptr, end, &ch) && ch == expected;
+}
+
+static bool
+expect_end(const unsigned char **bufptr, const unsigned char *end)
+{
+ return *bufptr == end;
+}
+
+static bool
+parse_addr_length(const unsigned char **bufptr, const unsigned char *end,
+ unsigned int *addr, unsigned int *length)
+{
+ return (parse_number(bufptr, end, addr)
+ && expect_char(bufptr, end, ',')
+ && parse_number(bufptr, end, length));
+}
+
+static void
+put_error(int error)
+{
+ unsigned char buf[3];
+ buf[0] = 'E';
+ buf[1] = hex_table[(error >> 4) & 0xf];
+ buf[2] = hex_table[error & 0xf];
+
+ put_packet(buf, sizeof(buf));
+}
+
+static void
+put_ok(void)
+{
+ const unsigned char buf[2] = "OK";
+ put_packet(buf, sizeof(buf));
+}
+
+/*
+ * Read memory and send the reply.
+ * We do it on the fly so that our packet size is effectively unlimited
+ */
+static void
+read_memory(unsigned int addr, unsigned int nbytes)
+{
+ int checksum = 0;
+ gdb_putc('$');
+
+ if ((addr & 0x3) == 0 && (nbytes & 0x3) == 0){ // word aligned
+ union {
+ unsigned int i;
+ unsigned char c[4];
+ } u;
+
+ unsigned int *p = (unsigned int *) addr;
+ unsigned int length = nbytes / 4;
+
+ for (unsigned int i = 0; i < length; i++){
+ u.i = p[i]; // do a word read
+ checksum = put_hex8_checksum(u.c[0], checksum);
+ checksum = put_hex8_checksum(u.c[1], checksum);
+ checksum = put_hex8_checksum(u.c[2], checksum);
+ checksum = put_hex8_checksum(u.c[3], checksum);
+ }
+ }
+ else { // byte aligned
+ unsigned char *p = (unsigned char *) addr;
+ for (unsigned int i = 0; i < nbytes; i++)
+ checksum = put_hex8_checksum(p[i], checksum);
+ }
+
+ put_packet_trailer(checksum);
+}
+
+static unsigned int
+get_unaligned_int(const unsigned char *p)
+{
+ // we're bigendian
+ return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]);
+}
+
+static bool
+write_memory(unsigned int addr, size_t nbytes,
+ const unsigned char *data)
+{
+ if ((addr & 0x3) == 0 && (nbytes & 0x3) == 0){ // word-aligned dst
+ unsigned int *dst = (unsigned int *) addr;
+ size_t length = nbytes / 4;
+ for (size_t i = 0; i < length; i++){
+ unsigned int t = get_unaligned_int(&data[4*i]);
+ dst[i] = t; // word writes
+ }
+ }
+ else { // non-word-aligned dst
+ unsigned char *dst = (unsigned char *) addr;
+ for (size_t i = 0; i < nbytes; i++){
+ dst[i] = data[i];
+ }
+ }
+ return true;
+}
+
+void
+gdbstub2_main_loop(void)
+{
+ unsigned char inpkt[MAX_PACKET + 24];
+ unsigned char binary_data[MAX_PACKET/2] __attribute__((aligned (4)));
+
+ hal_uart_set_mode(UART_MODE_RAW); //tell UART HAL not to map \n to \r\n
+
+ while (1){
+ size_t inpkt_len;
+ bool ok = get_packet(inpkt, sizeof(inpkt), &inpkt_len);
+ if (!ok){
+ gdb_putc('-');
+ continue;
+ }
+ gdb_putc('+');
+
+ const unsigned char *buf = inpkt;
+ const unsigned char *end = inpkt + inpkt_len;
+ unsigned char ch;
+
+ if (!parse_char(&buf, end, &ch)){ // empty packet
+ put_packet(0, 0);
+ continue;
+ }
+
+ unsigned int addr;
+ unsigned int length;
+
+ switch(ch){
+ case 'm': // m<addr>,<length> -- read <length> bytes starting at <addr>
+ if (!(parse_addr_length(&buf, end, &addr, &length) && expect_end(&buf, end))){
+ put_error(1);
+ }
+ else {
+ read_memory(addr, length);
+ }
+ break;
+
+ case 'M': // M<addr>,<length>:XX... -- write <length> bytes starting at <addr>
+ // XX... is the data in hex
+ if (!(parse_addr_length(&buf, end, &addr, &length)
+ && expect_char(&buf, end, ':')
+ && (end - buf) == 2 * length)){
+ put_error(1);
+ }
+ else {
+ if (!hex_to_bin_array(binary_data, buf, length))
+ put_error(2);
+ else if (!write_memory(addr, length, binary_data))
+ put_error(3);
+ else
+ put_ok();
+ }
+ break;
+
+ case 'X': // X<addr>,<length>:XX... -- write <length> bytes starting at <addr>
+ // XX... is the data in binary
+ if (!(parse_addr_length(&buf, end, &addr, &length)
+ && expect_char(&buf, end, ':')
+ && (end - buf) == length)){
+ put_error(1);
+ }
+ else {
+ if (!write_memory(addr, length, buf))
+ put_error(3);
+ else
+ put_ok();
+ }
+ break;
+
+ case 'c': // c<addr> -- continue. <addr> is the address to resume (goto).
+ if (!(parse_number(&buf, end, &addr)
+ && expect_end(&buf, end))){
+ put_error(1);
+ }
+ else {
+ typedef void (*fptr_t)(void);
+ (*(fptr_t) addr)(); // most likely no return
+ }
+ break;
+/*
+ case 0x80:
+ {
+ unsigned char *output = binary_data; // reuse
+ size_t sizeof_output = sizeof(binary_data);
+ size_t actual_olen;
+ loader_parser(buf, end-buf,
+ output, sizeof_output, &actual_olen);
+ put_packet(output, actual_olen);
+ }
+ break;
+*/
+ default: // unknown packet type
+ put_packet(0, 0);
+ break;
+ }
+ }
+}
diff --git a/firmware/microblaze/lib/gdbstub2.h b/firmware/microblaze/lib/gdbstub2.h
new file mode 100644
index 000000000..15cdde939
--- /dev/null
+++ b/firmware/microblaze/lib/gdbstub2.h
@@ -0,0 +1,25 @@
+/* -*- 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_GDBSTUB_H
+#define INCLUDED_GDBSTUB_H
+
+void gdbstub2_main_loop(void);
+
+#endif /* INCLUDED_GDBSTUB_H */
+
diff --git a/firmware/microblaze/lib/hal_io.c b/firmware/microblaze/lib/hal_io.c
index 58b1e681e..be4c570c7 100644
--- a/firmware/microblaze/lib/hal_io.c
+++ b/firmware/microblaze/lib/hal_io.c
@@ -18,9 +18,9 @@
// conditionalized on HAL_IO_USES_DBOARD_PINS && HAL_IO_USES_UART
-#include "hal_io.h"
#include "memory_map.h"
#include "hal_uart.h"
+#include "hal_io.h"
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
@@ -124,16 +124,29 @@ hal_finish(void)
// %c
inline int
+fputchar(hal_uart_name_t u, int ch)
+{
+ hal_uart_putc(u, ch);
+ return ch;
+}
+
+inline int
putchar(int ch)
{
- hal_uart_putc(ch);
+ hal_uart_putc(DEFAULT_UART, ch);
return ch;
}
int
+fgetchar(hal_uart_name_t u)
+{
+ return hal_uart_getc(u);
+}
+
+int
getchar(void)
{
- return hal_uart_getc();
+ return fgetchar(DEFAULT_UART);
}
#else // nop all i/o
@@ -172,34 +185,87 @@ getchar(void)
// \n
inline void
+fnewline(hal_uart_name_t u)
+{
+ fputchar(u, '\n');
+}
+
+inline void
newline(void)
{
- putchar('\n');
+ fnewline(DEFAULT_UART);
}
int
-putstr(const char *s)
+fputstr(hal_uart_name_t u, const char *s)
{
while (*s)
- putchar(*s++);
+ fputchar(u, *s++);
return 0;
}
int
-puts(const char *s)
+fnputstr(hal_uart_name_t u, const char *s, int len)
+{
+ int x = 0;
+ while (*s && (len > x++))
+ fputchar(u, *s++);
+
+ return x;
+}
+
+int
+putstr(const char *s)
{
- putstr(s);
- putchar('\n');
+ return fputstr(DEFAULT_UART, s);
+}
+
+int
+fputs(hal_uart_name_t u, const char *s)
+{
+ fputstr(u, s);
+ fputchar(u, '\n');
return 0;
}
+int puts(const char *s)
+{
+ return fputs(DEFAULT_UART, s);
+}
+
+char *
+fgets(hal_uart_name_t u, char * const s)
+{
+ char *x = s;
+ while((*x=(char)hal_uart_getc(u)) != '\n') x++;
+ *x = 0;
+ return s;
+}
+
+int
+fngets(hal_uart_name_t u, char * const s, int len)
+{
+ char *x = s;
+ while(((*x=(char)hal_uart_getc(u)) != '\n') && ((x-s) < len)) x++;
+ *x = 0;
+ return (x-s);
+}
+
+int
+fngets_timeout(hal_uart_name_t u, char * const s, int len)
+{
+ char *x = s;
+
+ while(((*x=(char)hal_uart_getc_timeout(u)) != '\n') && (*x != -1) && ((x-s) < len)) x++;
+ *x = 0;
+ //printf("Returning from fngets() with string %d of length %d\n", s[0], x-s);
+ return (x-s);
+}
+
char *
gets(char * const s)
{
- char *x = s;
- while((*x=(char)hal_uart_getc()) != '\n') x++;
- *x = 0;
- return s;
+ return fgets(DEFAULT_UART, s);
}
diff --git a/firmware/microblaze/lib/hal_io.h b/firmware/microblaze/lib/hal_io.h
index c67d96c62..950f8d591 100644
--- a/firmware/microblaze/lib/hal_io.h
+++ b/firmware/microblaze/lib/hal_io.h
@@ -20,10 +20,15 @@
#define INCLUDED_HAL_IO_H
#include "memory_map.h"
+#include "hal_uart.h"
void hal_io_init(void);
void hal_finish();
char *gets(char * const s);
+int fputstr(hal_uart_name_t u, const char *s);
+int fnputstr(hal_uart_name_t u, const char *s, int len);
+int fngets(hal_uart_name_t u, char * const s, int len);
+int fngets_timeout(hal_uart_name_t u, char * const s, int len);
/*
* ------------------------------------------------------------------------
diff --git a/firmware/microblaze/lib/hal_uart.c b/firmware/microblaze/lib/hal_uart.c
index fe3b7515a..7836240fe 100644
--- a/firmware/microblaze/lib/hal_uart.c
+++ b/firmware/microblaze/lib/hal_uart.c
@@ -16,71 +16,105 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "memory_map.h"
#include "hal_uart.h"
#include "hal_io.h"
-#include "memory_map.h"
+#include "mdelay.h"
-// First pass, no interrupts
-
-// Replaced with divisors.py which generates best divisor
-//#define CALC_DIVISOR(rate) (WISHBONE_CLK_RATE / ((rate) * 16))
+//just to save you from going insane, note that firmware/FPGA UARTs [0-2] correspond to serial ports [1-3].
+//so in software, we refer to UART_DEBUG as UART0, but it transmits on pin TXD<1>. see the UART assignments in hal_uart.h.
#define NSPEEDS 6
#define MAX_WB_DIV 4
+//if you're going to recalculate the divisors, it's just uart_clock_rate / baud_rate.
+//uart_clock_rate is 50MHz for USRP2.
static const uint16_t
-divisor_table[MAX_WB_DIV+1][NSPEEDS] = {
- { 2, 2, 2, 2, 2, 2}, // 0: can't happen
- { 651, 326, 163, 109, 54, 27 }, // 1: 100 MHz
- { 326, 163, 81, 54, 27, 14 }, // 2: 50 MHz
- { 217, 109, 54, 36, 18, 9 }, // 3: 33.3333 MHz
- { 163, 81, 41, 27, 14, 7 }, // 4: 25 MHz
+divisor_table[NSPEEDS] = {
+ 5208, // 9600
+ 2604, // 19200
+ 1302, // 38400
+ 868, // 57600
+ 434, // 115200
+ 217 // 230400
};
-#define u uart_regs
+static char uart_mode[4] = {
+ [UART_DEBUG] = UART_MODE_ONLCR,
+ [UART_EXP] = UART_MODE_ONLCR,
+ [UART_GPS] = UART_MODE_ONLCR
+};
-static char uart_mode = UART_MODE_ONLCR;
+static char uart_speeds[4] = {
+ [UART_DEBUG] = US_230400,
+ [UART_EXP] = US_230400,
+ [UART_GPS] = US_115200
+};
void
-hal_uart_set_mode(int mode)
+hal_uart_set_mode(hal_uart_name_t uart, int mode)
+{
+ uart_mode[uart] = mode;
+}
+
+void hal_uart_set_speed(hal_uart_name_t uart, hal_uart_speed_t speed)
{
- uart_mode = mode;
+ uart_regs[uart].clkdiv = divisor_table[speed];
}
void
hal_uart_init(void)
{
- hal_uart_set_mode(UART_MODE_ONLCR);
- u->clkdiv = 217; // 230400 bps
+ for(int i = 0; i < 3; i++) {
+ hal_uart_set_mode(i, uart_mode[i]);
+ hal_uart_set_speed(i, uart_speeds[i]);
+ }
}
void
-hal_uart_putc(int ch)
+hal_uart_putc(hal_uart_name_t u, int ch)
{
- if (ch == '\n')// && (uart_mode == UART_MODE_ONLCR)) //map \n->\r\n if necessary
- hal_uart_putc('\r');
+ if ((ch == '\n') && (uart_mode[u] == UART_MODE_ONLCR)) //map \n->\r\n if necessary
+ hal_uart_putc(u, '\r');
- while (u->txlevel == 0) // wait for fifo to have space
+ while (uart_regs[u].txlevel == 0) // wait for fifo to have space
;
- u->txchar = ch;
+ uart_regs[u].txchar = ch;
}
void
-hal_uart_putc_nowait(int ch)
+hal_uart_putc_nowait(hal_uart_name_t u, int ch)
{
- if (ch == '\n')// && (uart_mode == UART_MODE_ONLCR)) //map \n->\r\n if necessary
- hal_uart_putc('\r');
+ if ((ch == '\n') && (uart_mode[u] == UART_MODE_ONLCR)) //map \n->\r\n if necessary
+ hal_uart_putc(u, '\r');
- if(u->txlevel) // If fifo has space
- u->txchar = ch;
+ if(uart_regs[u].txlevel) // If fifo has space
+ uart_regs[u].txchar = ch;
}
int
-hal_uart_getc(void)
+hal_uart_getc(hal_uart_name_t u)
{
- while ((u->rxlevel) == 0) // wait for data to be ready
+ while ((uart_regs[u].rxlevel) == 0) // wait for data to be ready
;
- return u->rxchar;
+ return uart_regs[u].rxchar;
}
+
+int
+hal_uart_getc_timeout(hal_uart_name_t u)
+{
+ int timeout = 0;
+ while (((uart_regs[u].rxlevel) == 0) && (timeout++ < HAL_UART_TIMEOUT_MS))
+ mdelay(1);
+ return (timeout >= HAL_UART_TIMEOUT_MS) ? -1 : uart_regs[u].rxchar; //return -1 if nothing there, cause fngets to quit
+}
+
+int hal_uart_rx_flush(hal_uart_name_t u)
+{
+ char x;
+ while(uart_regs[u].rxlevel) x = uart_regs[u].rxchar;
+ return x;
+}
+
diff --git a/firmware/microblaze/lib/hal_uart.h b/firmware/microblaze/lib/hal_uart.h
index dfd73c323..758c8cb5e 100644
--- a/firmware/microblaze/lib/hal_uart.h
+++ b/firmware/microblaze/lib/hal_uart.h
@@ -25,29 +25,39 @@
#define UART_MODE_RAW 0x0000 // no mapping on input or output
#define UART_MODE_ONLCR 0x0001 // map \n to \r\n on output (default)
-/*
- * \brief Set uart mode
- */
-void hal_uart_set_mode(int flags);
+#define DEFAULT_UART UART_DEBUG //which UART printf, gets, etc. use
-/*!
- * \brief one-time call to init
- */
-void hal_uart_init(void);
+#define HAL_UART_TIMEOUT_MS 300
typedef enum {
- US_9600,
- US_19200,
- US_38400,
- US_57600,
- US_115200,
- US_230400,
+ US_9600 = 0,
+ US_19200 = 1,
+ US_38400 = 2,
+ US_57600 = 3,
+ US_115200 = 4,
+ US_230400 = 5
} hal_uart_speed_t;
typedef struct {
hal_uart_speed_t speed;
} hal_uart_config_t;
+typedef enum {
+ UART_DEBUG = 0,
+ UART_EXP = 1,
+ UART_GPS = 2
+} hal_uart_name_t;
+
+/*
+ * \brief Set uart mode
+ */
+void hal_uart_set_mode(hal_uart_name_t uart, int flags);
+
+/*!
+ * \brief one-time call to init
+ */
+void hal_uart_init(void);
+
/*!
* \brief Set uart parameters
* Default is 115,200 bps, 8N1.
@@ -62,17 +72,23 @@ void hal_uart_get_config(hal_uart_config_t *c);
/*!
* \brief Enqueue \p ch for output over serial port
*/
-void hal_uart_putc(int ch);
+void hal_uart_putc(hal_uart_name_t u, int ch);
/*!
* \brief Enqueue \p ch for output over serial port, silent fail if queue is full
*/
-void hal_uart_putc_nowait(int ch);
+void hal_uart_putc_nowait(hal_uart_name_t u, int ch);
/*
* \brief Blocking read of next char from serial port
*/
-int hal_uart_getc(void);
+int hal_uart_getc(hal_uart_name_t u);
+
+/*
+ * \brief Blocking read of next char from serial port with timeout
+ */
+int hal_uart_getc_timeout(hal_uart_name_t u);
+int hal_uart_rx_flush(hal_uart_name_t u);
#endif /* INCLUDED_HAL_UART_H */
diff --git a/firmware/microblaze/lib/ihex.c b/firmware/microblaze/lib/ihex.c
new file mode 100644
index 000000000..97ecf73b6
--- /dev/null
+++ b/firmware/microblaze/lib/ihex.c
@@ -0,0 +1,57 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2010 Ettus Research LLC
+ *
+ */
+
+#include "ihex.h"
+#include <ctype.h> //man that pulls in a lot of shit
+
+//this is not safe and you should run isxdigit beforehand
+uint8_t asc2nibble(char input) {
+ if(input > 'Z') return input - 'W';
+ else if(input > '9') return input - '7';
+ else return input - '0';
+}
+
+int ihex_parse(char input[], ihex_record_t *record) {
+ //given a NULL-TERMINATED input string (use gets()) in I16HEX format, write the binary record in record. return 0 on success.
+
+ uint8_t inputlen;
+ uint8_t t, i, checksum_calc=0, checksum_read;
+
+ //first check for ":" leading character
+ if(input[0] != ':') return -1;
+
+ //then check the string for only valid ASCII ['0'-'F']
+ inputlen=1;
+ while(input[inputlen]) {
+ if( !isxdigit(input[inputlen++]) ) return -2;
+ }
+
+ //then read the length.
+ record->length = (asc2nibble(input[1]) << 4) + asc2nibble(input[2]);
+ if(input[(record->length<<1) + 11] != 0) return -3; //if we're missing a null terminator in the right place
+
+ //then read the address.
+ record->addr = (asc2nibble(input[3]) << 12) + (asc2nibble(input[4]) << 8) + (asc2nibble(input[5]) << 4) + asc2nibble(input[6]);
+
+ //then read the record type.
+ record->type = (asc2nibble(input[7]) << 4) + asc2nibble(input[8]);
+// if(record->type > 4) return -4;
+
+ //then read the data, which goes from input[9] to input[9+length*2].
+ for(i=0; i < record->length; i++) {
+ t = 9 + (i<<1);
+ record->data[i] = (asc2nibble(input[t]) << 4) + (asc2nibble(input[t + 1]));
+ checksum_calc += record->data[i]; //might as well keep a running checksum as we read
+ }
+ checksum_calc += record->length + record->type + (record->addr >> 8) + (record->addr & 0xFF); //get the rest of the data into that checksum
+ checksum_calc = ~checksum_calc + 1; //checksum is 2's complement
+
+ //now read the checksum of the record
+ checksum_read = (asc2nibble(input[9 + (record->length<<1)]) << 4) + asc2nibble(input[10 + (record->length<<1)]);
+ if(checksum_calc != checksum_read) return -5; //compare 'em
+
+ return 0;
+}
diff --git a/firmware/microblaze/lib/ihex.h b/firmware/microblaze/lib/ihex.h
new file mode 100644
index 000000000..9f471fbe2
--- /dev/null
+++ b/firmware/microblaze/lib/ihex.h
@@ -0,0 +1,18 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2010 Ettus Research LLC
+ *
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+
+typedef struct {
+ uint8_t type;
+ size_t length;
+ uint32_t addr;
+ uint8_t *data;
+} ihex_record_t;
+
+
+int ihex_parse(char input[], ihex_record_t *record);
diff --git a/firmware/microblaze/lib/udp_fw_update.h b/firmware/microblaze/lib/udp_fw_update.h
new file mode 100644
index 000000000..d25525bd2
--- /dev/null
+++ b/firmware/microblaze/lib/udp_fw_update.h
@@ -0,0 +1,71 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2010 Ettus Research LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "net_common.h"
+
+#define USRP2_UDP_UPDATE_PORT 49154
+
+typedef enum {
+ USRP2_FW_UPDATE_ID_WAT = ' ',
+
+ USRP2_FW_UPDATE_ID_OHAI_LOL = 'a',
+ USRP2_FW_UPDATE_ID_OHAI_OMG = 'A',
+
+ USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL = 'f',
+ USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG = 'F',
+
+ USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL = 'e',
+ USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG = 'E',
+
+ USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL = 'd',
+ USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG = 'D',
+ USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG = 'B',
+
+ USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL = 'w',
+ USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG = 'W',
+
+ USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL = 'r',
+ USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG = 'R',
+
+ USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL = 's',
+ USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG = 'S',
+
+ USRP2_FW_UPDATE_ID_KTHXBAI = '~'
+
+} usrp2_fw_update_id_t;
+
+typedef struct {
+ uint32_t proto_ver;
+ uint32_t id;
+ uint32_t seq;
+ union {
+ uint32_t ip_addr;
+ struct {
+ uint32_t flash_addr;
+ uint32_t length;
+ uint8_t data[256];
+ } flash_args;
+ struct {
+ uint32_t sector_size_bytes;
+ uint32_t memory_size_bytes;
+ } flash_info_args;
+ } data;
+} usrp2_fw_update_data_t;
+
+void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst,
+ unsigned char *payload, int payload_len);
diff --git a/firmware/microblaze/lib/xilinx_s3_icap.c b/firmware/microblaze/lib/xilinx_s3_icap.c
new file mode 100644
index 000000000..8aa7fd297
--- /dev/null
+++ b/firmware/microblaze/lib/xilinx_s3_icap.c
@@ -0,0 +1,101 @@
+/* -*- c -*- */
+/*
+ * Copyright 2009 Ettus Research LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/* Changes required to work for the Spartan-3A series:
+ * The ICAP interface on the 3A is 8 bits wide, instead of 32.
+ * Everything is Xilinx standard LSB-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 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)
+{
+ uint8_t t = swap8(x);
+
+ icap_regs->icap = t; //DEBUG: does not swap bits
+}
+
+uint8_t
+rd_icap(void)
+{
+ return swap8(icap_regs->icap);
+}
+
+
+void
+icap_reload_fpga(uint32_t flash_address)
+//this DOES NOT WORK right now. reboot is not getting executed correctly.
+{
+ 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] = READ_CMD; //legacy command, use FAST_READ_CMD 0x0B after testing
+
+ //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
+
+}
diff --git a/firmware/microblaze/usrp2/Makefile.am b/firmware/microblaze/usrp2/Makefile.am
index 8da013980..17f7a4744 100644
--- a/firmware/microblaze/usrp2/Makefile.am
+++ b/firmware/microblaze/usrp2/Makefile.am
@@ -34,7 +34,8 @@ noinst_LIBRARIES = libusrp2.a
libusrp2_a_SOURCES = \
$(COMMON_SRCS) \
sd.c \
- ethernet.c
+ ethernet.c \
+ udp_fw_update.c
noinst_PROGRAMS = \
usrp2_txrx_uhd.elf
diff --git a/firmware/microblaze/usrp2/udp_fw_update.c b/firmware/microblaze/usrp2/udp_fw_update.c
new file mode 100644
index 000000000..14eb0b1ee
--- /dev/null
+++ b/firmware/microblaze/usrp2/udp_fw_update.c
@@ -0,0 +1,34 @@
+/* -*- 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 <nonstdio.h>
+#include "udp_fw_update.h"
+
+//Firmware update packet handler
+void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst,
+ unsigned char *payload, int payload_len) {
+
+ usrp2_fw_update_data_t update_data_out;
+ update_data_out.id = USRP2_FW_UPDATE_ID_WAT;
+
+ send_udp_pkt(USRP2_UDP_UPDATE_PORT, src, &update_data_out, sizeof(update_data_out));
+}
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..e6bff2dbc
--- /dev/null
+++ b/firmware/microblaze/usrp2p/Makefile.am
@@ -0,0 +1,68 @@
+#
+# 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.
+RELOCATE_ARGS = \
+ --change-section-address .vectors.sw_exception+0x8000 \
+ --change-section-address .vectors.hw_exception+0x8000 \
+ --change-section-address .vectors.interrupt+0x8000 \
+ --change-section-address .vectors.reset+0x8000
+
+# $(MB_OBJCOPY) -O ihex $< $@
+# the below would work if objcopy weren't written by apes
+# $(MB_OBJCOPY) -O ihex -w --change-section-address .vectors*+0x8000 $< $@
+# 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) \
+ spif.c \
+ spi_flash.c \
+ spi_flash_read.c \
+ bootloader_utils.c \
+ ethernet.c \
+ udp_fw_update.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/eth_phy.h b/firmware/microblaze/usrp2p/eth_phy.h
new file mode 100644
index 000000000..d233e96e8
--- /dev/null
+++ b/firmware/microblaze/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/microblaze/usrp2p/ethernet.c b/firmware/microblaze/usrp2p/ethernet.c
new file mode 100644
index 000000000..660f28934
--- /dev/null
+++ b/firmware/microblaze/usrp2p/ethernet.c
@@ -0,0 +1,399 @@
+/*
+ * 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 <eth_mac_regs.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);
+}
+
+static bool
+unprogrammed(const void *t, size_t len)
+{
+ int i;
+ uint8_t *p = (uint8_t *)t;
+ bool all_zeros = true;
+ bool all_ones = true;
+ for (i = 0; i < len; i++){
+ all_zeros &= p[i] == 0x00;
+ all_ones &= p[i] == 0xff;
+ }
+ return all_ones | all_zeros;
+}
+
+//////////////////// MAC Addr Stuff ///////////////////////
+
+static int8_t src_mac_addr_initialized = false;
+static eth_mac_addr_t src_mac_addr = {{
+ 0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff
+ }};
+
+const eth_mac_addr_t *
+ethernet_mac_addr(void)
+{
+ if (!src_mac_addr_initialized){ // fetch from eeprom
+ src_mac_addr_initialized = true;
+
+ // if we're simulating, don't read the EEPROM model, it's REALLY slow
+ if (hwconfig_simulation_p())
+ return &src_mac_addr;
+
+ eth_mac_addr_t tmp;
+ bool ok = eeprom_read(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_MAC_ADDR, &tmp, sizeof(tmp));
+ if (!ok || unprogrammed(&tmp, sizeof(tmp))){
+ // use the default
+ }
+ else
+ src_mac_addr = tmp;
+ }
+
+ return &src_mac_addr;
+}
+
+bool
+ethernet_set_mac_addr(const eth_mac_addr_t *t)
+{
+ bool ok = eeprom_write(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_MAC_ADDR, t, sizeof(eth_mac_addr_t));
+ if (ok){
+ src_mac_addr = *t;
+ src_mac_addr_initialized = true;
+ //eth_mac_set_addr(t); //this breaks the link
+ }
+
+ return ok;
+}
+
+//////////////////// IP Addr Stuff ///////////////////////
+
+static int8_t src_ip_addr_initialized = false;
+static struct ip_addr src_ip_addr = {
+ (192 << 24 | 168 << 16 | 10 << 8 | 2 << 0)
+};
+
+
+const struct ip_addr *get_ip_addr(void)
+{
+ if (!src_ip_addr_initialized){ // fetch from eeprom
+ src_ip_addr_initialized = true;
+
+ // if we're simulating, don't read the EEPROM model, it's REALLY slow
+ if (hwconfig_simulation_p())
+ return &src_ip_addr;
+
+ struct ip_addr tmp;
+ bool ok = eeprom_read(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_IP_ADDR, &tmp, sizeof(tmp));
+ if (!ok || unprogrammed(&tmp, sizeof(tmp))){
+ // use the default
+ }
+ else
+ src_ip_addr = tmp;
+ }
+
+ return &src_ip_addr;
+}
+
+bool set_ip_addr(const struct ip_addr *t){
+ bool ok = eeprom_write(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_IP_ADDR, t, sizeof(struct ip_addr));
+ if (ok){
+ src_ip_addr = *t;
+ src_ip_addr_initialized = true;
+ }
+
+ return ok;
+}
+
+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/microblaze/usrp2p/memory_map.h b/firmware/microblaze/usrp2p/memory_map.h
new file mode 100644
index 000000000..8d0d0c365
--- /dev/null
+++ b/firmware/microblaze/usrp2p/memory_map.h
@@ -0,0 +1,843 @@
+/* -*- 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 CLK_RESET (1<<4)
+#define CLK_ENABLE (1<<3) | (1<<2)
+#define CLK_SEL (1<<1) | (1<<0)
+
+#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 << 2)
+#define LED_B (1 << 0)
+#define LED_E (1 << 3)
+#define LED_D (1 << 1)
+#define LED_C (1 << 4)
+// 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
+ volatile uint32_t x[3]; //padding to reach 32B
+} 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..bbe7b650d
--- /dev/null
+++ b/firmware/microblaze/usrp2p/spi_flash.h
@@ -0,0 +1,119 @@
+/* -*- 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 */
+size_t spi_flash_log2_memory_size(void);
+
+static inline size_t
+spi_flash_sector_size(void)
+{
+ return ((size_t) 1) << spi_flash_log2_sector_size();
+}
+
+static inline size_t
+spi_flash_memory_size(void)
+{
+ return ((size_t) 1) << spi_flash_log2_memory_size();
+}
+
+void spi_flash_read(uint32_t flash_addr, size_t nbytes, void *buf);
+
+/*
+ * 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..4682c5fe6
--- /dev/null
+++ b/firmware/microblaze/usrp2p/spi_flash_read.c
@@ -0,0 +1,119 @@
+/* -*- 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
+
+static size_t _spi_flash_log2_memory_size;
+
+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];
+ _spi_flash_log2_memory_size = size; //while we're at it
+ return _spi_flash_log2_sector_size;
+}
+
+size_t
+spi_flash_log2_memory_size(void)
+{
+ if (_spi_flash_log2_memory_size != 0)
+ return _spi_flash_log2_memory_size;
+
+ uint32_t id = spi_flash_rdid();
+ int type = (id >> 8) & 0xff;
+ int size = id & 0xff;
+ if (type != 0x20 || size < 22 || size > 24)
+ abort();
+
+ _spi_flash_log2_memory_size = size;
+ return _spi_flash_log2_memory_size;
+}
+
+void
+spi_flash_read(uint32_t flash_addr, size_t nbytes, void *buf)
+{
+ /*
+ * 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/udp_fw_update.c b/firmware/microblaze/usrp2p/udp_fw_update.c
new file mode 100644
index 000000000..9242242e7
--- /dev/null
+++ b/firmware/microblaze/usrp2p/udp_fw_update.c
@@ -0,0 +1,106 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2010 Ettus Research LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//Routines to handle updating the SPI Flash firmware via UDP
+
+#include <net_common.h>
+#include "usrp2/fw_common.h"
+#include "spi.h"
+#include "spi_flash.h"
+#include <nonstdio.h>
+#include <string.h>
+#include "ethernet.h"
+#include "udp_fw_update.h"
+
+//Firmware update packet handler
+void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst,
+ unsigned char *payload, int payload_len) {
+
+ const usrp2_fw_update_data_t *update_data_in = (usrp2_fw_update_data_t *) payload;
+
+ usrp2_fw_update_data_t update_data_out;
+ usrp2_fw_update_id_t update_data_in_id = update_data_in->id;
+
+ //ensure that the protocol versions match
+ if (payload_len >= sizeof(uint32_t) && update_data_in->proto_ver != USRP2_FW_COMPAT_NUM){
+ printf("!Error in update packet handler: Expected compatibility number %d, but got %d\n",
+ USRP2_FW_COMPAT_NUM, update_data_in->proto_ver
+ );
+ update_data_in_id = USRP2_FW_UPDATE_ID_OHAI_LOL; //so we can respond
+ }
+
+ //ensure that this is not a short packet
+ if (payload_len < sizeof(usrp2_fw_update_data_t)){
+ printf("!Error in update packet handler: Expected payload length %d, but got %d\n",
+ (int)sizeof(usrp2_fw_update_data_t), payload_len
+ );
+ update_data_in_id = USRP2_FW_UPDATE_ID_WAT;
+ }
+
+ spi_flash_async_state_t spi_flash_async_state;
+
+ switch(update_data_in_id) {
+ case USRP2_FW_UPDATE_ID_OHAI_LOL: //why hello there you handsome devil
+ update_data_out.id = USRP2_FW_UPDATE_ID_OHAI_OMG;
+ memcpy(&update_data_out.data.ip_addr, (void *)get_ip_addr(), sizeof(struct ip_addr));
+ break;
+
+ case USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL: //query sector size, memory size so the host can mind the boundaries
+ update_data_out.data.flash_info_args.sector_size_bytes = spi_flash_sector_size();
+ update_data_out.data.flash_info_args.memory_size_bytes = spi_flash_memory_size();
+ update_data_out.id = USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG;
+ break;
+
+ case USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL: //out with the old
+ spi_flash_async_erase_start(&spi_flash_async_state, update_data_in->data.flash_args.flash_addr, update_data_in->data.flash_args.length);
+ update_data_out.id = USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG;
+ break;
+
+ case USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL:
+ //poll for done, set something in the reply packet
+ //spi_flash_async_erase_poll() also advances the state machine, so you should call it reasonably often to get things done quicker
+ if(spi_flash_async_erase_poll(&spi_flash_async_state)) update_data_out.id = USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG;
+ else update_data_out.id = USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG;
+ break;
+
+ case USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL: //and in with the new
+ //spi_flash_program() goes pretty quick compared to page erases, so we don't bother polling -- it'll come back in some milliseconds
+ //if it doesn't come back fast enough, we'll just write smaller packets at a time until it does
+ spi_flash_program(update_data_in->data.flash_args.flash_addr, update_data_in->data.flash_args.length, update_data_in->data.flash_args.data);
+ update_data_out.id = USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG;
+ break;
+
+ case USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL: //for verify
+ spi_flash_read(update_data_in->data.flash_args.flash_addr, update_data_in->data.flash_args.length, update_data_out.data.flash_args.data);
+ update_data_out.id = USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG;
+ break;
+
+ case USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL: //for if we ever get the ICAP working
+ //should reset via icap_reload_fpga(uint32_t flash_address);
+ update_data_out.id = USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG;
+ //you should note that if you get a reply packet to this the reset has obviously failed
+ break;
+
+// case USRP2_FW_UPDATE_ID_KTHXBAI: //see ya
+// break;
+
+ default: //uhhhh
+ update_data_out.id = USRP2_FW_UPDATE_ID_WAT;
+ }
+ send_udp_pkt(USRP2_UDP_UPDATE_PORT, src, &update_data_out, sizeof(update_data_out));
+}
diff --git a/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 */
diff --git a/host/lib/gain_group.cpp b/host/lib/gain_group.cpp
new file mode 100644
index 000000000..1be09dee2
--- /dev/null
+++ b/host/lib/gain_group.cpp
@@ -0,0 +1,149 @@
+//
+// 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 <uhd/utils/gain_group.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/utils/assert.hpp>
+#include <boost/foreach.hpp>
+#include <boost/bind.hpp>
+#include <algorithm>
+#include <vector>
+#include <iostream>
+
+using namespace uhd;
+
+static const bool verbose = false;
+
+static bool compare_by_step_size(
+ const size_t &rhs, const size_t &lhs, std::vector<gain_fcns_t> &fcns
+){
+ return fcns.at(rhs).get_range().step > fcns.at(lhs).get_range().step;
+}
+
+/***********************************************************************
+ * gain group implementation
+ **********************************************************************/
+class gain_group_impl : public gain_group{
+public:
+ gain_group_impl(void){
+ /*NOP*/
+ }
+
+ gain_range_t get_range(void){
+ float overall_min = 0, overall_max = 0, overall_step = 0;
+ BOOST_FOREACH(const gain_fcns_t &fcns, get_all_fcns()){
+ const gain_range_t range = fcns.get_range();
+ overall_min += range.min;
+ overall_max += range.max;
+ //the overall step is the min (zero is invalid, first run)
+ if (overall_step == 0) overall_step = range.step;
+ overall_step = std::min(overall_step, range.step);
+ }
+ return gain_range_t(overall_min, overall_max, overall_step);
+ }
+
+ float get_value(void){
+ float overall_gain = 0;
+ BOOST_FOREACH(const gain_fcns_t &fcns, get_all_fcns()){
+ overall_gain += fcns.get_value();
+ }
+ return overall_gain;
+ }
+
+ void set_value(float gain){
+ std::vector<gain_fcns_t> all_fcns = get_all_fcns();
+ if (all_fcns.size() == 0) return; //nothing to set!
+
+ //get the max step size among the gains
+ float max_step = 0;
+ BOOST_FOREACH(const gain_fcns_t &fcns, all_fcns){
+ max_step = std::max(max_step, fcns.get_range().step);
+ }
+
+ //create gain bucket to distribute power
+ std::vector<float> gain_bucket;
+
+ //distribute power according to priority (round to max step)
+ float gain_left_to_distribute = gain;
+ BOOST_FOREACH(const gain_fcns_t &fcns, all_fcns){
+ const gain_range_t range = fcns.get_range();
+ gain_bucket.push_back(
+ max_step*int(std::clip(gain_left_to_distribute, range.min, range.max)/max_step)
+ );
+ gain_left_to_distribute -= gain_bucket.back();
+ }
+
+ //get a list of indexes sorted by step size large to small
+ std::vector<size_t> indexes_step_size_dec;
+ for (size_t i = 0; i < all_fcns.size(); i++){
+ indexes_step_size_dec.push_back(i);
+ }
+ std::sort(
+ indexes_step_size_dec.begin(), indexes_step_size_dec.end(),
+ boost::bind(&compare_by_step_size, _1, _2, all_fcns)
+ );
+ UHD_ASSERT_THROW(
+ all_fcns.at(indexes_step_size_dec.front()).get_range().step >=
+ all_fcns.at(indexes_step_size_dec.back()).get_range().step
+ );
+
+ //distribute the remainder (less than max step)
+ //fill in the largest step sizes first that are less than the remainder
+ BOOST_FOREACH(size_t i, indexes_step_size_dec){
+ const gain_range_t range = all_fcns.at(i).get_range();
+ float additional_gain = range.step*int(
+ std::clip(gain_bucket.at(i) + gain_left_to_distribute, range.min, range.max
+ )/range.step) - gain_bucket.at(i);
+ gain_bucket.at(i) += additional_gain;
+ gain_left_to_distribute -= additional_gain;
+ }
+ if (verbose) std::cout << "gain_left_to_distribute " << gain_left_to_distribute << std::endl;
+
+ //now write the bucket out to the individual gain values
+ for (size_t i = 0; i < gain_bucket.size(); i++){
+ if (verbose) std::cout << gain_bucket.at(i) << std::endl;
+ all_fcns.at(i).set_value(gain_bucket.at(i));
+ }
+ }
+
+ void register_fcns(
+ const gain_fcns_t &gain_fcns, size_t priority
+ ){
+ _registry[priority].push_back(gain_fcns);
+ }
+
+private:
+ //! get the gain function sets in order (highest priority first)
+ std::vector<gain_fcns_t> get_all_fcns(void){
+ std::vector<gain_fcns_t> all_fcns;
+ BOOST_FOREACH(ssize_t key, std::sorted(_registry.keys())){
+ const std::vector<gain_fcns_t> &fcns = _registry[key];
+ all_fcns.insert(all_fcns.begin(), fcns.begin(), fcns.end());
+ }
+ return all_fcns;
+ }
+
+ uhd::dict<size_t, std::vector<gain_fcns_t> > _registry;
+};
+
+/***********************************************************************
+ * gain group factory function
+ **********************************************************************/
+gain_group::sptr gain_group::make(void){
+ return sptr(new gain_group_impl());
+}
diff --git a/host/lib/ic_reg_maps/CMakeLists.txt b/host/lib/ic_reg_maps/CMakeLists.txt
index f8e15c13d..dd188d4c0 100644
--- a/host/lib/ic_reg_maps/CMakeLists.txt
+++ b/host/lib/ic_reg_maps/CMakeLists.txt
@@ -68,3 +68,8 @@ LIBUHD_PYTHON_GEN_SOURCE(
${CMAKE_SOURCE_DIR}/lib/ic_reg_maps/gen_ad9522_regs.py
${CMAKE_BINARY_DIR}/lib/ic_reg_maps/ad9522_regs.hpp
)
+
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_SOURCE_DIR}/lib/ic_reg_maps/gen_ads62p44_regs.py
+ ${CMAKE_BINARY_DIR}/lib/ic_reg_maps/ads62p44_regs.hpp
+)
diff --git a/host/lib/ic_reg_maps/gen_ads62p44_regs.py b/host/lib/ic_reg_maps/gen_ads62p44_regs.py
new file mode 100755
index 000000000..f0a84d940
--- /dev/null
+++ b/host/lib/ic_reg_maps/gen_ads62p44_regs.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# Template for raw text data describing registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+REGS_TMPL="""\
+########################################################################
+## address 0
+########################################################################
+reset 0[1] 0
+serial_readout 0[0] 0
+########################################################################
+## address 16
+########################################################################
+clkout_strength 16[6:7] 0 weaker=1, default=0, stronger=3
+########################################################################
+## address 17
+########################################################################
+dataout_strength 17[0:1] 0 weaker=1, default=0, stronger=3, maximum=2
+lvds_current 17[2:3] 0 3_5ma, 2_5ma, 4_5ma, 1_75ma
+lvds_current_double 17[4:5] 0 default, dblclk, dbldataclock
+########################################################################
+## address 18
+########################################################################
+lvds_clk_term 18[0:2] 0 none, 300, 180, 110, 150, 100, 81, 60
+lvds_data_term 18[3:5] 0 none, 300, 180, 110, 150, 100, 81, 60
+########################################################################
+## address 19
+########################################################################
+offset_freeze 19[4] 0
+########################################################################
+## address 20
+########################################################################
+power_down 20[0:2] 0 normal, a_dis, b_dis, ab_dis, global_pd, a_sby, b_sby, mux
+ref_select 20[3] 0 internal, external
+coarse_gain 20[4] 0 0db, 3_5db
+output_interface 20[5] 0 cmos, lvds
+override 20[7] 0
+########################################################################
+## address 22
+########################################################################
+test_patterns 22[0:2] 0 normal, zeros, ones, toggle, ramp, custom
+lvds_bytewise 22[3] 0
+data_format 22[4] 0 twos_complement, binary
+########################################################################
+## address 23
+########################################################################
+fine_gain 23[0:3] 0
+########################################################################
+## address 24 and 25
+########################################################################
+custom_low 24[0:7] 0
+custom_high 25[0:5] 0
+########################################################################
+## address 26
+########################################################################
+gain_correction 26[0:3] 0
+offset_tc 26[4:6] 0 1_1s, 0_55s, 0_27s, 0_13s, 2_15s, 4_3s
+low_latency 26[7] 0
+########################################################################
+## address 27
+########################################################################
+decimation 27[0:2] 0 decimate_2, decimate_4, decimate_1, decimate_8
+odd_tap_enable 27[3] 0
+filter_enable 27[4] 0
+filter_coeff_sel 27[5] 0 predefined, userdefined
+offset_enable 27[7] 0
+########################################################################
+## address 29
+########################################################################
+decimation_filter_bands 29[0:1] 0
+"""
+
+########################################################################
+# Template for methods in the body of the struct
+########################################################################
+BODY_TMPL="""\
+boost::uint8_t get_reg(boost::uint8_t addr){
+ boost::uint8_t reg = 0;
+ switch(addr){
+ #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs)))
+ case $addr:
+ #for $reg in filter(lambda r: r.get_addr() == addr, $regs)
+ reg |= (boost::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift();
+ #end for
+ break;
+ #end for
+ }
+ return reg;
+}
+
+boost::uint16_t get_write_reg(boost::uint8_t addr){
+ return (boost::uint16_t(addr) << 8) | get_reg(addr);
+}
+
+boost::uint16_t get_read_reg(boost::uint8_t addr){
+ return (boost::uint16_t(addr) << 8) | (1 << 7);
+}
+"""
+
+if __name__ == '__main__':
+ import common; common.generate(
+ name='ads62p44_regs',
+ regs_tmpl=REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ )
diff --git a/host/lib/usrp/usrp2/CMakeLists.txt b/host/lib/usrp/usrp2/CMakeLists.txt
index 078485d6a..f7984fce5 100644
--- a/host/lib/usrp/usrp2/CMakeLists.txt
+++ b/host/lib/usrp/usrp2/CMakeLists.txt
@@ -44,6 +44,8 @@ IF(ENABLE_USRP2)
${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/dboard_impl.cpp
${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/dboard_iface.cpp
${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/dsp_impl.cpp
+ ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/gps_ctrl.hpp
+ ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/gps_ctrl.cpp
${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/io_impl.cpp
${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/mboard_impl.cpp
${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/serdes_ctrl.cpp
@@ -53,6 +55,7 @@ IF(ENABLE_USRP2)
${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/usrp2_impl.cpp
${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/usrp2_impl.hpp
${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/usrp2_regs.hpp
+ ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/usrp2_regs.cpp
)
ELSE(ENABLE_USRP2)
MESSAGE(STATUS " Skipping USRP2 support.")
diff --git a/host/lib/usrp/usrp2/clock_ctrl.cpp b/host/lib/usrp/usrp2/clock_ctrl.cpp
index 02227afad..ef027c1df 100644
--- a/host/lib/usrp/usrp2/clock_ctrl.cpp
+++ b/host/lib/usrp/usrp2/clock_ctrl.cpp
@@ -18,6 +18,7 @@
#include "clock_ctrl.hpp"
#include "ad9510_regs.hpp"
#include "usrp2_regs.hpp" //spi slave constants
+#include "usrp2_clk_regs.hpp"
#include <uhd/utils/assert.hpp>
#include <boost/cstdint.hpp>
@@ -29,10 +30,12 @@ using namespace uhd;
class usrp2_clock_ctrl_impl : public usrp2_clock_ctrl{
public:
usrp2_clock_ctrl_impl(usrp2_iface::sptr iface){
- _iface = iface;
+ _iface = iface; //_iface has get_hw_rev(), which lets us know if it's a USRP2+ (>=0x80) or USRP2 (<0x80).
+
+ clk_regs = usrp2_clk_regs_t(_iface->get_hw_rev());
_ad9510_regs.cp_current_setting = ad9510_regs_t::CP_CURRENT_SETTING_3_0MA;
- this->write_reg(0x09);
+ this->write_reg(clk_regs.pll_3);
// Setup the clock registers to 100MHz:
// This was already done by the firmware (or the host couldnt communicate).
@@ -42,20 +45,20 @@ public:
_ad9510_regs.pll_power_down = ad9510_regs_t::PLL_POWER_DOWN_NORMAL;
_ad9510_regs.prescaler_value = ad9510_regs_t::PRESCALER_VALUE_DIV2;
- this->write_reg(0x0A);
+ this->write_reg(clk_regs.pll_4);
_ad9510_regs.acounter = 0;
- this->write_reg(0x04);
+ this->write_reg(clk_regs.acounter);
_ad9510_regs.bcounter_msb = 0;
_ad9510_regs.bcounter_lsb = 5;
- this->write_reg(0x05);
- this->write_reg(0x06);
+ this->write_reg(clk_regs.bcounter_msb);
+ this->write_reg(clk_regs.bcounter_lsb);
_ad9510_regs.ref_counter_msb = 0;
_ad9510_regs.ref_counter_lsb = 1; // r divider = 1
- this->write_reg(0x0B);
- this->write_reg(0x0C);
+ this->write_reg(clk_regs.ref_counter_msb);
+ this->write_reg(clk_regs.ref_counter_lsb);
/* regs will be updated in commands below */
@@ -76,11 +79,12 @@ public:
}
//uses output clock 7 (cmos)
+ //this clock is the same between USRP2 and USRP2+ and so this fn does not get a switch statement
void enable_rx_dboard_clock(bool enb){
_ad9510_regs.power_down_lvds_cmos_out7 = enb? 0 : 1;
_ad9510_regs.lvds_cmos_select_out7 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT7_CMOS;
_ad9510_regs.output_level_lvds_out7 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT7_1_75MA;
- this->write_reg(0x43);
+ this->write_reg(clk_regs.output(clk_regs.rx_db));
this->update_regs();
}
@@ -96,8 +100,8 @@ public:
_ad9510_regs.divider_low_cycles_out7 = low - 1;
_ad9510_regs.divider_high_cycles_out7 = high - 1;
//write the registers
- this->write_reg(0x56);
- this->write_reg(0x57);
+ this->write_reg(clk_regs.div_lo(clk_regs.rx_db));
+ this->write_reg(clk_regs.div_hi(clk_regs.rx_db));
this->update_regs();
}
@@ -107,12 +111,22 @@ public:
return rates;
}
- //uses output clock 6 (cmos)
+ //uses output clock 6 (cmos) on USRP2 and output clock 5 (cmos) on USRP2+
void enable_tx_dboard_clock(bool enb){
- _ad9510_regs.power_down_lvds_cmos_out6 = enb? 0 : 1;
- _ad9510_regs.lvds_cmos_select_out6 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT6_CMOS;
- _ad9510_regs.output_level_lvds_out6 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT6_1_75MA;
- this->write_reg(0x42);
+ switch(clk_regs.tx_db) {
+ case 5: //USRP2+
+ _ad9510_regs.power_down_lvds_cmos_out5 = enb? 0 : 1;
+ _ad9510_regs.lvds_cmos_select_out5 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT5_CMOS;
+ _ad9510_regs.output_level_lvds_out5 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT5_1_75MA;
+ break;
+ case 6: //USRP2
+ _ad9510_regs.power_down_lvds_cmos_out6 = enb? 0 : 1;
+ _ad9510_regs.lvds_cmos_select_out6 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT6_CMOS;
+ _ad9510_regs.output_level_lvds_out6 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT6_1_75MA;
+ break;
+ }
+
+ this->write_reg(clk_regs.output(clk_regs.tx_db));
this->update_regs();
}
@@ -124,12 +138,25 @@ public:
//calculate the low and high dividers
size_t high = divider/2;
size_t low = divider - high;
- //set the registers (divider - 1)
- _ad9510_regs.divider_low_cycles_out6 = low - 1;
- _ad9510_regs.divider_high_cycles_out6 = high - 1;
+
+ switch(clk_regs.tx_db) {
+ case 5: //USRP2+
+ _ad9510_regs.bypass_divider_out5 = (divider == 1)? 1 : 0;
+ _ad9510_regs.divider_low_cycles_out5 = low - 1;
+ _ad9510_regs.divider_high_cycles_out5 = high - 1;
+ break;
+ case 6: //USRP2
+ //bypass when the divider ratio is one
+ _ad9510_regs.bypass_divider_out6 = (divider == 1)? 1 : 0;
+ //set the registers (divider - 1)
+ _ad9510_regs.divider_low_cycles_out6 = low - 1;
+ _ad9510_regs.divider_high_cycles_out6 = high - 1;
+ break;
+ }
+
//write the registers
- this->write_reg(0x54);
- this->write_reg(0x55);
+ this->write_reg(clk_regs.div_hi(clk_regs.tx_db));
+ this->write_reg(clk_regs.div_lo(clk_regs.tx_db));
this->update_regs();
}
@@ -147,7 +174,7 @@ public:
ad9510_regs_t::CHARGE_PUMP_MODE_3STATE ;
_ad9510_regs.pll_mux_control = ad9510_regs_t::PLL_MUX_CONTROL_DLD_HIGH;
_ad9510_regs.pfd_polarity = ad9510_regs_t::PFD_POLARITY_POS;
- this->write_reg(0x08);
+ this->write_reg(clk_regs.pll_2);
this->update_regs();
}
@@ -170,32 +197,44 @@ private:
*/
void update_regs(void){
_ad9510_regs.update_registers = 1;
- this->write_reg(0x5a);
+ this->write_reg(clk_regs.update);
}
//uses output clock 3 (pecl)
+ //this is the same between USRP2 and USRP2+ and doesn't get a switch statement
void enable_dac_clock(bool enb){
_ad9510_regs.power_down_lvpecl_out3 = (enb)?
ad9510_regs_t::POWER_DOWN_LVPECL_OUT3_NORMAL :
ad9510_regs_t::POWER_DOWN_LVPECL_OUT3_SAFE_PD;
_ad9510_regs.output_level_lvpecl_out3 = ad9510_regs_t::OUTPUT_LEVEL_LVPECL_OUT3_810MV;
_ad9510_regs.bypass_divider_out3 = 1;
- this->write_reg(0x3F);
- this->write_reg(0x4F);
+ this->write_reg(clk_regs.output(clk_regs.dac));
+ this->write_reg(clk_regs.div_hi(clk_regs.dac));
this->update_regs();
}
- //uses output clock 4 (lvds)
+ //uses output clock 4 (lvds) on USRP2 and output clock 2 (lvpecl) on USRP2+
void enable_adc_clock(bool enb){
- _ad9510_regs.power_down_lvds_cmos_out4 = enb? 0 : 1;
- _ad9510_regs.lvds_cmos_select_out4 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT4_LVDS;
- _ad9510_regs.output_level_lvds_out4 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT4_1_75MA;
- _ad9510_regs.bypass_divider_out4 = 1;
- this->write_reg(0x40);
- this->write_reg(0x51);
+ switch(clk_regs.adc) {
+ case 2:
+ _ad9510_regs.power_down_lvpecl_out2 = enb? ad9510_regs_t::POWER_DOWN_LVPECL_OUT2_NORMAL : ad9510_regs_t::POWER_DOWN_LVPECL_OUT2_SAFE_PD;
+ _ad9510_regs.output_level_lvpecl_out2 = ad9510_regs_t::OUTPUT_LEVEL_LVPECL_OUT2_500MV;
+ _ad9510_regs.bypass_divider_out2 = 1;
+ break;
+ case 4:
+ _ad9510_regs.power_down_lvds_cmos_out4 = enb? 0 : 1;
+ _ad9510_regs.lvds_cmos_select_out4 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT4_LVDS;
+ _ad9510_regs.output_level_lvds_out4 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT4_1_75MA;
+ _ad9510_regs.bypass_divider_out4 = 1;
+ break;
+ }
+
+ this->write_reg(clk_regs.output(clk_regs.adc));
+ this->write_reg(clk_regs.div_hi(clk_regs.adc));
this->update_regs();
}
+ usrp2_clk_regs_t clk_regs;
usrp2_iface::sptr _iface;
ad9510_regs_t _ad9510_regs;
};
diff --git a/host/lib/usrp/usrp2/codec_ctrl.cpp b/host/lib/usrp/usrp2/codec_ctrl.cpp
index 32cc13ded..22a892f02 100644
--- a/host/lib/usrp/usrp2/codec_ctrl.cpp
+++ b/host/lib/usrp/usrp2/codec_ctrl.cpp
@@ -17,10 +17,12 @@
#include "codec_ctrl.hpp"
#include "ad9777_regs.hpp"
+#include "ads62p44_regs.hpp"
#include "usrp2_regs.hpp"
#include <boost/cstdint.hpp>
#include <boost/foreach.hpp>
#include <iostream>
+#include <uhd/utils/exception.hpp>
static const bool codec_ctrl_debug = false;
@@ -57,7 +59,16 @@ public:
}
//power-up adc
- _iface->poke32(U2_REG_MISC_CTRL_ADC, U2_FLAG_MISC_CTRL_ADC_ON);
+ if(_iface->get_hw_rev() < USRP2P_FIRST_HW_REV) { //if we're on a USRP2
+ _iface->poke32(_iface->regs.misc_ctrl_adc, U2_FLAG_MISC_CTRL_ADC_ON);
+ } else { //we're on a USRP2+
+ _ads62p44_regs.reset = 1;
+ this->send_ads62p44_reg(0x00); //issue a reset to the ADC
+ //everything else should be pretty much default, i think
+// _ads62p44_regs.decimation = DECIMATION_DECIMATE_1;
+ _ads62p44_regs.power_down = ads62p44_regs_t::POWER_DOWN_NORMAL;
+ this->send_ads62p44_reg(0x14);
+ }
}
~usrp2_codec_ctrl_impl(void){
@@ -66,11 +77,39 @@ public:
this->send_ad9777_reg(0);
//power-down adc
- _iface->poke32(U2_REG_MISC_CTRL_ADC, U2_FLAG_MISC_CTRL_ADC_OFF);
+ if(_iface->get_hw_rev() < USRP2P_FIRST_HW_REV) { //if we're on a USRP2
+ _iface->poke32(_iface->regs.misc_ctrl_adc, U2_FLAG_MISC_CTRL_ADC_OFF);
+ } else { //we're on a USRP2+
+ //send a global power-down to the ADC here... it will get lifted on reset
+ _ads62p44_regs.power_down = ads62p44_regs_t::POWER_DOWN_GLOBAL_PD;
+ this->send_ads62p44_reg(0x14);
+ }
+ }
+
+ void set_rx_digital_gain(float gain) { //fine digital gain
+ if(_iface->get_hw_rev() >= USRP2P_FIRST_HW_REV) {
+ _ads62p44_regs.fine_gain = int(gain/0.5);
+ this->send_ads62p44_reg(0x17);
+ } else UHD_THROW_INVALID_CODE_PATH(); //should never have been called for USRP2
+ }
+
+ void set_rx_digital_fine_gain(float gain) { //gain correction
+ if(_iface->get_hw_rev() >= USRP2P_FIRST_HW_REV) {
+ _ads62p44_regs.gain_correction = int(gain / 0.05);
+ this->send_ads62p44_reg(0x1A);
+ } else UHD_THROW_INVALID_CODE_PATH(); //should never have been called for USRP2
+ }
+
+ void set_rx_analog_gain(bool gain) { //turns on/off analog 3.5dB preamp
+ if(_iface->get_hw_rev() >= USRP2P_FIRST_HW_REV) {
+ _ads62p44_regs.coarse_gain = gain ? ads62p44_regs_t::COARSE_GAIN_3_5DB : ads62p44_regs_t::COARSE_GAIN_0DB;
+ this->send_ads62p44_reg(0x14);
+ } else UHD_THROW_INVALID_CODE_PATH();
}
private:
ad9777_regs_t _ad9777_regs;
+ ads62p44_regs_t _ads62p44_regs;
usrp2_iface::sptr _iface;
void send_ad9777_reg(boost::uint8_t addr){
@@ -81,6 +120,14 @@ private:
reg, 16, false /*no rb*/
);
}
+
+ void send_ads62p44_reg(boost::uint8_t addr) {
+ boost::uint16_t reg = _ads62p44_regs.get_write_reg(addr);
+ _iface->transact_spi(
+ SPI_SS_ADS62P44, spi_config_t::EDGE_FALL,
+ reg, 16, false /*no rb*/
+ );
+ }
};
/***********************************************************************
diff --git a/host/lib/usrp/usrp2/codec_ctrl.hpp b/host/lib/usrp/usrp2/codec_ctrl.hpp
index ad014e0e1..57a37b94b 100644
--- a/host/lib/usrp/usrp2/codec_ctrl.hpp
+++ b/host/lib/usrp/usrp2/codec_ctrl.hpp
@@ -33,6 +33,27 @@ public:
*/
static sptr make(usrp2_iface::sptr iface);
+ /*!
+ * Set the analog preamplifier on the USRP2+ ADC (ADS62P44).
+ * \param gain enable or disable the 3.5dB preamp
+ */
+
+ virtual void set_rx_analog_gain(bool gain) = 0;
+
+ /*!
+ * Set the digital gain on the USRP2+ ADC (ADS62P44).
+ * \param gain from 0-6dB
+ */
+
+ virtual void set_rx_digital_gain(float gain) = 0;
+
+ /*!
+ * Set the digital gain correction on the USRP2+ ADC (ADS62P44).
+ * \param gain from 0-0.5dB
+ */
+
+ virtual void set_rx_digital_fine_gain(float gain) = 0;
+
};
#endif /* INCLUDED_CODEC_CTRL_HPP */
diff --git a/host/lib/usrp/usrp2/codec_impl.cpp b/host/lib/usrp/usrp2/codec_impl.cpp
index fc917b102..db98e50ab 100644
--- a/host/lib/usrp/usrp2/codec_impl.cpp
+++ b/host/lib/usrp/usrp2/codec_impl.cpp
@@ -17,10 +17,23 @@
#include "usrp2_impl.hpp"
#include <uhd/usrp/codec_props.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/types/ranges.hpp>
#include <boost/bind.hpp>
+#include <boost/assign/list_of.hpp>
+#include <uhd/utils/assert.hpp>
+#include <uhd/utils/exception.hpp>
using namespace uhd;
using namespace uhd::usrp;
+using namespace boost::assign;
+
+//this only applies to USRP2P
+static const uhd::dict<std::string, gain_range_t> codec_rx_gain_ranges = map_list_of
+ ("analog", gain_range_t(0, 3.5, 3.5))
+ ("digital", gain_range_t(0, 6.0, 0.5))
+ ("digital-fine", gain_range_t(0, 0.5, 0.05));
+
/***********************************************************************
* Helper Methods
@@ -40,7 +53,8 @@ void usrp2_mboard_impl::codec_init(void){
/***********************************************************************
* RX Codec Properties
**********************************************************************/
-void usrp2_mboard_impl::rx_codec_get(const wax::obj &key, wax::obj &val){
+void usrp2_mboard_impl::rx_codec_get(const wax::obj &key_, wax::obj &val){
+ named_prop_t key = named_prop_t::extract(key_);
//handle the get request conditioned on the key
switch(key.as<codec_prop_t>()){
@@ -53,21 +67,74 @@ void usrp2_mboard_impl::rx_codec_get(const wax::obj &key, wax::obj &val){
return;
case CODEC_PROP_GAIN_NAMES:
- val = prop_names_t(); //no gain elements to be controlled
+ if(_iface->get_hw_rev() >= USRP2P_FIRST_HW_REV) {
+ val = prop_names_t(codec_rx_gain_ranges.keys());
+ } else val = prop_names_t();
+ return;
+
+ case CODEC_PROP_GAIN_I:
+ case CODEC_PROP_GAIN_Q:
+ assert_has(_codec_rx_gains.keys(), key.name, "codec rx gain name");
+ val = _codec_rx_gains[key.name];
return;
+ case CODEC_PROP_GAIN_RANGE:
+ assert_has(codec_rx_gain_ranges.keys(), key.name, "codec rx gain range name");
+ val = codec_rx_gain_ranges[key.name];
+ return;
+
default: UHD_THROW_PROP_GET_ERROR();
}
}
-void usrp2_mboard_impl::rx_codec_set(const wax::obj &, const wax::obj &){
- UHD_THROW_PROP_SET_ERROR();
+void usrp2_mboard_impl::rx_codec_set(const wax::obj &key_, const wax::obj &val){
+ named_prop_t key = named_prop_t::extract(key_);
+ float gain;
+
+ switch(key.as<codec_prop_t>()) {
+ case CODEC_PROP_GAIN_I:
+ case CODEC_PROP_GAIN_Q:
+ if(_iface->get_hw_rev() < USRP2P_FIRST_HW_REV) UHD_THROW_PROP_SET_ERROR();//this capability is only found in USRP2P
+
+ gain = val.as<float>();
+ this->rx_codec_set_gain(gain, key.name);
+ return;
+
+ default:
+ UHD_THROW_PROP_SET_ERROR();
+ }
+}
+
+/***********************************************************************
+ * Helper function to set RX codec gain
+ ***********************************************************************/
+
+void usrp2_mboard_impl::rx_codec_set_gain(float gain, const std::string &name){
+ assert_has(codec_rx_gain_ranges.keys(), name, "codec rx gain name");
+
+ _codec_rx_gains[name] = gain;
+
+ if(name == "analog") {
+ _codec_ctrl->set_rx_analog_gain(gain > 0); //just turn it on or off
+ return;
+ }
+ if(name == "digital") {
+ _codec_ctrl->set_rx_digital_gain(gain);
+ return;
+ }
+ if(name == "digital-fine") {
+ _codec_ctrl->set_rx_digital_fine_gain(gain);
+ return;
+ }
+ UHD_THROW_PROP_SET_ERROR();
}
+
/***********************************************************************
* TX Codec Properties
**********************************************************************/
-void usrp2_mboard_impl::tx_codec_get(const wax::obj &key, wax::obj &val){
+void usrp2_mboard_impl::tx_codec_get(const wax::obj &key_, wax::obj &val){
+ named_prop_t key = named_prop_t::extract(key_);
//handle the get request conditioned on the key
switch(key.as<codec_prop_t>()){
diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp
index f6d2b718a..c79044bce 100644
--- a/host/lib/usrp/usrp2/dboard_iface.cpp
+++ b/host/lib/usrp/usrp2/dboard_iface.cpp
@@ -176,8 +176,8 @@ void usrp2_dboard_iface::set_pin_ctrl(unit_t unit, boost::uint16_t value){
//write the selection mux value to register
switch(unit){
- case UNIT_RX: _iface->poke32(U2_REG_GPIO_RX_SEL, new_sels); return;
- case UNIT_TX: _iface->poke32(U2_REG_GPIO_TX_SEL, new_sels); return;
+ case UNIT_RX: _iface->poke32(_iface->regs.gpio_rx_sel, new_sels); return;
+ case UNIT_TX: _iface->poke32(_iface->regs.gpio_tx_sel, new_sels); return;
}
}
@@ -185,18 +185,18 @@ void usrp2_dboard_iface::set_gpio_ddr(unit_t unit, boost::uint16_t value){
_ddr_shadow = \
(_ddr_shadow & ~(0xffff << unit_to_shift[unit])) |
(boost::uint32_t(value) << unit_to_shift[unit]);
- _iface->poke32(U2_REG_GPIO_DDR, _ddr_shadow);
+ _iface->poke32(_iface->regs.gpio_ddr, _ddr_shadow);
}
void usrp2_dboard_iface::write_gpio(unit_t unit, boost::uint16_t value){
_gpio_shadow = \
(_gpio_shadow & ~(0xffff << unit_to_shift[unit])) |
(boost::uint32_t(value) << unit_to_shift[unit]);
- _iface->poke32(U2_REG_GPIO_IO, _gpio_shadow);
+ _iface->poke32(_iface->regs.gpio_io, _gpio_shadow);
}
boost::uint16_t usrp2_dboard_iface::read_gpio(unit_t unit){
- return boost::uint16_t(_iface->peek32(U2_REG_GPIO_IO) >> unit_to_shift[unit]);
+ return boost::uint16_t(_iface->peek32(_iface->regs.gpio_io) >> unit_to_shift[unit]);
}
void usrp2_dboard_iface::set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value){
@@ -205,16 +205,16 @@ void usrp2_dboard_iface::set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t
unit_t, uhd::dict<atr_reg_t, boost::uint32_t>
> unit_to_atr_to_addr = map_list_of
(UNIT_RX, map_list_of
- (ATR_REG_IDLE, U2_REG_ATR_IDLE_RXSIDE)
- (ATR_REG_TX_ONLY, U2_REG_ATR_INTX_RXSIDE)
- (ATR_REG_RX_ONLY, U2_REG_ATR_INRX_RXSIDE)
- (ATR_REG_FULL_DUPLEX, U2_REG_ATR_FULL_RXSIDE)
+ (ATR_REG_IDLE, _iface->regs.atr_idle_rxside)
+ (ATR_REG_TX_ONLY, _iface->regs.atr_intx_rxside)
+ (ATR_REG_RX_ONLY, _iface->regs.atr_inrx_rxside)
+ (ATR_REG_FULL_DUPLEX, _iface->regs.atr_full_rxside)
)
(UNIT_TX, map_list_of
- (ATR_REG_IDLE, U2_REG_ATR_IDLE_TXSIDE)
- (ATR_REG_TX_ONLY, U2_REG_ATR_INTX_TXSIDE)
- (ATR_REG_RX_ONLY, U2_REG_ATR_INRX_TXSIDE)
- (ATR_REG_FULL_DUPLEX, U2_REG_ATR_FULL_TXSIDE)
+ (ATR_REG_IDLE, _iface->regs.atr_idle_txside)
+ (ATR_REG_TX_ONLY, _iface->regs.atr_intx_txside)
+ (ATR_REG_RX_ONLY, _iface->regs.atr_inrx_txside)
+ (ATR_REG_FULL_DUPLEX, _iface->regs.atr_full_txside)
)
;
_iface->poke16(unit_to_atr_to_addr[unit][atr], value);
@@ -234,8 +234,8 @@ void usrp2_dboard_iface::set_gpio_debug(unit_t unit, int which){
//write the selection mux value to register
switch(unit){
- case UNIT_RX: _iface->poke32(U2_REG_GPIO_RX_SEL, new_sels); return;
- case UNIT_TX: _iface->poke32(U2_REG_GPIO_TX_SEL, new_sels); return;
+ case UNIT_RX: _iface->poke32(_iface->regs.gpio_rx_sel, new_sels); return;
+ case UNIT_TX: _iface->poke32(_iface->regs.gpio_tx_sel, new_sels); return;
}
}
diff --git a/host/lib/usrp/usrp2/dsp_impl.cpp b/host/lib/usrp/usrp2/dsp_impl.cpp
index 6422142ce..97bda51fc 100644
--- a/host/lib/usrp/usrp2/dsp_impl.cpp
+++ b/host/lib/usrp/usrp2/dsp_impl.cpp
@@ -87,7 +87,7 @@ void usrp2_mboard_impl::ddc_set(const wax::obj &key, const wax::obj &val){
case DSP_PROP_FREQ_SHIFT:{
double new_freq = val.as<double>();
- _iface->poke32(U2_REG_DSP_RX_FREQ,
+ _iface->poke32(_iface->regs.dsp_rx_freq,
dsp_type1::calc_cordic_word_and_update(new_freq, get_master_clock_freq())
);
_ddc_freq = new_freq; //shadow
@@ -99,11 +99,11 @@ void usrp2_mboard_impl::ddc_set(const wax::obj &key, const wax::obj &val){
_ddc_decim = pick_closest_rate(extact_rate, _allowed_decim_and_interp_rates);
//set the decimation
- _iface->poke32(U2_REG_DSP_RX_DECIM_RATE, dsp_type1::calc_cic_filter_word(_ddc_decim));
+ _iface->poke32(_iface->regs.dsp_rx_decim_rate, dsp_type1::calc_cic_filter_word(_ddc_decim));
//set the scaling
static const boost::int16_t default_rx_scale_iq = 1024;
- _iface->poke32(U2_REG_DSP_RX_SCALE_IQ,
+ _iface->poke32(_iface->regs.dsp_rx_scale_iq,
dsp_type1::calc_iq_scale_word(default_rx_scale_iq, default_rx_scale_iq)
);
}
@@ -162,7 +162,7 @@ void usrp2_mboard_impl::duc_set(const wax::obj &key, const wax::obj &val){
case DSP_PROP_FREQ_SHIFT:{
double new_freq = val.as<double>();
- _iface->poke32(U2_REG_DSP_TX_FREQ,
+ _iface->poke32(_iface->regs.dsp_tx_freq,
dsp_type1::calc_cordic_word_and_update(new_freq, get_master_clock_freq())
);
_duc_freq = new_freq; //shadow
@@ -174,10 +174,10 @@ void usrp2_mboard_impl::duc_set(const wax::obj &key, const wax::obj &val){
_duc_interp = pick_closest_rate(extact_rate, _allowed_decim_and_interp_rates);
//set the interpolation
- _iface->poke32(U2_REG_DSP_TX_INTERP_RATE, dsp_type1::calc_cic_filter_word(_duc_interp));
+ _iface->poke32(_iface->regs.dsp_tx_interp_rate, dsp_type1::calc_cic_filter_word(_duc_interp));
//set the scaling
- _iface->poke32(U2_REG_DSP_TX_SCALE_IQ, dsp_type1::calc_iq_scale_word(_duc_interp));
+ _iface->poke32(_iface->regs.dsp_tx_scale_iq, dsp_type1::calc_iq_scale_word(_duc_interp));
}
return;
diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h
index e812e1221..12ff1a76f 100644
--- a/host/lib/usrp/usrp2/fw_common.h
+++ b/host/lib/usrp/usrp2/fw_common.h
@@ -83,6 +83,12 @@ typedef enum{
USRP2_CTRL_ID_PEEK_AT_THIS_REGISTER_FOR_ME_BRO = 'r',
USRP2_CTRL_ID_WOAH_I_DEFINITELY_PEEKED_IT_DUDE = 'R',
+ USRP2_CTRL_ID_HEY_WRITE_THIS_UART_FOR_ME_BRO = 'u',
+ USRP2_CTRL_ID_MAN_I_TOTALLY_WROTE_THAT_UART_DUDE = 'U',
+
+ USRP2_CTRL_ID_SO_LIKE_CAN_YOU_READ_THIS_UART_BRO = 'v',
+ USRP2_CTRL_ID_I_HELLA_READ_THAT_UART_DUDE = 'V',
+
USRP2_CTRL_ID_PEACE_OUT = '~'
} usrp2_ctrl_id_t;
@@ -123,6 +129,11 @@ typedef struct{
__stdint(uint32_t) datahi;
__stdint(uint8_t) num_bytes; //1, 2, 4, 8
} poke_args;
+ struct {
+ __stdint(uint8_t) dev;
+ __stdint(uint8_t) bytes;
+ __stdint(uint8_t) data[20];
+ } uart_args;
} data;
} usrp2_ctrl_data_t;
diff --git a/host/lib/usrp/usrp2/gps_ctrl.cpp b/host/lib/usrp/usrp2/gps_ctrl.cpp
new file mode 100644
index 000000000..2273b2cd9
--- /dev/null
+++ b/host/lib/usrp/usrp2/gps_ctrl.cpp
@@ -0,0 +1,207 @@
+//
+// 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 "gps_ctrl.hpp"
+#include <uhd/utils/assert.hpp>
+#include <boost/cstdint.hpp>
+#include <string>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/thread.hpp>
+#include <boost/algorithm/string/trim.hpp>
+#include <boost/tokenizer.hpp>
+
+using namespace uhd;
+using namespace boost::gregorian;
+using namespace boost::posix_time;
+using namespace boost::algorithm;
+
+/*!
+ * A usrp2 GPS control for Jackson Labs devices
+ */
+
+//TODO: multiple baud rate support (requires mboard_impl changes for poking UART registers)
+class usrp2_gps_ctrl_impl : public usrp2_gps_ctrl{
+public:
+ usrp2_gps_ctrl_impl(usrp2_iface::sptr iface){
+ _iface = iface;
+
+ std::string reply;
+ bool i_heard_some_nmea = false, i_heard_something_weird = false;
+
+ gps_type = GPS_TYPE_NONE;
+
+// set_uart_baud_rate(GPS_UART, 115200);
+ //first we look for a Jackson Labs Firefly (since that's what we sell with the USRP2+...)
+
+ _iface->read_uart(GPS_UART); //get whatever junk is in the rx buffer right now, and throw it away
+ _iface->write_uart(GPS_UART, "HAAAY GUYYYYS\n"); //to elicit a response from the Firefly
+
+ //then we loop until we either timeout, or until we get a response that indicates we're a JL device
+ int timeout = GPS_TIMEOUT_TRIES;
+ while(timeout--) {
+ reply = safe_gps_read();
+ if(trim_right_copy(reply) == "Command Error") {
+ gps_type = GPS_TYPE_JACKSON_LABS;
+ break;
+ }
+ else if(reply.substr(0, 3) == "$GP") i_heard_some_nmea = true; //but keep looking for that "Command Error" response
+ else if(reply.length() != 0) i_heard_something_weird = true; //probably wrong baud rate
+ }
+
+ if((i_heard_some_nmea) && (gps_type != GPS_TYPE_JACKSON_LABS)) gps_type = GPS_TYPE_GENERIC_NMEA;
+
+ //otherwise, we can try some other common baud rates looking to see if a GPS is connected (todo, later)
+ if((gps_type == GPS_TYPE_NONE) && i_heard_something_weird) {
+ std::cout << "Invalid reply, possible incorrect baud rate" << std::endl;
+ }
+
+ bool found_gprmc = false;
+
+ switch(gps_type) {
+ case GPS_TYPE_JACKSON_LABS:
+ std::cout << "Found a Jackson Labs GPS" << std::endl;
+ //issue some setup stuff so it spits out the appropriate data
+ //none of these should issue replies so we don't bother looking for them
+ //we have to sleep between commands because the JL device, despite not acking, takes considerable time to process each command.
+ boost::this_thread::sleep(boost::posix_time::milliseconds(FIREFLY_STUPID_DELAY_MS));
+ _iface->write_uart(GPS_UART, "SYST:COMM:SER:ECHO OFF\n");
+ boost::this_thread::sleep(boost::posix_time::milliseconds(FIREFLY_STUPID_DELAY_MS));
+ _iface->write_uart(GPS_UART, "SYST:COMM:SER:PRO OFF\n");
+ boost::this_thread::sleep(boost::posix_time::milliseconds(FIREFLY_STUPID_DELAY_MS));
+ _iface->write_uart(GPS_UART, "GPS:GPGGA 0\n");
+ boost::this_thread::sleep(boost::posix_time::milliseconds(FIREFLY_STUPID_DELAY_MS));
+ _iface->write_uart(GPS_UART, "GPS:GGAST 0\n");
+ boost::this_thread::sleep(boost::posix_time::milliseconds(FIREFLY_STUPID_DELAY_MS));
+ _iface->write_uart(GPS_UART, "GPS:GPRMC 1\n");
+ boost::this_thread::sleep(boost::posix_time::milliseconds(FIREFLY_STUPID_DELAY_MS));
+
+// break;
+
+ case GPS_TYPE_GENERIC_NMEA:
+ if(gps_type == GPS_TYPE_GENERIC_NMEA) std::cout << "Found a generic NMEA GPS device" << std::endl;
+ found_gprmc = false;
+ //here we loop around looking for a GPRMC packet. if we don't get one, we don't have a usable GPS.
+ timeout = GPS_TIMEOUT_TRIES;
+ while(timeout--) {
+ reply = safe_gps_read();
+ if(reply.substr(0, 6) == "$GPRMC") {
+ found_gprmc = true;
+ break;
+ }
+ }
+ if(!found_gprmc) {
+ if(gps_type == GPS_TYPE_JACKSON_LABS) std::cout << "Firefly GPS not locked or warming up." << std::endl;
+ else std::cout << "GPS does not output GPRMC packets. Cannot retrieve time." << std::endl;
+ gps_type = GPS_TYPE_NONE;
+ }
+ break;
+
+ case GPS_TYPE_NONE:
+ default:
+ break;
+
+ }
+
+
+ }
+
+ ~usrp2_gps_ctrl_impl(void){
+
+ }
+
+ std::string safe_gps_read() {
+ std::string reply;
+ try {
+ reply = _iface->read_uart(GPS_UART);
+ //std::cerr << "Got reply from GPS: " << reply.c_str() << " with length = " << reply.length() << std::endl;
+ } catch (std::runtime_error err) {
+ if(err.what() != std::string("usrp2 no control response")) throw; //sorry can't cope with that
+ else { //we don't actually have a GPS installed
+ reply = std::string();
+ }
+ }
+ return reply;
+ }
+
+ ptime get_time(void) {
+ std::string reply;
+ ptime now;
+ boost::tokenizer<boost::escaped_list_separator<char> > tok(reply);
+ std::vector<std::string> toked;
+ int timeout = GPS_TIMEOUT_TRIES;
+ bool found_gprmc = false;
+ switch(gps_type) {
+ case GPS_TYPE_JACKSON_LABS: //deprecated in favor of a single NMEA parser
+ case GPS_TYPE_GENERIC_NMEA:
+
+ while(timeout--) {
+ reply = safe_gps_read();
+ if(reply.substr(0, 6) == "$GPRMC") {
+ found_gprmc = true;
+ break;
+ }
+ }
+ UHD_ASSERT_THROW(found_gprmc);
+
+ tok.assign(reply);
+ toked.assign(tok.begin(), tok.end());
+
+ UHD_ASSERT_THROW(toked.size() == 11); //if it's not we got something weird in there
+
+ now = ptime( date(
+ greg_year(boost::lexical_cast<int>(toked[8].substr(4, 2)) + 2000), //just trust me on this one
+ greg_month(boost::lexical_cast<int>(toked[8].substr(2, 2))),
+ greg_day(boost::lexical_cast<int>(toked[8].substr(0, 2)))
+ ),
+ hours( boost::lexical_cast<int>(toked[1].substr(0, 2)))
+ + minutes(boost::lexical_cast<int>(toked[1].substr(2, 2)))
+ + seconds(boost::lexical_cast<int>(toked[1].substr(4, 2)))
+ );
+ break;
+ case GPS_TYPE_NONE:
+ default:
+ throw std::runtime_error("get_time(): Unsupported GPS or no GPS detected\n");
+ break;
+ }
+ return now;
+ }
+
+ bool gps_detected(void) {
+ return (gps_type != GPS_TYPE_NONE);
+ }
+
+private:
+ usrp2_iface::sptr _iface;
+
+ enum {
+ GPS_TYPE_JACKSON_LABS,
+ GPS_TYPE_GENERIC_NMEA,
+ GPS_TYPE_NONE
+ } gps_type;
+
+ static const int GPS_UART = 2; //TODO: this should be plucked from fw_common.h or memory_map.h or somewhere in common with the firmware
+ static const int GPS_TIMEOUT_TRIES = 5;
+ static const int FIREFLY_STUPID_DELAY_MS = 200;
+
+};
+
+/***********************************************************************
+ * Public make function for the GPS control
+ **********************************************************************/
+usrp2_gps_ctrl::sptr usrp2_gps_ctrl::make(usrp2_iface::sptr iface){
+ return sptr(new usrp2_gps_ctrl_impl(iface));
+}
diff --git a/host/lib/usrp/usrp2/gps_ctrl.hpp b/host/lib/usrp/usrp2/gps_ctrl.hpp
new file mode 100644
index 000000000..5936a6fb6
--- /dev/null
+++ b/host/lib/usrp/usrp2/gps_ctrl.hpp
@@ -0,0 +1,53 @@
+//
+// 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/>.
+//
+
+#ifndef INCLUDED_GPS_CTRL_HPP
+#define INCLUDED_GPS_CTRL_HPP
+
+#include "usrp2_iface.hpp"
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+using namespace boost::posix_time;
+
+class usrp2_gps_ctrl : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<usrp2_gps_ctrl> sptr;
+
+ /*!
+ * Make a GPS config for Jackson Labs or generic NMEA GPS devices
+ */
+ static sptr make(usrp2_iface::sptr iface);
+
+ /*!
+ * Get the current GPS time and date
+ * \return current GPS time and date as boost::posix_time::ptime object
+ */
+ virtual ptime get_time(void) = 0;
+
+ /*!
+ * Tell you if there's a supported GPS connected or not
+ * \return true if a supported GPS is connected
+ */
+ virtual bool gps_detected(void) = 0;
+
+ //TODO: other fun things you can do with a GPS.
+
+};
+
+#endif /* INCLUDED_CLOCK_CTRL_HPP */
diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp
index 4e883cf81..94ff5c2ce 100644
--- a/host/lib/usrp/usrp2/io_impl.cpp
+++ b/host/lib/usrp/usrp2/io_impl.cpp
@@ -150,7 +150,7 @@ void usrp2_impl::io_init(void){
std::memcpy(send_buff->cast<void*>(), &data, sizeof(data));
send_buff->commit(sizeof(data));
//drain the recv buffers (may have junk)
- while (data_transport->get_recv_buff().get());
+ while (data_transport->get_recv_buff().get()){;}
}
//the number of recv frames is the number for the first transport
diff --git a/host/lib/usrp/usrp2/mboard_impl.cpp b/host/lib/usrp/usrp2/mboard_impl.cpp
index 0b9f8ee83..2669df9bc 100644
--- a/host/lib/usrp/usrp2/mboard_impl.cpp
+++ b/host/lib/usrp/usrp2/mboard_impl.cpp
@@ -28,9 +28,11 @@
#include <boost/assign/list_of.hpp>
#include <boost/asio/ip/address_v4.hpp>
#include <iostream>
+#include <boost/date_time/posix_time/posix_time.hpp>
using namespace uhd;
using namespace uhd::usrp;
+using namespace boost::posix_time;
/***********************************************************************
* Structors
@@ -50,10 +52,16 @@ usrp2_mboard_impl::usrp2_mboard_impl(
_rev_lo = _iface->read_eeprom(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_REV_LSB, 1).at(0);
_rev_hi = _iface->read_eeprom(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_REV_MSB, 1).at(0);
+ //set the device revision (USRP2 or USRP2+) based on the above
+ _iface->set_hw_rev((_rev_hi << 8) | _rev_lo);
+
//contruct the interfaces to mboard perifs
_clock_ctrl = usrp2_clock_ctrl::make(_iface);
_codec_ctrl = usrp2_codec_ctrl::make(_iface);
_serdes_ctrl = usrp2_serdes_ctrl::make(_iface);
+ //_gps_ctrl = usrp2_gps_ctrl::make(_iface);
+
+ //if(_gps_ctrl->gps_detected()) std::cout << "GPS time: " << _gps_ctrl->get_time() << std::endl;
//TODO move to dsp impl...
//load the allowed decim/interp rates
@@ -74,25 +82,25 @@ usrp2_mboard_impl::usrp2_mboard_impl(
//most if not all junk packets will never make it to the socket.
this->issue_ddc_stream_cmd(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);
- //init the rx control registers
- _iface->poke32(U2_REG_RX_CTRL_NSAMPS_PER_PKT, _io_helper.get_max_recv_samps_per_packet());
- _iface->poke32(U2_REG_RX_CTRL_NCHANNELS, 1);
- _iface->poke32(U2_REG_RX_CTRL_CLEAR_OVERRUN, 1); //reset
- _iface->poke32(U2_REG_RX_CTRL_VRT_HEADER, 0
+ //setup the vrt rx registers
+ _iface->poke32(_iface->regs.rx_ctrl_nsamps_per_pkt, _io_helper.get_max_recv_samps_per_packet());
+ _iface->poke32(_iface->regs.rx_ctrl_nchannels, 1);
+ _iface->poke32(_iface->regs.rx_ctrl_clear_overrun, 1); //reset
+ _iface->poke32(_iface->regs.rx_ctrl_vrt_header, 0
| (0x1 << 28) //if data with stream id
| (0x1 << 26) //has trailer
| (0x3 << 22) //integer time other
| (0x1 << 20) //fractional time sample count
);
- _iface->poke32(U2_REG_RX_CTRL_VRT_STREAM_ID, 0);
- _iface->poke32(U2_REG_RX_CTRL_VRT_TRAILER, 0);
- _iface->poke32(U2_REG_TIME64_TPS, size_t(get_master_clock_freq()));
+ _iface->poke32(_iface->regs.rx_ctrl_vrt_stream_id, 0);
+ _iface->poke32(_iface->regs.rx_ctrl_vrt_trailer, 0);
+ _iface->poke32(_iface->regs.time64_tps, size_t(get_master_clock_freq()));
//init the tx control registers
- _iface->poke32(U2_REG_TX_CTRL_NUM_CHAN, 0); //1 channel
- _iface->poke32(U2_REG_TX_CTRL_CLEAR_STATE, 1); //reset
- _iface->poke32(U2_REG_TX_CTRL_REPORT_SID, 1); //sid 1 (different from rx)
- _iface->poke32(U2_REG_TX_CTRL_POLICY, U2_FLAG_TX_CTRL_POLICY_NEXT_PACKET);
+ _iface->poke32(_iface->regs.tx_ctrl_num_chan, 0); //1 channel
+ _iface->poke32(_iface->regs.tx_ctrl_clear_state, 1); //reset
+ _iface->poke32(_iface->regs.tx_ctrl_report_sid, 1); //sid 1 (different from rx)
+ _iface->poke32(_iface->regs.tx_ctrl_policy, U2_FLAG_TX_CTRL_POLICY_NEXT_PACKET);
//init the ddc
init_ddc_config();
@@ -149,13 +157,13 @@ void usrp2_mboard_impl::update_clock_config(void){
}
//set the pps flags
- _iface->poke32(U2_REG_TIME64_FLAGS, pps_flags);
+ _iface->poke32(_iface->regs.time64_flags, pps_flags);
//clock source ref 10mhz
switch(_clock_config.ref_source){
- case clock_config_t::REF_INT : _iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x10); break;
- case clock_config_t::REF_SMA : _iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x1C); break;
- case clock_config_t::REF_MIMO: _iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x15); break;
+ case clock_config_t::REF_INT : _iface->poke32(_iface->regs.misc_ctrl_clock, 0x10); break;
+ case clock_config_t::REF_SMA : _iface->poke32(_iface->regs.misc_ctrl_clock, 0x1C); break;
+ case clock_config_t::REF_MIMO: _iface->poke32(_iface->regs.misc_ctrl_clock, 0x15); break;
default: throw std::runtime_error("usrp2: unhandled clock configuration reference source");
}
@@ -166,22 +174,22 @@ void usrp2_mboard_impl::update_clock_config(void){
void usrp2_mboard_impl::set_time_spec(const time_spec_t &time_spec, bool now){
//set the ticks
- _iface->poke32(U2_REG_TIME64_TICKS, time_spec.get_tick_count(get_master_clock_freq()));
+ _iface->poke32(_iface->regs.time64_ticks, time_spec.get_tick_count(get_master_clock_freq()));
//set the flags register
boost::uint32_t imm_flags = (now)? U2_FLAG_TIME64_LATCH_NOW : U2_FLAG_TIME64_LATCH_NEXT_PPS;
- _iface->poke32(U2_REG_TIME64_IMM, imm_flags);
+ _iface->poke32(_iface->regs.time64_imm, imm_flags);
//set the seconds (latches in all 3 registers)
- _iface->poke32(U2_REG_TIME64_SECS, boost::uint32_t(time_spec.get_full_secs()));
+ _iface->poke32(_iface->regs.time64_secs, boost::uint32_t(time_spec.get_full_secs()));
}
void usrp2_mboard_impl::issue_ddc_stream_cmd(const stream_cmd_t &stream_cmd){
- _iface->poke32(U2_REG_RX_CTRL_STREAM_CMD, dsp_type1::calc_stream_cmd_word(
+ _iface->poke32(_iface->regs.rx_ctrl_stream_cmd, dsp_type1::calc_stream_cmd_word(
stream_cmd, _io_helper.get_max_recv_samps_per_packet()
));
- _iface->poke32(U2_REG_RX_CTRL_TIME_SECS, boost::uint32_t(stream_cmd.time_spec.get_full_secs()));
- _iface->poke32(U2_REG_RX_CTRL_TIME_TICKS, stream_cmd.time_spec.get_tick_count(get_master_clock_freq()));
+ _iface->poke32(_iface->regs.rx_ctrl_time_secs, boost::uint32_t(stream_cmd.time_spec.get_full_secs()));
+ _iface->poke32(_iface->regs.rx_ctrl_time_ticks, stream_cmd.time_spec.get_tick_count(get_master_clock_freq()));
}
/***********************************************************************
@@ -265,7 +273,7 @@ void usrp2_mboard_impl::get(const wax::obj &key_, wax::obj &val){
case MBOARD_PROP_TIME_NOW:{
usrp2_iface::pair64 time64(
- _iface->peek64(U2_REG_TIME64_SECS_RB, U2_REG_TIME64_TICKS_RB)
+ _iface->peek64(_iface->regs.time64_secs_rb, _iface->regs.time64_ticks_rb)
);
val = time_spec_t(
time64.first, time64.second, get_master_clock_freq()
@@ -331,7 +339,7 @@ void usrp2_mboard_impl::set(const wax::obj &key, const wax::obj &val){
//sanity check
UHD_ASSERT_THROW(_rx_subdev_spec.size() == 1);
//set the mux
- _iface->poke32(U2_REG_DSP_RX_MUX, dsp_type1::calc_rx_mux_word(
+ _iface->poke32(_iface->regs.dsp_rx_mux, dsp_type1::calc_rx_mux_word(
_dboard_manager->get_rx_subdev(_rx_subdev_spec.front().sd_name)[SUBDEV_PROP_CONNECTION].as<subdev_conn_t>()
));
return;
@@ -342,7 +350,7 @@ void usrp2_mboard_impl::set(const wax::obj &key, const wax::obj &val){
//sanity check
UHD_ASSERT_THROW(_tx_subdev_spec.size() == 1);
//set the mux
- _iface->poke32(U2_REG_DSP_TX_MUX, dsp_type1::calc_tx_mux_word(
+ _iface->poke32(_iface->regs.dsp_tx_mux, dsp_type1::calc_tx_mux_word(
_dboard_manager->get_tx_subdev(_tx_subdev_spec.front().sd_name)[SUBDEV_PROP_CONNECTION].as<subdev_conn_t>()
));
return;
diff --git a/host/lib/usrp/usrp2/serdes_ctrl.cpp b/host/lib/usrp/usrp2/serdes_ctrl.cpp
index e83dceb96..1cda22f45 100644
--- a/host/lib/usrp/usrp2/serdes_ctrl.cpp
+++ b/host/lib/usrp/usrp2/serdes_ctrl.cpp
@@ -27,11 +27,11 @@ class usrp2_serdes_ctrl_impl : public usrp2_serdes_ctrl{
public:
usrp2_serdes_ctrl_impl(usrp2_iface::sptr iface){
_iface = iface;
- _iface->poke32(U2_REG_MISC_CTRL_SERDES, U2_FLAG_MISC_CTRL_SERDES_ENABLE | U2_FLAG_MISC_CTRL_SERDES_RXEN);
+ _iface->poke32(_iface->regs.misc_ctrl_serdes, U2_FLAG_MISC_CTRL_SERDES_ENABLE | U2_FLAG_MISC_CTRL_SERDES_RXEN);
}
~usrp2_serdes_ctrl_impl(void){
- _iface->poke32(U2_REG_MISC_CTRL_SERDES, 0); //power-down
+ _iface->poke32(_iface->regs.misc_ctrl_serdes, 0); //power-down
}
private:
diff --git a/host/lib/usrp/usrp2/usrp2_clk_regs.hpp b/host/lib/usrp/usrp2/usrp2_clk_regs.hpp
new file mode 100644
index 000000000..d057fb342
--- /dev/null
+++ b/host/lib/usrp/usrp2/usrp2_clk_regs.hpp
@@ -0,0 +1,63 @@
+//
+// 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/>.
+//
+
+#ifndef INCLUDED_USRP2_CLK_REGS_HPP
+#define INCLUDED_USRP2_CLK_REGS_HPP
+
+#include "usrp2_regs.hpp"
+
+class usrp2_clk_regs_t {
+public:
+ usrp2_clk_regs_t(void) { ; }
+ usrp2_clk_regs_t(int hw_rev) {
+ test = 0;
+ fpga = 1;
+ adc = (hw_rev>=USRP2P_FIRST_HW_REV) ? 2 : 4;
+ dac = 3;
+ serdes = (hw_rev>=USRP2P_FIRST_HW_REV) ? 4 : 2; //only used by usrp2+
+ tx_db = (hw_rev>=USRP2P_FIRST_HW_REV) ? 5 : 6;
+ exp = (hw_rev>=USRP2P_FIRST_HW_REV) ? 6 : 5;
+ rx_db = 7;
+ }
+
+ static int output(int clknum) { return 0x3C + clknum; }
+ static int div_lo(int clknum) { return 0x48 + 2 * clknum; }
+ static int div_hi(int clknum) { return 0x49 + 2 * clknum; }
+
+ const static int acounter = 0x04;
+ const static int bcounter_msb = 0x05;
+ const static int bcounter_lsb = 0x06;
+ const static int pll_1 = 0x07;
+ const static int pll_2 = 0x08;
+ const static int pll_3 = 0x09;
+ const static int pll_4 = 0x0A;
+ const static int ref_counter_msb = 0x0B;
+ const static int ref_counter_lsb = 0x0C;
+ const static int pll_5 = 0x0D;
+ const static int update = 0x5A;
+
+ int test;
+ int fpga;
+ int adc;
+ int dac;
+ int serdes;
+ int exp;
+ int tx_db;
+ int rx_db;
+};
+
+#endif //INCLUDED_USRP2_CLK_REGS_HPP
diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp
index 2d450bfc6..0771c4945 100644
--- a/host/lib/usrp/usrp2/usrp2_iface.cpp
+++ b/host/lib/usrp/usrp2/usrp2_iface.cpp
@@ -24,6 +24,7 @@
#include <boost/asio.hpp> //used for htonl and ntohl
#include <boost/assign/list_of.hpp>
#include <boost/format.hpp>
+#include <boost/tokenizer.hpp>
#include <stdexcept>
#include <algorithm>
@@ -49,15 +50,16 @@ public:
**********************************************************************/
usrp2_iface_impl(udp_simple::sptr ctrl_transport){
_ctrl_transport = ctrl_transport;
-
+/*
//check the fpga compatibility number
- const boost::uint32_t fpga_compat_num = this->peek32(U2_REG_COMPAT_NUM_RB);
+ const boost::uint32_t fpga_compat_num = this->peek32(this->regs.compat_num_rb);
if (fpga_compat_num != USRP2_FPGA_COMPAT_NUM){
throw std::runtime_error(str(boost::format(
"Expected fpga compatibility number %d, but got %d:\n"
"The fpga build is not compatible with the host code build."
) % int(USRP2_FPGA_COMPAT_NUM) % fpga_compat_num));
}
+*/
}
~usrp2_iface_impl(void){
@@ -172,6 +174,58 @@ public:
}
/***********************************************************************
+ * UART
+ **********************************************************************/
+ void write_uart(boost::uint8_t dev, const std::string &buf){
+ //first tokenize the string into 20-byte substrings
+ boost::offset_separator f(20, 1, true, true);
+ boost::tokenizer<boost::offset_separator> tok(buf, f);
+ std::vector<std::string> queue(tok.begin(), tok.end());
+
+ BOOST_FOREACH(std::string item, queue) {
+ //setup the out data
+ usrp2_ctrl_data_t out_data;
+ out_data.id = htonl(USRP2_CTRL_ID_HEY_WRITE_THIS_UART_FOR_ME_BRO);
+ out_data.data.uart_args.dev = dev;
+ out_data.data.uart_args.bytes = item.size();
+
+ //limitation of uart transaction size
+ UHD_ASSERT_THROW(item.size() <= sizeof(out_data.data.uart_args.data));
+
+ //copy in the data
+ std::copy(item.begin(), item.end(), out_data.data.uart_args.data);
+
+ //send and recv
+ usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data);
+ UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_MAN_I_TOTALLY_WROTE_THAT_UART_DUDE);
+ }
+ }
+
+ std::string read_uart(boost::uint8_t dev){
+ int readlen = 20;
+ std::string result;
+ while(readlen == 20) { //while we keep receiving full packets
+ //setup the out data
+ usrp2_ctrl_data_t out_data;
+ out_data.id = htonl(USRP2_CTRL_ID_SO_LIKE_CAN_YOU_READ_THIS_UART_BRO);
+ out_data.data.uart_args.dev = dev;
+ out_data.data.uart_args.bytes = 20;
+
+ //limitation of uart transaction size
+ //UHD_ASSERT_THROW(num_bytes <= sizeof(out_data.data.uart_args.data));
+
+ //send and recv
+ usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data);
+ UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_I_HELLA_READ_THAT_UART_DUDE);
+ readlen = in_data.data.uart_args.bytes;
+
+ //copy out the data
+ result += std::string((const char *)in_data.data.uart_args.data, (size_t)readlen);
+ }
+ return result;
+ }
+
+/***********************************************************************
* Send/Recv over control
**********************************************************************/
usrp2_ctrl_data_t ctrl_send_and_recv(const usrp2_ctrl_data_t &out_data){
@@ -203,6 +257,19 @@ public:
throw std::runtime_error("usrp2 no control response");
}
+ /***********************************************************************
+ * Get/set hardware revision
+ **********************************************************************/
+ void set_hw_rev(int rev) {
+ hw_rev = rev;
+ regs = usrp2_get_regs(rev); //might be a better place to do this
+ }
+
+ int get_hw_rev(void) {
+ return hw_rev;
+ }
+
+
private:
//this lovely lady makes it all possible
udp_simple::sptr _ctrl_transport;
diff --git a/host/lib/usrp/usrp2/usrp2_iface.hpp b/host/lib/usrp/usrp2/usrp2_iface.hpp
index 12fd4730a..55fbfa7e4 100644
--- a/host/lib/usrp/usrp2/usrp2_iface.hpp
+++ b/host/lib/usrp/usrp2/usrp2_iface.hpp
@@ -25,6 +25,7 @@
#include <boost/cstdint.hpp>
#include <utility>
#include "fw_common.h"
+#include "usrp2_regs.hpp"
/*!
* The usrp2 interface class:
@@ -102,6 +103,30 @@ public:
size_t num_bits,
bool readback
) = 0;
+
+ virtual void write_uart(boost::uint8_t dev, const std::string &buf) = 0;
+
+ virtual std::string read_uart(boost::uint8_t dev) = 0;
+
+ /*!
+ * Set the hardware revision number. Also selects the proper register set for the device.
+ * \param rev the 16-bit revision
+ */
+ virtual void set_hw_rev(int rev) = 0;
+
+ /*! Return the hardware revision number
+ * \return hardware revision
+ */
+ virtual int get_hw_rev(void) = 0;
+
+ /*!
+ * Register map selected from USRP2/USRP2+.
+ */
+ usrp2_regs_t regs;
+ /*!
+ * Hardware revision as returned by the device.
+ */
+ int hw_rev;
};
#endif /* INCLUDED_USRP2_IFACE_HPP */
diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp
index 157d17057..b37c61488 100644
--- a/host/lib/usrp/usrp2/usrp2_impl.hpp
+++ b/host/lib/usrp/usrp2/usrp2_impl.hpp
@@ -21,6 +21,7 @@
#include "usrp2_iface.hpp"
#include "clock_ctrl.hpp"
#include "codec_ctrl.hpp"
+#include "gps_ctrl.hpp"
#include "serdes_ctrl.hpp"
#include <uhd/device.hpp>
#include <uhd/utils/pimpl.hpp>
@@ -151,6 +152,7 @@ private:
usrp2_clock_ctrl::sptr _clock_ctrl;
usrp2_codec_ctrl::sptr _codec_ctrl;
usrp2_serdes_ctrl::sptr _serdes_ctrl;
+ usrp2_gps_ctrl::sptr _gps_ctrl;
//rx and tx dboard methods and objects
uhd::usrp::dboard_manager::sptr _dboard_manager;
@@ -172,6 +174,9 @@ private:
wax_obj_proxy::sptr _rx_codec_proxy;
wax_obj_proxy::sptr _tx_codec_proxy;
+ void rx_codec_set_gain(float, const std::string &);
+ uhd::dict<std::string, float> _codec_rx_gains;
+
//properties interface for rx dboard
void rx_dboard_get(const wax::obj &, wax::obj &);
void rx_dboard_set(const wax::obj &, const wax::obj &);
diff --git a/host/lib/usrp/usrp2/usrp2_regs.cpp b/host/lib/usrp/usrp2/usrp2_regs.cpp
new file mode 100644
index 000000000..49f077221
--- /dev/null
+++ b/host/lib/usrp/usrp2/usrp2_regs.cpp
@@ -0,0 +1,99 @@
+//
+// 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 "usrp2_regs.hpp"
+
+int sr_addr(int misc_output_base, int sr) {
+ return misc_output_base + 4 * sr;
+}
+
+usrp2_regs_t usrp2_get_regs(int hw_rev) {
+
+ //how about you just make this dependent on hw_rev instead of doing the init before main, and give up the const globals, since the application won't ever need both.
+ const int misc_output_base = (hw_rev>=USRP2P_FIRST_HW_REV) ? USRP2P_MISC_OUTPUT_BASE : USRP2_MISC_OUTPUT_BASE,
+ gpio_base = (hw_rev>=USRP2P_FIRST_HW_REV) ? USRP2P_GPIO_BASE : USRP2_GPIO_BASE,
+ atr_base = (hw_rev>=USRP2P_FIRST_HW_REV) ? USRP2P_ATR_BASE : USRP2_ATR_BASE,
+ bp_base = (hw_rev>=USRP2P_FIRST_HW_REV) ? USRP2P_BP_STATUS_BASE : USRP2_BP_STATUS_BASE;
+
+ usrp2_regs_t x;
+ x.sr_misc = 0;
+ x.sr_tx_prot_eng = 32;
+ x.sr_rx_prot_eng = 48;
+ x.sr_buffer_pool_ctrl = 64;
+ x.sr_udp_sm = 96;
+ x.sr_tx_dsp = 208;
+ x.sr_tx_ctrl = 224;
+ x.sr_rx_dsp = 160;
+ x.sr_rx_ctrl = 176;
+ x.sr_time64 = 192;
+ x.sr_simtimer = 198;
+ x.sr_last = 255;
+ x.misc_ctrl_clock = sr_addr(misc_output_base, 0);
+ x.misc_ctrl_serdes = sr_addr(misc_output_base, 1);
+ x.misc_ctrl_adc = sr_addr(misc_output_base, 2);
+ x.misc_ctrl_leds = sr_addr(misc_output_base, 3);
+ x.misc_ctrl_phy = sr_addr(misc_output_base, 4);
+ x.misc_ctrl_dbg_mux = sr_addr(misc_output_base, 5);
+ x.misc_ctrl_ram_page = sr_addr(misc_output_base, 6);
+ x.misc_ctrl_flush_icache = sr_addr(misc_output_base, 7);
+ x.misc_ctrl_led_src = sr_addr(misc_output_base, 8);
+ x.time64_secs = sr_addr(misc_output_base, x.sr_time64 + 0);
+ x.time64_ticks = sr_addr(misc_output_base, x.sr_time64 + 1);
+ x.time64_flags = sr_addr(misc_output_base, x.sr_time64 + 2);
+ x.time64_imm = sr_addr(misc_output_base, x.sr_time64 + 3);
+ x.time64_tps = sr_addr(misc_output_base, x.sr_time64 + 4);
+ x.time64_secs_rb = bp_base + 4*10;
+ x.time64_ticks_rb = bp_base + 4*11;
+ x.compat_num_rb = bp_base + 4*12;
+ x.dsp_tx_freq = sr_addr(misc_output_base, x.sr_tx_dsp + 0);
+ x.dsp_tx_scale_iq = sr_addr(misc_output_base, x.sr_tx_dsp + 1);
+ x.dsp_tx_interp_rate = sr_addr(misc_output_base, x.sr_tx_dsp + 2);
+ x.dsp_tx_mux = sr_addr(misc_output_base, x.sr_tx_dsp + 4);
+ x.dsp_rx_freq = sr_addr(misc_output_base, x.sr_rx_dsp + 0);
+ x.dsp_rx_scale_iq = sr_addr(misc_output_base, x.sr_rx_dsp + 1);
+ x.dsp_rx_decim_rate = sr_addr(misc_output_base, x.sr_rx_dsp + 2);
+ x.dsp_rx_dcoffset_i = sr_addr(misc_output_base, x.sr_rx_dsp + 3);
+ x.dsp_rx_dcoffset_q = sr_addr(misc_output_base, x.sr_rx_dsp + 4);
+ x.dsp_rx_mux = sr_addr(misc_output_base, x.sr_rx_dsp + 5);
+ x.gpio_io = gpio_base + 0;
+ x.gpio_ddr = gpio_base + 4;
+ x.gpio_tx_sel = gpio_base + 8;
+ x.gpio_rx_sel = gpio_base + 12;
+ x.atr_idle_txside = atr_base + 0;
+ x.atr_idle_rxside = atr_base + 2;
+ x.atr_intx_txside = atr_base + 4;
+ x.atr_intx_rxside = atr_base + 6;
+ x.atr_inrx_txside = atr_base + 8;
+ x.atr_inrx_rxside = atr_base + 10;
+ x.atr_full_txside = atr_base + 12;
+ x.atr_full_rxside = atr_base + 14;
+ x.rx_ctrl_stream_cmd = sr_addr(misc_output_base, x.sr_rx_ctrl + 0);
+ x.rx_ctrl_time_secs = sr_addr(misc_output_base, x.sr_rx_ctrl + 1);
+ x.rx_ctrl_time_ticks = sr_addr(misc_output_base, x.sr_rx_ctrl + 2);
+ x.rx_ctrl_clear_overrun = sr_addr(misc_output_base, x.sr_rx_ctrl + 3);
+ x.rx_ctrl_vrt_header = sr_addr(misc_output_base, x.sr_rx_ctrl + 4);
+ x.rx_ctrl_vrt_stream_id = sr_addr(misc_output_base, x.sr_rx_ctrl + 5);
+ x.rx_ctrl_vrt_trailer = sr_addr(misc_output_base, x.sr_rx_ctrl + 6);
+ x.rx_ctrl_nsamps_per_pkt = sr_addr(misc_output_base, x.sr_rx_ctrl + 7);
+ x.rx_ctrl_nchannels = sr_addr(misc_output_base, x.sr_rx_ctrl + 8);
+ x.tx_ctrl_num_chan = sr_addr(misc_output_base, x.sr_tx_ctrl + 0);
+ x.tx_ctrl_clear_state = sr_addr(misc_output_base, x.sr_tx_ctrl + 1);
+ x.tx_ctrl_report_sid = sr_addr(misc_output_base, x.sr_tx_ctrl + 2);
+ x.tx_ctrl_policy = sr_addr(misc_output_base, x.sr_tx_ctrl + 3);
+
+ return x;
+}
diff --git a/host/lib/usrp/usrp2/usrp2_regs.hpp b/host/lib/usrp/usrp2/usrp2_regs.hpp
index 064ad4e95..f74c78b7b 100644
--- a/host/lib/usrp/usrp2/usrp2_regs.hpp
+++ b/host/lib/usrp/usrp2/usrp2_regs.hpp
@@ -18,6 +18,93 @@
#ifndef INCLUDED_USRP2_REGS_HPP
#define INCLUDED_USRP2_REGS_HPP
+//these are used to set the
+
+#define USRP2_MISC_OUTPUT_BASE 0xD400
+#define USRP2_GPIO_BASE 0xC800
+#define USRP2_ATR_BASE 0xE400
+#define USRP2_BP_STATUS_BASE 0xCC00
+
+#define USRP2P_MISC_OUTPUT_BASE 0x2000
+#define USRP2P_GPIO_BASE 0x3200
+#define USRP2P_ATR_BASE 0x3800
+#define USRP2P_BP_STATUS_BASE 0x3300
+
+#define USRP2P_FIRST_HW_REV 0x0A00
+
+typedef struct {
+ int sr_misc;
+ int sr_tx_prot_eng;
+ int sr_rx_prot_eng;
+ int sr_buffer_pool_ctrl;
+ int sr_udp_sm;
+ int sr_tx_dsp;
+ int sr_tx_ctrl;
+ int sr_rx_dsp;
+ int sr_rx_ctrl;
+ int sr_time64;
+ int sr_simtimer;
+ int sr_last;
+ int misc_ctrl_clock;
+ int misc_ctrl_serdes;
+ int misc_ctrl_adc;
+ int misc_ctrl_leds;
+ int misc_ctrl_phy;
+ int misc_ctrl_dbg_mux;
+ int misc_ctrl_ram_page;
+ int misc_ctrl_flush_icache;
+ int misc_ctrl_led_src;
+ int time64_secs; // value to set absolute secs to on next PPS
+ int time64_ticks; // value to set absolute ticks to on next PPS
+ int time64_flags; // flags -- see chart below
+ int time64_imm; // set immediate (0=latch on next pps, 1=latch immediate, default=0)
+ int time64_tps; // ticks per second rollover count
+ int time64_secs_rb;
+ int time64_ticks_rb;
+ int compat_num_rb;
+ int dsp_tx_freq;
+ int dsp_tx_scale_iq;
+ int dsp_tx_interp_rate;
+ int dsp_tx_mux;
+ int dsp_rx_freq;
+ int dsp_rx_scale_iq;
+ int dsp_rx_decim_rate;
+ int dsp_rx_dcoffset_i;
+ int dsp_rx_dcoffset_q;
+ int dsp_rx_mux;
+ int gpio_base;
+ int gpio_io;
+ int gpio_ddr;
+ int gpio_tx_sel;
+ int gpio_rx_sel;
+ int atr_base;
+ int atr_idle_txside;
+ int atr_idle_rxside;
+ int atr_intx_txside;
+ int atr_intx_rxside;
+ int atr_inrx_txside;
+ int atr_inrx_rxside;
+ int atr_full_txside;
+ int atr_full_rxside;
+ int rx_ctrl_stream_cmd;
+ int rx_ctrl_time_secs;
+ int rx_ctrl_time_ticks;
+ int rx_ctrl_clear_overrun;
+ int rx_ctrl_vrt_header;
+ int rx_ctrl_vrt_stream_id;
+ int rx_ctrl_vrt_trailer;
+ int rx_ctrl_nsamps_per_pkt;
+ int rx_ctrl_nchannels;
+ int tx_ctrl_num_chan;
+ int tx_ctrl_clear_state;
+ int tx_ctrl_report_sid;
+ int tx_ctrl_policy;
+} usrp2_regs_t;
+
+extern const usrp2_regs_t usrp2_regs; //the register definitions, set in usrp2_regs.cpp and usrp2p_regs.cpp
+
+usrp2_regs_t usrp2_get_regs(int hw_rev);
+
////////////////////////////////////////////////////
// Settings Bus, Slave #7, Not Byte Addressable!
//
@@ -25,27 +112,12 @@
// 1KB of address space (== 256 32-bit write-only regs)
-#define MISC_OUTPUT_BASE 0xD400
+//#define MISC_OUTPUT_BASE 0xD400
//#define TX_PROTOCOL_ENGINE_BASE 0xD480
//#define RX_PROTOCOL_ENGINE_BASE 0xD4C0
//#define BUFFER_POOL_CTRL_BASE 0xD500
//#define LAST_SETTING_REG 0xD7FC // 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) + (4*(sr)))
-
/////////////////////////////////////////////////
// SPI Slave Constants
////////////////////////////////////////////////
@@ -58,20 +130,11 @@
#define SPI_SS_TX_DAC 32
#define SPI_SS_TX_ADC 64
#define SPI_SS_TX_DB 128
+#define SPI_SS_ADS62P44 256 //for usrp2p
/////////////////////////////////////////////////
// Misc Control
////////////////////////////////////////////////
-#define U2_REG_MISC_CTRL_CLOCK _SR_ADDR(0)
-#define U2_REG_MISC_CTRL_SERDES _SR_ADDR(1)
-#define U2_REG_MISC_CTRL_ADC _SR_ADDR(2)
-#define U2_REG_MISC_CTRL_LEDS _SR_ADDR(3)
-#define U2_REG_MISC_CTRL_PHY _SR_ADDR(4) // LSB is reset line to eth phy
-#define U2_REG_MISC_CTRL_DBG_MUX _SR_ADDR(5)
-#define U2_REG_MISC_CTRL_RAM_PAGE _SR_ADDR(6) // FIXME should go somewhere else...
-#define U2_REG_MISC_CTRL_FLUSH_ICACHE _SR_ADDR(7) // Flush the icache
-#define U2_REG_MISC_CTRL_LED_SRC _SR_ADDR(8) // HW or SW control for LEDs
-
#define U2_FLAG_MISC_CTRL_SERDES_ENABLE 8
#define U2_FLAG_MISC_CTRL_SERDES_PRBSEN 4
#define U2_FLAG_MISC_CTRL_SERDES_LOOPEN 2
@@ -99,15 +162,6 @@
*
* </pre>
*/
-#define U2_REG_TIME64_SECS _SR_ADDR(SR_TIME64 + 0) // value to set absolute secs to on next PPS
-#define U2_REG_TIME64_TICKS _SR_ADDR(SR_TIME64 + 1) // value to set absolute ticks to on next PPS
-#define U2_REG_TIME64_FLAGS _SR_ADDR(SR_TIME64 + 2) // flags - see chart above
-#define U2_REG_TIME64_IMM _SR_ADDR(SR_TIME64 + 3) // set immediate (0=latch on next pps, 1=latch immediate, default=0)
-#define U2_REG_TIME64_TPS _SR_ADDR(SR_TIME64 + 4) // the ticks per second rollover count
-
-#define U2_REG_TIME64_SECS_RB (0xCC00 + 4*10)
-#define U2_REG_TIME64_TICKS_RB (0xCC00 + 4*11)
-#define U2_REG_COMPAT_NUM_RB (0xCC00 + 4*12)
//pps flags (see above)
#define U2_FLAG_TIME64_PPS_NEGEDGE (0 << 0)
@@ -121,34 +175,72 @@
/////////////////////////////////////////////////
// DSP TX Regs
////////////////////////////////////////////////
-#define U2_REG_DSP_TX_FREQ _SR_ADDR(SR_TX_DSP + 0)
-#define U2_REG_DSP_TX_SCALE_IQ _SR_ADDR(SR_TX_DSP + 1) // {scale_i,scale_q}
-#define U2_REG_DSP_TX_INTERP_RATE _SR_ADDR(SR_TX_DSP + 2)
-#define U2_REG_DSP_TX_MUX _SR_ADDR(SR_TX_DSP + 4)
+
+ /*!
+ * \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>
+ */
+
/////////////////////////////////////////////////
// DSP RX Regs
////////////////////////////////////////////////
-#define U2_REG_DSP_RX_FREQ _SR_ADDR(SR_RX_DSP + 0)
-#define U2_REG_DSP_RX_SCALE_IQ _SR_ADDR(SR_RX_DSP + 1) // {scale_i,scale_q}
-#define U2_REG_DSP_RX_DECIM_RATE _SR_ADDR(SR_RX_DSP + 2)
-#define U2_REG_DSP_RX_DCOFFSET_I _SR_ADDR(SR_RX_DSP + 3) // Bit 31 high sets fixed offset mode, using lower 14 bits,
- // otherwise it is automatic
-#define U2_REG_DSP_RX_DCOFFSET_Q _SR_ADDR(SR_RX_DSP + 4) // Bit 31 high sets fixed offset mode, using lower 14 bits
-#define U2_REG_DSP_RX_MUX _SR_ADDR(SR_RX_DSP + 5) // called adc_mux in dsp_core_rx.v
+
+ /*!
+ * \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>
+ */
////////////////////////////////////////////////
// GPIO, Slave 4
////////////////////////////////////////////////
-//
-// These go to the daughterboard i/o pins
-//
-#define U2_REG_GPIO_BASE 0xC800
-
-#define U2_REG_GPIO_IO U2_REG_GPIO_BASE + 0 // 32 bits, gpio io pins (tx high 16 bits, rx low 16 bits)
-#define U2_REG_GPIO_DDR U2_REG_GPIO_BASE + 4 // 32 bits, gpio ddr, 1 means output (tx high 16 bits, rx low 16 bits)
-#define U2_REG_GPIO_TX_SEL U2_REG_GPIO_BASE + 8 // 16 2-bit fields select which source goes to TX DB
-#define U2_REG_GPIO_RX_SEL U2_REG_GPIO_BASE + 12 // 16 2-bit fields select which source goes to RX DB
// each 2-bit sel field is layed out this way
#define U2_FLAG_GPIO_SEL_GPIO 0 // if pin is an output, set by GPIO register
@@ -159,40 +251,20 @@
///////////////////////////////////////////////////
// ATR Controller, Slave 11
////////////////////////////////////////////////
-#define U2_REG_ATR_BASE 0xE400
-#define U2_REG_ATR_IDLE_TXSIDE U2_REG_ATR_BASE + 0
-#define U2_REG_ATR_IDLE_RXSIDE U2_REG_ATR_BASE + 2
-#define U2_REG_ATR_INTX_TXSIDE U2_REG_ATR_BASE + 4
-#define U2_REG_ATR_INTX_RXSIDE U2_REG_ATR_BASE + 6
-#define U2_REG_ATR_INRX_TXSIDE U2_REG_ATR_BASE + 8
-#define U2_REG_ATR_INRX_RXSIDE U2_REG_ATR_BASE + 10
-#define U2_REG_ATR_FULL_TXSIDE U2_REG_ATR_BASE + 12
-#define U2_REG_ATR_FULL_RXSIDE U2_REG_ATR_BASE + 14
///////////////////////////////////////////////////
// RX CTRL regs
///////////////////////////////////////////////////
-// The following 3 are logically a single command register.
-// They are clocked into the underlying fifo when time_ticks is written.
-#define U2_REG_RX_CTRL_STREAM_CMD _SR_ADDR(SR_RX_CTRL + 0) // {now, chain, num_samples(30)
-#define U2_REG_RX_CTRL_TIME_SECS _SR_ADDR(SR_RX_CTRL + 1)
-#define U2_REG_RX_CTRL_TIME_TICKS _SR_ADDR(SR_RX_CTRL + 2)
-
-#define U2_REG_RX_CTRL_CLEAR_OVERRUN _SR_ADDR(SR_RX_CTRL + 3) // write anything to clear overrun
-#define U2_REG_RX_CTRL_VRT_HEADER _SR_ADDR(SR_RX_CTRL + 4) // word 0 of packet. FPGA fills in packet counter
-#define U2_REG_RX_CTRL_VRT_STREAM_ID _SR_ADDR(SR_RX_CTRL + 5) // word 1 of packet.
-#define U2_REG_RX_CTRL_VRT_TRAILER _SR_ADDR(SR_RX_CTRL + 6)
-#define U2_REG_RX_CTRL_NSAMPS_PER_PKT _SR_ADDR(SR_RX_CTRL + 7)
-#define U2_REG_RX_CTRL_NCHANNELS _SR_ADDR(SR_RX_CTRL + 8) // 1 in basic case, up to 4 for vector sources
+
///////////////////////////////////////////////////
// TX CTRL regs
///////////////////////////////////////////////////
-#define U2_REG_TX_CTRL_NUM_CHAN _SR_ADDR(SR_TX_CTRL + 0)
-#define U2_REG_TX_CTRL_CLEAR_STATE _SR_ADDR(SR_TX_CTRL + 1)
-#define U2_REG_TX_CTRL_REPORT_SID _SR_ADDR(SR_TX_CTRL + 2)
-#define U2_REG_TX_CTRL_POLICY _SR_ADDR(SR_TX_CTRL + 3)
+//#define U2_REG_TX_CTRL_NUM_CHAN _SR_ADDR(SR_TX_CTRL + 0)
+//#define U2_REG_TX_CTRL_CLEAR_STATE _SR_ADDR(SR_TX_CTRL + 1)
+//#define U2_REG_TX_CTRL_REPORT_SID _SR_ADDR(SR_TX_CTRL + 2)
+//#define U2_REG_TX_CTRL_POLICY _SR_ADDR(SR_TX_CTRL + 3)
#define U2_FLAG_TX_CTRL_POLICY_WAIT (0x1 << 0)
#define U2_FLAG_TX_CTRL_POLICY_NEXT_PACKET (0x1 << 1)
diff --git a/host/utils/usrp2p_fw_update.py b/host/utils/usrp2p_fw_update.py
new file mode 100755
index 000000000..5eff83c07
--- /dev/null
+++ b/host/utils/usrp2p_fw_update.py
@@ -0,0 +1,259 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# TODO: make it work
+# TODO: make it autodetect UHD devices
+# TODO: you should probably watch sequence numbers
+
+import optparse
+import math
+import os
+import re
+import struct
+import socket
+import sys
+
+########################################################################
+# constants
+########################################################################
+UDP_FW_UPDATE_PORT = 49154
+UDP_MAX_XFER_BYTES = 1024
+UDP_TIMEOUT = 3
+UDP_POLL_INTERVAL = 0.10 #in seconds
+
+USRP2_FW_PROTO_VERSION = 6
+
+#from bootloader_utils.h
+PROD_FPGA_IMAGE_LOCATION_ADDR = 0x00200000
+PROD_FW_IMAGE_LOCATION_ADDR = 0x00400000
+SAFE_FW_IMAGE_LOCATION_ADDR = 0x007F0000
+SAFE_FPGA_IMAGE_LOCATION_ADDR = 0x00000000
+FPGA_IMAGE_SIZE_BYTES = 2097152
+FW_IMAGE_SIZE_BYTES = 31744
+
+FLASH_DATA_PACKET_SIZE = 256
+
+#see fw_common.h
+FLASH_ARGS_FMT = '!LLLLL256s'
+FLASH_INFO_FMT = '!LLLLL256x'
+FLASH_IP_FMT = '!LLLL260x'
+
+class update_id_t:
+ USRP2_FW_UPDATE_ID_WAT = ord(' ')
+ USRP2_FW_UPDATE_ID_OHAI_LOL = ord('a')
+ USRP2_FW_UPDATE_ID_OHAI_OMG = ord('A')
+ USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL = ord('f')
+ USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG = ord('F')
+ USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL = ord('e')
+ USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG = ord('E')
+ USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL = ord('d')
+ USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG = ord('D')
+ USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG = ord('B')
+ USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL = ord('w')
+ USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG = ord('W')
+ USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL = ord('r')
+ USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG = ord('R')
+ USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL = ord('s')
+ USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG = ord('S')
+ USRP2_FW_UPDATE_ID_KTHXBAI = ord('~')
+
+_seq = -1
+def seq():
+ global _seq
+ _seq = _seq+1
+ return _seq
+
+########################################################################
+# helper functions
+########################################################################
+def unpack_flash_args_fmt(s):
+ return struct.unpack(FLASH_ARGS_FMT, s) #(proto_ver, pktid, seq, flash_addr, length, data)
+
+def unpack_flash_info_fmt(s):
+ return struct.unpack(FLASH_INFO_FMT, s) #(proto_ver, pktid, seq, sector_size_bytes, memory_size_bytes)
+
+def unpack_flash_ip_fmt(s):
+ return struct.unpack(FLASH_IP_FMT, s) #(proto_ver, pktid, seq, ip_addr)
+
+def pack_flash_args_fmt(proto_ver, pktid, seq, flash_addr, length, data):
+ return struct.pack(FLASH_ARGS_FMT, proto_ver, pktid, seq, flash_addr, length, data)
+
+def pack_flash_info_fmt(proto_ver, pktid, seq, sector_size_bytes, memory_size_bytes):
+ return struct.pack(FLASH_INFO_FMT, proto_ver, pktid, seq, sector_size_bytes, memory_size_bytes)
+
+def send_and_recv(pkt, ip):
+ update_socket = create_socket()
+
+ try:
+ update_socket.sendto(pkt, (ip, UDP_FW_UPDATE_PORT))
+ except Exception, e:
+ print e
+ sys.exit(1)
+
+ try:
+ (recv_pkt, recv_addr) = update_socket.recvfrom(UDP_MAX_XFER_BYTES)
+ except Exception, e:
+ print e
+ sys.exit(1)
+
+ if recv_addr != (options.ip, UDP_FW_UPDATE_PORT):
+ raise Exception, "Packet received from invalid IP %s, expected %s" % (recv_addr, options.ip)
+
+ return recv_pkt
+
+def create_socket():
+ socket.setdefaulttimeout(UDP_TIMEOUT)
+ update_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ return update_socket
+
+#just here to validate comms
+def init_update(ip):
+ out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_OHAI_LOL, seq(), 0, 0, "")
+ in_pkt = send_and_recv(out_pkt, ip)
+ (proto_ver, pktid, rxseq, ip_addr) = unpack_flash_ip_fmt(in_pkt)
+ if pktid == update_id_t.USRP2_FW_UPDATE_ID_OHAI_OMG:
+ print "USRP2P found."
+ else:
+ raise Exception, "Invalid reply received from device."
+
+# print "Incoming:\n\tVer: %i\n\tID: %c\n\tSeq: %i\n\tIP: %i\n" % (proto_ver, chr(pktid), rxseq, ip_addr)
+
+def get_flash_info(ip):
+ out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL, seq(), 0, 0, "")
+ in_pkt = send_and_recv(out_pkt, ip)
+
+ (proto_ver, pktid, rxseq, sector_size_bytes, memory_size_bytes) = unpack_flash_info_fmt(in_pkt)
+
+ if pktid != update_id_t.USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG:
+ raise Exception, "Invalid reply %c from device." % (chr(pktid))
+
+
+ return (memory_size_bytes, sector_size_bytes)
+
+def burn_fw(ip, fw, fpga):
+ init_update(ip)
+ (flash_size, sector_size) = get_flash_info(ip)
+
+ print "Flash size: %i\nSector size: %i" % (flash_size, sector_size)
+
+ if fpga:
+ fpga_file = open(fpga, 'rb')
+ fpga_image = fpga_file.read()
+ erase_image(ip, SAFE_FPGA_IMAGE_LOCATION_ADDR, FPGA_IMAGE_SIZE_BYTES)
+ write_image(ip, fpga_image, SAFE_FPGA_IMAGE_LOCATION_ADDR) #TODO: DO NOT WRITE SAFE IMAGE! this is only here because the bootloader isn't finished yet
+ verify_image(ip, fpga_image, SAFE_FPGA_IMAGE_LOCATION_ADDR)
+
+ if fw:
+ fw_file = open(fw, 'rb')
+ fw_image = fw_file.read()
+ erase_image(ip, PROD_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES)
+ write_image(ip, fw_image, PROD_FW_IMAGE_LOCATION_ADDR)
+ verify_image(ip, fw_image, PROD_FW_IMAGE_LOCATION_ADDR)
+
+def write_image(ip, image, addr):
+#we split the image into smaller (256B) bits and send them down the wire
+ while image:
+ out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL, seq(), addr, FLASH_DATA_PACKET_SIZE, image[:FLASH_DATA_PACKET_SIZE])
+ in_pkt = send_and_recv(out_pkt, ip)
+
+ (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt)
+
+ if pktid != update_id_t.USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG:
+ raise Exception, "Invalid reply %c from device." % (chr(pktid))
+
+ image = image[FLASH_DATA_PACKET_SIZE:]
+ addr += FLASH_DATA_PACKET_SIZE
+
+def verify_image(ip, image, addr):
+ readsize = len(image)
+ readdata = str()
+ while readsize > 0:
+ if readsize < FLASH_DATA_PACKET_SIZE: thisreadsize = readsize
+ else: thisreadsize = FLASH_DATA_PACKET_SIZE
+ out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL, seq(), addr, thisreadsize, "")
+ in_pkt = send_and_recv(out_pkt, ip)
+
+ (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt)
+
+ if pktid != update_id_t.USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG:
+ raise Exception, "Invalid reply %c from device." % (chr(pktid))
+
+ readdata += data[:thisreadsize]
+ readsize -= FLASH_DATA_PACKET_SIZE
+ addr += FLASH_DATA_PACKET_SIZE
+
+ print "Read back %i bytes" % len(readdata)
+# print readdata
+
+# for i in range(256, 512):
+# print "out: %i in: %i" % (ord(image[i]), ord(readdata[i]))
+
+ if readdata != image:
+ print "Verify failed. Image did not write correctly."
+ else:
+ print "Success."
+
+def erase_image(ip, addr, length):
+ #get flash info first
+ out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL, seq(), addr, length, "")
+ in_pkt = send_and_recv(out_pkt, ip)
+
+ (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt)
+
+ if pktid != update_id_t.USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG:
+ raise Exception, "Invalid reply %c from device." % (chr(pktid))
+
+ print "Erasing %i bytes at %i" % (length, addr)
+
+ #now wait for it to finish
+ while(1):
+ out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL, seq(), 0, 0, "")
+ in_pkt = send_and_recv(out_pkt, ip)
+
+ (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt)
+
+ if pktid == update_id_t.USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG: break
+ elif pktid != update_id_t.USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG:
+ raise Exception, "Invalid reply %c from device." % (chr(pktid))
+
+ print "\tFinished."
+
+#def verify_image(ip, image, addr):
+
+
+########################################################################
+# command line options
+########################################################################
+def get_options():
+ parser = optparse.OptionParser()
+ parser.add_option("--ip", type="string", help="USRP2P firmware address", default='')
+ parser.add_option("--fw", type="string", help="firmware image path (optional)", default='')
+ parser.add_option("--fpga", type="string", help="fpga image path (optional)", default='')
+ (options, args) = parser.parse_args()
+
+ return options
+
+########################################################################
+# main
+########################################################################
+if __name__=='__main__':
+ options = get_options()
+ if not options.ip: raise Exception, 'no ip address specified'
+
+ if not options.fpga and not options.fw: raise Exception, 'Must specify either a firmware image or FPGA image.'
+ burn_fw(ip=options.ip, fw=options.fw, fpga=options.fpga)