diff options
Diffstat (limited to 'firmware/zpu/usrp2p')
25 files changed, 3177 insertions, 0 deletions
diff --git a/firmware/zpu/usrp2p/CMakeLists.txt b/firmware/zpu/usrp2p/CMakeLists.txt new file mode 100644 index 000000000..41ef8f1dd --- /dev/null +++ b/firmware/zpu/usrp2p/CMakeLists.txt @@ -0,0 +1,48 @@ +# +# 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(${CMAKE_SOURCE_DIR}/lib/CMakeLists.txt) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +ADD_DEFINITIONS(-DUSRP2P) + +ADD_LIBRARY(libusrp2pfw STATIC + ${COMMON_SRCS} + spif.c + spi_flash.c + spi_flash_read.c + bootloader_utils.c + ethernet.c + xilinx_s3_icap.c + udp_fw_update.c +) + +ADD_SUBDIRECTORY(bootloader) + +######################################################################## +ADD_EXECUTABLE(usrp2p_txrx_uhd.elf ${CMAKE_SOURCE_DIR}/apps/txrx_uhd.c) +TARGET_LINK_LIBRARIES(usrp2p_txrx_uhd.elf libusrp2pfw) +GEN_OUTPUTS(usrp2p_txrx_uhd.elf) + +ADD_EXECUTABLE(usrp2p_blinkenlights.elf ${CMAKE_SOURCE_DIR}/apps/blinkenlights.c) +TARGET_LINK_LIBRARIES(usrp2p_blinkenlights.elf libusrp2pfw) +GEN_OUTPUTS(usrp2p_blinkenlights.elf) + +ADD_EXECUTABLE(usrp2p_uart_flash_loader.elf ${CMAKE_SOURCE_DIR}/apps/uart_flash_loader.c) +TARGET_LINK_LIBRARIES(usrp2p_uart_flash_loader.elf libusrp2pfw) +GEN_OUTPUTS(usrp2p_uart_flash_loader.elf) diff --git a/firmware/zpu/usrp2p/Makefile.am b/firmware/zpu/usrp2p/Makefile.am new file mode 100644 index 000000000..40766b406 --- /dev/null +++ b/firmware/zpu/usrp2p/Makefile.am @@ -0,0 +1,71 @@ +# +# 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) \ + -DUSRP2P + +AM_LDFLAGS = \ + $(COMMON_LFLAGS) \ + -Wl,-defsym -Wl,_TEXT_START_ADDR=0x8050 \ + -Wl,-defsym -Wl,_STACK_SIZE=3072 + +LDADD = libusrp2p.a + +#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 \ + xilinx_s3_icap.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/zpu/usrp2p/bootconfig.h b/firmware/zpu/usrp2p/bootconfig.h new file mode 100644 index 000000000..35c2726ed --- /dev/null +++ b/firmware/zpu/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/zpu/usrp2p/bootloader/CMakeLists.txt b/firmware/zpu/usrp2p/bootloader/CMakeLists.txt new file mode 100644 index 000000000..41c86cc9a --- /dev/null +++ b/firmware/zpu/usrp2p/bootloader/CMakeLists.txt @@ -0,0 +1,39 @@ +# +# Copyright 2010 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +INCLUDE(FindPythonInterp) + +MACRO(GEN_RMI target) + GET_FILENAME_COMPONENT(name ${target} NAME_WE) + #command to create a rmi from elf + ADD_CUSTOM_COMMAND( + OUTPUT ${name}.rmi DEPENDS ${name}.bin + COMMAND ${PYTHON_EXECUTABLE} + ARGS ${CMAKE_SOURCE_DIR}/bin/bin_to_ram_macro_init.py ${name}.bin ${name}.rmi + ) + #add a top level target for output files + ADD_CUSTOM_TARGET( + ${name}_rmi ALL DEPENDS ${name}.rmi + ) +ENDMACRO(GEN_RMI) + +######################################################################## +ADD_EXECUTABLE(init_bootloader.elf init_bootloader.c) +TARGET_LINK_LIBRARIES(init_bootloader.elf libusrp2pfw) +GEN_OUTPUTS(init_bootloader.elf) +GEN_RMI(init_bootloader.bin) diff --git a/firmware/zpu/usrp2p/bootloader/Makefile.am b/firmware/zpu/usrp2p/bootloader/Makefile.am new file mode 100644 index 000000000..1fc5daf9c --- /dev/null +++ b/firmware/zpu/usrp2p/bootloader/Makefile.am @@ -0,0 +1,39 @@ +# +# Copyright 2007,2008,2009 Free Software Foundation, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +include $(top_srcdir)/Makefile.common + +ROM_LINKER_SCRIPT = u2p2-rom.ld + +# loads into 8K boot ram located at 0x0000_0000 +AM_CFLAGS = $(COMMON_CFLAGS) -I$(top_srcdir)/usrp2p +AM_LDFLAGS = -Wl,-T,$(ROM_LINKER_SCRIPT) $(COMMON_LFLAGS) -Wl,-defsym -Wl,_STACK_SIZE=1024 + +EXTRA_DIST = $(ROM_LINKER_SCRIPT) + +LDADD = $(top_srcdir)/usrp2p/libusrp2p.a + +noinst_PROGRAMS = \ + init_bootloader.elf + +init_bootloader_elf_SOURCES = init_bootloader.c + +.bin.rmi: + $(top_srcdir)/bin/bin_to_ram_macro_init.py $< $@ + +_generated_from_elf += \ + $(noinst_PROGRAMS:.elf=.rmi) diff --git a/firmware/zpu/usrp2p/bootloader/fpga_bootloader.c b/firmware/zpu/usrp2p/bootloader/fpga_bootloader.c new file mode 100644 index 000000000..9feff6ecd --- /dev/null +++ b/firmware/zpu/usrp2p/bootloader/fpga_bootloader.c @@ -0,0 +1,202 @@ +/* -*- c -*- */ +/* + * Copyright 2009 Ettus Research LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * This code is bootloader f/w for the slot 0 fpga image. It's job is + * to figure out which fpga image should be loaded, and then to load + * that image from the SPI flash. (FIXME handle retries, errors, + * etc.) + * + * If the center button is down during boot, it loads firwmare + * from 0:0 instead of its normal action. + */ + +#include <stdlib.h> +#include <hal_io.h> +#include <nonstdio.h> +#include <mdelay.h> +#include <quadradio/flashdir.h> +#include <xilinx_v5_icap.h> +#include <bootconfig.h> +#include <bootconfig_private.h> +#include <spi_flash.h> +#include <string.h> +#include <bootloader_utils.h> +#include <hal_interrupts.h> + +#define VERBOSE 1 + +#define OUR_FPGA_IMAGE_NUMBER 0 // this code only runs in slot 0 + +void hal_uart_init(void); +void spif_init(void); +void i2c_init(void); +void bootconfig_init(void); + +void pic_interrupt_handler() __attribute__ ((interrupt_handler)); + +void pic_interrupt_handler() +{ + // nop stub +} + +static int +flash_addr_of_fpga_slot(unsigned int fpga_slot) +{ + const struct flashdir *fd = get_flashdir(); + return fd->slot[fpga_slot + fd->fpga_slot0].start << spi_flash_log2_sector_size(); +} + + +/* + * If the first 256 bytes of the image contain the string of bytes, + * ff ff ff ff aa 99 55 66, we consider it a likely bitstream. + */ +static bool +looks_like_a_bitstream(unsigned int fpga_slot) +{ + unsigned char buf[256]; + static const unsigned char pattern[] = { + 0xff, 0xff, 0xff, 0xff, 0xaa, 0x99, 0x55, 0x66 + }; + + // Read the first 256 bytes of the bitstream + spi_flash_read(flash_addr_of_fpga_slot(fpga_slot), sizeof(buf), buf); + + for (int i = 0; i <= sizeof(buf) - sizeof(pattern); i++) + if (memcmp(pattern, &buf[i], sizeof(pattern)) == 0) + return true; + + return false; +} + +static bool +plausible_bootconfig(bootconfig_t bc) +{ + // Are the fields in range? + if (!validate_bootconfig(bc)) + return false; + + if (!looks_like_a_bitstream(map_fpga_image_number_to_fpga_slot(bc.fpga_image_number))) + return false; + + return true; +} + +// Attempt to boot the fpga image specified in next_boot +static void +initial_boot_attempt(eeprom_boot_info_t *ee) +{ + if (ee->next_boot.fpga_image_number == OUR_FPGA_IMAGE_NUMBER){ + load_firmware(); + return; + } + + ee->nattempts = 1; + _bc_write_eeprom_shadow(); + + unsigned int target_slot = + map_fpga_image_number_to_fpga_slot(ee->next_boot.fpga_image_number); + int flash_addr = flash_addr_of_fpga_slot(target_slot); + + putstr("fpga_bootloader: chaining to "); + puthex4(ee->next_boot.fpga_image_number); + putchar(':'); + puthex4(ee->next_boot.firmware_image_number); + newline(); + mdelay(100); + + while (1){ + icap_reload_fpga(flash_addr); + } +} + +int +main(int argc, char **argv) +{ + hal_disable_ints(); // In case we got here via jmp 0x0 + hal_uart_init(); + i2c_init(); + bootconfig_init(); // Must come after i2c_init. + spif_init(); // Needed for get_flashdir. + + sr_leds->leds = 0xAAAA; + + putstr("\n\n>>> fpga_bootloader <<<\n"); + + putstr("\nBOOTSTS "); + int bootsts = icap_read_config_reg(rBOOTSTS); + puthex32_nl(bootsts); + putstr("STAT "); + int stat = icap_read_config_reg(rSTAT); + puthex32_nl(stat); + + bool fallback = + ((bootsts & (BOOTSTS_VALID_0 | BOOTSTS_FALLBACK_0)) + == (BOOTSTS_VALID_0 | BOOTSTS_FALLBACK_0)); + + if (fallback){ + puts("FALLBACK_0 is set"); + // FIXME handle fallback condition. + } + + const struct flashdir *fd = get_flashdir(); + if (fd == 0) + abort(); + + eeprom_boot_info_t *ee = _bc_get_eeprom_shadow(); + + if (VERBOSE){ + putstr("nattempts: "); + puthex8_nl(ee->nattempts); + } + + mdelay(500); // wait for low-pass on switches + putstr("switches: "); puthex32_nl(readback->switches); + + bool center_btn_down = (readback->switches & BTN_CENTER) != 0; + if (center_btn_down){ + putstr("Center button is down!\n"); + // Force boot of image 0:0 + ee->next_boot = make_bootconfig(0, 0); + } + + // if next_boot is valid, try it + if (plausible_bootconfig(ee->next_boot)) + initial_boot_attempt(ee); // no return + + // if default_boot is valid, try it + if (plausible_bootconfig(ee->default_boot)){ + ee->next_boot = ee->default_boot; + initial_boot_attempt(ee); // no return + } + + // If we're here, we're in trouble. Try all of them... + for (int i = 0; i < 4; i++){ + bootconfig_t bc = make_bootconfig(i, 0); + if (plausible_bootconfig(bc)){ + ee->next_boot = bc; + initial_boot_attempt(ee); // no return + } + } + + // FIXME, try to find something we can load + puts("\n!!! Failed to find a valid FPGA bitstream!\n\n"); + + return 0; +} diff --git a/firmware/zpu/usrp2p/bootloader/fw_bootloader.c b/firmware/zpu/usrp2p/bootloader/fw_bootloader.c new file mode 100644 index 000000000..a2c32bf8e --- /dev/null +++ b/firmware/zpu/usrp2p/bootloader/fw_bootloader.c @@ -0,0 +1,50 @@ +/* -*- c -*- */ +/* + * Copyright 2009 Ettus Research LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <memory_map.h> +#include <nonstdio.h> +#include <stdlib.h> +#include <bootconfig.h> +#include <bootconfig_private.h> +#include <bootloader_utils.h> +#include <hal_interrupts.h> + + +void hal_uart_init(void); +void spif_init(void); +void i2c_init(void); +void bootconfig_init(void); + +void pic_interrupt_handler() __attribute__ ((interrupt_handler)); + +void pic_interrupt_handler() +{ + // nop stub +} + +int +main(int argc, char **argv) +{ + hal_disable_ints(); // In case we got here via jmp 0x0 + hal_uart_init(); + i2c_init(); + bootconfig_init(); // Must come after i2c_init. + spif_init(); // Needed for get_flashdir. + + load_firmware(); +} diff --git a/firmware/zpu/usrp2p/bootloader/icap_test.c b/firmware/zpu/usrp2p/bootloader/icap_test.c new file mode 100644 index 000000000..5feb9d014 --- /dev/null +++ b/firmware/zpu/usrp2p/bootloader/icap_test.c @@ -0,0 +1,31 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + */ + +#include <memory_map.h> +#include <hal_io.h> +#include <xilinx_s3_icap.h> +#include <nonstdio.h> + +void delay(uint32_t t) { + while(t-- != 0) asm("NOP"); +} + + +int main(int argc, char *argv[]) { + pic_init(); + hal_uart_init(); + puts("\nStarting delay...\n"); + + output_regs->leds = 0xFF; + delay(4000000); + output_regs->leds = 0x00; + delay(4000000); + + puts("Rebooting FPGA to 0x00000000\n"); + icap_reload_fpga((uint32_t)0x00000000); + + return 0; +} diff --git a/firmware/zpu/usrp2p/bootloader/init_bootloader.c b/firmware/zpu/usrp2p/bootloader/init_bootloader.c new file mode 100644 index 000000000..cfa80ffea --- /dev/null +++ b/firmware/zpu/usrp2p/bootloader/init_bootloader.c @@ -0,0 +1,120 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + */ + +#include <memory_map.h> +#include <nonstdio.h> +#include <hal_io.h> +#include <xilinx_s3_icap.h> +#include <spi_flash.h> +#include <spi_flash_private.h> +//#include <clocks.h> +#include <ihex.h> +#include <bootloader_utils.h> +#include <string.h> +#include <hal_uart.h> +#include <i2c.h> +#include "usrp2/fw_common.h" + +//void pic_interrupt_handler() __attribute__ ((interrupt_handler)); + +void pic_interrupt_handler() +{ + // nop stub +} + +void load_ihex(void) { //simple IHEX parser to load proper records into RAM. loads program when it receives end of record. + char buf[128]; //input data buffer + uint8_t ihx[32]; //ihex data buffer + + ihex_record_t ihex_record; + ihex_record.data = ihx; + + while(1) { + gets(buf); + + if(!ihex_parse(buf, &ihex_record)) { //RAM data record is valid + if(ihex_record.addr >= RAM_BASE) { //it's expecting to see FULLY RELOCATED IHX RECORDS. every address referenced to 0x8000, including vectors. + memcpy((void *) (ihex_record.addr), ihex_record.data, ihex_record.length); + puts("OK"); + } else if(ihex_record.type == 1) { //end of record + puts("OK"); + //load main firmware + start_program(); + puts("ERROR: main image returned! Back in IHEX load mode."); + } else puts("NOK"); //RAM loads do not support extended segment address records (04) -- upper 16 bits are always "0". + } else puts("NOK"); + } +} + +void delay(uint32_t t) { + while(t-- != 0) asm("NOP"); +} + +int main(int argc, char *argv[]) { + hal_disable_ints(); // In case we got here via jmp 0x0 + output_regs->leds = 0xFF; + delay(5000); + output_regs->leds = 0x00; + hal_uart_init(); + spif_init(); + i2c_init(); //for EEPROM + puts("USRP2+ bootloader super ultra ZPU edition\n"); + + bool production_image = find_safe_booted_flag(); + set_safe_booted_flag(0); //haven't booted yet + + if(BUTTON_PUSHED) { //see memory_map.h + puts("Starting USRP2+ in safe mode."); + if(is_valid_fw_image(SAFE_FW_IMAGE_LOCATION_ADDR)) { + set_safe_booted_flag(1); //let the firmware know it's the safe image + spi_flash_read(SAFE_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES, (void *)RAM_BASE); + start_program(); + puts("ERROR: return from main program! This should never happen!"); + icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR); + } else { + puts("ERROR: no safe firmware image available. I am a brick. Feel free to load IHEX to RAM."); + load_ihex(); + } + } + + if(!production_image) { + puts("Checking for valid production FPGA image..."); + if(is_valid_fpga_image(PROD_FPGA_IMAGE_LOCATION_ADDR)) { + puts("Valid production FPGA image found. Attempting to boot."); + set_safe_booted_flag(1); + delay(300); //so serial output can finish + icap_reload_fpga(PROD_FPGA_IMAGE_LOCATION_ADDR); + } + puts("No valid production FPGA image found.\nAttempting to load production firmware..."); + } + if(is_valid_fw_image(PROD_FW_IMAGE_LOCATION_ADDR)) { + puts("Valid production firmware found. Loading..."); + spi_flash_read(PROD_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES, (void *)RAM_BASE); + puts("Finished loading. Starting image."); + delay(300); + start_program(); + puts("ERROR: Return from main program! This should never happen!"); + //if this happens, though, the safest thing to do is reboot the whole FPGA and start over. + delay(300); + icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR); + return 1; + } + puts("No valid production firmware found. Trying safe firmware..."); + if(is_valid_fw_image(SAFE_FW_IMAGE_LOCATION_ADDR)) { + spi_flash_read(SAFE_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES, (void *)RAM_BASE); + puts("Finished loading. Starting image."); + delay(300); + start_program(); + puts("ERROR: return from main program! This should never happen!"); + delay(300); + icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR); + return 1; + } + puts("ERROR: no safe firmware image available. I am a brick. Feel free to load IHEX to RAM."); + load_ihex(); + + return 0; +} diff --git a/firmware/zpu/usrp2p/bootloader/serial_loader_burner.c b/firmware/zpu/usrp2p/bootloader/serial_loader_burner.c new file mode 100644 index 000000000..4ac4df454 --- /dev/null +++ b/firmware/zpu/usrp2p/bootloader/serial_loader_burner.c @@ -0,0 +1,49 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Ettus Research LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <hal_io.h> +#include <nonstdio.h> +#include <mdelay.h> +#include <gdbstub2.h> + +void hal_uart_init(void); +void spif_init(void); + +void pic_interrupt_handler() __attribute__ ((interrupt_handler)); + +void pic_interrupt_handler() +{ + // nop stub +} + +int +main(int argc, char **argv) +{ + hal_uart_init(); + spif_init(); + + sr_leds->leds = 0; + mdelay(100); + sr_leds->leds = ~0; + mdelay(100); + sr_leds->leds = 0; + + puts("\n\n>>> stage1: serial_loader_burner <<<"); + + gdbstub2_main_loop(); +} diff --git a/firmware/zpu/usrp2p/bootloader/spi_bootloader.c b/firmware/zpu/usrp2p/bootloader/spi_bootloader.c new file mode 100644 index 000000000..678e66cf7 --- /dev/null +++ b/firmware/zpu/usrp2p/bootloader/spi_bootloader.c @@ -0,0 +1,134 @@ +/* -*- c -*- */ +/* + * Copyright 2009 Ettus Research LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <hal_io.h> +#include <nonstdio.h> +#include <mdelay.h> +#include <spi_flash.h> +#include <quadradio/flashdir.h> +#include <quadradio/simple_binary_format.h> +#include <stdlib.h> + + +void hal_uart_init(void); +void spif_init(void); + +void pic_interrupt_handler() __attribute__ ((interrupt_handler)); + +void pic_interrupt_handler() +{ + // nop stub +} + +static void +error(int e) +{ + putstr("ERR"); + puthex8(e); + newline(); +} + +static void +load(uint32_t flash_addr, uint32_t ram_addr, uint32_t size) +{ + spi_flash_read(flash_addr, size, (void *) ram_addr); +} + +static bool +load_from_slot(const struct flashdir *fd, int fw_slot) +{ + putstr("Loading f/w image "); + putchar('0' + fw_slot); + putstr("... "); + + if (fw_slot >= fd->fw_nslots){ + error(1); + return false; + } + + int slot = fw_slot + fd->fw_slot0; + if (fd->slot[slot].start == 0 || fd->slot[slot].start == 0xffff + || fd->slot[slot].len == 0 || fd->slot[slot].len == 0xffff){ + error(2); + return false; + } + + uint32_t sbf_base = fd->slot[slot].start << spi_flash_log2_sector_size(); + uint32_t sbf_len = fd->slot[slot].len << spi_flash_log2_sector_size(); + uint32_t sbf_offset = 0; + + struct sbf_header sbf; + spi_flash_read(sbf_base, sizeof(struct sbf_header), &sbf); + if (sbf.magic != SBF_MAGIC || sbf.nsections > SBF_MAX_SECTIONS){ + error(3); + return false; + } + sbf_offset += sizeof(struct sbf_header); + + unsigned int i; + for (i = 0; i < sbf.nsections; i++){ + if (sbf_offset + sbf.sec_desc[i].length > sbf_len){ + error(4); + return false; + } + load(sbf_offset + sbf_base, + sbf.sec_desc[i].target_addr, + sbf.sec_desc[i].length); + sbf_offset += sbf.sec_desc[i].length; + } + putstr("Done!"); + + typedef void (*fptr_t)(void); + (*(fptr_t) sbf.entry)(); // almost certainly no return + + return true; +} + +int +main(int argc, char **argv) +{ + hal_uart_init(); + spif_init(); + + sr_leds->leds = 0; + mdelay(100); + sr_leds->leds = ~0; + mdelay(100); + sr_leds->leds = 0; + + putstr("\n>>> spi_bootloader <<<\n"); + + const struct flashdir *fd = get_flashdir(); + if (fd == 0) + abort(); + + while(1){ + int sw; + int fw_slot; + + sw = readback->switches; + fw_slot = sw & 0x7; + + if (!load_from_slot(fd, fw_slot)){ + if (fw_slot != 0){ + putstr("Falling back to slot 0\n"); + load_from_slot(fd, 0); + } + } + } +} diff --git a/firmware/zpu/usrp2p/bootloader/u2p2-rom.ld b/firmware/zpu/usrp2p/bootloader/u2p2-rom.ld new file mode 100644 index 000000000..4c9eaa8e5 --- /dev/null +++ b/firmware/zpu/usrp2p/bootloader/u2p2-rom.ld @@ -0,0 +1,190 @@ +/* + * Same as default, but with bss and stack moved to top 2K of main ram + * Copied from qr-rom.ld + */ + +/* Default linker script, for normal executables */ +OUTPUT_FORMAT("elf32-microblaze", "", "") +/*SEARCH_DIR("/home/eb/build/Xilinx_EDK_GNU_10.1i/mb/release/lin/mb/microblaze-xilinx-elf/lib");*/ + + +ENTRY(_start) +_TEXT_START_ADDR = DEFINED(_TEXT_START_ADDR) ? _TEXT_START_ADDR : 0x50; +_HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x0; +_STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : 0x400; +_BSS_START_ADDR = DEFINED(_BSS_START_ADDR) ? _BSS_START_ADDR : 0xF800; +SECTIONS +{ + .vectors.reset 0x0 : { KEEP (*(.vectors.reset)) } = 0 + .vectors.sw_exception 0x8 : { KEEP (*(.vectors.sw_exception)) } = 0 + .vectors.interrupt 0x10 : { KEEP (*(.vectors.interrupt)) } = 0 + .vectors.debug_sw_break 0x18 : { KEEP (*(.vectors.debug_sw_break)) } = 0 + .vectors.hw_exception 0x20 : { KEEP (*(.vectors.hw_exception)) } = 0 + . = _TEXT_START_ADDR; + _ftext = .; + .text : { + *(.text) + *(.text.*) + *(.gnu.linkonce.t.*) + } + _etext = .; + .init : { KEEP (*(.init)) } =0 + .fini : { KEEP (*(.fini)) } =0 + PROVIDE (__CTOR_LIST__ = .); + PROVIDE (___CTOR_LIST__ = .); + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + /* We don't want to include the .ctor section from + from the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + PROVIDE (__CTOR_END__ = .); + PROVIDE (___CTOR_END__ = .); + PROVIDE (__DTOR_LIST__ = .); + PROVIDE (___DTOR_LIST__ = .); + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + PROVIDE (__DTOR_END__ = .); + PROVIDE (___DTOR_END__ = .); + . = ALIGN(4); + _frodata = . ; + .rodata : { + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + CONSTRUCTORS; /* Is this needed? */ + } + _erodata = .; + /* Alignments by 8 to ensure that _SDA2_BASE_ on a word boundary */ + /* Note that .sdata2 and .sbss2 must be contiguous */ + . = ALIGN(8); + _ssrw = .; + .sdata2 : { + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + } + . = ALIGN(4); + .sbss2 : { + PROVIDE (__sbss2_start = .); + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + PROVIDE (__sbss2_end = .); + } + . = ALIGN(8); + _essrw = .; + _ssrw_size = _essrw - _ssrw; + PROVIDE (_SDA2_BASE_ = _ssrw + (_ssrw_size / 2 )); + . = ALIGN(4); + _fdata = .; + .data : { + *(.data) + *(.gnu.linkonce.d.*) + CONSTRUCTORS; /* Is this needed? */ + } + _edata = . ; + /* Added to handle pic code */ + .got : { + *(.got) + } + .got1 : { + *(.got1) + } + .got2 : { + *(.got2) + } + /* Added by Sathya to handle C++ exceptions */ + .eh_frame : { + *(.eh_frame) + } + .jcr : { + *(.jcr) + } + .gcc_except_table : { + *(.gcc_except_table) + } + /* Alignments by 8 to ensure that _SDA_BASE_ on a word boundary */ + /* Note that .sdata and .sbss must be contiguous */ + . = ALIGN(8); + _ssro = .; + .sdata : { + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + } + . = ALIGN(4); + .sbss : { + PROVIDE (__sbss_start = .); + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + PROVIDE (__sbss_end = .); + } + . = ALIGN(8); + _essro = .; + _ssro_size = _essro - _ssro; + PROVIDE (_SDA_BASE_ = _ssro + (_ssro_size / 2 )); + . = _BSS_START_ADDR; + . = ALIGN(4); + _fbss = .; + .bss : { + PROVIDE (__bss_start = .); + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + PROVIDE (__bss_end = .); + } + . = ALIGN(4); + .heap : { + _heap = .; + _heap_start = .; + . += _HEAP_SIZE; + _heap_end = .; + } + _end = .; + . = ALIGN(4); + . = 0xFFF0; + .stack : { + /* + _stack_end = .; + . += _STACK_SIZE; + . = ALIGN(8); + _stack = .; + _end = .; + */ + _stack_end = .; + _stack = .; + } + .tdata : { + *(.tdata) + *(.tdata.*) + *(.gnu.linkonce.td.*) + } + .tbss : { + *(.tbss) + *(.tbss.*) + *(.gnu.linkonce.tb.*) + } +} diff --git a/firmware/zpu/usrp2p/bootloader_utils.c b/firmware/zpu/usrp2p/bootloader_utils.c new file mode 100644 index 000000000..379c5f957 --- /dev/null +++ b/firmware/zpu/usrp2p/bootloader_utils.c @@ -0,0 +1,48 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + */ + +//contains routines for loading programs from Flash. depends on Flash libraries. +//also contains routines for reading / writing EEPROM flags for the bootloader +#include <stdbool.h> +#include <string.h> +#include <bootloader_utils.h> +#include <spi_flash.h> +#include <memory_map.h> +#include <nonstdio.h> + +int is_valid_fpga_image(uint32_t addr) { +// printf("is_valid_fpga_image(): starting with addr=%x...\n", addr); + uint8_t imgbuf[64]; + spi_flash_read(addr, 64, imgbuf); + //we're just looking for leading 0xFF padding, followed by the sync bytes 0xAA 0x99 + for(size_t i = 0; i<63; i++) { + if(imgbuf[i] == 0xFF) continue; + if(imgbuf[i] == 0xAA && imgbuf[i+1] == 0x99) { + //printf("is_valid_fpga_image(): found valid FPGA image\n"); + return 1; + } + } + + return 0; +} + +int is_valid_fw_image(uint32_t addr) { + static const uint8_t fwheader[] = {0x0b, 0x0b, 0x0b, 0x0b, 0x80, 0x70}; //just lookin for a jump to anywhere located at the reset vector + //printf("is_valid_fw_image(): starting with addr=%x...\n", addr); + uint8_t buf[12]; + spi_flash_read(addr, 6, buf); + //printf("is_valid_fw_image(): read "); + //for(int i = 0; i < 5; i++) printf("%x ", buf[i]); + //printf("\n"); + return memcmp(buf, fwheader, 6) == 0; +} + +void start_program(void) +{ + //ignoring the addr now + //all this does is tap that register + *((volatile uint32_t *) SR_ADDR_BLDRDONE) = 1; +} diff --git a/firmware/zpu/usrp2p/bootloader_utils.h b/firmware/zpu/usrp2p/bootloader_utils.h new file mode 100644 index 000000000..0f49ae6cd --- /dev/null +++ b/firmware/zpu/usrp2p/bootloader_utils.h @@ -0,0 +1,22 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + */ + +#include <stdint.h> + +//we're working in bytes and byte addresses so we can run the same code with Flash chips of different sector sizes. +//it's really 1463736, but rounded up to 1.5MB +#define FPGA_IMAGE_SIZE_BYTES 1572864 +//16K +#define FW_IMAGE_SIZE_BYTES 0x3fff + +#define SAFE_FPGA_IMAGE_LOCATION_ADDR 0x00000000 +#define SAFE_FW_IMAGE_LOCATION_ADDR 0x003F0000 +#define PROD_FPGA_IMAGE_LOCATION_ADDR 0x00180000 +#define PROD_FW_IMAGE_LOCATION_ADDR 0x00300000 + +int is_valid_fpga_image(uint32_t addr); +int is_valid_fw_image(uint32_t addr); +void start_program(void); diff --git a/firmware/zpu/usrp2p/eth_phy.h b/firmware/zpu/usrp2p/eth_phy.h new file mode 100644 index 000000000..d233e96e8 --- /dev/null +++ b/firmware/zpu/usrp2p/eth_phy.h @@ -0,0 +1,235 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* Much of this was extracted from the Linux e1000_hw.h file */ + +#ifndef INCLUDED_ETH_PHY_H +#define INCLUDED_ETH_PHY_H + +/* PHY 1000 MII Register/Bit Definitions */ +/* PHY Registers defined by IEEE */ + +#define PHY_CTRL 0x00 /* Control Register */ +#define PHY_STATUS 0x01 /* Status Regiser */ +#define PHY_ID1 0x02 /* Phy Id Reg (word 1) */ +#define PHY_ID2 0x03 /* Phy Id Reg (word 2) */ +#define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */ +#define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */ +#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */ +#define PHY_NEXT_PAGE_TX 0x07 /* Next Page TX */ +#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */ +#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */ +#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */ +#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */ + +/* PHY 1000 MII Register additions in ET1011C */ +#define PHY_INT_MASK 24 +#define PHY_INT_STATUS 25 +#define PHY_PHY_STATUS 26 +#define PHY_LED2 28 + +/* Bit definitions for some of the registers above */ + +/* PHY Control Register (PHY_CTRL) */ +#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ +#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ +#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ +#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */ +#define MII_CR_POWER_DOWN 0x0800 /* Power down */ +#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ +#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ +#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ + +/* PHY Status Register (PHY_STATUS) */ +#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ +#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ +#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ +#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ +#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ +#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ +#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ +#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ +#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ +#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ +#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ +#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ +#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ +#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ +#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ + +/* Autoneg Advertisement Register (PHY_AUTONEG_ADV) */ +#define NWAY_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */ +#define NWAY_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */ +#define NWAY_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */ +#define NWAY_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */ +#define NWAY_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */ +#define NWAY_AR_100T4_CAPS 0x0200 /* 100T4 Capable */ +#define NWAY_AR_PAUSE 0x0400 /* Pause operation desired */ +#define NWAY_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */ +#define NWAY_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */ +#define NWAY_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Link Partner Ability Register (Base Page) (PHY_LP_ABILITY) */ +#define NWAY_LPAR_SELECTOR_FIELD 0x0000 /* LP protocol selector field */ +#define NWAY_LPAR_10T_HD_CAPS 0x0020 /* LP is 10T Half Duplex Capable */ +#define NWAY_LPAR_10T_FD_CAPS 0x0040 /* LP is 10T Full Duplex Capable */ +#define NWAY_LPAR_100TX_HD_CAPS 0x0080 /* LP is 100TX Half Duplex Capable */ +#define NWAY_LPAR_100TX_FD_CAPS 0x0100 /* LP is 100TX Full Duplex Capable */ +#define NWAY_LPAR_100T4_CAPS 0x0200 /* LP is 100T4 Capable */ +#define NWAY_LPAR_PAUSE 0x0400 /* LP Pause operation desired */ +#define NWAY_LPAR_ASM_DIR 0x0800 /* LP Asymmetric Pause Direction bit */ +#define NWAY_LPAR_REMOTE_FAULT 0x2000 /* LP has detected Remote Fault */ +#define NWAY_LPAR_ACKNOWLEDGE 0x4000 /* LP has rx'd link code word */ +#define NWAY_LPAR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Autoneg Expansion Register (PHY_AUTONEG_EXP) */ +#define NWAY_ER_LP_NWAY_CAPS 0x0001 /* LP has Auto Neg Capability */ +#define NWAY_ER_PAGE_RXD 0x0002 /* LP is 10T Half Duplex Capable */ +#define NWAY_ER_NEXT_PAGE_CAPS 0x0004 /* LP is 10T Full Duplex Capable */ +#define NWAY_ER_LP_NEXT_PAGE_CAPS 0x0008 /* LP is 100TX Half Duplex Capable */ +#define NWAY_ER_PAR_DETECT_FAULT 0x0010 /* LP is 100TX Full Duplex Capable */ + +/* Next Page TX Register (PHY_NEXT_PAGE_TX) */ +#define NPTX_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define NPTX_TOGGLE 0x0800 /* Toggles between exchanges + * of different NP + */ +#define NPTX_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg + * 0 = cannot comply with msg + */ +#define NPTX_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ +#define NPTX_NEXT_PAGE 0x8000 /* 1 = addition NP will follow + * 0 = sending last NP + */ + +/* Link Partner Next Page Register (PHY_LP_NEXT_PAGE) */ +#define LP_RNPR_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define LP_RNPR_TOGGLE 0x0800 /* Toggles between exchanges + * of different NP + */ +#define LP_RNPR_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg + * 0 = cannot comply with msg + */ +#define LP_RNPR_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ +#define LP_RNPR_ACKNOWLDGE 0x4000 /* 1 = ACK / 0 = NO ACK */ +#define LP_RNPR_NEXT_PAGE 0x8000 /* 1 = addition NP will follow + * 0 = sending last NP + */ + +/* 1000BASE-T Control Register (PHY_1000T_CTRL) */ +#define CR_1000T_ASYM_PAUSE 0x0080 /* Advertise asymmetric pause bit */ +#define CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */ +#define CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */ +#define CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port */ + /* 0=DTE device */ +#define CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master */ + /* 0=Configure PHY as Slave */ +#define CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value */ + /* 0=Automatic Master/Slave config */ +#define CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ +#define CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ +#define CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ +#define CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ +#define CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ + +/* 1000BASE-T Status Register (PHY_1000T_STATUS) */ +#define SR_1000T_IDLE_ERROR_CNT 0x00FF /* Num idle errors since last read */ +#define SR_1000T_ASYM_PAUSE_DIR 0x0100 /* LP asymmetric pause direction bit */ +#define SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */ +#define SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */ +#define SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */ +#define SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */ +#define SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local TX is Master, 0=Slave */ +#define SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */ +#define SR_1000T_REMOTE_RX_STATUS_SHIFT 12 +#define SR_1000T_LOCAL_RX_STATUS_SHIFT 13 +#define SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT 5 +#define FFE_IDLE_ERR_COUNT_TIMEOUT_20 20 +#define FFE_IDLE_ERR_COUNT_TIMEOUT_100 100 + +/* Extended Status Register (PHY_EXT_STATUS) */ +#define IEEE_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */ +#define IEEE_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */ +#define IEEE_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */ +#define IEEE_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */ + +#define PHY_TX_POLARITY_MASK 0x0100 /* register 10h bit 8 (polarity bit) */ +#define PHY_TX_NORMAL_POLARITY 0 /* register 10h bit 8 (normal polarity) */ + +#define AUTO_POLARITY_DISABLE 0x0010 /* register 11h bit 4 */ + /* (0=enable, 1=disable) */ + +/* PHY Status Register (PHY_PHY_STATUS) */ +#define PHYSTAT_ASYMMETRIC (1 << 0) +#define PHYSTAT_PAUSE (1 << 1) +#define PHYSTAT_AUTONEG_EN (1 << 2) +#define PHYSTAT_COLLISION (1 << 3) +#define PHYSTAT_RXSTAT (1 << 4) +#define PHYSTAT_TXSTAT (1 << 5) +#define PHYSTAT_LINK (1 << 6) +#define PHYSTAT_DUPLEX (1 << 7) +#define PHYSTAT_SPEED_MASK ((1 << 8) | (1 << 9)) +#define PHYSTAT_SPEED_1000 (1 << 9) +#define PHYSTAT_SPEED_100 (1 << 8) +#define PHYSTAT_SPEED_10 0 +#define PHYSTAT_POLARITY (1 << 10) +#define PHYSTAT_MDIX (1 << 11) +#define PHYSTAT_AUTONEG_STAT (1 << 12) +#define PHYSTAT_STANDBY (1 << 13) + +/* Interrupt status, mask and clear regs (PHY_INT_{STATUS,MASK,CLEAR}) */ +#define PHY_INT_ENABLE (1 << 0) +#define PHY_INT_DOWNSHIFT (1 << 1) +#define PHY_INT_LINK_STATUS_CHANGE (1 << 2) +#define PHY_INT_RX_STATUS_CHANGE (1 << 3) +#define PHY_INT_FIFO_ERROR (1 << 4) +#define PHY_INT_ERR_CTR_FULL (1 << 5) +#define PHY_INT_NEXT_PAGE_RX (1 << 6) +#define PHY_INT_CRC_ERROR (1 << 7) +#define PHY_INT_AUTONEG_STATUS_CHANGE (1 << 8) +#define PHY_INT_MDIO_SYNC_LOST (1 << 9) +#define PHY_INT_TDR_IP_PHONE (1 << 10) + +/* PHY LED status register 2 (used for controlling link LED for activity light) */ +#define PHY_LED_TXRX_LSB 12 +#define PHY_LED_LINK_LSB 8 +#define PHY_LED_100_LSB 4 +#define PHY_LED_1000_LSB 0 + +#define LED_1000 0 +#define LED_100_TX 1 +#define LED_10 2 +#define LED_1000_ON_100_BLINK 3 +#define LED_LINK 4 +#define LED_TX 5 +#define LED_RX 6 +#define LED_ACTIVITY 7 +#define LED_FULLDUPLEX 8 +#define LED_COLLISION 9 +#define LED_LINK_ON_ACTIVITY_BLINK 10 +#define LED_LINK_ON_RX_BLINK 11 +#define LED_FULL_DUPLEX_ON_COLLISION_BLINK 12 +#define LED_BLINK 13 +#define LED_ON 14 +#define LED_OFF 15 + + +#endif /* INCLUDED_ETH_PHY_H */ diff --git a/firmware/zpu/usrp2p/ethernet.c b/firmware/zpu/usrp2p/ethernet.c new file mode 100644 index 000000000..03891f959 --- /dev/null +++ b/firmware/zpu/usrp2p/ethernet.c @@ -0,0 +1,304 @@ +/* + * 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); +} + +int +ethernet_check_errors(void) +{ + // these registers are reset when read + + int r = 0; + /* + if (eth_mac_read_rmon(0x05) != 0) + r |= RME_RX_CRC; + if (eth_mac_read_rmon(0x06) != 0) + r |= RME_RX_FIFO_FULL; + if (eth_mac_read_rmon(0x07) != 0) + r |= RME_RX_2SHORT_2LONG; + + if (eth_mac_read_rmon(0x25) != 0) + r |= RME_TX_JAM_DROP; + if (eth_mac_read_rmon(0x26) != 0) + r |= RME_TX_FIFO_UNDER; + if (eth_mac_read_rmon(0x27) != 0) + r |= RME_TX_FIFO_OVER; + */ + return r; +} diff --git a/firmware/zpu/usrp2p/memory_map.h b/firmware/zpu/usrp2p/memory_map.h new file mode 100644 index 000000000..6f5c577e6 --- /dev/null +++ b/firmware/zpu/usrp2p/memory_map.h @@ -0,0 +1,718 @@ +/* -*- 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/>. + */ + +#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 BP_NLINES 0x0200 // number of 32-bit lines 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 0x6000 // 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 0x6100 + +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 0x6200 + +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 0x6300 + +typedef struct { + volatile uint32_t _padding[8]; + volatile uint32_t status; + 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) + +// 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 0x6400 + +#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 0x5000 + +#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)) + +#define SR_ADDR_BLDRDONE _SR_ADDR(5) + +// --- buffer pool control regs --- + +typedef struct { + volatile uint32_t misc_ctrl; + volatile uint32_t ip_addr; + volatile uint32_t ctrl_ports; //ctrl (low 16) other (high 16) + volatile uint32_t data_ports; //dsp0 (low 16) dsp1 (high 16) + volatile uint32_t cpu_out_ctrl; + volatile uint32_t cpu_inp_ctrl; +} buffer_pool_ctrl_t; + +#define buffer_pool_ctrl ((buffer_pool_ctrl_t *) _SR_ADDR(SR_BUFFER_POOL_CTRL)) + +// --- 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[1]; + volatile uint32_t dsp0_port; + volatile uint32_t err0_port; + volatile uint32_t dsp1_port; + volatile uint32_t err1_port; +} sr_udp_sm_t; + +// control bits (all expect UDP_SM_LAST_WORD are mutually exclusive) + +// Insert a UDP source port from the table +#define UDP_SM_INS_UDP_SRC_PORT (1 << 21) + +// Insert a UDP dest port from the table +#define UDP_SM_INS_UDP_DST_PORT (1 << 20) + +// 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, + volatile uint32_t report_sid; + volatile uint32_t policy; + volatile uint32_t cyc_per_up; + volatile uint32_t packets_per_up; +} 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 *) _SR_ADDR(SR_TX_PROT_ENG)) + +/* + * --- 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 *) _SR_ADDR(SR_RX_PROT_ENG)) + + + +/////////////////////////////////////////////////// +// Simple Programmable Interrupt Controller, Slave 8 + +#define PIC_BASE 0x6500 + +// 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 0x6700 + +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 0x6800 + +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 0x6A00 +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 0x6B00 +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/zpu/usrp2p/spi_flash.c b/firmware/zpu/usrp2p/spi_flash.c new file mode 100644 index 000000000..09b74a513 --- /dev/null +++ b/firmware/zpu/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/zpu/usrp2p/spi_flash.h b/firmware/zpu/usrp2p/spi_flash.h new file mode 100644 index 000000000..bbe7b650d --- /dev/null +++ b/firmware/zpu/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/zpu/usrp2p/spi_flash_private.h b/firmware/zpu/usrp2p/spi_flash_private.h new file mode 100644 index 000000000..9a1b8d3e3 --- /dev/null +++ b/firmware/zpu/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/zpu/usrp2p/spi_flash_read.c b/firmware/zpu/usrp2p/spi_flash_read.c new file mode 100644 index 000000000..36c326e96 --- /dev/null +++ b/firmware/zpu/usrp2p/spi_flash_read.c @@ -0,0 +1,121 @@ +/* -*- 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 +#include <nonstdio.h> + +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){ + + spif_regs->ctrl = FLAGS | LEN(16 * 8); // xfer 16 bytes + spif_regs->ctrl = FLAGS | LEN(16 * 8) | SPI_CTRL_GO_BSY; + spif_wait(); + + uint32_t w[4]; + w[0] = spif_regs->txrx3; // txrx3 has first bits in it + w[1] = spif_regs->txrx2; + w[2] = spif_regs->txrx1; + w[3] = spif_regs->txrx0; + unsigned char *src = (unsigned char *) &w[0]; + m = min(nbytes - n, 16); + for (size_t i = 0; i < m; i++) + *(dst++) = src[i]; + } + spif_regs->ss = 0; // deassert chip select +} diff --git a/firmware/zpu/usrp2p/spif.c b/firmware/zpu/usrp2p/spif.c new file mode 100644 index 000000000..1c1a348f4 --- /dev/null +++ b/firmware/zpu/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/zpu/usrp2p/udp_fw_update.c b/firmware/zpu/usrp2p/udp_fw_update.c new file mode 100644 index 000000000..ead08ad2c --- /dev/null +++ b/firmware/zpu/usrp2p/udp_fw_update.c @@ -0,0 +1,108 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +//Routines to handle updating the SPI Flash firmware via UDP + +#include <net_common.h> +#include "usrp2/fw_common.h" +#include "spi.h" +#include "spi_flash.h" +#include <nonstdio.h> +#include <string.h> +#include "ethernet.h" +#include "udp_fw_update.h" +#include "xilinx_s3_icap.h" + +//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 + icap_reload_fpga(0); + break; + +// case USRP2_FW_UPDATE_ID_KTHXBAI: //see ya +// break; + + default: //uhhhh + update_data_out.id = USRP2_FW_UPDATE_ID_WAT; + } + send_udp_pkt(USRP2_UDP_UPDATE_PORT, src, &update_data_out, sizeof(update_data_out)); +} diff --git a/firmware/zpu/usrp2p/xilinx_s3_icap.c b/firmware/zpu/usrp2p/xilinx_s3_icap.c new file mode 100644 index 000000000..50c85231c --- /dev/null +++ b/firmware/zpu/usrp2p/xilinx_s3_icap.c @@ -0,0 +1,99 @@ +/* -*- c -*- */ +/* + * Copyright 2009 Ettus Research LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +/* Changes required to work for the Spartan-3A series: + * The ICAP interface on the 3A is 8 bits wide, instead of 32. + * Everything is Xilinx standard LSBit-first. + * The operations are all different. + * Commands are 16 bits long, presented to the ICAP interface 8 bits at a time. +*/ + +#include <xilinx_s3_icap.h> +#include <memory_map.h> +#include <spi_flash_private.h> //for READ_CMD + + +/* bit swap end-for-end */ +static inline unsigned char +swap8(unsigned char x) +{ + unsigned char r = 0; + r |= (x >> 7) & 0x01; + r |= (x >> 5) & 0x02; + r |= (x >> 3) & 0x04; + r |= (x >> 1) & 0x08; + + r |= (x << 1) & 0x10; + r |= (x << 3) & 0x20; + r |= (x << 5) & 0x40; + r |= (x << 7) & 0x80; + + return r; +} + +void +wr_icap(uint8_t x) +{ + icap_regs->icap = swap8(x); +} + +uint8_t +rd_icap(void) +{ + return swap8(icap_regs->icap); +} + + +void +icap_reload_fpga(uint32_t flash_address) +{ + union { + uint32_t i; + uint8_t c[4]; + } t; + t.i = flash_address; + + //note! t.c[0] MUST contain the byte-wide read command for the flash device used. + //for the 25P64, and most other flash devices, this is 0x03. + t.c[0] = FAST_READ_CMD; + + //TODO: look up the watchdog timer, ensure it won't fire too soon + + //UG332 p279 + wr_icap(0xff); + wr_icap(0xff); //dummy word, probably unnecessary + wr_icap(0xAA); + wr_icap(0x99); //sync word + wr_icap(0x32); + wr_icap(0x61); //Type 1 write General 1 (1 word) + wr_icap(t.c[2]); //bits 15-8 + wr_icap(t.c[3]); //bits 7-0 + wr_icap(0x32); + wr_icap(0x81); //Type 1 write General 2 (1 word) + wr_icap(t.c[0]); //C0-C8, the byte-wide read command + wr_icap(t.c[1]); //Upper 8 bits of 24-bit address + wr_icap(0x30); + wr_icap(0xA1); //Type 1 write CMD (1 word) + wr_icap(0x00); + wr_icap(0x0E); //REBOOT command + wr_icap(0x20); + wr_icap(0x00); //Type 1 NOP + wr_icap(0x20); + wr_icap(0x00); +} diff --git a/firmware/zpu/usrp2p/xilinx_s3_icap.h b/firmware/zpu/usrp2p/xilinx_s3_icap.h new file mode 100644 index 000000000..7b7e9eccc --- /dev/null +++ b/firmware/zpu/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 */ |