mirror of
https://github.com/betaflight/betaflight.git
synced 2025-07-16 12:55:19 +03:00
544 lines
15 KiB
ArmAsm
544 lines
15 KiB
ArmAsm
/*
|
|
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include "pico.h"
|
|
#include "pico/asm_helper.S"
|
|
|
|
#include "pico/platform/cpu_regs.h"
|
|
|
|
#include "hardware/regs/addressmap.h"
|
|
#include "hardware/regs/sio.h"
|
|
#include "pico/binary_info/defs.h"
|
|
#include "boot/picobin.h"
|
|
#include "pico/bootrom.h"
|
|
|
|
#ifdef NDEBUG
|
|
#ifndef COLLAPSE_IRQS
|
|
#define COLLAPSE_IRQS
|
|
#endif
|
|
#endif
|
|
|
|
pico_default_asm_setup
|
|
|
|
.section .vectors, "ax"
|
|
.align 2
|
|
|
|
.global __vectors, __VECTOR_TABLE
|
|
__VECTOR_TABLE:
|
|
__vectors:
|
|
.word __StackTop
|
|
.word _reset_handler
|
|
.word isr_nmi
|
|
.word isr_hardfault
|
|
.word isr_invalid // Reserved, should never fire
|
|
.word isr_invalid // Reserved, should never fire
|
|
.word isr_invalid // Reserved, should never fire
|
|
.word isr_invalid // Reserved, should never fire
|
|
.word isr_invalid // Reserved, should never fire
|
|
.word isr_invalid // Reserved, should never fire
|
|
.word isr_invalid // Reserved, should never fire
|
|
.word isr_svcall
|
|
.word isr_invalid // Reserved, should never fire
|
|
.word isr_invalid // Reserved, should never fire
|
|
.word isr_pendsv
|
|
.word isr_systick
|
|
#if PICO_NO_STORED_VECTOR_TABLE && !PICO_NO_FLASH // note in no flash binary, we only have the single RAM vector table anyway
|
|
#if PICO_NO_RAM_VECTOR_TABLE
|
|
#error Can't specify PICO_NO_STORED_VECTOR_TABLE and PICO_NO_RAM_VECTOR_TABLE
|
|
#endif
|
|
// we don't include any IRQ vectors; we will initialize them during runtime_init in the RAM vector table
|
|
#else
|
|
|
|
.macro if_irq_word num func
|
|
.if \num < NUM_IRQS
|
|
.word \func
|
|
.endif
|
|
.endm
|
|
|
|
// we include a lot of these to allow for different number of IRQs.
|
|
// if_irq_word will only include IRQs that are valid, but we can't
|
|
// use a macro loop because isr_irqx MUST appear in the source
|
|
// as CMSIS rename exceptions #defines it to another value
|
|
if_irq_word 0 isr_irq0
|
|
if_irq_word 1 isr_irq1
|
|
if_irq_word 2 isr_irq2
|
|
if_irq_word 3 isr_irq3
|
|
if_irq_word 4 isr_irq4
|
|
if_irq_word 5 isr_irq5
|
|
if_irq_word 6 isr_irq6
|
|
if_irq_word 7 isr_irq7
|
|
if_irq_word 8 isr_irq8
|
|
if_irq_word 9 isr_irq9
|
|
if_irq_word 10 isr_irq10
|
|
if_irq_word 11 isr_irq11
|
|
if_irq_word 12 isr_irq12
|
|
if_irq_word 13 isr_irq13
|
|
if_irq_word 14 isr_irq14
|
|
if_irq_word 15 isr_irq15
|
|
if_irq_word 16 isr_irq16
|
|
if_irq_word 17 isr_irq17
|
|
if_irq_word 18 isr_irq18
|
|
if_irq_word 19 isr_irq19
|
|
if_irq_word 20 isr_irq20
|
|
if_irq_word 21 isr_irq21
|
|
if_irq_word 22 isr_irq22
|
|
if_irq_word 23 isr_irq23
|
|
if_irq_word 24 isr_irq24
|
|
if_irq_word 25 isr_irq25
|
|
if_irq_word 26 isr_irq26
|
|
if_irq_word 27 isr_irq27
|
|
if_irq_word 28 isr_irq28
|
|
if_irq_word 29 isr_irq29
|
|
if_irq_word 30 isr_irq30
|
|
if_irq_word 31 isr_irq31
|
|
if_irq_word 32 isr_irq32
|
|
if_irq_word 33 isr_irq33
|
|
if_irq_word 34 isr_irq34
|
|
if_irq_word 35 isr_irq35
|
|
if_irq_word 36 isr_irq36
|
|
if_irq_word 37 isr_irq37
|
|
if_irq_word 38 isr_irq38
|
|
if_irq_word 39 isr_irq39
|
|
if_irq_word 40 isr_irq40
|
|
if_irq_word 41 isr_irq41
|
|
if_irq_word 42 isr_irq42
|
|
if_irq_word 43 isr_irq43
|
|
if_irq_word 44 isr_irq44
|
|
if_irq_word 45 isr_irq45
|
|
if_irq_word 46 isr_irq46
|
|
if_irq_word 47 isr_irq47
|
|
if_irq_word 48 isr_irq48
|
|
if_irq_word 49 isr_irq49
|
|
if_irq_word 50 isr_irq50
|
|
if_irq_word 51 isr_irq51
|
|
if_irq_word 52 isr_irq52
|
|
if_irq_word 53 isr_irq53
|
|
if_irq_word 54 isr_irq54
|
|
if_irq_word 55 isr_irq55
|
|
if_irq_word 56 isr_irq56
|
|
if_irq_word 57 isr_irq57
|
|
if_irq_word 58 isr_irq58
|
|
if_irq_word 59 isr_irq59
|
|
if_irq_word 60 isr_irq60
|
|
if_irq_word 61 isr_irq61
|
|
if_irq_word 62 isr_irq62
|
|
if_irq_word 63 isr_irq63
|
|
if_irq_word 64 isr_irq64
|
|
if_irq_word 65 isr_irq65
|
|
if_irq_word 66 isr_irq66
|
|
if_irq_word 67 isr_irq67
|
|
if_irq_word 68 isr_irq68
|
|
if_irq_word 69 isr_irq69
|
|
if_irq_word 70 isr_irq70
|
|
if_irq_word 71 isr_irq71
|
|
if_irq_word 72 isr_irq72
|
|
if_irq_word 73 isr_irq73
|
|
if_irq_word 74 isr_irq74
|
|
if_irq_word 75 isr_irq75
|
|
if_irq_word 76 isr_irq76
|
|
if_irq_word 77 isr_irq77
|
|
if_irq_word 78 isr_irq78
|
|
if_irq_word 79 isr_irq79
|
|
#if NUM_IRQS > 80
|
|
#error more IRQ entries required
|
|
#endif
|
|
#endif
|
|
|
|
// all default exception handlers do nothing, and we can check for them being set to our
|
|
// default values by seeing if they point to somewhere between __defaults_isrs_start and __default_isrs_end
|
|
.global __default_isrs_start
|
|
__default_isrs_start:
|
|
|
|
// Declare a weak symbol for each ISR.
|
|
// By default, they will fall through to the undefined IRQ handler below (breakpoint),
|
|
// but can be overridden by C functions with correct name.
|
|
|
|
.macro decl_isr_bkpt name
|
|
.weak \name
|
|
.type \name,%function
|
|
.thumb_func
|
|
\name:
|
|
bkpt #0
|
|
.endm
|
|
|
|
// these are separated out for clarity
|
|
decl_isr_bkpt isr_invalid
|
|
decl_isr_bkpt isr_nmi
|
|
decl_isr_bkpt isr_hardfault
|
|
decl_isr_bkpt isr_svcall
|
|
decl_isr_bkpt isr_pendsv
|
|
decl_isr_bkpt isr_systick
|
|
|
|
.global __default_isrs_end
|
|
__default_isrs_end:
|
|
|
|
.altmacro
|
|
.macro decl_isr name
|
|
#if !PICO_NO_STORED_VECTOR_TABLE | PICO_NO_FLASH
|
|
// We declare a weak label, so user can override
|
|
.weak \name
|
|
#else
|
|
// We declare a strong label, so user can't override (their version would not automatically be used)
|
|
#endif
|
|
.type \name,%function
|
|
.thumb_func
|
|
\name:
|
|
.endm
|
|
|
|
.macro if_irq_decl num func
|
|
.if \num < NUM_IRQS
|
|
decl_isr \func
|
|
.endif
|
|
.endm
|
|
|
|
if_irq_decl 0 isr_irq0
|
|
if_irq_decl 1 isr_irq1
|
|
if_irq_decl 2 isr_irq2
|
|
if_irq_decl 3 isr_irq3
|
|
if_irq_decl 4 isr_irq4
|
|
if_irq_decl 5 isr_irq5
|
|
if_irq_decl 6 isr_irq6
|
|
if_irq_decl 7 isr_irq7
|
|
if_irq_decl 8 isr_irq8
|
|
if_irq_decl 9 isr_irq9
|
|
if_irq_decl 10 isr_irq10
|
|
if_irq_decl 11 isr_irq11
|
|
if_irq_decl 12 isr_irq12
|
|
if_irq_decl 13 isr_irq13
|
|
if_irq_decl 14 isr_irq14
|
|
if_irq_decl 15 isr_irq15
|
|
if_irq_decl 16 isr_irq16
|
|
if_irq_decl 17 isr_irq17
|
|
if_irq_decl 18 isr_irq18
|
|
if_irq_decl 19 isr_irq19
|
|
if_irq_decl 20 isr_irq20
|
|
if_irq_decl 21 isr_irq21
|
|
if_irq_decl 22 isr_irq22
|
|
if_irq_decl 23 isr_irq23
|
|
if_irq_decl 24 isr_irq24
|
|
if_irq_decl 25 isr_irq25
|
|
if_irq_decl 26 isr_irq26
|
|
if_irq_decl 27 isr_irq27
|
|
if_irq_decl 28 isr_irq28
|
|
if_irq_decl 29 isr_irq29
|
|
if_irq_decl 30 isr_irq30
|
|
if_irq_decl 31 isr_irq31
|
|
if_irq_decl 32 isr_irq32
|
|
if_irq_decl 33 isr_irq33
|
|
if_irq_decl 34 isr_irq34
|
|
if_irq_decl 35 isr_irq35
|
|
if_irq_decl 36 isr_irq36
|
|
if_irq_decl 37 isr_irq37
|
|
if_irq_decl 38 isr_irq38
|
|
if_irq_decl 39 isr_irq39
|
|
if_irq_decl 40 isr_irq40
|
|
if_irq_decl 41 isr_irq41
|
|
if_irq_decl 42 isr_irq42
|
|
if_irq_decl 43 isr_irq43
|
|
if_irq_decl 44 isr_irq44
|
|
if_irq_decl 45 isr_irq45
|
|
if_irq_decl 46 isr_irq46
|
|
if_irq_decl 47 isr_irq47
|
|
if_irq_decl 48 isr_irq48
|
|
if_irq_decl 49 isr_irq49
|
|
if_irq_decl 50 isr_irq50
|
|
if_irq_decl 51 isr_irq51
|
|
if_irq_decl 52 isr_irq52
|
|
if_irq_decl 53 isr_irq53
|
|
if_irq_decl 54 isr_irq54
|
|
if_irq_decl 55 isr_irq55
|
|
if_irq_decl 56 isr_irq56
|
|
if_irq_decl 57 isr_irq57
|
|
if_irq_decl 58 isr_irq58
|
|
if_irq_decl 59 isr_irq59
|
|
if_irq_decl 60 isr_irq60
|
|
if_irq_decl 61 isr_irq61
|
|
if_irq_decl 62 isr_irq62
|
|
if_irq_decl 63 isr_irq63
|
|
if_irq_decl 64 isr_irq64
|
|
if_irq_decl 65 isr_irq65
|
|
if_irq_decl 66 isr_irq66
|
|
if_irq_decl 67 isr_irq67
|
|
if_irq_decl 68 isr_irq68
|
|
if_irq_decl 69 isr_irq69
|
|
if_irq_decl 70 isr_irq70
|
|
if_irq_decl 71 isr_irq71
|
|
if_irq_decl 72 isr_irq72
|
|
if_irq_decl 73 isr_irq73
|
|
if_irq_decl 74 isr_irq74
|
|
if_irq_decl 75 isr_irq75
|
|
if_irq_decl 76 isr_irq76
|
|
if_irq_decl 77 isr_irq77
|
|
if_irq_decl 78 isr_irq78
|
|
if_irq_decl 79 isr_irq79
|
|
#if NUM_IRQS > 80
|
|
#error more IRQ entries required
|
|
#endif
|
|
|
|
// All unhandled USER IRQs fall through to here
|
|
.global __unhandled_user_irq
|
|
.thumb_func
|
|
__unhandled_user_irq:
|
|
mrs r0, ipsr
|
|
subs r0, #16
|
|
.global unhandled_user_irq_num_in_r0
|
|
unhandled_user_irq_num_in_r0:
|
|
bkpt #0
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
.section .binary_info_header, "a"
|
|
|
|
// Header must be in first 256 bytes of main image (i.e. excluding flash boot2).
|
|
// For flash builds we put it immediately after vector table; for NO_FLASH the
|
|
// vectors are at a +0x100 offset because the bootrom enters RAM images directly
|
|
// at their lowest address, so we put the header in the VTOR alignment hole.
|
|
|
|
#if !PICO_NO_BINARY_INFO
|
|
binary_info_header:
|
|
.word BINARY_INFO_MARKER_START
|
|
.word __binary_info_start
|
|
.word __binary_info_end
|
|
.word data_cpy_table // we may need to decode pointers that are in RAM at runtime.
|
|
.word BINARY_INFO_MARKER_END
|
|
#endif
|
|
|
|
#include "embedded_start_block.inc.S"
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
.section .reset, "ax"
|
|
|
|
// On flash builds, the vector table comes first in the image (conventional).
|
|
// On NO_FLASH builds, the reset handler section comes first, as the entry
|
|
// point is at offset 0 (fixed due to bootrom), and VTOR is highly-aligned.
|
|
// Image is entered in various ways:
|
|
//
|
|
// - NO_FLASH builds are entered from beginning by UF2 bootloader
|
|
//
|
|
// - Flash builds vector through the table into _reset_handler from boot2
|
|
//
|
|
// - Either type can be entered via _entry_point by the debugger, and flash builds
|
|
// must then be sent back round the boot sequence to properly initialise flash
|
|
|
|
// ELF entry point:
|
|
.type _entry_point,%function
|
|
.thumb_func
|
|
.global _entry_point
|
|
_entry_point:
|
|
|
|
#if PICO_NO_FLASH
|
|
// on the NO_FLASH case, we do not do a rest thru bootrom below, so the RCP may or may not have been initialized:
|
|
//
|
|
// in the normal (e.g. UF2 download etc. case) we will have passed thru bootrom initialization, but if
|
|
// a NO_FLASH binary is loaded by the debugger, and run directly after a reset, then we won't have.
|
|
//
|
|
// we must therefore initialize the RCP if it hasn't already been
|
|
|
|
#if HAS_REDUNDANCY_COPROCESSOR
|
|
// just enable the RCP which is fine if it already was (we assume no other co-processors are enabled at this point to save space)
|
|
ldr r0, = PPB_BASE + M33_CPACR_OFFSET
|
|
movs r1, #ARM_CPU_PREFIXED(CPACR_CP7_BITS)
|
|
str r1, [r0]
|
|
// only initialize canary seeds if they haven't been (as to do so twice is a fault)
|
|
mrc p7, #1, apsr_nzcv, c0, c0, #0
|
|
bmi 1f
|
|
// i dont think it much matters what we initialized to, as to have gotten here we must have not
|
|
// gone thru the bootrom (which a secure boot would have)
|
|
mcrr p7, #8, r0, r0, c0
|
|
mcrr p7, #8, r0, r0, c1
|
|
sev
|
|
1:
|
|
#endif
|
|
ldr r0, =__vectors
|
|
// Vector through our own table (SP, VTOR will not have been set up at
|
|
// this point). Same path for debugger entry and bootloader entry.
|
|
#else
|
|
// Debugger tried to run code after loading, so SSI is in 03h-only mode.
|
|
// Go back through bootrom + boot2 to properly initialise flash.
|
|
ldr r0, =BOOTROM_VTABLE_OFFSET
|
|
#endif
|
|
|
|
_enter_vtable_in_r0:
|
|
ldr r1, =(PPB_BASE + ARM_CPU_PREFIXED(VTOR_OFFSET))
|
|
str r0, [r1]
|
|
ldmia r0!, {r1, r2}
|
|
msr msp, r1
|
|
bx r2
|
|
|
|
// Reset handler:
|
|
// - initialises .data
|
|
// - clears .bss
|
|
// - calls runtime_init
|
|
// - calls main
|
|
// - calls exit (which should eventually hang the processor via _exit)
|
|
|
|
.type _reset_handler,%function
|
|
.thumb_func
|
|
_reset_handler:
|
|
// Only core 0 should run the C runtime startup code; core 1 is normally
|
|
// sleeping in the bootrom at this point but check to be sure (e.g. if
|
|
// debugger put core 1 at the ELF entry point for some reason)
|
|
ldr r0, =(SIO_BASE + SIO_CPUID_OFFSET)
|
|
ldr r0, [r0]
|
|
#if __ARM_ARCH_6M__
|
|
cmp r0, #0
|
|
beq 1f
|
|
#else
|
|
cbz r0, 1f
|
|
#endif
|
|
hold_non_core0_in_bootrom:
|
|
// Send back to the ROM to wait for core 0 to launch it.
|
|
ldr r0, =BOOTROM_VTABLE_OFFSET
|
|
b _enter_vtable_in_r0
|
|
1:
|
|
|
|
#if !PICO_RP2040 && PICO_EMBED_XIP_SETUP && !PICO_NO_FLASH
|
|
// Execute boot2 on the core 0 stack (it also gets copied into BOOTRAM due
|
|
// to inclusion in the data copy table below). Note the reference
|
|
// to __boot2_entry_point here is what prevents the .boot2 section from
|
|
// being garbage-collected.
|
|
_copy_xip_setup:
|
|
ldr r1, =__boot2_entry_point
|
|
mov r3, sp
|
|
add sp, #-256
|
|
mov r2, sp
|
|
bl data_cpy
|
|
_call_xip_setup:
|
|
mov r0, sp
|
|
adds r0, #1
|
|
blx r0
|
|
add sp, #256
|
|
#endif
|
|
|
|
// In a NO_FLASH binary, don't perform .data etc copy, since it's loaded
|
|
// in-place by the SRAM load. Still need to clear .bss
|
|
#if !PICO_NO_FLASH
|
|
adr r4, data_cpy_table
|
|
|
|
// assume there is at least one entry
|
|
1:
|
|
ldmia r4!, {r1-r3}
|
|
cmp r1, #0
|
|
beq 2f
|
|
bl data_cpy
|
|
b 1b
|
|
2:
|
|
#endif
|
|
|
|
// Zero out the BSS
|
|
ldr r1, =__bss_start__
|
|
ldr r2, =__bss_end__
|
|
movs r0, #0
|
|
b bss_fill_test
|
|
bss_fill_loop:
|
|
stm r1!, {r0}
|
|
bss_fill_test:
|
|
cmp r1, r2
|
|
bne bss_fill_loop
|
|
|
|
platform_entry: // symbol for stack traces
|
|
// Use 32-bit jumps, in case these symbols are moved out of branch range
|
|
// (e.g. if main is in SRAM and crt0 in flash)
|
|
#if !__ARM_ARCH_6M__
|
|
// Make sure stack limit is 0 - the user can set it themselves
|
|
// todo probably worth adding to the EXE_DEF in the future
|
|
movs r0, #0
|
|
msr msplim, r0
|
|
#endif
|
|
ldr r1, =runtime_init
|
|
blx r1
|
|
ldr r1, =main
|
|
blx r1
|
|
ldr r1, =exit
|
|
blx r1
|
|
// exit should not return. If it does, hang the core.
|
|
1: // separate label because _exit can be moved out of branch range
|
|
bkpt #0
|
|
b 1b
|
|
|
|
|
|
#if !PICO_NO_FLASH
|
|
data_cpy_loop:
|
|
ldm r1!, {r0}
|
|
stm r2!, {r0}
|
|
data_cpy:
|
|
cmp r2, r3
|
|
blo data_cpy_loop
|
|
bx lr
|
|
#endif
|
|
|
|
// Note the data copy table is still included for NO_FLASH builds, even though
|
|
// we skip the copy, because it is listed in binary info
|
|
|
|
.align 2
|
|
data_cpy_table:
|
|
#if PICO_RP2350 && PICO_EMBED_XIP_SETUP && !PICO_NO_FLASH
|
|
.word __boot2_start__
|
|
.word BOOTRAM_BASE
|
|
.word BOOTRAM_BASE + 256
|
|
#endif
|
|
|
|
#if PICO_COPY_TO_RAM
|
|
.word __ram_text_source__
|
|
.word __ram_text_start__
|
|
.word __ram_text_end__
|
|
#endif
|
|
.word __etext
|
|
.word __data_start__
|
|
.word __data_end__
|
|
|
|
.word __scratch_x_source__
|
|
.word __scratch_x_start__
|
|
.word __scratch_x_end__
|
|
|
|
.word __scratch_y_source__
|
|
.word __scratch_y_start__
|
|
.word __scratch_y_end__
|
|
|
|
.word 0 // null terminator
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Provide safe defaults for _exit and runtime_init
|
|
// Full implementations usually provided by platform.c
|
|
|
|
.weak runtime_init
|
|
.type runtime_init,%function
|
|
.thumb_func
|
|
runtime_init:
|
|
bx lr
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Stack/heap dummies to set size
|
|
|
|
// Prior to SDK 1.5.1 these were `.section .stack` without the `, "a"`... Clang linker gives a warning about this,
|
|
// however setting it explicitly to `, "a"` makes GCC *now* discard the section unless it is also KEEP. This
|
|
// seems like very surprising behavior!
|
|
//
|
|
// Strictly the most correct thing to do (as .stack and .heap are unreferenced) is to mark them as "a", and also KEEP, which
|
|
// works correctly for both GCC and Clang, however doing so may break anyone who already has custom linker scripts without
|
|
// the KEEP. Therefore we will only add the "a" on Clang, but will also use KEEP to our own linker scripts.
|
|
|
|
.macro spacer_section name
|
|
#if PICO_ASSEMBLER_IS_CLANG
|
|
.section \name, "a"
|
|
#else
|
|
.section \name
|
|
#endif
|
|
.endm
|
|
|
|
spacer_section .stack
|
|
// align to allow for memory protection (although this alignment is pretty much ignored by linker script)
|
|
.p2align 5
|
|
.equ StackSize, PICO_STACK_SIZE
|
|
.space StackSize
|
|
|
|
spacer_section .heap
|
|
.p2align 2
|
|
.equ HeapSize, PICO_HEAP_SIZE
|
|
.space HeapSize
|
|
|
|
#include "embedded_end_block.inc.S"
|