diff --git a/linker.lds b/linker.lds index d51d46c..eefb8f8 100644 --- a/linker.lds +++ b/linker.lds @@ -1,8 +1,5 @@ ENTRY( _start ) -__stack_size = 512; -PROVIDE( _stack_size = __stack_size ); - MEMORY { /* Try to squeeze down to this target size */ @@ -17,6 +14,9 @@ MEMORY SECTIONS { + PROVIDE(_bootloader_limit = ORIGIN(FLASH) + LENGTH(FLASH)); + PROVIDE(_bootloader_stack = ORIGIN(USBRAM) + LENGTH(USBRAM)); + .vector : { *(.vector); diff --git a/startup.S b/startup.S index ba82497..bc9d1c3 100644 --- a/startup.S +++ b/startup.S @@ -1,13 +1,98 @@ +#define R16_BKP_DATAR10 0x40006C28 +#define BOOT_MAGIC_APP_IMMEDIATELY 0x4170 +#define BOOT_MAGIC_BOOTLOADER 0x624c + +#define R32_PWR_CTLR 0x40007000 + +#define RCC_BASE 0x40021000 +#define R32_RCC_APB1PCENR 0x4002101C +#define R32_RCC_RSTSCKR 0x40021024 + +#define SYSTICK_BASE 0xE000F000 +#define STK_CTLR 0xE000F000 +#define STK_SR 0xE000F004 +#define STK_CMPLR 0xE000F010 +#define WAIT_TIME (500 /* ms */ * 1000 /* assume 8 MHz system clock, div8 */) + .section .vector,"ax",@progbits .align 1 .global _start _start: + // Use Adafruit(?)-inspired boot entry, namely "double-reset in short succession to enter bootloader" + // 1. If the reset wasn't triggered by the nRST pin, go to application + // 2. On nRST, if not first reset, go to bootloader + // 3. On nRST, if *is* first reset, set magic and wait + // 4a. If wait times out, go to application + // 4b. If wait doesn't time out but a reset happens again, detect "not first" reset and go to bootloader + la a0, RCC_BASE + lw a1, (R32_RCC_RSTSCKR-RCC_BASE)(a0) + slli a1, a1, (31 - 26) + bgez a1, _enter_application_code // if PINRSTF == 0 + + // Enable BKP and PWR clock + lw a1, (R32_RCC_APB1PCENR-RCC_BASE)(a0) + la a2, (1 << 27) | (1 << 28) + or a1, a1, a2 + sw a1, (R32_RCC_APB1PCENR-RCC_BASE)(a0) + // Enable BKP write + la a0, R32_PWR_CTLR + lw a1, (a0) + ori a1, a1, (1 << 8) + sw a1, (a0) + + // Check for magic values + la a3, R16_BKP_DATAR10 + lhu a1, (a3) + la a2, BOOT_MAGIC_APP_IMMEDIATELY + beq a1, a2, _enter_application_code + la a2, BOOT_MAGIC_BOOTLOADER + beq a1, a2, _enter_bootloader + + // Here we're on the first reset + sh a2, (a3) + la a0, SYSTICK_BASE + la a1, WAIT_TIME + sw a1, (STK_CMPLR-SYSTICK_BASE)(a0) + la a1, 0b111000 + sw a1, (STK_CTLR-SYSTICK_BASE)(a0) + // Seems to spuriously fire immediately if start bit is set concurrently + la a1, 0b111001 + sw a1, (STK_CTLR-SYSTICK_BASE)(a0) +1: + lw a1, (STK_SR-SYSTICK_BASE)(a0) + slli a1, a1, (31 - 0) + bgez a1, 1b + // Delay done, clear systick and magic + sw zero, (STK_CTLR-SYSTICK_BASE)(a0) + sw zero, (STK_SR-SYSTICK_BASE)(a0) + sh zero, (a3) + // If the delay expires, boot the application after all + +_enter_application_code: + // Clear all of these enables that we had set + la a0, R32_PWR_CTLR + lw a1, (a0) + xori a1, a1, (1 << 8) + sw a1, (a0) + la a0, R32_RCC_APB1PCENR + lw a1, (a0) + la a2, (1 << 27) | (1 << 28) + xor a1, a1, a2 + sw a1, (a0) + +_wipwipwip: + j _wipwipwip + + j _bootloader_limit + +_enter_bootloader: // Set up global pointer .option push .option norelax la gp, __global_pointer$ .option pop + la sp, _bootloader_stack // Load data section from flash to RAM la a0, _data_lma