| name | arm-embedded |
| description | Architecture ARM embarquée — Cortex-M/R/A, programmation, debug, optimisation |
| category | edge-ai |
| author | E.V.A |
| version | 1.0.0 |
Architecture ARM Embarquée
Vue d'ensemble
Guide complet pour le développement sur architectures ARM en contexte embarqué (bare-metal, RTOS, Linux embarqué).
Architectures
Cortex-M (Microcontrôleur)
- M0/M0+ : Von Neumann, 2-3 loads, pas de bit-banding, IT à 1 cycle
- M3 : Harvard, 3 étages, bit-banding, division matérielle
- M4 : M3 + FPU simple précision + DSP SIMD (SMLAD, SMLALD)
- M7 : 6 étages, FPU double précision, cache L1 (I/D), TCM
- M33/M55 : ARMv8-M, TrustZone, Helium (MVE)
Cortex-R (Temps réel)
- R4/R5 : Pipeline dual-issue, lockstep pour sécurité
- R52 : ARMv8-R, hyperviseur, partitionnement temporel
- R82 : 64-bit, MMU, Linux + RT sur même SoC
Cortex-A (Application)
- A5/A7/A8 : ARMv7-A, MMU, caches L1 séparés
- A53/A55 : ARMv8-A, 64-bit, big.LITTLE
- A72/A76/A78 : Haute performance, out-of-order
- A78AE/X1 : Automobile/Edge, hétérogène
Modes et Privilèges
__asm volatile("MSR CONTROL, %0" : : "r" (val) : "memory");
__asm volatile("ISB");
Pipeline et Optimisation
Cortex-M7
Étages : Fetch → Decode → Issue → Execute → Memory → Writeback
Branches : 2 cycles de pénalité (prédicteur BTB 512 entrées)
Load-use : 1 cycle de bulle (résolu par forwarding)
Optimisations clés
- Alignement : fonctions critiques sur 32-byte boundary
- IT blocks : max 4 conditions, préférer if/else pour M7+
- TCM : ISR et data temps réel dans TCM (0 wait-state)
- Section .text.ram : code critique chargé en RAM au boot
void __attribute__((section(".ramfunc"))) TIM2_IRQHandler(void) {
}
Toolchain et Linker
Startup et Linker Script
/* Régions mémoire typiques Cortex-M4 */
MEMORY {
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K
}
SECTIONS {
.vector_table : ALIGN(4) {
LONG(ORIGIN(RAM) + LENGTH(RAM)); /* SP initial */
LONG(Reset_Handler); /* Reset */
LONG(NMI_Handler);
/* ... vecteurs ... */
} > FLASH
.text : {
_stext = .;
*(.text*)
*(.rodata*)
_etext = .;
} > FLASH
.data : ALIGN(4) {
_sdata = .;
*(.data*)
_edata = .;
} > RAM AT > FLASH
_sidata = LOADADDR(.data);
.bss : ALIGN(4) {
_sbss = .;
*(.bss*)
*(COMMON)
_ebss = .;
} > RAM
}
Startup Assembly
.syntax unified
.cpu cortex-m4
.thumb
.global Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
/* Copier .data de FLASH vers RAM */
ldr r0, =_sidata
ldr r1, =_sdata
ldr r2, =_edata
subs r2, r1
ble .L_zero_bss
.L_copy_data:
ldrb r4, [r0], #1
strb r4, [r1], #1
subs r2, #1
bgt .L_copy_data
.L_zero_bss:
ldr r1, =_sbss
ldr r2, =_ebss
subs r2, r1
ble .L_call_main
movs r3, #0
.L_zero_bss_loop:
str r3, [r1], #4
subs r2, #4
bgt .L_zero_bss_loop
.L_call_main:
bl main
bkpt #0
Exception et Interruptions
Table des vecteurs (Cortex-M)
Offset 0x00 : SP initial
Offset 0x04 : Reset
Offset 0x08 : NMI
Offset 0x0C : HardFault
Offset 0x10 : MemManage
Offset 0x14 : BusFault
Offset 0x18 : UsageFault
Offset 0x1C : (réservé)
...
Offset 0x40+ : IRQ 0..N (exceptions périphériques)
NVIC (Nested Vectored Interrupt Controller)
NVIC_SetPriority(USART1_IRQn, 0);
NVIC_SetPriority(TIM2_IRQn, 10);
NVIC_EnableIRQ(USART1_IRQn);
NVIC_EnableIRQ(TIM2_IRQn);
NVIC_SetPriority(PendSV_IRQn, 15);
Gestion des Faults
void HardFault_Handler(void) {
uint32_t cfsr = SCB->CFSR;
uint32_t hfsr = SCB->HFSR;
uint32_t mmfar = SCB->MMFAR;
uint32_t bfar = SCB->BFAR;
if (cfsr & (1 << 0))
if (cfsr & (1 << 8))
if (cfsr & (1 << 24))
__asm volatile("TST LR, #4\n\t"
"ITE EQ\n\t"
"MRSEQ R0, MSP\n\t"
"MRSNE R0, PSP\n\t"
: : : "r0");
while(1);
}
FPU (Floating Point Unit)
Activation (Cortex-M4/M7)
SCB->CPACR |= (3 << 20) | (3 << 22);
__asm volatile("DSB; ISB");
FPU->FPCCR |= (1 << 20);
Attributs pour forcer FP
__attribute__((cmse_nonsecure_call)) void fpu_isr(void);
__attribute__((__target__("fpu=fpv5-d16")))
MPU (Memory Protection Unit)
void MPU_Config(void) {
MPU->CTRL = 0;
MPU->RNR = 0;
MPU->RBAR = 0x08000000;
MPU->RASR = (0x1F << 1) |
(0x1 << 16) |
(0x1 << 18) |
(0x1 << 28);
MPU->RNR = 1;
MPU->RBAR = 0x20000000;
MPU->RASR = (0x17 << 1) |
(0x3 << 16) |
(0x1 << 28);
MPU->CTRL = (1 << 0) | (1 << 2);
__asm volatile("DSB; ISB");
}
TrustZone (ARMv8-M)
Séparation monde sécurisé / non-sécurisé
void SAU_Config(void) {
SAU->CTRL = 0;
SAU->RNR = 0;
SAU->RBAR = 0x08000000;
SAU->RLAR = (0x0807FFFF) | 1;
SAU->RNR = 1;
SAU->RBAR = 0x20000000;
SAU->RLAR = (0x2001FFFF) | 1;
SAU->CTRL = 1;
}
System Tick (SysTick)
void SysTick_Config(uint32_t reload) {
SysTick->LOAD = reload - 1;
SysTick->VAL = 0;
SysTick->CTRL = (1 << 0) |
(1 << 1) |
(1 << 2);
}
void SysTick_Handler(void) {
uwTick++;
}
Outils de Debug
GDB + OpenOCD
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg
arm-none-eabi-gdb build/firmware.elf
(gdb) target remote :3333
(gdb) monitor reset halt
(gdb) load
(gdb) continue
Traces ITM/SWO
ITM->LAR = 0xC5ACCE55;
ITM->TCR = (1 << 0) |
(1 << 2) |
(1 << 3);
ITM->TER[0] = 1;
void ITM_SendChar(char c) {
while(!(ITM->STIM[0] & 1));
ITM->STIM[0] = c;
}
Pitfalls
- Stack Overflow : Toujours configurer le MPU ou le stack guard (M3+)
- Data Alignment : Cortex-M supporte l'accès non-aligné (sauf M0/M0+)
- Volatile : Tous les registres mémoire-mappés doivent être
volatile
- Section .data : Vérifier que LOADADDR(.data) != ADDR(.data) dans le linker
- Interrupt Stacking : 8 words minimum (M4 avec FPU = 26 words)
- Cortex-M0 : Pas de bit-banding, pas de divide hardware, 1 cycle IT
- MPU sur M7 : Utiliser le Data Side MPU (DMPU) pour le cache
- WFI/WFE : Ne pas appeler WFI dans une boucle main sans event handler
Ressources