http://coactionos.com/embedded%20design%20tips/2013/10/09/Tips-Context-Switching-on-the-Cortex-M3/

The ARM Cortex-M3 architecture is designed with special features to facilitate implementing a pre-emptive RTOS. The system code takes advantage of these features when implementing context switching code.

ARM Cortex-M3 Context Switching Hardware

Interrupts

The SysTick and PendSV interrupts can both be used for context switching. The SysTick peripheral is a 24-bit timer that interrupts the processor each time it counts down to zero. This makes it well-suited to round-robin style context switching. The PendSV interrupt allows a task to cede control of the CPU when it is inactive (such as when sleeping or waiting for a hardware resource) which is helpful for FIFO style context switching. In addition to these interrupts, the ARM Cortex-M3 also includes two stack pointers.

Stacks

The stack pointers for the ARM Cortex-M3 include the main stack pointer (MSP) and the process stack pointer (PSP). The MSP is always used when handling interrupts and optionally used during regular program execution. The PSP is only used during regular program execution. ARM recommends using the MSP for the kernel as well as interrupts and recommends the PSP for executing other tasks. While the architecture provides the interrupts and the stack pointers, the implementation must provide the context switching code.

Context Switching Software Implementation

The RTOS manages the interrupts and stacks in order to achieve context switching. When switching contexts, the RTOS needs a way to keep track of which tasks are doing what using a task or scheduler table. Three routines are then required to: perform the context switch, initialize the system, and create new tasks.

Task Table

The task table, at a minimum, saves each task’s stack pointer; it is also helpful to save other information, such as the task parent and status, to allow the context switcher to selectively execute tasks. The following code shows an example of a structure that can be used for an entry in the task table:

typedef struct
{
void * sp; //The task's current stack pointer
int flags; //Status flags includes activity status, parent task, etc
} task_table_t;
int current_task;
task_table_t task_table[MAX_TASKS];

The sp member stores the value of the task’s stack pointer, while flags holds the task status. In this example, the task uses two status bits: one to indicate that the table entry is in use and the other to specify whether or not to execute the task.

Context Switching Routine

The context switcher needs to:

  • save the state of the current task,
  • update the current task index to the next task to be executed,
  • set up the CPU to either use the MSP (if it’s time to run the kernel) or the PSP,
  • and finally load the context of the task which is about to execute.

The following code is an example of a context switcher, preceded by some helper functions, and the interrupt handlers.

static uint32_t * stack; //This is stored on the heap rather than the stack

#define MAIN_RETURN 0xFFFFFFF9  //Tells the handler to return using the MSP
#define THREAD_RETURN 0xFFFFFFFD //Tells the handler to return using the PSP //Reads the main stack pointer
static inline void * rd_stack_ptr(void){
void * result=NULL;
asm volatile ("MRS %0, msp\n\t"
//"MOV r0, %0 \n\t"
: "=r" (result) );
return result;
} //This saves the context on the PSP, the Cortex-M3 pushes the other registers using hardware
static inline void save_context(void){
uint32_t scratch;
asm volatile ("MRS %0, psp\n\t"
"STMDB %0!, {r4-r11}\n\t"
"MSR psp, %0\n\t" : "=r" (scratch) );
} //This loads the context from the PSP, the Cortex-M3 loads the other registers using hardware
static inline void load_context(void){
uint32_t scratch;
asm volatile ("MRS %0, psp\n\t"
"LDMFD %0!, {r4-r11}\n\t"
"MSR psp, %0\n\t" : "=r" (scratch) );
} //The SysTick interrupt handler -- this grabs the main stack value then calls the context switcher
void systick_handler(void){
save_context(); //The context is immediately saved
stack = (uint32_t *)rd_stack_ptr();
if ( SysTick->CTRL & (<) ){ //Indicates timer counted to zero
context_switcher();
}
load_context(); //Since the PSP has been updated, this loads the last state of the new task
} //This does the same thing as the SysTick handler -- it is just triggered in a different way
void pendsv_handler(void){
save_context(); //The context is immediately saved
stack = (uint32_t *)rd_stack_ptr();
core_proc_context_switcher();
load_context(); //Since the PSP has been updated, this loads the last state of the new task
} //This reads the PSP so that it can be stored in the task table
static inline void * rd_thread_stack_ptr(void){
void * result=NULL;
asm volatile ("MRS %0, psp\n\t" : "=r" (result) );
return(result);
} //This writes the PSP so that the task table stack pointer can be used again
static inline void wr_thread_stack_ptr(void * ptr){
asm volatile ("MSR psp, %0\n\t" : : "r" (ptr) );
}

This is the function for the actual context switcher. This context switcher uses the MSP for task 0 (assumed to be the kernel) and the PSP for other tasks. It is also possible to use the PSP for the kernel and just use the MSP during interrupt handling.

//This is the context switcher
void context_switcher(void){
task_table[current_task].sp = rd_proc_stack_ptr(); //Save the current task's stack pointer
do {
current_task++;
if ( current_task == MAX_TASKS ){
current_task = ;
*((uint32_t*)stack) = MAIN_RETURN; //Return to main process using main stack
break;
} else if ( task_table[current_task].flags & EXEC_FLAG ){ //Check exec flag
//change to unprivileged mode
*((uint32_t*)stack) = THREAD_RETURN; //Use the thread stack upon handler return
break;
}
} while();
wr_proc_stack_ptr( task_table[current_task].sp ); //write the value of the PSP to the new task
}

The following diagram shows the chronology of the stack pointer when a switch happens between task one and task two. Note that because this implementation uses the MSP for task zero, the mechanics of a context switch are slightly different when switching to and from task zero. A context switching implementation can just as easily use the PSP for all tasks and the MSP for interrupts by using THREAD_RETURN rather than MAIN_RETURN above.

Initialization

The first thing that must be done is to initialize the main stack’s task table entry.

//This defines the stack frame that is saved  by the hardware
typedef struct {
uint32_t r0;
uint32_t r1;
uint32_t r2;
uint32_t r3;
uint32_t r12;
uint32_t lr;
uint32_t pc;
uint32_t psr;
} hw_stack_frame_t; //This defines the stack frame that must be saved by the software
typedef struct {
uint32_t r4;
uint32_t r5;
uint32_t r6;
uint32_t r7;
uint32_t r8;
uint32_t r9;
uint32_t r10;
uint32_t r11;
} sw_stack_frame_t; static char m_stack[sizeof(sw_stack_frame_t)]; void task_init(void){
...
task_table[].sp = m_stack + sizeof(sw_stack_frame_t);
....
//The systick needs to be configured to the desired round-robin time
//..when the systick interrupt fires, context switching will begin
}

Creating a New Task

Once the context switcher is initialized, there needs to be a mechanism to start new tasks. Starting a new task involves finding an available entry in the task table and initializing the new task’s stack.

int new_task(void *(*p)(void*), void * arg, void * stackaddr, int stack_size){
int i, j;
void * mem;
uint32_t * argp;
void * pc;
hw_stack_frame_t * process_frame;
//Disable context switching to support multi-threaded calls to this function
systick_disable_irq();
for(i=; i < MAX_TASKS; i++){
if( core_proc_table[i].flags == ){
process_frame = (hw_stack_frame_t *)(stackaddr - sizeof(hw_stack_frame_t));
process_frame->r0 = (uint32_t)arg;
process_frame->r1 = ;
process_frame->r2 = ;
process_frame->r3 = ;
process_frame->r12 = ;
process_frame->pc = ((uint32_t)p);
process_frame->lr = (uint32_t)del_process;
process_frame->psr = 0x21000000; //default PSR value
core_proc_table[i].flags = IN_USE_FLAG | EXEC_FLAG;
core_proc_table[i].sp = mem +
stack_size -
sizeof(hw_stack_frame_t) -
sizeof(sw_stack_frame_t);
break;
}
}
systick_enable_irq(); //Enable context switching
if ( i == MAX_TASKS ){
//New task could not be created
return ;
} else {
//New task ID is i
return i;
}
} //This is called when the task returns
void del_process(void){
task_table[current_task_index].flags = ; //clear the in use and exec flags
SCB->ICSR |= (<<); //switch the context
while(); //once the context changes, the program will no longer return to this thread
}

Conclusion

ARM, with the Cortex M architecture, delivers valuable hardware resources to enable context switching. The interrupts support both round robing and FIFO style scheduling while the dual stacks allow the kernel process and interrupts to execute on a dedicated stack. With just a few software routines to perform the context switching, initialize the system, and create new stacks, system developers can create a functioning pre-emptive kernel.

For more information on context switching on the Cortex-M3, see the Cortex-M3 technical reference manual from ARM.

Context Switching on the Cortex-M3的更多相关文章

  1. Cortex-M3 Context Switching

    http://www.embedded.com/design/embedded/4231326/Taking-advantage-of-the-Cortex-M3-s-pre-emptive-cont ...

  2. ARM Cortex M3系列GPIO口介绍(工作方式探讨)

    一.Cortex M3的GPIO口特性    在介绍GPIO口功能前,有必要先说明一下M3的结构框图,这样能够更好理解总线结构和GPIO所处的位置. Cortex M3结构框图     从图中可以看出 ...

  3. ARM Cortex M3(V7-M架构)硬件启动程序 一

    Cortex-m3启动代码分析笔记 启动代码文件名是STM32F10X.S,它的作用先总结下,然后再分析. 启动代码作用一般是: 1)堆和栈的初始化: 2)中断向量表定义: 3)地址重映射及中断向量表 ...

  4. ARM 架构、ARM7、ARM9、STM32、Cortex M3 M4 、51、AVR 之间有什么区别和联系?(转载自知乎)

    ARM架构:  由英国ARM公司设计的一系列32位的RISC微处理器架构总称,现有ARMv1~ARMv8种类. ARM7:       一类采用ARMv3或ARMv4架构的,使用冯诺依曼结构的内核. ...

  5. STM32学习之路入门篇之指令集及cortex——m3的存储系统

    STM32学习之路入门篇之指令集及cortex——m3的存储系统 一.汇编语言基础 一).汇编语言:基本语法 1.汇编指令最典型的书写模式: 标号 操作码        操作数1, 操作数2,... ...

  6. Implementation of Serial Wire JTAG flash programming in ARM Cortex M3 Processors

    Implementation of Serial Wire JTAG flash programming in ARM Cortex M3 Processors The goal of the pro ...

  7. Linux Context , Interrupts 和 Context Switching 说明

    一. 进程Context 定义 当一个进程在执行时, CPU的所有寄存器中的值.进程的状态以及堆栈中的内容,比如各个变量和数据,包括所有的寄存器变量.进程打开的文件.内存信息等.这些信息被称为该进程的 ...

  8. Linux Context , Interrupts 和 Context Switching 说明【转】

    转自:http://blog.csdn.net/tianlesoftware/article/details/6461207 一. 进程Context 定义 当一个进程在执行时, CPU的所有寄存器中 ...

  9. DYNAMIC CONTEXT SWITCHING BETWEEN ARCHITECTURALLY DISTINCT GRAPHICS PROCESSORS

    FIELD OF INVENTION This invention relates to computer graphics processing, and more specifically to ...

随机推荐

  1. 洛谷 P3916 【图的遍历】反向加边+dfs

    前言: 对于这类带环的图,一般记忆化搜索不能很好的对所有遍历的边进行更新取值.因为环上的点可以相互到达,所以他们的答案因当是同步更新的,而dfs一旦你回溯完环上某个点就不会在更新这个点的答案了,做不到 ...

  2. [cookie篇]cookie-parser之parser.js

    cookie-parser的作用,官方的说法是:Parse Cookie header and populate req.cookies with an object keyed by the coo ...

  3. .net的锁

    一.lock .Monitor  处理并行任务的时候,效率最高的就是多线程.当不同线程需要访问同一资源时候,就需要同步了.就像生活中很多人要一起赶飞机大家都要访问飞机这个资源每个人是一条线程那么就有门 ...

  4. jira ao UpgradeTask

    插件发布到市场后,后续版本迭代的过程中,可能会对ao实体类的字段作添加或删除,或者要将某一字段的值映射解析到另一字段上. 本来这个工作,可以在插件启动的时候,在实现了com.atlassian.sal ...

  5. Java基础92 MySQL插入当前系统时间

    1.时间格式 用yyyy表示年份(year). -- y  表示年的最后一位,yy 表示年的最后2位,yyy 表示年的最后3位,yyyy 用4位数表示年用MM表示月份(month).-- mm 用2位 ...

  6. 卷积神经网络CNN经典模型整理Lenet,Alexnet,Googlenet,VGG,Deep Residual Learning(转)

    参考:http://blog.csdn.net/xbinworld/article/details/45619685

  7. Java编程的逻辑 (5) - 小数计算为什么会出错?

    本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http:/ ...

  8. Java编程的逻辑 (11) - 初识函数

    本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http:/ ...

  9. json字符串的标准格式

    现在越来越多的项目和开发插件等默认都会支持和使用json数据格式,作为数据保持.传输的一种方式. 说是其中一种,就标示还有好多其他格式.比如:最多是xml.webservice的标准数据格式. 不过由 ...

  10. 黑马程序员_java基础笔记(14)...交通灯管理系统_编码思路及代码

    —————————— ASP.Net+Android+IOS开发..Net培训.期待与您交流! —————————— 1,面试题——交通灯管理系统 模拟实现十字路口的交通灯管理系统逻辑,具体需求如下: ...