Exception For System Level Services in Arm Cortex Processor : When you’re working with ARM Cortex processors, especially in embedded systems, you’ll hear a lot about exceptions. But what are they, and how do they help your system run more smoothly — especially at the system level?
Let’s break it down simply. 😊
Exception For System Level Services in Arm Cortex Processor
What is an Exception?
An exception is a special kind of signal that tells the processor to stop what it’s doing and do something more important first. Think of it like this:
“Hey CPU, pause your current work. There’s something urgent to handle!”
This could be an interrupt from hardware, a system fault, or even a software request like a system call.
Why Are Exceptions Important in ARM Cortex?
ARM Cortex-M processors are designed for embedded and real-time systems. Exceptions allow the processor to respond quickly to:
- 🔌 Hardware interrupts (like GPIO or Timer events)
- 🧱 Faults (like trying to access invalid memory)
- 🧬 System-level services (like context switching in an RTOS)
They help keep the system responsive, safe, and organized.
Types of Exceptions in Cortex-M
There are two main categories:
1. System Exceptions (Predefined by ARM)
These are built into the Cortex-M core itself. Examples:
- Reset – Triggered when the system starts or restarts.
- NMI (Non-Maskable Interrupt) – High-priority interrupt that can’t be disabled.
- HardFault – Happens when something goes very wrong (e.g., divide by zero).
- SVCall (Supervisor Call) – Used by the OS to switch between user/system mode.
- PendSV – Used for context switching in RTOS (e.g., FreeRTOS).
- SysTick – A system timer interrupt, often used for OS ticks.
2. External Interrupts (IRQs)
These come from outside peripherals — like buttons, UART, I2C, etc.
System-Level Services Using Exceptions
Here’s how exceptions help in system-level operations like OS scheduling:
SVCall (Supervisor Call)
When a user app needs the OS to perform a system-level task (like memory allocation), it can make a supervisor call. This triggers the SVCall exception.
Think of it like:
“Hey OS, please do something privileged for me.”
PendSV (Pendable Service Call)
The RTOS uses this to perform context switches. When it’s time to switch tasks, the OS sets PendSV as “pending”. When the CPU is ready, it runs the PendSV handler to switch tasks.
SysTick (System Timer)
This fires at regular intervals (e.g., every 1ms). It’s often used to keep track of time and schedule tasks in real-time operating systems.
Simple Real-World Use Case
Let’s say you’re building a smart fan with FreeRTOS running on a Cortex-M4:
- SysTick triggers every 1ms and updates the system tick.
- Based on tick count, the OS decides to switch to another task.
- It sets the PendSV exception.
- The processor runs the PendSV handler, which saves the current task context and switches to the next.
- A user app asks for memory — it triggers SVCall to request memory allocation from the kernel.
All these system-level services run smoothly using exceptions!
Bonus: What Happens on Errors?
If your program does something wrong, the processor triggers:
- HardFault
- MemManage Fault
- BusFault
- UsageFault
These exceptions protect your system and help with debugging.
Summary
Exception | Purpose |
---|---|
Reset | System starts |
NMI | High-priority external signal |
HardFault | Serious error handling |
SVCall | Request system-level services |
PendSV | Context switching in RTOS |
SysTick | System timekeeping |
ARM Cortex-Mx processors, focusing on SVC and PendSV:
Exceptions for System-Level Services (Cortex-Mx)
The ARM Cortex-Mx processors support various exceptions, but two key system-level service exceptions used in operating systems are:
1. SVC (Supervisor Call) Exception
- Purpose: Used to request privileged operations or access to system-level services from a less privileged task.
- When Used: A user-mode application or thread generates an SVC exception using the
SVC
instruction. - Common Use Case:
- Requesting services from the OS kernel such as:
- Accessing peripherals
- Managing device drivers
- Allocating memory
- Requesting services from the OS kernel such as:
- Why Needed: Ensures that only authorized and controlled code (OS) can access critical system resources.
📌 Example:
User task calls SVC 0x01
→ triggers an SVC exception → OS handler processes it → returns result to user task.
2. PendSV (Pendable Service Call) Exception
- Purpose: Designed specifically for context switching between tasks in an OS.
- When Used: When a context switch is required, the OS sets the PendSV interrupt to pending.
- Pendable: It is a low-priority, software-triggered exception that can be delayed until no higher-priority exceptions are active.
- Common Use Case:
- Task switching in RTOS (e.g., switching from Task A to Task B)
- Why Needed: Ensures context switching doesn’t interfere with high-priority interrupts or exceptions.
📌 Example:
Task A finishes → OS schedules Task B → PendSV is triggered → saves Task A context & restores Task B context.
Summary Table
Exception | Purpose | Trigger Source | Use Case in OS |
---|---|---|---|
SVC | Request system services | SVC instruction | Accessing device drivers, memory |
PendSV | Perform context switching | Software | Switching between tasks |
What is SVC (Supervisor Call)?
SVC is a special instruction in the Thumb Instruction Set Architecture (ISA) of ARM Cortex-M processors.
- When a CPU executes
SVC
, it causes a special exception called the SVC exception. - This transfers control from user code (unprivileged mode) to kernel code (privileged mode).
- It’s like raising your hand to ask the operating system (OS) to do something you’re not allowed to do directly.
Why Do We Use SVC?
In embedded systems or RTOS, we often run:
- User tasks in unprivileged mode (to protect the system)
- Kernel code in privileged mode (can access hardware, critical memory, etc.)
Since user code cannot access critical resources (like hardware registers or memory-mapped peripherals) directly, it uses SVC
to request the OS to do so on its behalf.
Example Uses:
- Creating/deleting tasks
- Accessing hardware (GPIO, UART)
- Requesting memory allocation
- Starting/stopping timers
How SVC Works — Step by Step:
Let’s break it down:
1. The User Code Issues an SVC Instruction
__asm("SVC #0"); // Inline Assembly — calls SVC with number 0
- The
#0
is the SVC number — a value used to indicate which service you’re requesting (like a function ID). - This instruction triggers the SVC exception.
2. Processor Automatically Saves Context
When the SVC
is executed:
- The processor switches to handler mode (privileged mode).
- It pushes some registers (R0–R3, R12, LR, PC, xPSR) onto the stack to save the current context.
- This is similar to how interrupts are handled.
3. Processor Jumps to the SVC Handler
The processor now jumps to the address of the SVC handler, defined in the vector table:
void SVC_Handler(void) {
// Kernel code handles the request here
}
4. The SVC Handler Decodes the Request
The handler checks the SVC number used in the instruction to determine what service is requested.
How to extract the SVC number:
Because the number is embedded in the instruction (not passed in R0–R3), the handler must read the instruction that caused the exception:
void SVC_Handler(void) {
uint32_t *stack_ptr;
uint8_t svc_number;
// Get stack pointer (usually MSP or PSP depending on context)
__asm("TST lr, #4\n"
"ITE EQ\n"
"MRSEQ %0, MSP\n"
"MRSNE %0, PSP\n"
: "=r" (stack_ptr));
// Find the instruction that caused the SVC
uint16_t *svc_instr_addr = ((uint16_t*)stack_ptr[6]) - 1;
uint16_t svc_instr = *svc_instr_addr;
// Extract the SVC number (last 8 bits)
svc_number = (uint8_t)(svc_instr & 0xFF);
switch (svc_number) {
case 0:
// Handle SVC #0 request
break;
case 1:
// Handle SVC #1 request
break;
// and so on...
}
}
5. Kernel Code Executes the Requested Service
- After identifying the request (like start a task, turn on LED), the kernel performs the task in privileged mode.
6. Return to User Code
- Once the handler completes, the context is restored, and execution resumes at the next instruction after
SVC
.
Key Points
Concept | Explanation |
---|---|
SVC Instruction | Causes a software exception (like a controlled interrupt) |
Used By | Unprivileged user code to request services from kernel |
SVC Number | A number (0–255) passed with the SVC instruction to indicate what service is needed |
Handler Name | SVC_Handler() — OS or firmware must implement this |
Privilege Escalation | Switches temporarily from user mode to privileged mode |
Security | Ensures user tasks don’t access critical resources directly |
Analogy
Think of it like this:
- You are in a library (user task), and you need access to a restricted book (privileged resource).
- You cannot go to the restricted section.
- So, you ring a bell (SVC instruction) with a code number saying which book you want.
- The librarian (SVC handler) checks your request and fetches the book on your behalf.
When Is It Used in RTOS?
- Thread switching or task management
- Requesting kernel services from user apps
- Accessing hardware securely
- System calls in embedded systems without a full-fledged OS
Summary
✅ SVC is a software interrupt used to safely access system-level services
✅ Switches from unprivileged user mode to privileged kernel mode
✅ The SVC handler reads the SVC number and executes the requested action
✅ Helps in security, task isolation, and controlled hardware access
The SVC (Supervisor Call) exception is a mechanism used in ARM Cortex-M processors to transition from unprivileged to privileged mode or to request OS services (like system calls) from the application layer. There are two methods to trigger an SVC exception:
1. Direct Execution of the SVC
Instruction
This is the standard and most efficient way to trigger an SVC exception.
How it works:
- The CPU executes an
SVC
instruction with an immediate value (which usually indicates the specific service requested). - This causes the processor to immediately enter the SVC handler, switching to Handler mode.
Example:
SVC #0x04 ; Assembly instruction to trigger SVC exception with immediate value 0x04
In C (using inline assembly or CMSIS):
__asm("SVC #0x04");
Use Cases:
- Used in RTOS for system calls
- Switching from Thread mode (unprivileged) to Handler mode (privileged)
Advantages:
- Low latency
- Immediate and deterministic triggering
- Directly supported by the CPU instruction set
2. Setting the Exception Pending Bit in SHCSR (System Handler Control and State Register)
This is a less common and non-standard method used mainly for testing or simulation.
How it works:
- The System Handler Control and State Register (SHCSR) contains a bit for SVC pending (
SVCALLPENDED
). - Manually setting this bit to
1
via software tricks the processor into thinking an SVC is pending. - When conditions allow, the SVC handler will be called.
Example:
#define SCB_SHCSR (*((volatile uint32_t*)0xE000ED24))
SCB_SHCSR |= (1 << 15); // Set SVCALLPENDED bit manually
- Bit 15 in
SHCSR
isSVCALLPENDED
.
Important Notes:
- This does not immediately trigger the SVC handler.
- The exception must be enabled, and priority levels must allow it to be serviced.
- Not a common practice — typically used in test environments, fault injection, or debugging.
Summary Table:
Method | Description | Efficiency | Use Case | Notes |
---|---|---|---|---|
SVC #imm | Direct instruction | ✅ High | System calls, privilege switch | Standard and efficient |
SHCSR bit | Set pending bit | ❌ Low | Testing, debug | Uncommon and delayed execution |
What is SVC?
SVC
is an instruction used in ARM Cortex-M to request a privileged system service.- The syntax is:
SVC #<number>
Example:SVC #0x20 ; Here, 0x20 is the SVC number
Goal
When an SVC instruction is executed, it causes a SVC exception, and the processor jumps to the SVC_Handler.
Your task is to:
- Get the address of the SVC instruction that caused the exception.
- Read the SVC opcode at that address.
- Extract the SVC number from that opcode.
What Happens During SVC?
- The CPU runs a line like
SVC #0x05
. - This triggers an SVC exception.
- The CPU automatically saves some context (like registers) on the stack — this includes the Program Counter (PC) where it stopped, i.e., the instruction after the SVC.
Step-by-Step Breakdown
Step 1: Understand Stack Frame Format
When an exception like SVC occurs, ARM Cortex-M pushes the following registers onto the stack in this order:
Offset | Register |
---|---|
0x00 | R0 |
0x04 | R1 |
0x08 | R2 |
0x0C | R3 |
0x10 | R12 |
0x14 | LR |
0x18 | PC (🟢 Points to next instruction after SVC) |
0x1C | xPSR |
Step 2: Get the Stack Pointer in the Handler
void SVC_Handler(void)
{
__asm volatile
(
"TST lr, #4 \n" // Test bit 2 of LR to know which stack (MSP or PSP)
"ITE EQ \n"
"MRSEQ r0, MSP \n" // If 0: Main Stack Pointer
"MRSNE r0, PSP \n" // If 1: Process Stack Pointer
"B SVC_Handler_C \n" // Branch to C handler and pass r0
);
}
Step 3: In C, Extract the SVC Number
Here is the C function that receives the stack frame pointer:
void SVC_Handler_C(uint32_t *stack_frame)
{
// PC is at offset 6 (index 6), because each register is 4 bytes
uint32_t return_address = stack_frame[6];
// SVC instruction is at (return_address - 2)
// because PC points to instruction AFTER SVC
uint16_t *svc_instruction_address = (uint16_t *)(return_address - 2);
uint8_t svc_number = (uint8_t)(*svc_instruction_address & 0xFF); // SVC number is in the lower byte
// Now you can use svc_number for your logic
}
Why return_address - 2
?
- On Thumb instruction set (which Cortex-M uses),
SVC
is a 16-bit instruction. - The saved PC points to the next instruction, so subtract 2 to get the SVC instruction itself.
Example
Suppose user code runs:
SVC #0x23
Then in the handler:
PC = address after SVC
=0x08000102
SVC instruction is at 0x08000100
- At that memory:
0xDF23
- 0xDF is opcode for SVC
- 0x23 is the SVC number
- Extract using:
uint8_t svc_number = *svc_instruction_address & 0xFF; // svc_number = 0x23
Final Summary
Step | Action |
---|---|
1 | Determine active stack (MSP/PSP) in assembly |
2 | Pass stack frame pointer to C function |
3 | Extract PC from stack (offset 6) |
4 | Subtract 2 to get SVC instruction address |
5 | Read 16-bit value, extract lower byte for SVC number |
Full Example: Execute SVC
, extract number, increment by 4 and return
#include <stdio.h>
#include <stdint.h>
uint32_t svc_result = 0;
void SVC_Handler_Main(uint32_t *stack_frame);
__attribute__((naked)) void SVC_Handler(void) {
__asm volatile (
"TST lr, #4 \n" // Test bit 2 of LR to find stack pointer (MSP or PSP)
"ITE EQ \n"
"MRSEQ r0, MSP \n" // If 0, use MSP
"MRSNE r0, PSP \n" // Else, use PSP
"B SVC_Handler_Main \n" // Branch to handler in C
);
}
// This function is called with the pointer to the stack frame
void SVC_Handler_Main(uint32_t *stack_frame) {
// Stack frame: R0, R1, R2, R3, R12, LR, PC, xPSR
uint16_t *pc_ptr = (uint16_t *)stack_frame[6]; // PC points to next instruction after SVC
uint8_t svc_number = ((uint8_t *)pc_ptr)[-1]; // SVC immediate is 1 byte before PC
printf("SVC number: %d\n", svc_number);
svc_result = svc_number + 4;
}
uint32_t call_svc(uint8_t svc_num) {
__asm volatile (
"mov r0, %[num] \n" // Move svc_num to r0 (optional for general use)
"svc %[immediate] \n" // Trigger SVC
:
: [num] "r" (svc_num), [immediate] "I" (0x05) // SVC #5 hardcoded for demo
: "r0"
);
return svc_result;
}
int main(void) {
printf("Calling SVC now...\n");
uint32_t result = call_svc(5);
printf("Returned value from SVC handler: %u\n", result);
return 0;
}
Explanation:
SVC_Handler
: Naked function with inline assembly that determines which stack pointer was used (MSP or PSP), then passes stack pointer toSVC_Handler_Main
.SVC_Handler_Main
: Extracts SVC number from instruction that caused the exception (usingPC - 1
), prints it, increments by 4, and stores insvc_result
.call_svc(5)
: Executes thesvc #0x5
instruction and later returns the modified result from the handler.
Output:
Calling SVC now...
SVC number: 5
Returned value from SVC handler: 9
Leave a Reply