下载 FreeRTOS
 

出色的 RTOS & 嵌入式软件

内核
最新资讯
简化任何设备的身份验证云连接。
利用 CoAP 设计节能型云连接 IoT 解决方案。
11.0.0 版 FreeRTOS 内核简介:
FreeRTOS 路线图和代码贡献流程。
使用 FreeRTOS 实现 OPC-UA over TSN。

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


序言

正如在升级到 FreeRTOS V10.3.0 页面上所指出的那样,configCLINT_BASE_ADDRESS 配置设置已经被弃用, 并被本页所述的 configMTIME_BASE_ADDRESSconfigMTIMECMP_BASE_ADDRESS 设置取代 。 如果旧版应用程序仍然使用 configCLINT_BASE_ADDRESS 设置, 会出现一个编译器警告,但应用程序还是会照旧 继续构建和运行。


简介

RISC-V 指令集架构 (ISA) 易于扩展, 并且没有指明物理 RISC-V 微控制器或片上系统 (SoC) 实现 的一切信息。 因此,FreeRTOS RISC-V 移植也是可扩展的—— 它提供了一个处理所有 RISC-V 实现所共有的寄存器的基本端口,以及一组必须实现的宏, 以处理硬件实现的特定功能和扩展, 如额外寄存器。


快速使用指南

此页面正文提供了关于 为 RISC-V 核心构建 FreeRTOS 的详细信息,但最简单的方法是 使用以下预配置的示例项目之一(在编写时 正确列出):

示例 RISC-V 项目 位于以 "RISC-V" 开头的 FreeRTOS/Demo 子目录中, 该子目录在主 FreeRTOS zip 文件下载中。 这些项目可以直接使用, 也可单纯用作下文详述的源文件、 配置选项和编译器设置的工作示例和参考。

总之,要为 RISC-V 核心构建 FreeRTOS,必须:

  1. 把核心 FreeRTOS 源 文件和 FreeRTOS RISC-V 端口层源文件 纳入您的项目中
  2. 确保汇编器的包含路径 包括描述任何芯片特定实现细节 的头文件的路径
  3. 在 FreeRTOSConfig.h 或一个链接器变量中定义一个常量, 来指定要用作 中断堆栈的内存
  4. 定义 configMTIME_BASE_ADDRESS 和 configMTIMECMP_BASE_ADDRESS,位置为 FreeRTOSConfig.h
  5. 对于汇编器,#define(定义) portasmHANDLE_INTERRUPT 为您的芯片或工具供应商提供的,用于处理外部中断的函数的名称
  6. 安装 FreeRTOS 陷阱处理程序


其他可能有用的链接包括:



详细信息

本页面包含:


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/[compiler]/RISC-V/chip_specific_extensions 目录下的子目录中。

要为芯片包含正确的 freertos_risc_v_chip_specific_extensions.h 头文件, 只需将该头文件的路径添加到汇编器的包含路径中(注意是 汇编器的包含路径,而不是编译器的包含路径)。 例如:

  • 如果您的芯片使用基本的 RV32I 或 RV64I 架构, 包括一个核心本地中断器 (CLINT),但没有其他的寄存器扩展, 则请把 /FreeRTOS/Source/Portable/[compiler]/RISC-V/chip_specific_extensions/RV32I_CLINT_no_extensions 添加到汇编器的包含路径中。

  • 如果您的芯片使用 RV32M1RM Vega 板上实现的 PULP RI5KY 内核,其中包括六个额外的寄存器, 并且不包括内核本地中断器 (CLINT),那么请将 /FreeRTOS/Source/Portable/[compiler]/RISC-V/chip_specific_extensions/Pulpino_Vega_RV32M1RM 添加到汇编器的包含路径中。

另请参阅 编译器和汇编器命令行 选项部分,以了解关于设置汇编器命令行选项的信息;以及参阅 将 FreeRTOS 移植到新 RISC-V 实现 部分,以了解关于创建您自己的 freertos_risc_v_chip_specific_extensions.h 头文件的信息。


FreeRTOSConfig.h 设置

configMTIME_BASE_ADDRESSconfigMTIMECMP_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 端口支持此方法:

    1. 声明一个名为 __freertos_irq_stack_top, 保存中断堆栈最高地址的链接器变量,并
    2. 确保 configISR_STACK_SIZE_WORDS 定义。

    使用此方法需要编辑链接器脚本。 如果您不 熟悉链接器脚本,则在使用 GCC 时,至少应知道 '.' 是 位置计数器,用于保存链接器脚本中该点的 内存地址的值。 无需了解链接器脚本的细节, 只需复制下面的示例即可。

    在调度器启动之前由 main() 使用的堆栈 在调度器启动之后就不再需要了,所以理想的情况是 通过设置 __freertos_irq_stack_top 等于分配给 main() 使用的堆栈的最高地址的值 来重新使用该堆栈。 例如, 如果您的链接器脚本包含如下内容 (实际使用的链接器脚本会有所不同):

      .stack : ALIGN(0x10)
      {
        __stack_bottom = .;
        . += STACK_SIZE;
        __stack_top = .;
      } > ram
    

    然后 __stack_top(仅为示例名称)是链接器变量, 其值等于 main() 使用的堆栈的最高地址(请记住 '.' 表示链接器脚本中 任何指定位置的内存地址值)。 在这种情况下,要给 __freertos_irq_stack_top 赋予与 __stack_top 相同的值, 只需在 __stack_top 后立即定义 __freertos_irq_stack_top 即可。 请参阅下面的示例:

      .stack : ALIGN(0x10)
      {
        __stack_bottom = .;
        . += STACK_SIZE;
        __stack_top = .;
        __freertos_irq_stack_top= .; /* ADDED THIS LINE. */
      } > ram
    

注意: 写入时,与任务堆栈不同,内核不会检查 中断栈中的溢出。


所需的编译器和汇编器命令行选项

不同的 RISC-V 实现为外部中断提供了不同的处理程序, 因此,有必要告诉 FreeRTOS 内核应调用哪个外部中断处理程序 。 要设置外部中断处理程序的名称,请执行以下操作:
  1. 找到您的 RISC-V 运行时软件发布版(通常是由芯片供应商提供的软件)提供的外部中断处理程序的名称 。 中断处理程序必须有一个参数, 即进入中断时的 RISC-V 原因寄存器的值 。 例如,中断处理程序的原型应该是 (仅为示例名称, 请使用适合您软件的正确名称):
    void external_interrupt_handler( uint32_t cause );
    

  2. 定义一个汇编器宏(请注意,这是一个汇编器宏, 而不是一个编译器宏),名为 portasmHANDLE_INTERRUPT,使其名称与 中断处理程序的名称相同。

    如果使用 GCC,则可以通过将以下内容添加到 汇编器命令行来实现这一点,假设中断处理程序的名称为 external_interrupt_handler

    -DportasmHANDLE_INTERRUPT=external_interrupt_handler
    
    如果使用 IAR,则可以通过打开项目选项对话框 并添加以下行作为汇编器类别的定义符号来实现这一点, 假设中断处理程序的名称为 vApplicationHandleTrap
    portasmHANDLE_INTERRUPT=vApplicationHandleTrap
    

    设置 IAR 汇编器选项


还需要把正确的 freertos_risc_v_chip_specific_extensions.h 头文件(使用中的 RISC-V 芯片的头文件)的路径添加到汇编器的包含路径中 (请注意,这是汇编器的包含路径,不是编译器的包含路径)。 请参阅 上文的源文件部分。


安装 FreeRTOS 陷阱处理程序

FreeRTOS 陷阱处理程序称为 freertos_risc_v_trap_handler(), 是所有中断和异常的中心入口点。 当陷阱来源是外部中断时,FreeRTOS 陷阱处理程序会调用外部中断处理程序

要安装陷阱处理程序,请执行以下操作:

  1. 如果使用的 RISC-V 内核包括一个核心本地中断器 (CLINT), 则可在 freertos_risc_v_chip_specific_extensions.h 中将 portasmHAS_SIFIVE_CLINT 定义为 1, 这会导致 freertos_risc_v_trap_handler() 自动安装, 因此无需采取其他具体行动。

  2. 在所有其他情况下,必须手动安装 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_SIZE

    RISC-V 指令集架构 (ISA) 是可扩展的,因此 RISC-V 芯片 可以在基本架构规范要求的基础上 包括额外的寄存器。

    #define(定义)portasmADDITIONAL_CONTEXT_SIZE 为 目标芯片中存在的额外寄存器的数量(可能等于零)。 例如, Vega 板上的 RI5CY 内核包括六个额外的寄存器, 因此,提供的与该芯片一同使用的 freertos_risc_v_chip_specific_extensions.h 包含以下行:

    #define portasmADDITIONAL_CONTEXT_SIZE 6
    

  • portasmSAVE_ADDITIONAL_REGISTERS

    portasmSAVE_ADDITIONAL_REGISTERS是一个汇编宏(而不是 #define), 必须实现该宏才能保存任何特定的芯片额外寄存器。

    如果没有芯片特定的扩展寄存器(portasmADDITIONAL_CONTEXT_SIZE 设置为零),则 portasmSAVE_ADDITIONAL_REGISTERS 必须为 空汇编宏,如下所示:

    .macro portasmSAVE_ADDITIONAL_REGISTERS
        /* No additional registers to save, so this macro does nothing. */
        .endm
    

    如果有芯片特定的扩展寄存器(portasmADDITIONAL_CONTEXT_SIZE 大于零),则 portasmSAVE_ADDITIONAL_REGISTERS 必须:

    1. 递减堆栈指针,为额外的寄存器创造足够的堆栈空间 ,然后……
    2. 将额外寄存器保存到创建的堆栈空间中。

    例如,如果芯片有三个额外的寄存器, 则必须按照以下方式实现 portasmSAVE_ADDITIONAL_REGISTERS (其中,寄存器名称取决于 芯片,而不是如此处所示):

    .macro portasmSAVE_ADDITIONAL_REGISTERS
        /* Use the portasmADDITIONAL_CONTEXT_SIZE and portWORD_SIZE
        macros to calculate how much additional stack space is needed,
        and subtract that from the stack pointer.  This line can just
        be copied from here provided portasmADDITIONAL_CONTEXT_SIZE
        is set correctly.  Note the minus sign ('-').  portWORD_SIZE
        is already defined elsewhere. */
        addi sp, sp, -(portasmADDITIONAL_CONTEXT_SIZE * portWORD_SIZE)
    
        /* Next save the additional registers, which here are assumed
        to be called xx0 to xx2, but will be called something different
        on your chip, to the stack.  Assumes portasmADDITIONAL_CONTEXT_SIZE
        is 3. */
        sw xx0, 1 * portWORD_SIZE( sp )
        sw xx1, 2 * portWORD_SIZE( sp )
        sw xx2, 3 * portWORD_SIZE( sp )
        .endm
    
  • portasmRESTORE_ADDITIONAL_REGISTERS

    portasmRESTORE_ADDITIONAL_REGISTERSportasmSAVE_ADDITIONAL_REGISTERS 相反。

    如果没有芯片特定的扩展寄存器(portasmADDITIONAL_CONTEXT_SIZE 设置为零),则 portasmRESTORE_ADDITIONAL_REGISTERS 必须为 空汇编宏,如下所示:

    .macro portasmRESTORE_ADDITIONAL_REGISTERS
        /* No additional registers to restore, so this macro does nothing. */
        .endm
    

    如果有芯片特定的扩展寄存器(portasmADDITIONAL_CONTEXT_SIZE 大于零),则 portasmRESTORE_ADDITIONAL_REGISTERS 必须:

    1. portasmSAVE_ADDITIONAL_REGISTERS 使用的堆栈位置读取额外的寄存器,然后……
    2. 通过将堆栈指针按正确的数量递增, 删除用于存放额外寄存器的堆栈空间。

    例如,如果 芯片有三个额外的寄存器,则必须按照以下方式实现 portasmRESTORE_ADDITIONAL_REGISTERS (其中,寄存器名称取决于 芯片,而不是如此处所示):

    .macro portasmRESTORE_ADDITIONAL_REGISTERS
        /* Restore the additional registers, which here are assumed
        to be called xx0 to xx2, but will be called something different
        on your chip from the stack.  Assumes
        portasmADDITIONAL_CONTEXT_SIZE is 3. */
        lw xx0, 1 * portWORD_SIZE( sp )
        lw xx1, 2 * portWORD_SIZE( sp )
        lw xx2, 3 * portWORD_SIZE( sp )
    
        /* Use the portasmADDITIONAL_CONTEXT_SIZE and portWORD_SIZE
        macros to calculate how much space to remove from the stack.
        This line can just be copied from here provided
        portasmADDITIONAL_CONTEXT_SIZE is set correctly.  portWORD_SIZE
        is already defined elsewhere. */
        addi sp, sp, (portasmADDITIONAL_CONTEXT_SIZE * portWORD_SIZE)
        .endm
    


您也可以选择在 freertos_risc_v_chip_specific_extensions.h 文件中包括:

  • portasmHAS_SIFIVE_CLINT

    如果目标 RISC-V 芯片包括一个核心本地中断器 (CLINT),则 #define(定义)portasmHAS_SIFIVE_CLINT 为 1——如果您希望 FreeRTOS RISC-V 陷阱处理程序自动安装,则可进行此设置。





Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.