FreeRTOS 中 RISC-V-Qemu-virt_GCC 的 运行流程

该文章 中讲述了 FreeRTOS RISC-V-Qemu-virt_GCC 的 启动流程,本篇文章讲述一下 运行了流程

我将  RISC-V-Qemu-virt_GCC 做了一些改动
FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/main_blinky.c 改为如下

static void prvQueueSendTask( void *pvParameters )
{

        for( ;; )
        {
                vSendString( "1111" );
       		 	vTaskDelay(1000);
        }
}

/*-----------------------------------------------------------*/

static void prvQueueReceiveTask( void *pvParameters )
{
        for( ;; )
        {
                vSendString( "2222" );
        		vTaskDelay(1000);
        }
}

/*-----------------------------------------------------------*/

int main_blinky( void )
{
    vSendString( "Hello FreeRTOS!" );

    /* Create the queue. */
    /* Start the two tasks as described in the comments at the top of this
       file. */
    xTaskCreate( prvQueueReceiveTask, "Rx", configMINIMAL_STACK_SIZE * 2U, NULL,
            mainQUEUE_RECEIVE_TASK_PRIORITY, NULL );
    xTaskCreate( prvQueueSendTask, "Tx", configMINIMAL_STACK_SIZE * 2U, NULL,
            mainQUEUE_SEND_TASK_PRIORITY, NULL );

   	vTaskStartScheduler();

    return 0;
}

如何从代码编译角度查看系统中有多少任务
1. 系统创建的任务
	grep portTASK_FUNCTION_PROTO * -nr
	// 该实例中 系统创建了 两个 prvTimerTask 和 prvIdleTask
	// prvTimerTask 在 freertos boot 时 被 用到 , 后来就没有进入该函数了,不知道该task是否被销毁
2. 用户 创建的任务
	grep -w "xTaskCreate(" * -nr
	// 该实例中 用户创建了 两个 prvQueueReceiveTask prvQueueSendTask
流程
我将按时间流走过的流程总结为如下
	1. boot
	2. fist task 			// prvTimerTask
	3. mtimer interrupt 	// freertos_risc_v_trap_handler xTaskIncrementTick  processed_source
	4. fist task & ecall    // prvTimerTask ecall freertos_risc_v_trap_handler vTaskSwitchContext processed_source
	5. second task & ecall  // prvQueueReceiveTask ecall freertos_risc_v_trap_handler vTaskSwitchContext processed_source
	6. third task & ecall   // prvQueueSendTask ecall freertos_risc_v_trap_handler vTaskSwitchContext processed_source
	7. fourth task & ecall  // prvIdleTask  ??? freertos_risc_v_trap_handler vTaskSwitchContext 
	8. third task ...

	当前配置里面 不会在 mtimer interrupt 中 调度, 只能用户任务主动调度

  • 1.boot
_start
	main
		main_blinky
			vTaskStartScheduler
				xPortStartScheduler
					xPortStartFirstTask
						...
						ret
  • 2.fist task
任务1:prvTimerTask // 该任务开始执行后, 会时不时的进入 machine timer 中断
	prvProcessTimerOrBlockTask
		刚进入就产生 mtimer 中断
  • 3.mtimer interrupt
freertos_risc_v_trap_handler // mtimer 中断导致进入
	保存任务1
	test_if_asynchronous
	handle_asynchronous
	test_if_mtimer
		xTaskIncrementTick
	processed_source
		mret
  • 4.fist task & ecall
任务1:prvTimerTask
	prvProcessTimerOrBlockTask
		.. 中间有很多 mtimer 中断
		portYIELD_WITHIN_API/ecall
		
freertos_risc_v_trap_handler // ecall 中断导致进入
	保存任务1
	test_if_asynchronous
	handle_synchronous
	test_if_environment_call
	vTaskSwitchContext
		taskSELECT_HIGHEST_PRIORITY_TASK
			// 这里切换 pxCurrentTCB 到 任务2
	processed_source
		mret
  • 5.second task & ecall
任务2:prvQueueReceiveTask
	vSendString("2222");
	vTaskDelay/portYIELD_WITHIN_API/ecall

freertos_risc_v_trap_handler // ecall 中断导致进入
	保存任务1
	test_if_asynchronous
	handle_synchronous
	test_if_environment_call
	vTaskSwitchContext
		taskSELECT_HIGHEST_PRIORITY_TASK
			// 这里切换 pxCurrentTCB 到 任务3
	processed_source
		mret
  • 6.third task & ecall
任务3:prvQueueSendTask
	vSendString("1111");
	vTaskDelay/portYIELD_WITHIN_API/ecall


freertos_risc_v_trap_handler // ecall 中断导致进入
	保存任务1
	test_if_asynchronous
	handle_synchronous
	test_if_environment_call
	vTaskSwitchContext
		taskSELECT_HIGHEST_PRIORITY_TASK
			// 这里切换 pxCurrentTCB 到 任务4
	processed_source
		mret
  • 7.fourth task & ecall
任务4:prvIdleTask
	// 进来了很多 mtimer 中断
	// 一次 taskYIELD/ecall


freertos_risc_v_trap_handler // ecall 中断导致进入
	保存任务1
	test_if_asynchronous
	handle_synchronous
	test_if_environment_call
	vTaskSwitchContext
		taskSELECT_HIGHEST_PRIORITY_TASK
			// 这里切换 pxCurrentTCB 到 任务3
	processed_source
		mret
  • 8.third task …
任务3:prvQueueSendTask
	vSendString("1111");
	vTaskDelay/portYIELD_WITHIN_API/ecall
	...
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的第一个成员 , 即
		// volatile StackType_t * pxTopOfStack							
		// 32bit x/30xw pxCurrentTCB->pxTopOfStack							
		// 64bit x/30xg pxCurrentTCB->pxTopOfStack
	// 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 任务
		

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值