kehuadong

rt-thread模糊到清晰系列: thread切换相关

 

// 创建thread
tid = rt_thread_create("main", main_thread_entry, RT_NULL,
                           RT_MAIN_THREAD_STACK_SIZE, RT_MAIN_THREAD_PRIORITY, 20);
rt_thread_t rt_thread_create(
const char *name, void (*entry)(void *parameter), void *parameter, rt_uint32_t stack_size, rt_uint8_t priority, rt_uint32_t tick) { struct rt_thread *thread; void *stack_start; thread = (struct rt_thread *)rt_object_allocate(RT_Object_Class_Thread, name); if (thread == RT_NULL) return RT_NULL; stack_start = (void *)RT_KERNEL_MALLOC(stack_size); if (stack_start == RT_NULL) { /* allocate stack failure */ rt_object_delete((rt_object_t)thread); return RT_NULL; } _rt_thread_init(thread, name, entry, parameter, stack_start, stack_size, priority, tick); return thread; }

 

thread的栈相关 (这是只是初始化时所指定的寄存器值,用于第一次调度是恢复给psp,  第二次以后的调度, psp会发生变化, 从而sp也会更新为和初始化时可能不一样的位置)

static rt_err_t _rt_thread_init(struct rt_thread *thread,
                                const char       *name,
                                void (*entry)(void *parameter),
                                void             *parameter,
                                void             *stack_start,
                                rt_uint32_t       stack_size,
                                rt_uint8_t        priority,
                                rt_uint32_t       tick)
{
    /* init thread list */
    rt_list_init(&(thread->tlist));

    thread->entry = (void *)entry;
    thread->parameter = parameter;

    /* stack init */
    thread->stack_addr = stack_start;
    thread->stack_size = stack_size;

    /* init thread stack */
    rt_memset(thread->stack_addr, '#', thread->stack_size);
#ifdef ARCH_CPU_STACK_GROWS_UPWARD
    thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
                                          (void *)((char *)thread->stack_addr),
                                          (void *)rt_thread_exit);
#else
    thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
                                          (rt_uint8_t *)((char *)thread->stack_addr + thread->stack_size - sizeof(rt_ubase_t)),
                                          (void *)rt_thread_exit);
#endif
// ...
}

rt_uint8_t *rt_hw_stack_init(void       *tentry,
                             void       *parameter,
                             rt_uint8_t *stack_addr,
                             void       *texit)
{
    struct stack_frame *stack_frame;
    rt_uint8_t         *stk;
    unsigned long       i;

// 8字节对齐,并预留末端记录寄存器结构 stk
= stack_addr + sizeof(rt_uint32_t); stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8); stk -= sizeof(struct stack_frame); stack_frame = (struct stack_frame *)stk; /* init all register */ for (i = 0; i < sizeof(struct stack_frame) / sizeof(rt_uint32_t); i ++) { ((rt_uint32_t *)stack_frame)[i] = 0xdeadbeef; } stack_frame->exception_stack_frame.r0 = (unsigned long)parameter; /* r0 : argument */ stack_frame->exception_stack_frame.r1 = 0; /* r1 */ stack_frame->exception_stack_frame.r2 = 0; /* r2 */ stack_frame->exception_stack_frame.r3 = 0; /* r3 */ stack_frame->exception_stack_frame.r12 = 0; /* r12 */
// 返回接退出
stack_frame->exception_stack_frame.lr = (unsigned long)texit; /* lr */
// 入口
stack_frame->exception_stack_frame.pc = (unsigned long)tentry; /* entry point, pc */ stack_frame->exception_stack_frame.psr = 0x01000000L; /* PSR */ /* return task's current stack address */ return stk; }

 

切换thread

rt_hw_context_switch_interrupt
    EXPORT rt_hw_context_switch_interrupt
rt_hw_context_switch    PROC
    EXPORT rt_hw_context_switch

    ; set rt_thread_switch_interrupt_flag to 1
    LDR     r2, =rt_thread_switch_interrupt_flag
    LDR     r3, [r2]
    CMP     r3, #1
    BEQ     _reswitch
    MOV     r3, #1
    STR     r3, [r2]

    LDR     r2, =rt_interrupt_from_thread   ; set rt_interrupt_from_thread
    STR     r0, [r2]

_reswitch
    LDR     r2, =rt_interrupt_to_thread     ; set rt_interrupt_to_thread
    STR     r1, [r2]

    LDR     r0, =NVIC_INT_CTRL              ; trigger the PendSV exception (causes context switch)
    LDR     r1, =NVIC_PENDSVSET
    STR     r1, [r0]
    BX      LR
    ENDP

; r0 --> switch from thread stack
; r1 --> switch to thread stack
; psr, pc, lr, r12, r3, r2, r1, r0 are pushed into [from] stack
PendSV_Handler   PROC
    EXPORT PendSV_Handler

    ; disable interrupt to protect context switch
    MRS     r2, PRIMASK
    CPSID   I

    ; get rt_thread_switch_interrupt_flag
    LDR     r0, =rt_thread_switch_interrupt_flag
    LDR     r1, [r0]
    CBZ     r1, pendsv_exit         ; pendsv already handled

    ; clear rt_thread_switch_interrupt_flag to 0
    MOV     r1, #0x00
    STR     r1, [r0]

    LDR     r0, =rt_interrupt_from_thread
    LDR     r1, [r0]
    CBZ     r1, switch_to_thread    ; skip register save at the first time

    MRS     r1, psp                 ; get from thread stack pointer
    STMFD   r1!, {r4 - r11}         ; push r4 - r11 register
    LDR     r0, [r0]
    STR     r1, [r0]                ; update from thread stack pointer

switch_to_thread
    LDR     r1, =rt_interrupt_to_thread
    LDR     r1, [r1]
    LDR     r1, [r1]                ; load thread stack pointer

    LDMFD   r1!, {r4 - r11}         ; pop r4 - r11 register
    MSR     psp, r1                 ; update stack pointer

pendsv_exit
    ; restore interrupt
    MSR     PRIMASK, r2

    ORR     lr, lr, #0x04
    BX      lr
    ENDP

等价伪代码

// 设置thread的切换标志, 记录切换前后的thread的sp, 产生PendSV_Handler中断
void
rt_hw_context_switch_interrupt(pp_from_thread_sp, pp_to_thread_sp) { if (rt_thread_switch_interrupt_flag) { goto _reswitch; } rt_thread_switch_interrupt_flag = 1; rt_interrupt_from_thread = pp_from_thread_sp; _reswitch: rt_interrupt_to_thread = pp_to_thread_sp; [NVIC_INT_CTRL] = NVIC_PENDSVSET; }

// 1. 离开当前thread  >>> 把当前{r4-r11}入栈,并把栈位置记录给thread中的sp

// 2. 进入新的thread  >>> 1中的逆操作

void PendSV_Handler()
{
    r2 = PRIMASK;
    CPSID   I
    
    if (!rt_thread_switch_interrupt_flag) {
        goto pendsv_exit;
    }
    
    rt_thread_switch_interrupt_flag = 0;
    
    // 第一次调度,还没存在要离开的thread,因此不用压栈和记录栈顶位置给sp    
    if (!pp_from_thread_sp) {
        goto switch_to_thread;
    }
    
    MRS     r1, psp                 ; get from thread stack pointer
// r1! 代表 r1最终指向最后一个入栈之后 (没有叹号代表r1不变,即指向第一个入栈位置之前) STMFD r1
!, {r4 - r11} ; push r4 - r11 register // stack.push(r11) stack.push(r10) ... stack.push(r4) **pp_from_thread_sp = r1 switch_to_thread: r1 = **pp_to_thread_sp LDMFD r1!, {r4 - r11} MSR psp, r1 pendsv_exit: PRIMASK, r2 }

 

posted on 2020-11-08 21:57  kehuadong  阅读(180)  评论(0编辑  收藏  举报

导航