Files
templates/c/set-protocol/ports/stm32f407-usb-boot/boot_flash.c

89 lines
3.7 KiB
C

/* Flash geometry and jump sequence derived from climate's F407 bootloader.
* Sector 3 is a durable commit record; sectors 4..7 hold the application.
* Reset checks vectors like climate; USB END always verifies Flash. */
#include "boot_port.h"
#include "boot_image.h"
#include "stm32f4xx_hal.h"
#include <string.h>
#define META 0x0800C000UL
#define COMMIT 0x53455442UL
#define ERRORS (FLASH_SR_PGSERR|FLASH_SR_PGPERR|FLASH_SR_PGAERR|FLASH_SR_WRPERR|FLASH_SR_SOP)
static void unlock(void) {
if(FLASH->CR & FLASH_CR_LOCK) { FLASH->KEYR=0x45670123; FLASH->KEYR=0xCDEF89AB; }
FLASH->SR=ERRORS;
}
static bool finish(void) {
while(FLASH->SR & FLASH_SR_BSY) {}
return !(FLASH->SR & ERRORS);
}
static void cache_reset(void) {
FLASH->ACR &= ~(FLASH_ACR_ICEN|FLASH_ACR_DCEN);
FLASH->ACR |= FLASH_ACR_ICRST|FLASH_ACR_DCRST;
FLASH->ACR &= ~(FLASH_ACR_ICRST|FLASH_ACR_DCRST);
FLASH->ACR |= FLASH_ACR_ICEN|FLASH_ACR_DCEN;
__DSB(); __ISB();
}
static bool erase_sector(unsigned sector) {
bool ok; unlock();
FLASH->CR=(FLASH->CR & ~(FLASH_CR_SNB|FLASH_CR_PSIZE)) |
FLASH_CR_SER | (2UL<<FLASH_CR_PSIZE_Pos) | (sector<<FLASH_CR_SNB_Pos);
FLASH->CR|=FLASH_CR_STRT; ok=finish();
FLASH->CR &= ~FLASH_CR_SER; FLASH->CR |= FLASH_CR_LOCK;
cache_reset(); return ok;
}
static bool program(uint32_t address, const uint8_t *p, uint32_t n) {
bool ok=true; unlock();
/* Byte programming supports arbitrary SET block boundaries and final tails. */
FLASH->CR=(FLASH->CR & ~FLASH_CR_PSIZE)|FLASH_CR_PG;
while(n--) {
*(volatile uint8_t *)address=*p;
if(!finish()) { ok=false; break; }
++address; ++p;
}
FLASH->CR &= ~FLASH_CR_PG; FLASH->CR |= FLASH_CR_LOCK;
cache_reset(); return ok;
}
static bool invalidate(void *u) { (void)u; return erase_sector(3); }
static bool erase(void *u, uint32_t size) {
unsigned s; (void)u;
if(size<8 || size>F407_APP_SIZE) return false;
for(s=4;s<=7;++s) {
uint32_t start=s==4 ? F407_APP_BASE : 0x08020000UL+(s-5)*0x20000UL;
if(start>=F407_APP_BASE+size) break;
if(!erase_sector(s)) return false;
}
return true;
}
static bool write(void *u,uint32_t off,const uint8_t *p,uint16_t n) {
(void)u;
if(!n || off>F407_APP_SIZE || n>F407_APP_SIZE-off) return false;
return program(F407_APP_BASE+off,p,n) && !memcmp((const void *)(F407_APP_BASE+off),p,n);
}
static bool verify(void *u,uint32_t size,uint32_t crc) {
(void)u;
return f407_vectors_valid((const uint8_t *)F407_APP_BASE,size) &&
setp_crc32((const uint8_t *)F407_APP_BASE,size)==crc;
}
static bool commit(void *u,uint32_t size,uint32_t crc) {
uint32_t record[4]={size,crc,~size,COMMIT}; (void)u;
return program(META,(const uint8_t *)record,sizeof(record)) &&
!memcmp((const void *)META,record,sizeof(record));
}
bool f407_app_valid(void) {
/* Like climate: direct Keil/SWD writes need no USB completion record. */
return f407_vectors_valid((const uint8_t *)F407_APP_BASE,F407_APP_SIZE);
}
static void activate(void *u) { (void)u; /* main waits for USB response before reset */ }
const setp_boot_ops f407_boot_ops={invalidate,erase,write,verify,commit,activate};
__attribute__((naked,noreturn)) static void enter(uint32_t sp,uint32_t pc) {
__asm volatile("msr msp, r0\nmovs r2, #0\nmsr control, r2\ndsb\nisb\ncpsie i\nbx r1\n");
}
void f407_jump_app(void) {
unsigned i; uint32_t sp=*(const uint32_t *)F407_APP_BASE;
uint32_t pc=*(const uint32_t *)(F407_APP_BASE+4);
__disable_irq(); SysTick->CTRL=0;
for(i=0;i<8;++i) { NVIC->ICER[i]=0xffffffff; NVIC->ICPR[i]=0xffffffff; }
SCB->ICSR=SCB_ICSR_PENDSTCLR_Msk|SCB_ICSR_PENDSVCLR_Msk;
SCB->VTOR=F407_APP_BASE; enter(sp,pc);
}