/* -*- 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 .
*/
/*
* 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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;
}