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

2023-05-16

该文章 中讲述了 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 任务
		

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS 中 RISC-V-Qemu-virt_GCC 的 运行流程 的相关文章

随机推荐

  • 记录ubuntu18.04下搭建nuttx RTOS的过程

    官方参考链接 xff1a https nuttx apache org docs latest quickstart install html Getting started 61 61 gt Installing 主要记录一下按照链接指导
  • 词袋

    brief描述子 一般Sb 61 48 Lb为256 brief描述子不具备旋转尺度不变性 词袋 提取大量图片的描述子 xff0c 将描述子用k means聚类成K堆 xff0c 这是第n层 xff0c 把每一堆再次聚类形成下一层 xff0
  • 板子和电脑配置ros通信

    它们需要连在同一个路由器上 1 获取板子和电脑的ip 比如板子名为RV1126 RV1109 xff0c ip为192 168 5 48 电脑名为qian hw xff0c ip为192 168 5 25 2 在电脑端设置 xff1a ba
  • 通过跟踪效果来看vins输出结果

    下面是vins跑出来的结果 xff0c vio输出是绿线轨迹 线速度是0 28m s xff0c 拐角的地方是我根据蓝线把vio轨迹掰正了 vio在初始阶段走的比较弯曲 xff0c 后有一段笔直的轨迹 xff0c 这里旋转非常慢 xff0c
  • vins-fusion 融合rtk原理

    vins fusion融合rtk原理 xff1a 使用优化的方式融合 xff0c 假设融合后的位姿是fusion T n vio输出的位姿是vio T n xff0c rtk输出的位姿是rtk T 只有最后一帧 那么 fusion T的初值
  • Docker 方式搭建 Prometheus + grafana

    prometheus 官方仓库 prometheus 官方文档 GETTING STARTED 参考 基于docker 搭建Prometheus 43 Grafana的过程详解 按照官方仓库文档中写的 Docker images Docke
  • 报错:The following signatures couldn‘t be verified because the public key is not available: NO_PUBKEY

    apt update 最后报错 sudo apt update 报错 91 packages can be upgraded Run 39 apt list upgradable 39 to see them W An error occu
  • ubuntu上 eclipse+arm-linux-gcc+jlink+s3c2440a开发环境搭建

    0 环境搭建概述 所需工具 xff1a xff08 1 xff09 Eclipse Eclipse的本身只是一个框架平台 xff0c 但是众多插件的支持 xff0c 使得Eclipse拥有较好的灵活性 xff08 2 xff09 CDT C
  • 在函数里修改全局变量

    names 61 39 Lilei 39 def change name global name 全局变量我来撑控 names 61 39 Hi 39 print names change name 39 Hi 39 print names
  • 守护进程详解及创建,daemon()使用

    转载于 xff1a http www cnblogs com mickole p 3188321 html 侵删 一 xff0c 守护进程概述 Linux Daemon xff08 守护进程 xff09 是运行在后台的一种特殊进程 它独立于
  • 在VsCode中如何使用Git

    大部分前端都是用VsCode作为代码编辑器 xff0c 那么我们如何在VsCode中使用Git呢 xff1f 准备工作 xff1a 1 在vscode中安装插件GitLens 使用教程 xff1a 1 点击文件 新建窗口 2 克隆git仓库
  • 关于C++继承类和对象

    继承是C 43 43 中一个很好的特性 xff0c 它能够从已有的类派生出新的类 xff0c 而继承类继承了原有类 xff08 称为基类 xff09 的特征 继承类有三种 xff0c 公有继承 保护继承 私有继承 使用公有继承 xff0c
  • 是程序员就去Leetcode刷题吧

    leetcode 是一个美国的在线编程网站 xff0c 上面主要收集了各大IT公司的笔试面试题 xff0c 对于应届毕业生找工作是一个不可多得的好帮手 这个网站的的好处在于它会告诉你测试数据以及你的输出和正确的输出是什么 xff0c 方便大
  • 四旋翼飞行器的控制规律及算法实现

    四旋翼飞行器的控制规律及算法实现 http bbs loveuav com forum php mod 61 viewthread amp tid 61 211 amp fromuid 61 19507 出处 爱无人机 专注无人机开发与设计
  • 基于数据融合和串级PID的小型四旋翼无人机高度测量与控制系统

    作者 xff1a Tomy 摘要 xff1a 简单介绍了一种基于 Mahony 算法无人机姿态解算算法 xff0c 并在这个的基础上设计了一 种融合惯导加速度计和气压计的小型无人机高度测量系统 xff0c 同时利用该高度测量系统设计了 基于
  • 四旋翼飞行器Quadrotor飞控之 PID调节(参考APM程序)

    做四轴也有一段时间了 xff0c 最近一直在做PID方面的工作 现在四轴基本可以实现室内比较稳定的飞行 xff0c 操控手感也可以接受 稍后上试飞视频 在此把一些PID方面的经验总结总结和大家分享一下 首先介绍一下大概的硬件组成 xff1a
  • 四旋翼微型飞行器设计

    作者 xff1a 红桃K 0 引言 微型飞行器 Micro Air Vehicle xff0c 简称MAV 是20世纪90年代发展起来的一种新型飞行 器 根据美国国防高级研究计划局 DARPA 的定义 xff0c 微型飞行器的基本技术指标包
  • 下载软件 (一) JLink_Windows

    硬件下载器 用 jlink 或者 jlink ob 软件用 JLink Windows 套件中的 JFlash span class token punctuation span exe https span class token ope
  • VFS: Cannot open root device 解决思路

    理论 VFS Cannot open root device 34 s 34 A or s B error d C n 34 Please append a correct 34 root 61 34 boot option here ar
  • FreeRTOS 中 RISC-V-Qemu-virt_GCC 的 运行流程

    该文章 中讲述了 FreeRTOS RISC V Qemu virt GCC 的 启动流程 本篇文章讲述一下 运行了流程 我将 RISC span class token operator span V span class token o