summaryrefslogtreecommitdiffstats
path: root/firmware/zpu/usrp2p/bootloader/fpga_bootloader.c
blob: f5a71a8bb9fe14411ff6d520b0c62564f25c71f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/* -*- c -*- */
/*
 * Copyright 2009-2011 Ettus Research LLC
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

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