joe 发表于 2021-7-22 14:32:25

在 RISC-V 微控制器上使用 FreeRTOS

在 RISC-V 微控制器上使用 FreeRTOS
前言如升级到 FreeRTOS V10.3.0 页面所述,configCLINT_BASE_ADDRESS配置设置已被弃用,并替换 为本页面所述的configMTIME_BASE_ADDRESS和configMTIMECMP_BASE_ADDRESS设置。仍然使用 configCLINT_BASE_ADDRESS 的旧应用程序将生成编译器警告,但除此之外继续像以前一样构建和运行。
介绍RISC-V 指令集架构 (ISA) 易于扩展,并且没有指定有关物理 RISC-V 微控制器或片上系统 (SoC) 实现的所有内容。相应地,FreeRTOS RISC-V 端口也是可扩展的——它提供了一个基本端口来处理所有 RISC-V 实现通用的寄存器,以及一组 必须被实现来处理硬件实现特定功能和扩展的宏,例如附加的寄存器。
快速开始本页面的正文提供了有关为 RISC-V 内核构建 FreeRTOS 的详细信息,但最简单的入门方法是使用以下预配置的示例项目之一(在撰写本文时列出正确):
[*]MiFive M2GL025 Creative Board 和 Renode 使用 GCC 和 SoftConsole IDE
[*]使用 GCC 和 Eclipse 的 VEGAboard PULP RI5CY 演示
[*]SiFive sifive_e QEMU 模拟器使用 Freedom Studio 和 GCC
示例 RISC-V 项目位于FreeRTOS/Demo的子目录中,在主要 FreeRTOS zip 文件下载中以“RISC-V”开头。这些项目可以直接使用,也可以简单地用作源文件、配置选项和下面详述的编译器设置的工作示例和参考。总之,要为 RISC-V 内核构建 FreeRTOS,您需要:
[*]在您的项目中包含核心 FreeRTOS 源文件和 FreeRTOS RISC-V 端口层源文件。
[*]确保汇编器的包含路径包括头文件的路径,该头文件描述了任何特定于芯片的实现细节。
[*]在 FreeRTOSConfig.h 中定义常量或链接器变量以指定用作中断堆栈的内存。
[*]在 FreeRTOSConfig.h 中定义 configMTIME_BASE_ADDRESS 和 configMTIMECMP_BASE_ADDRESS。
[*]对于汇编程序,#define portasmHANDLE_INTERRUPT 为您的芯片或工具供应商提供的用于处理外部中断的函数的名称。
[*]安装 FreeRTOS 陷阱处理程序。

其他可能有用的链接包括:
[*]FreeRTOS 内核快速入门指南
[*]使 FreeRTOS 演示适应不同的硬件
[*]创建新的 FreeRTOS 项目

详细资料在本页面:
[*]FreeRTOS RISC-V 端口的特性
[*]源文件
[*]FreeRTOSConfig.h 设置
[*]中断(系统)堆栈设置
[*]所需的编译器命令行选项
[*]安装 FreeRTOS 中断处理程序
[*]移植到新的 32 位 RISC-V 或 64 位 RISC-V 实现

FreeRTOS RISC-V 端口的特性FreeRTOS RISC-V 端口:
[*]为 GCC 和IAR编译器提供。
[*]仅支持在 32 位和 64 位 RISC-V 内核上执行机器模式整数,但正在积极开发中,未来的 FreeRTOS 版本将根据用户的需要添加特性和功能。
[*]实现一个单独的中断堆栈,通过消除每个任务都需要一个足够大的堆栈来容纳中断和非中断堆栈帧,从而大大减少了小型微控制器上的 RAM 使用量。
[*]提供可轻松扩展以适应 RISC-V 实现特定架构扩展的基本端口。

源文件该FreeRTOS的内核源代码的组织页面包含在其上添加FreeRTOS的内核到您的项目信息。除了该页面上的信息之外,FreeRTOS RISC-V 端口还需要一个额外的头文件。额外的头文件描述了芯片特定的细节,并且是必需的,因为 RISC-V 芯片通常包含芯片特定的架构扩展。附加头文件称为freertos_risc_v_chip_specific_extensions.h。对于每个受支持的架构扩展,此头文件都有一个实现,所有实现都位于 /FreeRTOS/Source/Portable//RISC-V/chip_specific_extensions 目录的子目录中。要 为您的芯片包含正确的freertos_risc_v_chip_specific_extensions.h头文件,只需将该头文件的路径添加到汇编器的包含路径(注意这是 汇编器的包含路径,而不是编译器的包含路径)。例如:
[*]如果您的芯片实现基本 RV32I 或 RV64I 架构,包括核心本地中断器 (CLINT),但没有其他寄存器扩展,则将 /FreeRTOS/Source/Portable//RISC-V/chip_specific_extensions/ RV32I_CLINT_no_extensions 添加 到汇编器包括路径。
[*]如果您的芯片使用在 RV32M1RM Vega 板上实现的 PULP RI5KY 内核,其中包括六个额外的寄存器并且不包括内核本地中断器 (CLINT),然后添加 /FreeRTOS/Source/Portable//RISC-V/ chip_specific_extensions/ Pulpino_Vega_RV32M1RM 到汇编程序的包含路径。
另请参阅下面的编译器和汇编器命令行选项部分,了解有关设置汇编器命令行选项的信息,以及 将 FreeRTOS 移植到新的 RISC-V 实现 部分,了解有关创建您自己的freertos_risc_v_chip_specific_extensions.h 头文件的信息。
FreeRTOSConfig.h 设置configMTIME_BASE_ADDRESS和configMTIMECMP_BASE_ADDRESS必须在FreeRTOSConfig.h 中定义 。如果目标 RISC-V 芯片包含机器定时器 (MTIME),则将configMTIME_BASE_ADDRESS设置 为 MTIME 基地址,将 configMTIMECMP_BASE_ADDRESS 设置为 MTIME 比较寄存器 (MTIMECMP) 的地址。否则,将两个定义都设置为 0。例如,如果 MTIME 基地址是 0x2000BFF8 并且 MTIMECMP 地址是 0x20004000,那么在FreeRTOSConfig.h 中添加以下几行:#define configMTIME_BASE_ADDRESS ( 0x2000BFF8UL )#define configMTIMECMP_BASE_ADDRESS (0x20004000UL)如果没有 MTIME 时钟,则在FreeRTOSConfig.h 中添加以下几行:#define configMTIME_BASE_ADDRESS ( 0 )#define configMTIMECMP_BASE_ADDRESS ( 0 )
中断(系统)堆栈设置FreeRTOS RISC-V 端口在从中断服务例程 (ISR) 调用任何 C 函数之前切换到专用中断(或系统)堆栈。用作中断堆栈的内存可以在链接描述文件中定义,也可以在 FreeRTOS 端口层中声明为静态分配的数组。链接器脚本方法在内存受限的 MCU 上是首选,因为它允许在启动调度程序之前由 main() 使用的堆栈(在调度程序启动后不再用于该目的)被重新用作中断堆栈。
[*]使用静态分配的数组作为中断堆栈:在FreeRTOSConfig.h 中定义 configISR_STACK_SIZE_WORDS为要分配的中断堆栈的大小。请注意,大小是按字定义的,而不是字节。
例如,要使用 500 个字(在 RV32 上为 2000 个字节,其中每个字为 4 个字节)静态分配的中断堆栈,请将以下内容添加到 FreeRTOSConfig.h:
#define configISR_STACK_SIZE_WORDS (500)
[*]要在链接描述文件中定义中断堆栈 - 在撰写本文时请注意此方法仅在 GCC 端口中受支持:

[*]声明一个名为__freertos_irq_stack_top的链接器变量,该变量保存中断堆栈的最高地址,并且
[*]确保configISR_STACK_SIZE_WORDS是没有定义的。
使用此方法需要编辑链接描述文件。如果您不熟悉链接器脚本,那么至少在使用 GCC 时,重要的是要知道 ' . ' 是所谓的位置计数器,并保存链接描述文件中该点的内存地址值。不过,无需了解链接器脚本的细节,只需复制下面的示例即可。
调度程序启动之前 main() 使用的堆栈在调度程序启动后不再需要,因此理想情况下通过将__freertos_irq_stack_top设置为等于分配给 main() 使用的堆栈的最高地址的值来重用该堆栈. 例如,如果您的链接器脚本包含如下内容(实际使用的链接器脚本会有所不同):
.stack:对齐(0x10){    __stack_bottom = .;    . += 堆栈大小;    __stack_top = .;} > 公羊然后__stack_top(仅示例名称)是一个链接器变量,其值等于 main() 使用的堆栈的最高地址(回想一下,'.' 保存了链接描述文件中任何给定位置的内存地址值)。在这种情况下,给 __freertos_irq_stack_top相同的值__stack_top,只是定义__freertos_irq_stack_top后立即__stack_top。请参阅下面的示例:
.stack:对齐(0x10){    __stack_bottom = .;    . += 堆栈大小;    __stack_top = .;    __freertos_irq_stack_top= .; /* 添加了这一行。*/} > 公羊
注意:在写入时,与任务堆栈不同,内核不检查中断堆栈中的溢出。
所需的编译器和汇编器命令行选项不同的 RISC-V 实现为外部中断提供了不同的处理程序,因此需要告诉 FreeRTOS 内核调用哪个外部中断处理程序。设置外部中断处理程序的名称:
[*]找到由您的 RISC-V 运行时软件分发提供的外部中断处理程序的名称 - 这通常是芯片供应商提供的软件。中断处理程序必须有一个参数,即进入中断时RISC-V原因寄存器的值。例如,中断处理程序的原型应该是(仅示例名称,为您的软件使用正确的名称):void external_interrupt_handler(uint32_t 原因);
[*]定义一个名为portasmHANDLE_INTERRUPT的汇编器宏(注意这是一个汇编器宏,而不是编译器宏)以等于中断处理程序的名称。如果使用 GCC,这可以通过将以下内容添加到汇编程序的命令行来实现,假设中断处理程序称为 external_interrupt_handler:
-DportasmHANDLE_INTERRUPT=external_interrupt_handler如果使用 IAR,这可以通过打开项目选项对话框并添加以下行作为 Assembler 类别中的定义符号来实现,假设中断处理程序称为vApplicationHandleTrap。portasmHANDLE_INTERRUPT=vApplicationHandleTraphttps://www.freertos.org/fr-content-src/uploads/2020/02/IAR_RISC-V_Assembly_Options.jpg
设置 IAR 汇编器选项
还需要将 正在使用的 RISC-V 芯片的正确freertos_risc_v_chip_specific_extensions.h头文件的路径添加到汇编器的包含路径中(注意这是 汇编器的包含路径,而不是编译器的包含路径)。请参阅上面的源文件部分。
安装 FreeRTOS 陷阱处理程序FreeRTOS 陷阱处理程序称为freertos_risc_v_trap_handler()并且是所有中断和异常的中央入口点。当陷阱 源是外部中断时,FreeRTOS 陷阱处理程序调用外部中断处理程序。要安装陷阱处理程序:
[*]如果正在使用的 RISC-V 核心包括核心本地中断器 (CLINT),那么 portasmHAS_SIFIVE_CLINT 可以在freertos_risc_v_chip_specific_extensions.h 中定义为 1 ,这会导致 freertos_risc_v_trap_handler() 的自动安装,因此不需要其他特定操作。
[*]在所有其他情况下,必须手动安装 freertos_risc_v_trap_handler()。这可以通过编辑芯片供应商提供的启动代码来完成。

注意:如果 RISC-V 芯片使用向量中断控制器,则安装 freertos_risc_v_trap_handler()作为每个向量的处理程序。
移植到新的 32 位或 64 位 RISC-V 实现在阅读本部分之前,请阅读上面 的FreeRTOS RISC-V 源文件部分。该freertos_risc_v_chip_specific_extensions.h文件包含必须定义下面的宏:
[*]portasmHAS_MTIME如果芯片有机器定时器(MTIME),则将 portasmHAS_MTIME 设置为 1,否则将 portasmHAS_MTIME 设置为 0。
[*]portasmADDITIONAL_CONTEXT_SIZERISC-V 指令集架构 (ISA) 是可扩展的,因此 RISC-V 芯片可能包含超出基本架构规范所需的额外寄存器。
#define portasmADDITIONAL_CONTEXT_SIZE为目标芯片上存在的附加寄存器的数量 - 可能为零。例如,Vega 板上的 RI5CY 内核包括六个额外的寄存器,因此提供给该芯片使用的freertos_risc_v_chip_specific_extensions.h包括以下行:
#define portasmADDITIONAL_CONTEXT_SIZE 6
[*]portasmSAVE_ADDITIONAL_REGISTERSportasmSAVE_ADDITIONAL_REGISTERS是一个汇编宏(不是#define),必须实现它以保存任何特定于芯片的附加寄存器。
如果没有特定于芯片的扩展寄存器(portasmADDITIONAL_CONTEXT_SIZE 设置为零),则 portasmSAVE_ADDITIONAL_REGISTERS 必须是一个空的汇编宏,如下所示:
.macro portasmSAVE_ADDITIONAL_REGISTERS   /* 没有额外的寄存器要保存,所以这个宏什么都不做。*/    .endm如果有芯片特定的扩展寄存器(portasmADDITIONAL_CONTEXT_SIZE 大于零),那么 portasmSAVE_ADDITIONAL_REGISTERS 必须:

[*]递减堆栈指针以为附加寄存器创建足够的堆栈空间,然后...
[*]将附加寄存器保存到创建的堆栈空间中。
例如,如果芯片有三个额外的寄存器,则 必须按如下方式实现portasmSAVE_ADDITIONAL_REGISTERS(其中寄存器的名称将取决于芯片,而不是此处所示):
.macro portasmSAVE_ADDITIONAL_REGISTERS   /* 使用portasmADDITIONAL_CONTEXT_SIZE 和portWORD_SIZE    宏来计算需要多少额外的堆栈空间,    并从堆栈指针中减去它。这条线只能    从这里复制提供 portasmADDITIONAL_CONTEXT_SIZE    设置正确。请注意减号 ('-')。portWORD_SIZE    已经在别处定义了。*/    addi sp, sp, -(portasmADDITIONAL_CONTEXT_SIZE * portWORD_SIZE)    /* 接下来保存额外的寄存器,这里假设    被称为 xx0 到 xx2,但会被称为不同的东西    在你的芯片上,到堆栈。假设 portasmADDITIONAL_CONTEXT_SIZE    是 3。*/    sw xx0, 1 * portWORD_SIZE( sp )    sw xx1, 2 * portWORD_SIZE( sp )    sw xx2, 3 * portWORD_SIZE( sp )    .endm[*]portasmRESTORE_ADDITIONAL_REGISTERSportasmRESTORE_ADDITIONAL_REGISTERS与portasmSAVE_ADDITIONAL_REGISTERS相反 。
如果没有特定于芯片的扩展寄存器(portasmADDITIONAL_CONTEXT_SIZE 设置为零),则portasmRESTORE_ADDITIONAL_REGISTERS必须是一个空的汇编宏,如下所示:
.macro portasmRESTORE_ADDITIONAL_REGISTERS   /* 没有额外的寄存器要恢复,所以这个宏什么都不做。*/    .endm如果有芯片特定的扩展寄存器(portasmADDITIONAL_CONTEXT_SIZE 大于零),那么portasmRESTORE_ADDITIONAL_REGISTERS必须:

[*]从portasmSAVE_ADDITIONAL_REGISTERS使用的堆栈位置读取附加寄存器 ,然后...
[*]通过将堆栈指针增加正确的数量来移除用于保存附加寄存器的堆栈空间。
例如,如果芯片有三个附加寄存器,则 必须按如下方式实现portasmRESTORE_ADDITIONAL_REGISTERS(其中寄存器的名称将取决于芯片,而不是此处所示):
.macro portasmRESTORE_ADDITIONAL_REGISTERS   /* 恢复附加寄存器,这里假设    被称为 xx0 到 xx2,但会被称为不同的东西    在你的筹码上。假设    portasmADDITIONAL_CONTEXT_SIZE 为 3。*/    lw xx0, 1 * portWORD_SIZE( sp )    lw xx1, 2 * portWORD_SIZE( sp )    lw xx2, 3 * portWORD_SIZE( sp )    /* 使用 portasmADDITIONAL_CONTEXT_SIZE 和 portWORD_SIZE    宏来计算从堆栈中删除多少空间。    这行可以从这里提供    portasmADDITIONAL_CONTEXT_SIZE 设置正确。portWORD_SIZE    已经在别处定义了。*/    addi sp, sp, (portasmADDITIONAL_CONTEXT_SIZE * portWORD_SIZE)    .endm
该freertos_risc_v_chip_specific_extensions.h文件还可以optinally包括:
[*]portasmHAS_SIFIVE_CLINT如果目标 RISC-V 芯片包含核心本地中断器 (CLINT),那么如果您希望自动安装 FreeRTOS RISC-V 陷阱处理程序,则 #define portasmHAS_SIFIVE_CLINT为 1。

页: [1]
查看完整版本: 在 RISC-V 微控制器上使用 FreeRTOS