05. ARM Cortex‐M Core - hasanalpdoyduk/STM32-Notes GitHub Wiki
5.1. Core Register Set
These registers do not have unique addresses to access them. Hence, there are no parts of the processor memory map. You cannot access them in a C program using address dereferencing. To access these registers, you have to use assembly instructions.

- All the core registers are 32-bit wide.
- R0 to R12 (total 13) registers are for general-purpose.
- R13 is called as SP (stack pointer).
- R14 is called as LR (link register). It stores the return information for subroutines, functions, calls and exceptions.
- R15 is called as PC (program counter). It contains the current program address.

5.2. Reset Sequence
- When you reset the processor, the PC is loaded with the value 0x00000000.
- Then processor reads the value at memory location 0x00000000 in to MSP (Main Stack pointer register). MSP = value at 0x00000000
- After that processor reads the value at memory location 0x00000004 in to PC. That value is actually address of the reset handler.
- PC jumps to the reset handler.
- A reset handler is just a C or assembly function written by you to carry out any initializations required.
- From reset handler you call your
main()function of the application.
5.3. Operational Modes
- Thread Mode
- Handler Mode
All your application code will execute under the Thread Mode of the processor. This is also called User Mode. All the exception handlers or interrupt handlers will run under the Handler Mode of the processor.
The processor always starts with the Thread Mode. Whenever the core meets this, the system exception or any external interrupts, then the core will change its mode to Handler Mode in order to service the ISR.
5.4. Access Levels
- PAL (Privileged Access Level)
- NPAL (Non-privileged Access Level)
If your code is running with PAL, your code has full access to all the processor-specific resources and restricted registers. If your code is running with NPAL, then your code may not have access to some of the restricted registers.
By default, your code will run in PAL. When the processor is in Thread Mode, it’s possible to move the processor into NPAL. Once you move out of PAL to NPAL, being in the Thread Mode, it’s not possible to come back to PAL unless you change the processor operational mode to Handler Mode.
Handler Mode code execution is always with PAL.
Use the Control Register of the processor if you want to switch between the access levels.
To switch between access levels demonstration:
__asm volatile ("MRS R0, CONTROL"); //read
__asm volatile ("ORR R0, R0, #0x01"); // modify
__asm volatile ("MSR CONTROL, R0"); //write
5.5. Memory Map
The memory map explains the mapping of different peripheral registers and memories in the processor's addressable memory location range. The processor, addressable memory location range, depends upon the size of the address bus.
5.5.1. Code Region
0x00000000 to 0x1FFFFFFF (512 MB)
This is the region where the MCU vendors should connect code memory. The processor, by default, fetches vector table information from this region right after reset.
5.5.2. SRAM Region
0x20000000 to 0x3FFFFFFF (512 MB)
The first 1MB of the SRAM region is bit addressable. You can also execute program code from this region.
5.5.3. Peripheral Region
0x40000000 to 0x43FFFFFF (512 MB)
The first 1MB of the Peripheral region is bit addressable. This is an execute never (XN) region. Trying to execute code from this region will trigger a fault exception.
5.5.4. External RAM Region
0x60000000 to 0x9FFFFFFF (1GB)
This region is intended for either on-chip or off-chip memory. You can execute code i this region.
5.5.5. External Device Region
0xA0000000 to 0xDFFFFFFF (1 GB)
This region is intended for external devices and/or shared memory. This is an execute never (XN) region.
5.5.6. Private Peripheral Bus Region
0xE0000000 to 0xE00FFFFF
This region includes the NVIC, system timer, and system control block. This is an execute never (XN) region.
5.6. Bus Interfaces

In Cortex-Mx processors, the bus interfaces are based on the Advanced Microcontroller Bus Architecture (AMBA) specification. That is designed by ARM, which governs the standard for on-chip communication inside the system on-chip.
The AMBA specification supports several bus protocols:
- AHB Lite (AMBA High-performance Bus)
- APB (AMBA Peripheral Bus
AHB Lite bus is mainly used for the main bus interfaces. AHB Lite bus is mainly used for high-speed communication with peripherals that demand high operation speed.
The APB bus is used for PPB access and some on-chip peripheral access using an AHB-APB bridge. The APB bus is used for low-speed communication compared to AHB. Most of the peripherals that don’t require high operation speed are connected to this bus.

- I-CODE interface: If the instructions are present in between the memory locations 0x00000000 to 0x1FFFFFFC.
- System bus: If the instructions are present outside the memory locations 0x00000000 to 0x1FFFFFFC.
- D-CODE interface: If the data is present in between the memory locations 0x00000000 to 0x1FFFFFFF.
- System bus: If the data is present outside the memory locations 0x00000000 to 0x1FFFFFFF.

I-CODE and D-CODE are the only bus interfaces which connected to the FLASH.

5.7. Bit Banding
It is the capability to address a single bit of a memory address. Bit banding provides atomic operations to bit data.
Bit banding is only applicable for the initial 1MB of SRAM and Peripheral memory regions. You can access the bit band region by using bit band alias addresses.
Calculation of the bit band alias address:
- General formula:
$$ \text{Alias address} = \text{Alias base} + (32 \times (\text{Bit band memory address} - \text{Bit band base})) + \text{Bit} \times 4 $$
- For example 7th bit position of the memory location 0x20000200 using its alias address
$$ \text{Alias address} = 0x2200000+(32 \times (0x20000200 - 0x20000000)) + 7 \times 4 $$
uint8_t *ptr = (uint8_t*) 0x20000200;
\\normal method
*ptr &= ~(1 << 7);
\\bit banding method
uint8_t *alias_addr = (uint8_t*) 0x22000000 + (32 * (0x20000200 - 0x20000000)) + 7 * 4;
*alias_addr = 0;
5.8. Inline Assembly Code Usage
Inline assembly is a way to embed assembly instructions directly inside C code. Basic GCC inline assembly code syntax as shown below:
__asm volatile("LDR R0, [R1]");
__asm volatile("LDR R1, [R2]");
__asm volatile("ADD R1, R0");
__asm volatile("STR R1, [R3]");
You can write them in one asm block:
__asm volatile(
"LDR R0, [R1]"
"LDR R1, [R2]"
"ADD R1, R0"
"STR R1, [R3]"
);
Syntax is like that:
__asm volatile("assembly code" : output_operands : input_operands : clobbered_registers);
- assembly code: actual ARM instructions
- output_operands: variables modified by the assembly
- input_operands: variables passed into assembly
- clobbered_registers: registers or flags changed inside assembly
The example about moving the content of C variable “val” to ARM register R0 is shown below:
int val;
__asm volatile("MOV R0, %0": : "r" (val));
The example about moving the content of Control Register to C variable control_reg is shown below:
int control_reg;
__asm volatile("MRS %0, CONTROL": "=r" (control_reg));
| Constraint Modifier | Specifies |
|---|---|
| = | Write-only operand, usually used for all output operands |
| + | Read-write operand, must be listed as an output operand |
| & | A register that should be used for output only |
The example about copying the content of C variable var1 and var2 is shown below:
int var1 = 10, var2;
__asm("MOV %0, %1": "=r" (var2): "r" (var1));
The example about copying the contents of a pointer into another variable is shown below:
int p1, *p2;
p2 = (int*) 0x20000008;
__asm volatile("LDR %0, [%1]": "=r" (p1): "r" (p2)); //p1 = *p2
To access bytes:
-
LDR → Load Register
Loads a 32-bit word from memory into a register.
-
LDRH → Load Register Halfword
Loads a 16-bit halfword from memory into a register (zero-extends to 32-bit).
-
LDRB → Load Register Byte
Loads an 8-bit byte from memory into a register (zero-extends to 32-bit).
5.8.1. Attribute Naked Functions:
The compiler does not generate prologue and epilogue sequences for functions with __attribute__((naked)).
__attibute__((naked)) void UsageFaultHandler(void)
{
__asm (...)
}
5.9. T-Bit of the EPSR
Various ARM processors support ARM-Thumb interworking, which means the ability to switch between ARM and Thumb state.
The processor must be in ARM state to execute instructions that are from ARM ISA, and it must be in Thumb state to execute instructions of Thumb ISA.
If T-bit of the ESPR is set (1), the processor thinks that the next instruction that it is about to execute is from the Thumb ISA. If T-bit is reset (0), the processor thinks that the next is that is about to be executed is from the ARM ISA.
The Cortex-Mx processor does not support the ARM state. The value of T bit must always be 1.
The LSB (0th bit) of the PC is linked to this T-bit. When you load a value or an address into the PC, the LSB of the value is loaded into the T-bit. Hence, any address you place in the PC must have its LSB as 1. This is usually taken care of by the compiler. This is the reason why you see that all vector addresses are incremented by 1 in the vector table.