FreeRTOS
riscv 通用寄存器
CPU中包含32个通用寄存器,有时候也会被称为通用寄存器文件,如图1所示。通用寄存器的命名方式为X0-X31。其中第一个寄存器X0的值,被硬连线到0,因此值永远是0。其他寄存器X1-X31都是可读可写的。0-31也叫做索引号,索引号也可以理解为寄存器的地址,当指令需要调用通用寄存器时可以通过索引号查找。 对于32位系统,所有通用寄存器的宽度都是32bit,寄存器总个数也是32个。
PC(program counter)是程序计数器,也是一个寄存器。在CPU中PC寄存器并不和上述32个通用寄存器在一起,寄存器文件中不包含PC。PC的宽度和通用寄存器的宽度一样。XLEN的值一般跟RISC-V CPU架构有关系。 如果是32位架构的CPU,那么XLEN的值就是32。图1中XLEN-1 = 32-1 =31,即在一个通用寄存器中的最高位为31。在64位CPU中通用寄存器的宽度是64,同时PC宽度也是64位,最高位为64-1 =63。
寄存器别名
riscv没有栈基址寄存器
发生函数调用用:sp-size
函数返回: sp+-size
#include <stdio.h> void swap(int *a, int *b){ int c = *a; *a = *b; *b = c; } int main() { int a= 101; int b= 202; swap(&a, &b); printf("a = %d\n", a); printf("b = %d\n", b); return 0; }
对应的汇编代码如下(删除了print,栈溢出检查等无关部分):
void swap(int *a, int *b){ 103e: 7179 addi sp,sp,-48 // 继续给栈开辟48字节的空间 1040: f406 sd ra,40(sp) // 将ra保存至栈中 1042: f022 sd s0,32(sp) // 将s0保存至栈中 1044: 1800 addi s0,sp,48 // s0此时只想栈的高地址 1046: fea43423 sd a0,-24(s0) // 将a0寄存器的地址,到s0-16 ~ s0-24中 104a: feb43023 sd a1,-32(s0) // 将a1寄存器的地址,保存到s0-24 ~ s0-32中 int c = *a; 104e: fe843503 ld a0,-24(s0) // 从s0-16 ~ s0-24中读取八个字节到a0中,该地址为a0传入的地址,地址中存储的值为101 1052: 4108 lw a0,0(a0) // 将a0地址中的值加载到a0寄存器中,此时a0为101 1054: fca42e23 sw a0,-36(s0) // 将a0的值加载到s0-32 ~ s0-36中,此处存入的为一个值 *a = *b; 1058: fe043503 ld a0,-32(s0) // 将s0-24 ~ s0-32的地址读取到a0中 105c: 4108 lw a0,0(a0) // 将a0的值加载给a0,此时a0为202 105e: fe843583 ld a1,-24(s0) // 将s0-16 ~ s0-24的八字节地址给a1 1062: c188 sw a0,0(a1) // 将a0的值加载到a1的地址中,此时a1的地址,即s0-16 ~ s0-24中的值为202 *b = c; 1064: fdc42503 lw a0,-36(s0) // 将s0-32 ~ s0-36存的值加载给a0,此处的值为101 1068: fe043583 ld a1,-32(s0) // 将s0-24 ~ s0-32的八字节地址读取到a1中 106c: c188 sw a0,0(a1) // 将a0的值读取到a1中,原来s0-24 ~ s0-32的地址的值变成了101 } 106e: 7402 ld s0,32(sp) // 恢复s0 1070: 70a2 ld ra,40(sp) // 恢复ra 1072: 6145 addi sp,sp,48 // 恢复sp指针 1074: 8082 ret // 退出 0000000000001076 <main>: int main() { 1076: 7139 addi sp,sp,-64 // 给栈开辟64字节的,此处sp为栈指针 1078: fc06 sd ra,56(sp) // 将返回地址ra保存到栈中 107a: f822 sd s0,48(sp) // 将s0(fp)帧指针保存到栈中 107c: 0080 addi s0,sp,64 // sp指向的是栈底,s0此时的值为sp+64的值,也就是基地址 000000000000107e <.LBB1_3>: 107e: 00001517 auipc a0,0x1 // 将0x1取31位到12位,然后左移12位+pc的地址,结果写入到a0寄存器 1082: 19a53503 ld a0,410(a0) # 2218 <__stack_chk_guard@LIBC> // 将a0偏移410字节的值给a0,此步骤是为了防止堆栈溢出添加的检测保护 1086: 610c ld a1,0(a0) // 将a0的八字节赋给a1 1088: feb43423 sd a1,-24(s0) // 将a1寄存器的值保存到栈中,保存至s0-16 ~ s0-24的位置 108c: 4581 li a1,0 // 将a1寄存器赋值为0,此处的作用为初始化一个寄存器为0 108e: fcb42e23 sw a1,-36(s0) // 将a1寄存器的值保存到栈中表示的地址(s0-32 ~ s0-36的位置) 1092: 06500593 li a1,101 // 将a1寄存器赋值为101 int a= 101; 1096: feb42223 sw a1,-28(s0) // 由于int在类型为四个字节,此时将101存储到s0-24 ~ s0-28所表示的地址中 109a: 0ca00593 li a1,202 // 还是将a0寄存器进行操作,赋值为202 int b= 202; 109e: feb42023 sw a1,-32(s0) // 将202存储到s0-28 ~ s0-32的地址中 10a2: fe440593 addi a1,s0,-28 // 将s0-28的地址给到a1 10a6: fe040613 addi a2,s0,-32 // 将s0-32中地址给到a2 swap(&a, &b); 10aa: fca43823 sd a0,-48(s0) // 将a0的地址保存到s0-40 ~ s0-48中 10ae: 852e mv a0,a1 // 将a1复制给a0 10b0: 85b2 mv a1,a2 // 将a2复制给a1 10b2: 00000097 auipc ra,0x0 10b6: f8c080e7 jalr -116(ra) # 103e <swap> // 跳转到swap函数中执行 printf("a= %d\n", a); 10ba: fe442583 lw a1,-28(s0) //将s0-24 ~ s0-28的地址存入到a1寄存器中 00000000000010be <.LBB1_4>: 10be: fffff517 auipc a0,0xfffff 10c2: 3da50513 addi a0,a0,986 # 498 <abitag+0x1d8> 10c6: 00000097 auipc ra,0x0 10ca: 07a080e7 jalr 122(ra) # 1140 <printf@plt> // 跳转到printf中实现打印 printf("b= %d\n", b); 10ce: fe042583 lw a1,-32(s0) 00000000000010d2 <.LBB1_5>: 10d2: fffff617 auipc a2,0xfffff 10d6: 3cd60613 addi a2,a2,973 # 49f <abitag+0x1df> 10da: fca43423 sd a0,-56(s0) 10de: 8532 mv a0,a2 10e0: 00000097 auipc ra,0x0 10e4: 060080e7 jalr 96(ra) # 1140 <printf@plt> 10e8: fd043583 ld a1,-48(s0) // 将s0-40 ~ s0-48的地址赋值给a1,之前存入的是a0的地址 10ec: 6190 ld a2,0(a1) // 将a1的地址复制给a2 10ee: fe843683 ld a3,-24(s0) // 将s0-16 ~ s0-24的八字节赋给a3,也就是之前存入的a0的地址 10f2: 00d61963 bne a2,a3,1104 <.LBB1_2> //判断此处的栈是否溢出了 10f6: 0040006f j 10fa <.LBB1_1> 00000000000010fa <.LBB1_1>: 10fa: 4501 li a0,0 // 将a0寄存器赋值为0 return 0; 10fc: 7442 ld s0,48(sp) // 恢复s0的值 10fe: 70e2 ld ra,56(sp) // 恢复ra的值 1100: 6121 addi sp,sp,64 // 恢复sp的值 1102: 8082 ret // 整个函数退出
mtime的初始化
#define configCLINT_BASE_ADDRESS CLINT_CTRL_ADDR #define configMTIME_BASE_ADDRESS (CLINT_CTRL_ADDR + CLINT_MTIME) #define configMTIMECMP_BASE_ADDRESS (CLINT_CTRL_ADDR + CLINT_MTIMECMP)
在使用时基之前要先定义,而这个初始化应该是在FreeRTOS的任务调度之前完成,而实际,这件事做的确实很晚,是在vTaskStartScheduler();
函数里执行的,具体的调用路径:
vTaskStartScheduler(); //./FreeRTOS/Source/task.c | xPortStartScheduler(); //./FreeRTOS/Source/portable/GCC/RISC-V/port.c | vPortSetupTimerInterrupt(); //./FreeRTOS/Source/portable/GCC/RISC-V/port.c #if( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIMECMP_BASE_ADDRESS != 0 ) void vPortSetupTimerInterrupt( void ) { uint32_t ulCurrentTimeHigh, ulCurrentTimeLow; volatile uint32_t * const pulTimeHigh = ( volatile uint32_t * const ) ( ( configMTIME_BASE_ADDRESS ) + 4UL ); /* 8-byte typer so high 32-bit word is 4 bytes up. */ volatile uint32_t * const pulTimeLow = ( volatile uint32_t * const ) ( configMTIME_BASE_ADDRESS ); volatile uint32_t ulHartId; __asm volatile( "csrr %0, mhartid" : "=r"( ulHartId ) ); pullMachineTimerCompareRegister = ( volatile uint64_t * ) ( ullMachineTimerCompareRegisterBase + ( ulHartId * sizeof( uint64_t ) ) ); do { ulCurrentTimeHigh = *pulTimeHigh; ulCurrentTimeLow = *pulTimeLow; } while( ulCurrentTimeHigh != *pulTimeHigh ); ullNextTime = ( uint64_t ) ulCurrentTimeHigh; ullNextTime <<= 32ULL; /* High 4-byte word is 32-bits up. */ ullNextTime |= ( uint64_t ) ulCurrentTimeLow; ullNextTime += ( uint64_t ) uxTimerIncrementsForOneTick; *pullMachineTimerCompareRegister = ullNextTime; /* Prepare the time to use after the next tick interrupt. */ ullNextTime += ( uint64_t ) uxTimerIncrementsForOneTick; } #endif /* ( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIME_BASE_ADDRESS != 0 ) */
task管理
portable/GCC/RISC-V/portASM.S
.global xPortStartFirstTask .global freertos_risc_v_trap_handler .global pxPortInitialiseStack .extern pxCurrentTCB .extern ulPortTrapHandler .extern vTaskSwitchContext .extern xTaskIncrementTick .extern Timer_IRQHandler .extern pullMachineTimerCompareRegister .extern pullNextTime .extern uxTimerIncrementsForOneTick /* size_t type so 32-bit on 32-bit core and 64-bits on 64-bit core. */ .extern xISRStackTop .extern portasmHANDLE_INTERRUPT
xPortStartFirstTask
portable/IAR/RISC-V/chip_specific_extensions/RV32I_CLINT_no_extensions/freertos_risc_v_chip_specific_extensions.h:57:
#define portasmHAS_SIFIVE_CLINT 1
1 #if( portasmHAS_SIFIVE_CLINT != 0 ) 2 /* If there is a clint then interrupts can branch directly to the FreeRTOS 3 trap handler. Otherwise the interrupt controller will need to be configured 4 outside of this file. */ 5 la t0, freertos_risc_v_trap_handler 6 csrw mtvec, t0 7 #endif /* portasmHAS_CLILNT */ 8 9 load_x sp, pxCurrentTCB /* Load pxCurrentTCB. */ 10 load_x sp, 0( sp ) /* Read sp from first TCB member. */ 11 12 load_x x1, 0( sp ) /* Note for starting the scheduler the exception return address is used as the function return address. */ 13 14 portasmRESTORE_ADDITIONAL_REGISTERS /* Defined in freertos_risc_v_chip_specific_extensions.h to restore any registers unique to the RISC-V implementation. */ 15 16 load_x x6, 3 * portWORD_SIZE( sp ) /* t1 */ 17 load_x x7, 4 * portWORD_SIZE( sp ) /* t2 */ 18 load_x x8, 5 * portWORD_SIZE( sp ) /* s0/fp */ 19 load_x x9, 6 * portWORD_SIZE( sp ) /* s1 */ 20 load_x x10, 7 * portWORD_SIZE( sp ) /* a0 */ 21 load_x x11, 8 * portWORD_SIZE( sp ) /* a1 */ 22 load_x x12, 9 * portWORD_SIZE( sp ) /* a2 */ 23 load_x x13, 10 * portWORD_SIZE( sp ) /* a3 */ 24 load_x x14, 11 * portWORD_SIZE( sp ) /* a4 */ 25 load_x x15, 12 * portWORD_SIZE( sp ) /* a5 */ 26 load_x x16, 13 * portWORD_SIZE( sp ) /* a6 */ 27 load_x x17, 14 * portWORD_SIZE( sp ) /* a7 */ 28 load_x x18, 15 * portWORD_SIZE( sp ) /* s2 */ 29 load_x x19, 16 * portWORD_SIZE( sp ) /* s3 */ 30 load_x x20, 17 * portWORD_SIZE( sp ) /* s4 */ 31 load_x x21, 18 * portWORD_SIZE( sp ) /* s5 */ 32 load_x x22, 19 * portWORD_SIZE( sp ) /* s6 */ 33 load_x x23, 20 * portWORD_SIZE( sp ) /* s7 */ 34 load_x x24, 21 * portWORD_SIZE( sp ) /* s8 */ 35 load_x x25, 22 * portWORD_SIZE( sp ) /* s9 */ 36 load_x x26, 23 * portWORD_SIZE( sp ) /* s10 */ 37 load_x x27, 24 * portWORD_SIZE( sp ) /* s11 */ 38 load_x x28, 25 * portWORD_SIZE( sp ) /* t3 */ 39 load_x x29, 26 * portWORD_SIZE( sp ) /* t4 */ 40 load_x x30, 27 * portWORD_SIZE( sp ) /* t5 */ 41 load_x x31, 28 * portWORD_SIZE( sp ) /* t6 */ 42 43 load_x x5, 29 * portWORD_SIZE( sp ) /* Initial mstatus into x5 (t0) */ 44 addi x5, x5, 0x08 /* Set MIE bit so the first task starts with interrupts enabled - required as returns with ret not eret. */ 45 csrrw x0, mstatus, x5 /* Interrupts enabled from here! */ 46 load_x x5, 2 * portWORD_SIZE( sp ) /* Initial x5 (t0) value. */ 47 48 addi sp, sp, portCONTEXT_SIZE 49 ret 50 .endfunc 51 /*-----------------------------------------------------------*/
第8~9行,将mtvec的值设置为freertos_risc_v_trap_handler()函数的地址,即中断(异常)入口函数为freertos_risc_v_trap_handler()。
后面的代码会将当前TCB里保存的寄存器值恢复到对应的寄存器,当xPortStartFirstTask()函数返回后就会执行当前(pxCurrentTCB所指的)任务。
到这里,我们就可以知道接下来的重点就是freertos_risc_v_trap_handler()函
freertos_risc_v_trap_handle
1 .func 2 freertos_risc_v_trap_handler: 3 addi sp, sp, -portCONTEXT_SIZE 4 store_x x1, 1 * portWORD_SIZE( sp ) 5 store_x x5, 2 * portWORD_SIZE( sp ) 6 store_x x6, 3 * portWORD_SIZE( sp ) 7 store_x x7, 4 * portWORD_SIZE( sp ) 8 store_x x8, 5 * portWORD_SIZE( sp ) 9 store_x x9, 6 * portWORD_SIZE( sp ) 10 store_x x10, 7 * portWORD_SIZE( sp ) 11 store_x x11, 8 * portWORD_SIZE( sp ) 12 store_x x12, 9 * portWORD_SIZE( sp ) 13 store_x x13, 10 * portWORD_SIZE( sp ) 14 store_x x14, 11 * portWORD_SIZE( sp ) 15 store_x x15, 12 * portWORD_SIZE( sp ) 16 store_x x16, 13 * portWORD_SIZE( sp ) 17 store_x x17, 14 * portWORD_SIZE( sp ) 18 store_x x18, 15 * portWORD_SIZE( sp ) 19 store_x x19, 16 * portWORD_SIZE( sp ) 20 store_x x20, 17 * portWORD_SIZE( sp ) 21 store_x x21, 18 * portWORD_SIZE( sp ) 22 store_x x22, 19 * portWORD_SIZE( sp ) 23 store_x x23, 20 * portWORD_SIZE( sp ) 24 store_x x24, 21 * portWORD_SIZE( sp ) 25 store_x x25, 22 * portWORD_SIZE( sp ) 26 store_x x26, 23 * portWORD_SIZE( sp ) 27 store_x x27, 24 * portWORD_SIZE( sp ) 28 store_x x28, 25 * portWORD_SIZE( sp ) 29 store_x x29, 26 * portWORD_SIZE( sp ) 30 store_x x30, 27 * portWORD_SIZE( sp ) 31 store_x x31, 28 * portWORD_SIZE( sp ) 32 33 csrr t0, mstatus /* Required for MPIE bit. */ 34 store_x t0, 29 * portWORD_SIZE( sp ) 35 36 portasmSAVE_ADDITIONAL_REGISTERS /* Defined in freertos_risc_v_chip_specific_extensions.h to save any registers unique to the RISC-V implementation. */ 37 38 load_x t0, pxCurrentTCB /* Load pxCurrentTCB. */ 39 store_x sp, 0( t0 ) /* Write sp to first TCB member. */ 40 41 csrr a0, mcause 42 csrr a1, mepc 43 44 test_if_asynchronous: 45 srli a2, a0, __riscv_xlen - 1 /* MSB of mcause is 1 if handing an asynchronous interrupt - shift to LSB to clear other bits. */ 46 beq a2, x0, handle_synchronous /* Branch past interrupt handing if not asynchronous. */ 47 store_x a1, 0( sp ) /* Asynch so save unmodified exception return address. */ 48 49 handle_asynchronous: 50 /* TODO: 判断是定时器中断还是其他(外部)中断 */ 51 load_x sp, xISRStackTop /* Switch to ISR stack before function call. */ 52 call xPortClearTimerIntPending 53 jal xTaskIncrementTick 54 beqz a0, processed_source /* Don't switch context if incrementing tick didn't unblock a task. */ 55 jal vTaskSwitchContext 56 #jal portasmHANDLE_INTERRUPT /* Jump to the interrupt handler if there is no CLINT or if there is a CLINT and it has been determined that an external interrupt is pending. */ 57 j processed_source 58 59 handle_synchronous: 60 addi a1, a1, 4 /* Synchronous so updated exception return address to the instruction after the instruction that generated the exeption. */ 61 store_x a1, 0( sp ) /* Save updated exception return address. */ 62 63 test_if_environment_call: 64 li t0, 11 /* 11 == environment call. */ 65 bne a0, t0, is_exception /* Not an M environment call, so some other exception. */ 66 load_x sp, xISRStackTop /* Switch to ISR stack before function call. */ 67 jal vTaskSwitchContext 68 j processed_source 69 70 is_exception: 71 csrr t0, mcause /* For viewing in the debugger only. */ 72 csrr t1, mepc /* For viewing in the debugger only */ 73 csrr t2, mstatus 74 j is_exception /* No other exceptions handled yet. */ 75 76 as_yet_unhandled: 77 csrr t0, mcause /* For viewing in the debugger only. */ 78 j as_yet_unhandled 79 80 processed_source: 81 load_x t1, pxCurrentTCB /* Load pxCurrentTCB. */ 82 load_x sp, 0( t1 ) /* Read sp from first TCB member. */ 83 84 /* Load mret with the address of the next instruction in the task to run next. */ 85 load_x t0, 0( sp ) 86 csrw mepc, t0 87 88 portasmRESTORE_ADDITIONAL_REGISTERS /* Defined in freertos_risc_v_chip_specific_extensions.h to restore any registers unique to the RISC-V implementation. */ 89 90 /* Load mstatus with the interrupt enable bits used by the task. */ 91 load_x t0, 29 * portWORD_SIZE( sp ) 92 csrw mstatus, t0 /* Required for MPIE bit. */ 93 94 load_x x1, 1 * portWORD_SIZE( sp ) 95 load_x x5, 2 * portWORD_SIZE( sp ) /* t0 */ 96 load_x x6, 3 * portWORD_SIZE( sp ) /* t1 */ 97 load_x x7, 4 * portWORD_SIZE( sp ) /* t2 */ 98 load_x x8, 5 * portWORD_SIZE( sp ) /* s0/fp */ 99 load_x x9, 6 * portWORD_SIZE( sp ) /* s1 */ 100 load_x x10, 7 * portWORD_SIZE( sp ) /* a0 */ 101 load_x x11, 8 * portWORD_SIZE( sp ) /* a1 */ 102 load_x x12, 9 * portWORD_SIZE( sp ) /* a2 */ 103 load_x x13, 10 * portWORD_SIZE( sp ) /* a3 */ 104 load_x x14, 11 * portWORD_SIZE( sp ) /* a4 */ 105 load_x x15, 12 * portWORD_SIZE( sp ) /* a5 */ 106 load_x x16, 13 * portWORD_SIZE( sp ) /* a6 */ 107 load_x x17, 14 * portWORD_SIZE( sp ) /* a7 */ 108 load_x x18, 15 * portWORD_SIZE( sp ) /* s2 */ 109 load_x x19, 16 * portWORD_SIZE( sp ) /* s3 */ 110 load_x x20, 17 * portWORD_SIZE( sp ) /* s4 */ 111 load_x x21, 18 * portWORD_SIZE( sp ) /* s5 */ 112 load_x x22, 19 * portWORD_SIZE( sp ) /* s6 */ 113 load_x x23, 20 * portWORD_SIZE( sp ) /* s7 */ 114 load_x x24, 21 * portWORD_SIZE( sp ) /* s8 */ 115 load_x x25, 22 * portWORD_SIZE( sp ) /* s9 */ 116 load_x x26, 23 * portWORD_SIZE( sp ) /* s10 */ 117 load_x x27, 24 * portWORD_SIZE( sp ) /* s11 */ 118 load_x x28, 25 * portWORD_SIZE( sp ) /* t3 */ 119 load_x x29, 26 * portWORD_SIZE( sp ) /* t4 */ 120 load_x x30, 27 * portWORD_SIZE( sp ) /* t5 */ 121 load_x x31, 28 * portWORD_SIZE( sp ) /* t6 */ 122 addi sp, sp, portCONTEXT_SIZE 123 124 mret 125 .endfunc
第3~34行,保护现场,即将寄存器压栈。
第36行,保存额外的寄存器,这里什么都不做。
第38~39行,将sp的值保存在当前TCB的起始地址处。
第41行,读取mcause的值到a0寄存器。
第42行,读取mepc的值到a1寄存器。
第45行,将a0寄存器的值右移31位,将移位后的值写入a2寄存器。
第46行,判断a2寄存器的值是否等于0,即判断是中断(异步中断)还是异常(同步中断),如果等于0则跳转到第59行。这里假设a2的值不等于0,因此继续往下看。
第47行,将中断返回地址保存在栈顶。
第51行,使用中断栈,即在中断里有专门的栈空间来进行函数调用。
第52行,调用xPortClearTimerIntPending()函数,在这里该函数的作用是清定时器中断pending。目前在freertos这个demo里只有定时器中断,因此就没有判断是否是其他外部中断了。
第53行,调用xTaskIncrementTick()函数,这个函数的返回值决定了是否需要切换到其他任务。如果返回值为0,表示不需要切换,否则需要任务切换。
第54行,判断xTaskIncrementTick()函数的返回值是否等于0,如果是则跳转到第80行,这里假设返回值不等于0。
第55行,调用vTaskSwitchContext()函数,这个函数会切换当前TCB到将要执行的任务上。
第57行,跳转到第80行。
第81~82行,使用当前TCB的sp。
第85~86行,将中断返回地址写入mepc寄存器。
第88行,恢复额外的寄存器,这里什么都没做。
第91~122行,从栈里恢复寄存器的值,这和前面进入中断时的保存现场操作是成对的,即恢复现场。
第124行,中断返回,从mepc的值所指的地址处开始执行代码。
接下来,看回第46行,如果a2寄存器的值为0,则跳转到第第59行。
第60行,将a1的值加4,即将中断返回地址的值加4,后面会用到。
第61行,将a1的值写入sp寄存器。
第64~65行,判断a0的值是否等于11,即mcause的值是否等于11,即是否是ecall指令异常。如果不是则跳转到第70行。
第70~74行是一段死循环代码。
第66~68行,前面已经分析过了。
xTaskCreate
(gdb) bt #0 pxPortInitialiseStack () at ../..//Source/portable/GCC/RISC-V/portASM.S:420 #1 0x00000000800011fa in prvInitialiseNewTask (pxTaskCode=0x80000284 <prvQueueReceiveTask>, pcName=0x80004828 "Rx", ulStackDepth=140, pvParameters=0x0, uxPriority=2, pxCreatedTask=0x0, pxNewTCB=0x80020930 <ucHeap+1336>, xRegions=0x0) at ../..//Source/tasks.c:1022 #2 0x000000008000108e in xTaskCreate (pxTaskCode=0x80000284 <prvQueueReceiveTask>, pcName=0x80004828 "Rx", usStackDepth=140, pvParameters=0x0, uxPriority=2, pxCreatedTask=0x0) at ../..//Source/tasks.c:814
堆栈初始化
/* * Unlike other ports pxPortInitialiseStack() is written in assembly code as it * needs access to the portasmADDITIONAL_CONTEXT_SIZE constant. The prototype * for the function is as per the other ports: * StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ); * * As per the standard RISC-V ABI pxTopcOfStack is passed in in a0, pxCode in * a1, and pvParameters in a2. The new top of stack is passed out in a0. * * RISC-V maps registers to ABI names as follows (X1 to X31 integer registers * for the 'I' profile, X1 to X15 for the 'E' profile, currently I assumed). * * Register ABI Name Description Saver * x0 zero Hard-wired zero - * x1 ra Return address Caller * x2 sp Stack pointer Callee * x3 gp Global pointer - * x4 tp Thread pointer - * x5-7 t0-2 Temporaries Caller * x8 s0/fp Saved register/Frame pointer Callee * x9 s1 Saved register Callee * x10-11 a0-1 Function Arguments/return values Caller * x12-17 a2-7 Function arguments Caller * x18-27 s2-11 Saved registers Callee * x28-31 t3-6 Temporaries Caller * * The RISC-V context is saved t FreeRTOS tasks in the following stack frame, * where the global and thread pointers are currently assumed to be constant so * are not saved: * * mstatus * x31 * x30 * x29 * x28 * x27 * x26 * x25 * x24 * x23 * x22 * x21 * x20 * x19 * x18 * x17 * x16 * x15 * x14 * x13 * x12 * x11 * pvParameters * x9 * x8 * x7 * x6 * x5 * portTASK_RETURN_ADDRESS * [chip specific registers go here] * pxCode */ .align 8 .func pxPortInitialiseStack: csrr t0, mstatus /* Obtain current mstatus value. */ addi t1, x0, 0x188 /* Generate the value 0x1880, which are the MPIE and MPP bits to set in mstatus. */ slli t1, t1, 4 or t0, t0, t1 /* Set MPIE and MPP bits in mstatus value. */ addi a0, a0, -portWORD_SIZE store_x t0, 0(a0) /* mstatus onto the stack. */ addi a0, a0, -(22 * portWORD_SIZE) /* Space for registers x11-x31. */ store_x a2, 0(a0) /* Task parameters (pvParameters parameter) goes into register X10/a0 on the stack. */ addi a0, a0, -(6 * portWORD_SIZE) /* Space for registers x5-x9. */ store_x x0, 0(a0) /* Return address onto the stack, could be portTASK_RETURN_ADDRESS */ addi t0, x0, portasmADDITIONAL_CONTEXT_SIZE /* The number of chip specific additional registers. */ chip_specific_stack_frame: /* First add any chip specific registers to the stack frame being created. */ beq t0, x0, 1f /*跳转到label 1*/ /* No more chip specific registers to save. */ addi a0, a0, -portWORD_SIZE /* Make space for chip specific register. */ store_x x0, 0(a0) /* Give the chip specific register an initial value of zero. */ addi t0, t0, -1 /* Decrement the count of chip specific registers remaining. */ j chip_specific_stack_frame /* Until no more chip specific registers. */ 1: addi a0, a0, -portWORD_SIZE store_x a1, 0(a0) /* mret value (pxCode parameter) onto the stack. */ ret .endfunc /*-----------------------------------------------------------*/
portasmADDITIONAL_CONTEXT_SIZE为目标芯片增加的寄存器数。如Vega 开发板的RI5CY核,包含6个额外的寄存器。因此#define portasmADDITIONAL_CONTEXT_SIZE 6
vTaskSwitchContext切换
FreeRTOS任务相关的代码大约占总代码的一半左右,这些代码都在为一件事情而努力,即找到优先级最高的就绪任务,并使之获得CPU运行权。任务切换是这一过程的直接实施者,为了更快的找到优先级最高的就绪任务,任务切换的代码通常都是精心设计的,甚至会用到汇编指令或者与硬件相关的特性,比如Cortex-M3的CLZ指令。因此任务切换的大部分代码是由硬件移植层提供的,不同的平台,实现发方法也可能不同,这篇文章以Cortex-M3为例,讲述FreeRTOS任务切换的过程。
FreeRTOS有两种方法触发任务切换:
- 执行系统调用,比如普通任务可以使用taskYIELD()强制任务切换,中断服务程序中使用portYIELD_FROM_ISR()强制任务切换;
- 系统节拍时钟中断
在ARM平台对于Cortex-M3平台,这两种方法的实质是一样的,都会使能一个PendSV中断,在PendSV中断服务程序中,找到最高优先级的就绪任务,然后让这个任务获得CPU运行权,从而完成任务切换。
在riscv这个宏的定义如下:
/* Scheduler utilities. */ extern void vTaskSwitchContext( void ); #define portYIELD() __asm volatile( "ecall" ); #define portEND_SWITCHING_ISR( xSwitchRequired ) do { if( xSwitchRequired ) vTaskSwitchContext(); } while( 0 ) #define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x ) /*-----------------------------------------------------------*/
调度
调度过程
- 保存上一个 task 的上下文
- 找到下一个要执行的 task
- 让新的任务从它上次被打断的地方开始执行
arm下关键代码如下所示:
lib/FreeRTOS/portable/GCC/ARM_CA53_64_BIT/portASM.S: portSAVE_CONTEXT /*保存当前任务的上下文到当前任务栈中*/ BL vTaskSwitchContext /*挑选下一个最高优先级任务*/ portRESTORE_CONTEXT /*从新任务的栈中加载上下文信息,并运行新任务*/

选择调度task
vTaskSwitchContext() 函数里面会调用:
taskSELECT_HIGHEST_PRIORITY_TASK();
选择下一个最高优先级的任务
/* Select a new task to run using either the generic C or port
* optimised asm code. */
taskSELECT_HIGHEST_PRIORITY_TASK(); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */
traceTASK_SWITCHED_IN();
调度时机
谁来调度呢 ?
举个例子:系统中有两个同等优先级的任务:task1 和 task2,而且在就绪态队列中,它们的优先级是最高的,也就是说,此时系统中就它们兄弟俩在运行了,task1在执行的时候, 如果它不主动放弃 CPU,它就会一直执行,那么什么时候才能轮到 task2 执行呢??
所以此时需要一种机制能打断 task1 的执行,然后切换到 task2,后面在 task2 执行的时候,也能从 task2 中收回 CPU 的控制权,然后交给 task1,并且要让 task1 从上次被打断的地方开始执行。
通用的方法就是周期性的触发中断(一般称为 Tick 中断),在中断处理函数中,保存上一个任务的上下文,然后切换到下一个任务中。在 FreeRTOS 中使用一个硬件 timer 作为 Tick 中断。如果将 Tick 中断配置为 1ms 来一次,那么系统就会 1ms 进行一次任务切换。
至于中断来了怎么跑到对应的中断处理函数中,这个是和芯片中的中断控制器相关的,不同的中断控制器有不同的配置方法,本文就不细说了。总之,在 tick 中断的中断处理函数中做的事情都是一样的,就是上面所描述的任务切换。
FirstTask
(gdb) bt #0 xPortStartFirstTask () at ../..//Source/portable/GCC/RISC-V/portASM.S:306 #1 0x00000000800034ba in xPortStartScheduler () at ../..//Source/portable/GCC/RISC-V/port.c:196 #2 0x000000008000151c in vTaskStartScheduler () at ../..//Source/tasks.c:2067 #3 0x0000000080000228 in main_blinky () at ./blinky_demo/main_blinky.c:142 #4 0x0000000080000120 in main () at main.c:113 (gdb)
timers.c:66: #define configTIMER_SERVICE_TASK_NAME "Tmr Svc"
(gdb) b xTimerCreateTimerTask Breakpoint 1 at 0x8000264c: file ../..//Source/timers.c, line 235. (gdb) c Continuing. Breakpoint 1, xTimerCreateTimerTask () at ../..//Source/timers.c:235 235 BaseType_t xReturn = pdFAIL; (gdb) bt #0 xTimerCreateTimerTask () at ../..//Source/timers.c:235 #1 0x00000000800014e2 in vTaskStartScheduler () at ../..//Source/tasks.c:2014 #2 0x0000000080000228 in main_blinky () at ./blinky_demo/main_blinky.c:142 #3 0x0000000080000120 in main () at main.c:113 (gdb)
235 BaseType_t xReturn = pdFAIL; (gdb) list 230 Timer_t * pxNewTimer ) PRIVILEGED_FUNCTION; 231 /*-----------------------------------------------------------*/ 232 233 BaseType_t xTimerCreateTimerTask( void ) 234 { 235 BaseType_t xReturn = pdFAIL; 236 237 /* This function is called when the scheduler is started if 238 * configUSE_TIMERS is set to 1. Check that the infrastructure used by the 239 * timer service task has been created/initialised. If timers have already (gdb) s 241 prvCheckForValidListAndQueue(); (gdb) list 236 237 /* This function is called when the scheduler is started if 238 * configUSE_TIMERS is set to 1. Check that the infrastructure used by the 239 * timer service task has been created/initialised. If timers have already 240 * been created then the initialisation will already have been performed. */ 241 prvCheckForValidListAndQueue(); 242 243 if( xTimerQueue != NULL ) 244 { 245 #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) (gdb) n 243 if( xTimerQueue != NULL ) (gdb) n 267 xReturn = xTaskCreate( prvTimerTask, (gdb) n 281 configASSERT( xReturn ); (gdb) n 282 return xReturn; (gdb) n 283 } (gdb) n vTaskStartScheduler () at ../..//Source/tasks.c:2023 2023 if( xReturn == pdPASS ) (gdb) n 2039 portDISABLE_INTERRUPTS(); (gdb)
xPortStartFirstTask 启动timertask
Breakpoint 2, xPortStartFirstTask () at ../..//Source/portable/GCC/RISC-V/portASM.S:306 306 la t0, freertos_risc_v_trap_handler (gdb) s 307 csrw mtvec, t0 (gdb) n 310 load_x sp, pxCurrentTCB /* Load pxCurrentTCB. */ (gdb) set print pretty on (gdb) p *pxCurrentTCB $2 = { pxTopOfStack = 0x80021590 <ucHeap+4504>, xStateListItem = { xItemValue = 0, pxNext = 0x800202c8 <pxReadyTasksLists+256>, pxPrevious = 0x800202c8 <pxReadyTasksLists+256>, pvOwner = 0x800216a0 <ucHeap+4776>, pvContainer = 0x800202b8 <pxReadyTasksLists+240> }, xEventListItem = { xItemValue = 1, pxNext = 0x0, pxPrevious = 0x0, pvOwner = 0x800216a0 <ucHeap+4776>, pvContainer = 0x0 }, uxPriority = 6, pxStack = 0x80021370 <ucHeap+3960>, pcTaskName = "Tmr Svc\000\000\000\000\000\000\000\000", uxCriticalNesting = 0, uxBasePriority = 6, uxMutexesHeld = 0, ulNotifiedValue = {0}, ucNotifyState = "" }
Type "apropos word" to search for commands related to "word". 0x0000000000001000 in ?? () (gdb) b xPortStartFirstTask Breakpoint 1 at 0x80003700: file ../..//Source/portable/GCC/RISC-V/portASM.S, line 306. (gdb) c Continuing. Breakpoint 1, xPortStartFirstTask () at ../..//Source/portable/GCC/RISC-V/portASM.S:306 306 la t0, freertos_risc_v_trap_handler (gdb) n 307 csrw mtvec, t0 (gdb) n 310 load_x sp, pxCurrentTCB /* Load pxCurrentTCB. */ (gdb) n xPortStartFirstTask () at ../..//Source/portable/GCC/RISC-V/portASM.S:311 311 load_x sp, 0( sp ) /* Read sp from first TCB member. */ (gdb) n xPortStartFirstTask () at ../..//Source/portable/GCC/RISC-V/portASM.S:313 313 load_x x1, 0( sp ) /* Note for starting the scheduler the exception return address is used as the function return address. */ (gdb) n 317 load_x x6, 3 * portWORD_SIZE( sp ) /* t1 */ (gdb) n 318 load_x x7, 4 * portWORD_SIZE( sp ) /* t2 */ (gdb) n 319 load_x x8, 5 * portWORD_SIZE( sp ) /* s0/fp */ (gdb) n 320 load_x x9, 6 * portWORD_SIZE( sp ) /* s1 */ (gdb) n 321 load_x x10, 7 * portWORD_SIZE( sp ) /* a0 */ ………………………………………………………………………………………………………………………………………………………………………………………… (gdb) n 339 load_x x28, 25 * portWORD_SIZE( sp ) /* t3 */ (gdb) n 340 load_x x29, 26 * portWORD_SIZE( sp ) /* t4 */ (gdb) n 341 load_x x30, 27 * portWORD_SIZE( sp ) /* t5 */ (gdb) n 342 load_x x31, 28 * portWORD_SIZE( sp ) /* t6 */ (gdb) n 344 load_x x5, 29 * portWORD_SIZE( sp ) /* Initial mstatus into x5 (t0) */ (gdb) n 345 addi x5, x5, 0x08 /* Set MIE bit so the first task starts with interrupts enabled - required as returns with ret not eret. */ (gdb) n 346 csrrw x0, mstatus, x5 /* Interrupts enabled from here! */ (gdb) n 347 load_x x5, 2 * portWORD_SIZE( sp ) /* Initial x5 (t0) value. */ (gdb) n 349 addi sp, sp, portCONTEXT_SIZE (gdb) n xPortStartFirstTask () at ../..//Source/portable/GCC/RISC-V/portASM.S:350 350 ret (gdb) n ----------竟然跳到这里 prvTimerTask (pvParameters=0xa5a5a5a5a5a5a5a5) at ../..//Source/timers.c:569 569 { (gdb) bt #0 prvTimerTask (pvParameters=0xa5a5a5a5a5a5a5a5) at ../..//Source/timers.c:569 #1 0x000000008000293a in prvProcessExpiredTimer (xNextExpireTime=11936128518282651045, xTimeNow=11936128518282651045) at ../..//Source/timers.c:565 Backtrace stopped: frame did not save the PC (gdb) c Continuing.
prvTimerTask
static portTASK_FUNCTION( prvTimerTask, pvParameters )
ra地址-load前
prvAddNewTaskToReadyList 更改pxCurrentTCB
(gdb) b prvAddNewTaskToReadyList Breakpoint 1 at 0x80001228: file ../..//Source/tasks.c, line 1045. (gdb) c Continuing. Breakpoint 1, prvAddNewTaskToReadyList (pxNewTCB=0x80020930 <ucHeap+1336>) at ../..//Source/tasks.c:1045 1045 taskENTER_CRITICAL(); (gdb) s vTaskEnterCritical () at ../..//Source/tasks.c:4325 4325 portDISABLE_INTERRUPTS(); (gdb) list 4320 4321 #if ( portCRITICAL_NESTING_IN_TCB == 1 ) 4322 4323 void vTaskEnterCritical( void ) 4324 { 4325 portDISABLE_INTERRUPTS(); 4326 4327 if( xSchedulerRunning != pdFALSE ) 4328 { 4329 ( pxCurrentTCB->uxCriticalNesting )++; (gdb) n 4327 if( xSchedulerRunning != pdFALSE ) (gdb) n 4346 } (gdb) n prvAddNewTaskToReadyList (pxNewTCB=0x80020930 <ucHeap+1336>) at ../..//Source/tasks.c:1047 1047 uxCurrentNumberOfTasks++; (gdb) n 1049 if( pxCurrentTCB == NULL ) (gdb) n 1053 pxCurrentTCB = pxNewTCB; (gdb) n 1055 if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ) (gdb) n 1060 prvInitialiseTaskLists(); (gdb) n 1089 uxTaskNumber++; (gdb) n 1099 prvAddTaskToReadyList( pxNewTCB ); (gdb) n 1103 taskEXIT_CRITICAL(); (gdb) n 1105 if( xSchedulerRunning != pdFALSE ) (gdb) n 1122 } (gdb) n
0x0000000000001000 in ?? () (gdb) watch pxCurrentTCB Hardware watchpoint 1: pxCurrentTCB (gdb) c Continuing. Hardware watchpoint 1: pxCurrentTCB Old value = (TCB_t * volatile) 0x0 New value = (TCB_t * volatile) 0x80020930 <ucHeap+1336> prvAddNewTaskToReadyList (pxNewTCB=0x80020930 <ucHeap+1336>) at ../..//Source/tasks.c:1055 1055 if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ) (gdb) bt #0 prvAddNewTaskToReadyList (pxNewTCB=0x80020930 <ucHeap+1336>) at ../..//Source/tasks.c:1055 #1 0x0000000080001096 in xTaskCreate (pxTaskCode=0x80000284 <prvQueueReceiveTask>, pcName=0x80004828 "Rx", usStackDepth=140, pvParameters=0x0, uxPriority=2, pxCreatedTask=0x0) at ../..//Source/tasks.c:815 #2 0x00000000800001d0 in main_blinky () at ./blinky_demo/main_blinky.c:127 #3 0x0000000080000120 in main () at main.c:113 (gdb) c Continuing. Hardware watchpoint 1: pxCurrentTCB Old value = (TCB_t * volatile) 0x80020930 <ucHeap+1336> New value = (TCB_t * volatile) 0x800216a0 <ucHeap+4776> prvAddNewTaskToReadyList (pxNewTCB=0x800216a0 <ucHeap+4776>) at ../..//Source/tasks.c:1089 1089 uxTaskNumber++; (gdb) bt #0 prvAddNewTaskToReadyList (pxNewTCB=0x800216a0 <ucHeap+4776>) at ../..//Source/tasks.c:1089 #1 0x0000000080001096 in xTaskCreate (pxTaskCode=0x8000293a <prvTimerTask>, pcName=0x800048a0 "Tmr Svc", usStackDepth=100, pvParameters=0x0, uxPriority=6, pxCreatedTask=0x80020168 <xTimerTaskHandle>) at ../..//Source/tasks.c:815 #2 0x0000000080002684 in xTimerCreateTimerTask () at ../..//Source/timers.c:267 #3 0x00000000800014e2 in vTaskStartScheduler () at ../..//Source/tasks.c:2014 #4 0x0000000080000228 in main_blinky () at ./blinky_demo/main_blinky.c:142 #5 0x0000000080000120 in main () at main.c:113 (gdb) c Continuing. Hardware watchpoint 1: pxCurrentTCB Old value = (TCB_t * volatile) 0x800216a0 <ucHeap+4776> New value = (TCB_t * volatile) 0x80020930 <ucHeap+1336> vTaskSwitchContext () at ../..//Source/tasks.c:3064 3064 } (gdb) bt #0 vTaskSwitchContext () at ../..//Source/tasks.c:3064 #1 0x00000000800035d0 in test_if_environment_call () at ../..//Source/portable/GCC/RISC-V/portASM.S:237 Backtrace stopped: frame did not save the PC (gdb) b prvAddNewTaskToReadyList Breakpoint 2 at 0x80001228: file ../..//Source/tasks.c, line 1045. (gdb) c Continuing. Hardware watchpoint 1: pxCurrentTCB Old value = (TCB_t * volatile) 0x80020930 <ucHeap+1336> New value = (TCB_t * volatile) 0x80020e50 <ucHeap+2648> vTaskSwitchContext () at ../..//Source/tasks.c:3064 3064 } (gdb) bt #0 vTaskSwitchContext () at ../..//Source/tasks.c:3064 #1 0x00000000800035d0 in test_if_environment_call () at ../..//Source/portable/GCC/RISC-V/portASM.S:237 Backtrace stopped: frame did not save the PC (gdb) c Continuing. Hardware watchpoint 1: pxCurrentTCB Old value = (TCB_t * volatile) 0x80020e50 <ucHeap+2648> New value = (TCB_t * volatile) 0x800212c0 <ucHeap+3784> vTaskSwitchContext () at ../..//Source/tasks.c:3064 3064 } (gdb) c Continuing. Hardware watchpoint 1: pxCurrentTCB Old value = (TCB_t * volatile) 0x800212c0 <ucHeap+3784> New value = (TCB_t * volatile) 0x800216a0 <ucHeap+4776> vTaskSwitchContext () at ../..//Source/tasks.c:3064 3064 } (gdb) q A debugging session is active. Inferior 1 [process 1] will be detached.
Timer Task
当系统调用 xTimerStart() 开启一个 timer 的时候,会记录超时时间(调用该函数时的时间 + 定时时长)到 timer 中,并将 timer 插入到 list 上,升序排列,这样系统只需要比较 list 中第一个 timer 的超时时间和当前时间值。如果当前时间大于超时时间,说明该定时器到期,进而就会去处理定时器,也就是先从 list 中将该 timer 移除,然后执行该定时器的回调函数。如果小于,说明还没到期,等待下次比较。
时间是以 tick 数进行记录的,随着系统不断运行,记录该 tick 的变量是个整型数,不管是 32bit,还是 64bit 总有溢出的时候。在溢出的时候,如果 list 上还有 timer 没有被处理,而新的 timer 又被创建,这个时候如果将新 timer 插入到同一个 list 上,由于 list 上是按升序排列的,新加入的 timer 被插入到 list 的最前面,这就会导致溢出前的 timer 得不到执行。所以为了解决这个问题,引入第二个 list,将溢出后新创建的 timer 加入到第二个 list 中,而第一个 list 中的 timer 稍后再处理。这样就不影响了。
以上就解答了为什么需要两个 list 。
上面说,系统会不断的去比较当前时间和超时时间,那谁来做这个比较的事呢??
在 FreeRTOS 中创建了一个 task 来处理。当系统开启调度( xStartSchedule() )的时候会创建一个 Timer Task。
timer interrupt
pullMachineTimerCompareRegister = ( volatile uint64_t * ) ( ullMachineTimerCompareRegisterBase + ( ulHartId * sizeof( uint64_t ) ) );
timer 回调
(gdb) b vExampleTimerCallback Breakpoint 1 at 0x80000332: file ./blinky_demo/main_blinky.c, line 219. (gdb) c Continuing. Breakpoint 1, vExampleTimerCallback (xTimer=0x80020f00 <ucHeap+2824>) at ./blinky_demo/main_blinky.c:219 219 printf("timers Callback %d\r\n", cnt++); (gdb) bt #0 vExampleTimerCallback (xTimer=0x80020f00 <ucHeap+2824>) at ./blinky_demo/main_blinky.c:219 #1 0x0000000080002930 in prvProcessExpiredTimer (xNextExpireTime=1000, xTimeNow=1000) at ../..//Source/timers.c:564 #2 0x00000000800029b2 in prvProcessTimerOrBlockTask (xNextExpireTime=1000, xListWasEmpty=0) at ../..//Source/timers.c:625 #3 0x0000000080002962 in prvTimerTask (pvParameters=0x0) at ../..//Source/timers.c:596 #4 0x000000008000293a in prvProcessExpiredTimer (xNextExpireTime=0, xTimeNow=11936128518282651045) at ../..//Source/timers.c:565 Backtrace stopped: frame did not save the PC (gdb)
static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow ) { Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); /*lint !e9087 !e9079 void * is used as this macro is used with tasks and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ /* Remove the timer from the list of active timers. A check has already * been performed to ensure the list is not empty. */ ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); /* If the timer is an auto-reload timer then calculate the next * expiry time and re-insert the timer in the list of active timers. */ if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 ) { prvReloadTimer( pxTimer, xNextExpireTime, xTimeNow ); } else { pxTimer->ucStatus &= ( ( uint8_t ) ~tmrSTATUS_IS_ACTIVE ); } /* Call the timer callback. */ traceTIMER_EXPIRED( pxTimer ); pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); }
xTimerTaskHandle
(gdb) watch xTimerTaskHandle Hardware watchpoint 1: xTimerTaskHandle (gdb) c Continuing. Hardware watchpoint 1: xTimerTaskHandle Old value = (TaskHandle_t) 0x0 New value = (TaskHandle_t) 0x800216a0 <ucHeap+4776> prvInitialiseNewTask (pxTaskCode=0x8000293a <prvTimerTask>, pcName=0x800048a0 "Tmr Svc", ulStackDepth=100, pvParameters=0x0, uxPriority=6, pxCreatedTask=0x80020168 <xTimerTaskHandle>, pxNewTCB=0x800216a0 <ucHeap+4776>, xRegions=0x0) at ../..//Source/tasks.c:1038 1038 } (gdb) bt #0 prvInitialiseNewTask (pxTaskCode=0x8000293a <prvTimerTask>, pcName=0x800048a0 "Tmr Svc", ulStackDepth=100, pvParameters=0x0, uxPriority=6, pxCreatedTask=0x80020168 <xTimerTaskHandle>, pxNewTCB=0x800216a0 <ucHeap+4776>, xRegions=0x0) at ../..//Source/tasks.c:1038 #1 0x000000008000108e in xTaskCreate (pxTaskCode=0x8000293a <prvTimerTask>, pcName=0x800048a0 "Tmr Svc", usStackDepth=100, pvParameters=0x0, uxPriority=6, pxCreatedTask=0x80020168 <xTimerTaskHandle>) at ../..//Source/tasks.c:814 #2 0x0000000080002684 in xTimerCreateTimerTask () at ../..//Source/timers.c:267 #3 0x00000000800014e2 in vTaskStartScheduler () at ../..//Source/tasks.c:2014 #4 0x0000000080000228 in main_blinky () at ./blinky_demo/main_blinky.c:142 #5 0x0000000080000120 in main () at main.c:113 (gdb) p xTimerTaskHandle $1 = (TaskHandle_t) 0x800216a0 <ucHeap+4776> (gdb) c Continuing. ^C Program received signal SIGINT, Interrupt. 0x00000000800020f6 in prvIdleTask (pvParameters=0x0) at ../..//Source/tasks.c:3428 3428 prvCheckTasksWaitingTermination(); (gdb) p *xTimerTaskHandle $2 = {pxTopOfStack = 0x80021530 <ucHeap+4408>, xStateListItem = {xItemValue = 12000, pxNext = 0x80020e58 <ucHeap+2656>, pxPrevious = 0x800202f0 <xDelayedTaskList1+16>, pvOwner = 0x800216a0 <ucHeap+4776>, pvContainer = 0x800202e0 <xDelayedTaskList1>}, xEventListItem = {xItemValue = 1, pxNext = 0x80020fb8 <ucHeap+3008>, pxPrevious = 0x80020fb8 <ucHeap+3008>, pvOwner = 0x800216a0 <ucHeap+4776>, pvContainer = 0x80020fa8 <ucHeap+2992>}, uxPriority = 6, pxStack = 0x80021370 <ucHeap+3960>, pcTaskName = "Tmr Svc\000\000\000\000\000\000\000\000", uxCriticalNesting = 0, uxBasePriority = 6, uxMutexesHeld = 0, ulNotifiedValue = {0}, ucNotifyState = ""} (gdb)
prvAddCurrentTaskToDelayedList
Breakpoint 1, vQueueWaitForMessageRestricted (xQueue=0x80020f60 <ucHeap+2920>, xTicksToWait=998, xWaitIndefinitely=0) at ../..//Source/queue.c:2870 2870 Queue_t * const pxQueue = xQueue; (gdb) p *pxCurrentTCB $38 = { pxTopOfStack = 0x800213c0 <ucHeap+4040>, xStateListItem = { xItemValue = 6000, pxNext = 0x800202c8 <pxReadyTasksLists+256>, pxPrevious = 0x800202c8 <pxReadyTasksLists+256>, pvOwner = 0x800216a0 <ucHeap+4776>, pvContainer = 0x800202b8 <pxReadyTasksLists+240> }, xEventListItem = { xItemValue = 1, pxNext = 0x80020fb8 <ucHeap+3008>, pxPrevious = 0x80020fb8 <ucHeap+3008>, pvOwner = 0x800216a0 <ucHeap+4776>, pvContainer = 0x0 }, uxPriority = 6, pxStack = 0x80021370 <ucHeap+3960>, pcTaskName = "Tmr Svc\000\000\000\000\000\000\000\000", uxCriticalNesting = 0, uxBasePriority = 6, uxMutexesHeld = 0, ulNotifiedValue = {0}, ucNotifyState = "" } (gdb) c Continuing. Breakpoint 3, prvAddCurrentTaskToDelayedList (xTicksToWait=998, xCanBlockIndefinitely=0) at ../..//Source/tasks.c:5289 5289 const TickType_t xConstTickCount = xTickCount; (gdb) p *pxCurrentTCB $39 = { pxTopOfStack = 0x800214e0 <ucHeap+4328>, xStateListItem = { xItemValue = 6000, pxNext = 0x800202c8 <pxReadyTasksLists+256>, pxPrevious = 0x800202c8 <pxReadyTasksLists+256>, pvOwner = 0x800216a0 <ucHeap+4776>, pvContainer = 0x800202b8 <pxReadyTasksLists+240> }, xEventListItem = { xItemValue = 1, pxNext = 0x80020fb8 <ucHeap+3008>, pxPrevious = 0x80020fb8 <ucHeap+3008>, pvOwner = 0x800216a0 <ucHeap+4776>, pvContainer = 0x80020fa8 <ucHeap+2992> }, uxPriority = 6, pxStack = 0x80021370 <ucHeap+3960>, pcTaskName = "Tmr Svc\000\000\000\000\000\000\000\000", uxCriticalNesting = 0, uxBasePriority = 6, uxMutexesHeld = 0, ulNotifiedValue = {0}, ucNotifyState = "" } (gdb) c Continuing. Breakpoint 3, prvAddCurrentTaskToDelayedList (xTicksToWait=18446744073709551615, xCanBlockIndefinitely=1) at ../..//Source/tasks.c:5289 5289 const TickType_t xConstTickCount = xTickCount; (gdb) p *pxCurrentTCB $40 = { pxTopOfStack = 0x80020780 <ucHeap+904>, xStateListItem = { xItemValue = 0, pxNext = 0x80020228 <pxReadyTasksLists+96>, pxPrevious = 0x80020228 <pxReadyTasksLists+96>, pvOwner = 0x80020930 <ucHeap+1336>, pvContainer = 0x80020218 <pxReadyTasksLists+80> }, xEventListItem = { xItemValue = 5, pxNext = 0x80020468 <ucHeap+112>, pxPrevious = 0x80020468 <ucHeap+112>, pvOwner = 0x80020930 <ucHeap+1336>, pvContainer = 0x80020458 <ucHeap+96> }, uxPriority = 2, pxStack = 0x800204c0 <ucHeap+200>, pcTaskName = "Rx", '\000' <repeats 13 times>, uxCriticalNesting = 0, uxBasePriority = 2, uxMutexesHeld = 0, ulNotifiedValue = {0}, ucNotifyState = "" } (gdb) c Continuing. Breakpoint 3, prvAddCurrentTaskToDelayedList (xTicksToWait=996, xCanBlockIndefinitely=0) at ../..//Source/tasks.c:5289 5289 const TickType_t xConstTickCount = xTickCount; (gdb) p *pxCurrentTCB $41 = { pxTopOfStack = 0x80020ca0 <ucHeap+2216>, xStateListItem = { xItemValue = 6006, pxNext = 0x80020200 <pxReadyTasksLists+56>, pxPrevious = 0x80020200 <pxReadyTasksLists+56>, pvOwner = 0x80020e50 <ucHeap+2648>, pvContainer = 0x800201f0 <pxReadyTasksLists+40> }, xEventListItem = { xItemValue = 6, pxNext = 0x0, pxPrevious = 0x0, pvOwner = 0x80020e50 <ucHeap+2648>, pvContainer = 0x0 }, uxPriority = 1, pxStack = 0x800209e0 <ucHeap+1512>, pcTaskName = "TX", '\000' <repeats 13 times>, uxCriticalNesting = 0, uxBasePriority = 1, uxMutexesHeld = 0, ulNotifiedValue = {0}, ucNotifyState = "" } (gdb)
Tmr Svc prvAddCurrentTaskToDelayedList
Breakpoint 3, prvAddCurrentTaskToDelayedList (xTicksToWait=998, xCanBlockIndefinitely=0) at ../..//Source/tasks.c:5289
5289 const TickType_t xConstTickCount = xTickCount;
(gdb) p *pxCurrentTCB
$43 = {
pxTopOfStack = 0x800214e0 <ucHeap+4328>,
xStateListItem = {
xItemValue = 7000,
pxNext = 0x800202c8 <pxReadyTasksLists+256>,
pxPrevious = 0x800202c8 <pxReadyTasksLists+256>,
pvOwner = 0x800216a0 <ucHeap+4776>,
pvContainer = 0x800202b8 <pxReadyTasksLists+240>
},
xEventListItem = {
xItemValue = 1,
pxNext = 0x80020fb8 <ucHeap+3008>,
pxPrevious = 0x80020fb8 <ucHeap+3008>,
pvOwner = 0x800216a0 <ucHeap+4776>,
pvContainer = 0x80020fa8 <ucHeap+2992>
},
uxPriority = 6,
pxStack = 0x80021370 <ucHeap+3960>,
pcTaskName = "Tmr Svc\000\000\000\000\000\000\000\000",
uxCriticalNesting = 0,
uxBasePriority = 6,
uxMutexesHeld = 0,
ulNotifiedValue = {0},
ucNotifyState = ""
}
(gdb) bt
#0 prvAddCurrentTaskToDelayedList (xTicksToWait=998, xCanBlockIndefinitely=0) at ../..//Source/tasks.c:5289
#1 0x0000000080001d74 in vTaskPlaceOnEventListRestricted (pxEventList=0x80020fa8 <ucHeap+2992>,
xTicksToWait=998, xWaitIndefinitely=0) at ../..//Source/tasks.c:3147
#2 0x0000000080000fde in vQueueWaitForMessageRestricted (xQueue=0x80020f60 <ucHeap+2920>, xTicksToWait=998,
xWaitIndefinitely=0) at ../..//Source/queue.c:2891
#3 0x00000000800029f4 in prvProcessTimerOrBlockTask (xNextExpireTime=8000, xListWasEmpty=0)
at ../..//Source/timers.c:642
#4 0x0000000080002962 in prvTimerTask (pvParameters=0x0) at ../..//Source/timers.c:596
#5 0x000000008000293a in prvProcessExpiredTimer (xNextExpireTime=0, xTimeNow=11936128518282651045)
at ../..//Source/timers.c:565
Backtrace stopped: frame did not save the PC
(gdb)
pxCurrentTCB= Tmr Svc下 prvAddCurrentTaskToDelayedList
(gdb) n 5302 if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) (gdb) n 5306 portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); /*lint !e931 pxCurrentTCB cannot change as it is the calling task. pxCurrentTCB->uxPriority and uxTopReadyPriority cannot change as called with scheduler suspended or in a critical section. */ (gdb) n 5315 if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) ) (gdb) n 5327 xTimeToWake = xConstTickCount + xTicksToWait; (gdb) n 5330 listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake ); (gdb) n 5332 if( xTimeToWake < xConstTickCount ) (gdb) n 5342 vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) ); (gdb) n 5347 if( xTimeToWake < xNextTaskUnblockTime ) (gdb) n 5395 } (gdb) n
rx prvAddCurrentTaskToDelayedList
Breakpoint 3, prvAddCurrentTaskToDelayedList (xTicksToWait=18446744073709551615, xCanBlockIndefinitely=1) at ../..//Source/tasks.c:5289 5289 const TickType_t xConstTickCount = xTickCount; (gdb) p *pxCurrentTCB $44 = { pxTopOfStack = 0x80020780 <ucHeap+904>, xStateListItem = { xItemValue = 0, pxNext = 0x80020228 <pxReadyTasksLists+96>, pxPrevious = 0x80020228 <pxReadyTasksLists+96>, pvOwner = 0x80020930 <ucHeap+1336>, pvContainer = 0x80020218 <pxReadyTasksLists+80> }, xEventListItem = { xItemValue = 5, pxNext = 0x80020468 <ucHeap+112>, pxPrevious = 0x80020468 <ucHeap+112>, pvOwner = 0x80020930 <ucHeap+1336>, pvContainer = 0x80020458 <ucHeap+96> }, uxPriority = 2, pxStack = 0x800204c0 <ucHeap+200>, pcTaskName = "Rx", '\000' <repeats 13 times>, uxCriticalNesting = 0, uxBasePriority = 2, uxMutexesHeld = 0, ulNotifiedValue = {0}, ucNotifyState = "" } (gdb) bt #0 prvAddCurrentTaskToDelayedList (xTicksToWait=18446744073709551615, xCanBlockIndefinitely=1) at ../..//Source/tasks.c:5289 #1 0x0000000080001cbe in vTaskPlaceOnEventList (pxEventList=0x80020458 <ucHeap+96>, xTicksToWait=18446744073709551615) at ../..//Source/tasks.c:3088 #2 0x0000000080000b6e in xQueueReceive (xQueue=0x80020410 <ucHeap+24>, pvBuffer=0x800208e0 <ucHeap+1256>, xTicksToWait=18446744073709551615) at ../..//Source/queue.c:1454 #3 0x00000000800002c8 in prvQueueReceiveTask (pvParameters=0x0) at ./blinky_demo/main_blinky.c:197 #4 0x0000000000000000 in ?? () Backtrace stopped: frame did not save the PC (gdb)
tx prvAddCurrentTaskToDelayedList
Breakpoint 3, prvAddCurrentTaskToDelayedList (xTicksToWait=996, xCanBlockIndefinitely=0) at ../..//Source/tasks.c:5289 5289 const TickType_t xConstTickCount = xTickCount; (gdb) bt #0 prvAddCurrentTaskToDelayedList (xTicksToWait=996, xCanBlockIndefinitely=0) at ../..//Source/tasks.c:5289 #1 0x0000000080001484 in xTaskDelayUntil (pxPreviousWakeTime=0x80020e10 <ucHeap+2584>, xTimeIncrement=1000) at ../..//Source/tasks.c:1288 #2 0x0000000080000256 in prvQueueSendTask (pvParameters=0x0) at ./blinky_demo/main_blinky.c:170 #3 0x0000000000000000 in ?? () Backtrace stopped: frame did not save the PC (gdb) p *pxCurrentTCB $45 = { pxTopOfStack = 0x80020ca0 <ucHeap+2216>, xStateListItem = { xItemValue = 7006, pxNext = 0x80020200 <pxReadyTasksLists+56>, pxPrevious = 0x80020200 <pxReadyTasksLists+56>, pvOwner = 0x80020e50 <ucHeap+2648>, pvContainer = 0x800201f0 <pxReadyTasksLists+40> }, xEventListItem = { xItemValue = 6, pxNext = 0x0, pxPrevious = 0x0, pvOwner = 0x80020e50 <ucHeap+2648>, pvContainer = 0x0 }, uxPriority = 1, pxStack = 0x800209e0 <ucHeap+1512>, pcTaskName = "TX", '\000' <repeats 13 times>, uxCriticalNesting = 0, uxBasePriority = 1, uxMutexesHeld = 0, ulNotifiedValue = {0}, ucNotifyState = "" } (gdb)
prvAddCurrentTaskToDelayedList之后执行portYIELD_WITHIN_API()
Tmr Svc task下执行LEDTimer
Breakpoint 13, prvProcessExpiredTimer (xNextExpireTime=165000, xTimeNow=165000) at ../..//Source/timers.c:544 544 Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); /*lint !e9087 !e9079 void * is used as this macro is used with tasks and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ (gdb) p *pxTimer Cannot access memory at address 0x0 (gdb) n 549 ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); (gdb) p *pxTimer $75 = { pcTimerName = 0x80004838 "LEDTimer", xTimerListItem = { xItemValue = 165000, pxNext = 0x800203b8 <xActiveTimerList1+16>, pxPrevious = 0x800203b8 <xActiveTimerList1+16>, pvOwner = 0x80020f00 <ucHeap+2824>, pvContainer = 0x800203a8 <xActiveTimerList1> }, xTimerPeriodInTicks = 1000, pvTimerID = 0x0, pxCallbackFunction = 0x80000326 <vExampleTimerCallback>, ucStatus = 5 '\005' } (gdb) p *pxCurrentTCB $76 = { pxTopOfStack = 0x80021530 <ucHeap+4408>, xStateListItem = { xItemValue = 165000, pxNext = 0x800202c8 <pxReadyTasksLists+256>, pxPrevious = 0x800202c8 <pxReadyTasksLists+256>, pvOwner = 0x800216a0 <ucHeap+4776>, pvContainer = 0x800202b8 <pxReadyTasksLists+240> }, xEventListItem = { xItemValue = 1, pxNext = 0x80020fb8 <ucHeap+3008>, pxPrevious = 0x80020fb8 <ucHeap+3008>, pvOwner = 0x800216a0 <ucHeap+4776>, pvContainer = 0x0 }, uxPriority = 6, pxStack = 0x80021370 <ucHeap+3960>, pcTaskName = "Tmr Svc\000\000\000\000\000\000\000\000", uxCriticalNesting = 0, uxBasePriority = 6, uxMutexesHeld = 0, ulNotifiedValue = {0}, ucNotifyState = "" } (gdb)
idle
ecall
#define portYIELD() __asm volatile( "ecall" );
#define portYIELD_WITHIN_API portYIELD
指令
J型指令在RISC-V中代表的是jal指令
-
将当前PC+4的值存入返回地址
- 普通的j指令将x0存入返回地址(忽略返回地址)
-
然后将当前PC值加上19位的偏移值(相对跳转)
root@ubuntu:/home/ubuntu # cat test.s # Jump And Link (Short-Distance CALL) # Format: # JAL RD, IMM # Description: # This instruction is used to call a subroutine (i.e., function). # The jump and link (JAL) instruction uses the J-type format, where the # immediate (20 bits width) encodes a signed offset in multiples of 2 bytes. # The offset is sign-extended and added to the address of the jump # instruction to form the jump target address. JAL can therefore target # a ±1 MiB range. # JAL stores the address of the instruction following the jump (pc+4) into # register RD. # Note: # When programming, we just provide label instead of immediate value, and # leave linker to provide the final immediate value. # # Jump And Link Register # Format: # JALR RD, RS1, IMM # Description: # This instruction is used to call a subroutine (i.e., function). # The indirect jump instruction JALR (jump and link register) uses the # I-type encoding. The target address is obtained by adding the # sign-extended 12-bit I-immediate to the register RS1, then setting # the least-significant bit of the result to zero. JALR can therefore target # a ±1 KiB range, relative to the address in RS1. # The address of the instruction following the jump(pc+4) is written to register RD. # # Sample to demo how to call a sub-function and return from it # # int a = 1; # int b = 1; # # void sum() # { # a = a + b; # } # # void _start() # { # sum(); # } .text # Define beginning of text section .global _start # Define entry _start _start: li x6, 1 li x7, 2 jal x5, sum # call sum, return address is saved in x5 stop: j stop # Infinite loop to stop execution sum: add x6, x6, x7 # x6 = x6 + x7 jalr x0, 0(x5) # return .end # End of file
反汇编
root@ubuntu:code/asm/jalr# riscv64-unknown-elf-as test.s -o test
root@ubuntu:code/asm/jalr# riscv64-unknown-elf-objdump -D test > text2.txt
cat text.txt test: file format elf64-littleriscv Disassembly of section .text: 0000000000000000 <_start>: 0: 00100313 li t1,1 4: 00200393 li t2,2 8: 008002ef jal t0,10 <sum> 000000000000000c <stop>: c: 0000006f j c <stop> 0000000000000010 <sum>: 10: 00730333 add t1,t1,t2 14: 00028067 jr t0 Disassembly of section .riscv.attributes: 0000000000000000 <.riscv.attributes>: 0: 2d41 addiw s10,s10,16 2: 0000 unimp 4: 7200 ld s0,32(a2) 6: 7369 lui t1,0xffffa 8: 01007663 bgeu zero,a6,14 <.riscv.attributes+0x14> c: 00000023 sb zero,0(zero) # 0 <.riscv.attributes> 10: 7205 lui tp,0xfffe1 12: 3676 fld fa2,376(sp) 14: 6934 ld a3,80(a0) 16: 7032 0x7032 18: 5f30 lw a2,120(a4) 1a: 326d addiw tp,tp,-5 1c: 3070 fld fa2,224(s0) 1e: 615f 7032 5f30 0x5f307032615f 24: 3266 fld ft4,120(sp) 26: 3070 fld fa2,224(s0) 28: 645f 7032 0030 0x307032645f
jal没有rd寄存器,采用默认的ra
# Jump And Link (Short-Distance CALL) # Format: # JAL RD, IMM # Description: # This instruction is used to call a subroutine (i.e., function). # The jump and link (JAL) instruction uses the J-type format, where the # immediate (20 bits width) encodes a signed offset in multiples of 2 bytes. # The offset is sign-extended and added to the address of the jump # instruction to form the jump target address. JAL can therefore target # a ±1 MiB range. # JAL stores the address of the instruction following the jump (pc+4) into # register RD. # Note: # When programming, we just provide label instead of immediate value, and # leave linker to provide the final immediate value. # # Jump And Link Register # Format: # JALR RD, RS1, IMM # Description: # This instruction is used to call a subroutine (i.e., function). # The indirect jump instruction JALR (jump and link register) uses the # I-type encoding. The target address is obtained by adding the # sign-extended 12-bit I-immediate to the register RS1, then setting # the least-significant bit of the result to zero. JALR can therefore target # a ±1 KiB range, relative to the address in RS1. # The address of the instruction following the jump(pc+4) is written to register RD. # # Sample to demo how to call a sub-function and return from it # # int a = 1; # int b = 1; # # void sum() # { # a = a + b; # } # # void _start() # { # sum(); # } .text # Define beginning of text section .global _start # Define entry _start _start: li x6, 1 li x7, 2 jal sum # call sum, return address is saved in x5 stop: j stop # Infinite loop to stop execution sum: add x6, x6, x7 # x6 = x6 + x7 jalr x0 # return .end # End of file
cat text2.txt test: file format elf64-littleriscv Disassembly of section .text: 0000000000000000 <_start>: 0: 00100313 li t1,1 4: 00200393 li t2,2 8: 008000ef jal ra,10 <sum> 000000000000000c <stop>: c: 0000006f j c <stop> 0000000000000010 <sum>: 10: 00730333 add t1,t1,t2 14: 000000e7 jalr zero # 0 <_start> Disassembly of section .riscv.attributes: 0000000000000000 <.riscv.attributes>: 0: 2d41 addiw s10,s10,16 2: 0000 unimp 4: 7200 ld s0,32(a2) 6: 7369 lui t1,0xffffa 8: 01007663 bgeu zero,a6,14 <.riscv.attributes+0x14> c: 00000023 sb zero,0(zero) # 0 <.riscv.attributes> 10: 7205 lui tp,0xfffe1 12: 3676 fld fa2,376(sp) 14: 6934 ld a3,80(a0) 16: 7032 0x7032 18: 5f30 lw a2,120(a4) 1a: 326d addiw tp,tp,-5 1c: 3070 fld fa2,224(s0) 1e: 615f 7032 5f30 0x5f307032615f 24: 3266 fld ft4,120(sp) 26: 3070 fld fa2,224(s0) 28: 645f 7032 0030 0x307032645f
jalr的用法
属于I型指令
ecall 后的 freertos_risc_v_trap_handler
1. ecall // 此时的 寄存器状态为A(包括 ra sp gp tp t0 t1 t2 s0 s1 a0 a1 a2 a3 a4 a5 a6 a7 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11 t3 t4 t5 t6 pc ) 此时硬件做动作 mcause 被设置成 0xb mepc 被设置成 0x80001d28 2. freertos_risc_v_trap_handler // 此时寄存器状态为B // A和B 除了pc,其他都一样 freertos_risc_v_trap_handler // 1. 保存 寄存器信息 到 sp addi sp, sp, -portCONTEXT_SIZE // sp = sp - 120 // 可以保存 30个 寄存器 store_x x1, 1 * portWORD_SIZE( sp ) // 保存x1(第一个寄存器) 到 sp , sp = sp +4 // sw ra,4(sp) store_x x5, 2 * portWORD_SIZE( sp ) // 保存x2(第二个寄存器) ... store_x x31, 28 * portWORD_SIZE( sp ) // 保存x31(第28个寄存器) csrr t0, mstatus store_x t0, 29 * portWORD_SIZE( sp ) // 保存 mstatus(第29个寄存器)到sp portasmSAVE_ADDITIONAL_REGISTERS // 用户自定义的 保存寄存器 的 指令,一般为空 // 2. 保存sp 到 当前的 TCB load_x t0, pxCurrentTCB store_x sp, 0( t0 ) // 保存 sp 到 pxCurrentTCB // 3. 此时 保存完毕,开始处理异常 // 4. 读 mcause mepc csrr a0, mcause csrr a1, mepc // 5. 判断 同步还是异步 test_if_asynchronous: srli a2, a0, __riscv_xlen - 1 // 高 16 位存储到 a2 beq a2, x0, handle_synchronous // 如果 a2 等0 ,跳转到 handle_synchronous , 实际上ecall ,会走 handle_synchronous // 6. 处理同步 handle_synchronous: addi a1, a1, 4 // 处理返回地址 ,返回地址 = mepc +4 store_x a1, 0( sp ) // 填充到 返回地址 到 sp 指向的内存 // 7. 判断是不是 ecall test_if_environment_call: li t0, 11 // 将 11 放到 t0 // 11 表示 Environment call from M-mode bne a0, t0, is_exception // 如果 a0(即mcause)等于 t0(即11) , 那么就 不跳到 is_exception ,而是继续往下 -------------------------------------------------------------------------------------------//此时切换了sp // 从 0x80082cf8 -> 0x80091da0 // 为什么在此时才切换sp // 往前, 刚保存完 返回地址的recipe , 又往后 做了 三个汇编指令(该三条汇编指令用不到sp,也不会影响sp) // 往后, 要用sp 调用 C代码vTaskSwitchContext , vTaskSwitchContext 中会改动 sp // 所以 在此时 切换sp load_x sp, xISRStackTop // 设置 sp 为 xISRStackTop ------------------------------------------------------------------------------------------- // 8. 选择下一个 TCB , 将其 赋值给 pxCurrentTCB jal vTaskSwitchContext taskSELECT_HIGHEST_PRIORITY_TASK/__clzSI2(UWtype x) // __clzSI2 来自于crosstool-ng-crosstool-ng-1.24.0/.build/riscv64-unknown-elf/src/gcc/libgcc/libgcc2.c // 参数x 为 7 count_leading_zeros (ret, x); // 这里切换了 pxCurrentTCB // 9. load下个任务 j processed_source // 获取(恢复) 下个任务 的 sp // 此时 恢复的 sp 中 保存的 是 寄存器状态 值 load_x t1, pxCurrentTCB // 保存 pxCurrentTCB 到 t1 load_x sp, 0( t1 ) // 从 TCB 中读取 sp // 获取(恢复) 下个任务 的 mepc load_x t0, 0( sp ) csrw mepc, t0 // 获取(恢复) 下个任务 的 mstatus load_x t0, 29 * portWORD_SIZE( sp ) csrw mstatus, t0 // 恢复 各个寄存器 load_x x1, 1 * portWORD_SIZE( sp ) load_x x5, 2 * portWORD_SIZE( sp ) ... load_x x31, 28 * portWORD_SIZE( sp ) // 恢复 下个任务的 sp // 此时 恢复的 sp 中 保存的 是 运行状态的 函数堆栈 值 addi sp, sp, portCONTEXT_SIZE // 返回任务 mret // 该指令后,下一条指令就是 prvQueueReceiveTask 任务
FreeRTOS原理剖析:任务挂起和恢复
FreeRTOS源码探析之——任务调度相关
FreeRTOS高级篇4---FreeRTOS任务切换分析
FreeRTOS任务调度机制
FreeRTOS源码探析之——软件定时器
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!