AP_Bootloader: fully working on H7

This commit is contained in:
Andrew Tridgell 2019-02-08 16:37:03 +11:00
parent 599a1a3d67
commit ac070c92f5
3 changed files with 117 additions and 33 deletions

View File

@ -40,6 +40,7 @@
****************************************************************************/ ****************************************************************************/
#include <AP_HAL/AP_HAL.h> #include <AP_HAL/AP_HAL.h>
#include <AP_Math/AP_Math.h>
#include "ch.h" #include "ch.h"
#include "hal.h" #include "hal.h"
#include "hwdef.h" #include "hwdef.h"
@ -47,6 +48,8 @@
#include "bl_protocol.h" #include "bl_protocol.h"
#include "support.h" #include "support.h"
// #pragma GCC optimize("O0")
// bootloader flash update protocol. // bootloader flash update protocol.
// //
@ -125,6 +128,10 @@ static enum led_state {LED_BLINK, LED_ON, LED_OFF} led_state;
volatile unsigned timer[NTIMERS]; volatile unsigned timer[NTIMERS];
// keep back 32 bytes at the front of flash. This is long enough to allow for aligned
// access on STM32H7
#define RESERVE_LEAD_WORDS 8
/* /*
1ms timer tick callback 1ms timer tick callback
*/ */
@ -174,7 +181,7 @@ led_set(enum led_state state)
static void static void
do_jump(uint32_t stacktop, uint32_t entrypoint) do_jump(uint32_t stacktop, uint32_t entrypoint)
{ {
#if defined(STM32F7) #if defined(STM32F7) || defined(STM32H7)
// disable caches on F7 before starting program // disable caches on F7 before starting program
__DSB(); __DSB();
__ISB(); __ISB();
@ -200,12 +207,15 @@ jump_to_app()
const uint32_t *app_base = (const uint32_t *)(APP_START_ADDRESS); const uint32_t *app_base = (const uint32_t *)(APP_START_ADDRESS);
/* /*
* We refuse to program the first word of the app until the upload is marked * We hold back the programming of the lead words until the upload
* complete by the host. So if it's not 0xffffffff, we should try booting it. * is marked complete by the host. So if they are not 0xffffffff,
* we should try booting it.
*/ */
if (app_base[0] == 0xffffffff) { for (uint8_t i=0; i<RESERVE_LEAD_WORDS; i++) {
if (app_base[i] == 0xffffffff) {
return; return;
} }
}
/* /*
* The second word of the app is the entrypoint; it must point within the * The second word of the app is the entrypoint; it must point within the
@ -330,15 +340,68 @@ crc32(const uint8_t *src, unsigned len, unsigned state)
return state; return state;
} }
/*
we use a write buffer for flashing, both for efficiency and to
ensure that we only ever do 32 byte aligned writes on STM32H7. If
you attempt to do writes on a H7 of less than 32 bytes or not
aligned then the flash can end up in a CRC error state, which can
generate a hardware fault (a double ECC error) on flash read, even
after a power cycle
*/
static struct {
uint32_t buffer[8];
uint32_t address;
uint8_t n;
} fbuf;
/*
flush the write buffer
*/
static bool flash_write_flush(void)
{
if (fbuf.n == 0) {
return true;
}
fbuf.n = 0;
return flash_func_write_words(fbuf.address, fbuf.buffer, ARRAY_SIZE(fbuf.buffer));
}
/*
write to flash with buffering to 32 bytes alignment
*/
static bool flash_write_buffer(uint32_t address, const uint32_t *v, uint8_t nwords)
{
if (fbuf.n > 0 && address != fbuf.address + fbuf.n*4) {
if (!flash_write_flush()) {
return false;
}
}
while (nwords > 0) {
if (fbuf.n == 0) {
fbuf.address = address;
memset(fbuf.buffer, 0xff, sizeof(fbuf.buffer));
}
uint8_t n = MIN(ARRAY_SIZE(fbuf.buffer)-fbuf.n, nwords);
memcpy(&fbuf.buffer[fbuf.n], v, n*4);
address += n*4;
v += n;
nwords -= n;
fbuf.n += n;
if (fbuf.n == ARRAY_SIZE(fbuf.buffer)) {
if (!flash_write_flush()) {
return false;
}
}
}
return true;
}
#define TEST_FLASH 0 #define TEST_FLASH 0
#if TEST_FLASH #if TEST_FLASH
static void test_flash() static void test_flash()
{ {
for (uint8_t i=0; i<10; i++) {
uprintf("waiting %u...\n", i);
delay(300);
}
uint32_t loop = 1; uint32_t loop = 1;
bool init_done = false; bool init_done = false;
while (true) { while (true) {
@ -395,12 +458,18 @@ bootloader(unsigned timeout)
#endif #endif
uint32_t address = board_info.fw_size; /* force erase before upload will work */ uint32_t address = board_info.fw_size; /* force erase before upload will work */
uint32_t first_word = 0xffffffff; uint32_t first_words[RESERVE_LEAD_WORDS];
bool done_sync = false; bool done_sync = false;
bool done_get_device = false; bool done_get_device = false;
static bool done_timer_init;
memset(first_words, 0xFF, sizeof(first_words));
if (!done_timer_init) {
done_timer_init = true;
chVTObjectInit(&systick_vt); chVTObjectInit(&systick_vt);
chVTSet(&systick_vt, chTimeMS2I(1), sys_tick_handler, nullptr); chVTSet(&systick_vt, chTimeMS2I(1), sys_tick_handler, nullptr);
}
/* if we are working with a timeout, start it running */ /* if we are working with a timeout, start it running */
if (timeout) { if (timeout) {
@ -533,7 +602,9 @@ bootloader(unsigned timeout)
// erase all sectors // erase all sectors
for (uint8_t i = 0; flash_func_sector_size(i) != 0; i++) { for (uint8_t i = 0; flash_func_sector_size(i) != 0; i++) {
flash_func_erase_sector(i); if (!flash_func_erase_sector(i)) {
goto cmd_fail;
}
} }
// enable the LED while verifying the erase // enable the LED while verifying the erase
@ -601,18 +672,20 @@ bootloader(unsigned timeout)
goto cmd_bad; goto cmd_bad;
} }
if (address == 0) { // save the first words and don't program it until everything else is done
// save the first word and don't program it until everything else is done if (address < sizeof(first_words)) {
first_word = flash_buffer.w[0]; uint8_t n = MIN(sizeof(first_words)-address, arg);
// replace first word with bits we can overwrite later memcpy(&first_words[address/4], &flash_buffer.w[0], n);
flash_buffer.w[0] = 0xffffffff; // replace first words with 1 bits we can overwrite later
memset(&flash_buffer.w[0], 0xFF, n);
} }
arg /= 4; arg /= 4;
// program the words // program the words
if (!flash_func_write_words(address, flash_buffer.w, arg)) { if (!flash_write_buffer(address, flash_buffer.w, arg)) {
goto cmd_fail; goto cmd_fail;
} }
address += arg * 4;
break; break;
// fetch CRC of the entire flash area // fetch CRC of the entire flash area
@ -626,19 +699,21 @@ bootloader(unsigned timeout)
goto cmd_bad; goto cmd_bad;
} }
if (!flash_write_flush()) {
goto cmd_bad;
}
// compute CRC of the programmed area // compute CRC of the programmed area
uint32_t sum = 0; uint32_t sum = 0;
for (unsigned p = 0; p < board_info.fw_size; p += 4) { for (unsigned p = 0; p < board_info.fw_size; p += 4) {
uint32_t bytes; uint32_t bytes;
if ((p == 0) && (first_word != 0xffffffff)) { if (p < sizeof(first_words) && first_words[0] != 0xFFFFFFFF) {
bytes = first_word; bytes = first_words[p/4];
} else { } else {
bytes = flash_func_read_word(p); bytes = flash_func_read_word(p);
} }
sum = crc32((uint8_t *)&bytes, sizeof(bytes), sum); sum = crc32((uint8_t *)&bytes, sizeof(bytes), sum);
} }
@ -779,16 +854,17 @@ bootloader(unsigned timeout)
goto cmd_bad; goto cmd_bad;
} }
// program the deferred first word if (!flash_write_flush()) {
if (first_word != 0xffffffff) {
flash_func_write_word(0, first_word);
if (flash_func_read_word(0) != first_word) {
goto cmd_fail; goto cmd_fail;
} }
// program the deferred first word
if (first_words[0] != 0xffffffff) {
if (!flash_write_buffer(0, first_words, RESERVE_LEAD_WORDS)) {
goto cmd_fail;
}
// revert in case the flash was bad... // revert in case the flash was bad...
first_word = 0xffffffff; memset(first_words, 0xff, sizeof(first_words));
} }
// send a sync and wait for it to be collected // send a sync and wait for it to be collected

View File

@ -25,6 +25,8 @@ static uint8_t last_uart;
#define BOOTLOADER_BAUDRATE 115200 #define BOOTLOADER_BAUDRATE 115200
#endif #endif
// #pragma GCC optimize("O0")
int16_t cin(unsigned timeout_ms) int16_t cin(unsigned timeout_ms)
{ {
uint8_t b = 0; uint8_t b = 0;
@ -254,12 +256,18 @@ extern "C" {
void uprintf(const char *fmt, ...) void uprintf(const char *fmt, ...)
{ {
#if HAL_USE_SERIAL_USB == TRUE #if HAL_USE_SERIAL_USB == TRUE
char msg[200];
va_list ap; va_list ap;
static bool initialised;
char umsg[200];
if (!initialised) {
initialised = true;
sercfg.speed = 57600;
sdStart(&SD7, &sercfg);
}
va_start(ap, fmt); va_start(ap, fmt);
uint32_t n = vsnprintf(msg, sizeof(msg), fmt, ap); uint32_t n = vsnprintf(umsg, sizeof(umsg), fmt, ap);
va_end(ap); va_end(ap);
chnWriteTimeout(&SDU1, (const uint8_t *)msg, n, chTimeMS2I(100)); chnWriteTimeout(&SD7, (const uint8_t *)umsg, n, chTimeMS2I(100));
#endif #endif
} }

View File

@ -41,7 +41,7 @@ void led_on(unsigned led);
void led_off(unsigned led); void led_off(unsigned led);
void led_toggle(unsigned led); void led_toggle(unsigned led);
// printf to USB // printf to debug uart (or USB)
extern "C" { extern "C" {
void uprintf(const char *fmt, ...); void uprintf(const char *fmt, ...);
} }