构建基于 RISC-V 64 的 FreeRTOS

FreeRTOS 是一个轻量级的完全免费的实时操作系统内核,源码公开、可裁减,包括了任务管理、时间管理、信号量、消息队列、内存管理、记录功能等,可移植到各种单片机上运行,可基本满足较小系统的需要。

FreeRTOS 特性

  • 开源、免费的嵌入式实时操作系统。

  • 支持抢占式、合作式和时间片三种调度方式。

  • 提供了一个用于低功耗的 Tickless 模式。

  • 系统的组件在创建时可以选择动态或者静态的RAM,比如任务、消息队列、信号量、软件定时器等等。

  • 系统简单、小巧、易用,通常情况下内核占用 4k-9k 字节的空间。

  • 支持市面上的多达35种芯片架构,如Cortex-M 、Cortex-R 、Cortex-A、RISC-V等。

  • 高可移植性,代码主要 C 语言编写。

  • 强大的跟踪执行功能。

环境准备

目前官方提供的 Demo 中已经包含了基于 QEMU 的 RISC-V 32 架构的 FreeRTOS ,只需在其基础上进行 RISC-V 64 架构的适配即可,选择 Poky 作为构建工程,构建主机使用 Ubuntu-20.04 版本,此外还应满足以下需求:

  • 50G 空闲的磁盘空间

  • Git 1.8.3.1 或更高版本

  • tar 1.28 或更高版本

  • Python 3.6.0 或更高版本

  • gcc 5.0 或更高版本

满足以上条件后,需在主机上安装必要的工具包:

sudo apt install gawk wget git diffstat unzip texinfo gcc build-essential chrpath socat cpio python3 python3-pip python3-pexpect xz-utils debianutils iputils-ping python3-git python3-jinja2 libegl1-mesa libsdl1.2-dev pylint3 xterm python3-subunit mesa-common-dev zstd liblz4-tool

为了支持 RISC-V 64 架构,还需安装 RISC-V 编译工具链,具体步骤如下:

cd /opt/
curl -O -L "https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-20181030-x86_64-linux-ubuntu14.tar.gz"
tar xzf riscv64-unknown-elf-gcc-20181030-x86_64-linux-ubuntu14.tar.gz
sudo mv riscv64-unknown-elf-gcc-20181030-x86_64-linux-ubuntu14 riscv-new
export PATH="$PATH:/opt/riscv-new/bin"

移植 FreeRTOS

经验证测试,以官方 FreeRTOS 工程 release-candidate 分支为基础,进行 RISC-V 64 架构的 FreeRTOS 移植工作,首先获取 FreeRTOS 工程,并切换到 release-candidate 分支:

git clone https://github.com/FreeRTOS/FreeRTOS.git --recurse-submodules
cd FreeRTOS
sudo git checkout -b tflite remotes/origin/release-candidate

主要关注 FreeRTOS 下的文件,官方提供的Demo中已经包含了基于 QEMU 的 RISC-V 32 架构的 FreeRTOS ,其相关的目录结构如下图所示:

../../_images/FreeRTOS_arch.png

RISC-V 32 架构 FreeRTOS 目录结构

为了适配 RISC-V 64 架构,对 RISC-V-Qemu-virt_GCC 下的 Makefile 进行更改, git diff 输出如下:

diff --git a/git/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/Makefile b/git/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/Makefile
index 4d806bf49..695434a14 100644
--- a/git/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/Makefile
+++ b/git/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/Makefile
@@ -1,4 +1,4 @@
-CROSS   = riscv64-unknown-elf-
+CROSS   ?= /opt/riscv-new/bin/riscv64-unknown-elf-
CC      = $(CROSS)gcc
OBJCOPY = $(CROSS)objcopy
ARCH    = $(CROSS)ar
@@ -14,13 +14,13 @@ CPPFLAGS = \
    -I $(RTOS_SOURCE_DIR)/include \
    -I $(RTOS_SOURCE_DIR)/portable/GCC/RISC-V \
    -I $(RTOS_SOURCE_DIR)/portable/GCC/RISC-V/chip_specific_extensions/RV32I_CLINT_no_extensions
-CFLAGS  = -march=rv32ima -mabi=ilp32 -mcmodel=medany \
+CFLAGS  = -march=rv64imafdc -mabi=lp64d -mcmodel=medany \
    -Wall \
    -fmessage-length=0 \
    -ffunction-sections \
    -fdata-sections \
    -fno-builtin-printf
-ASFLAGS = -march=rv32ima -mabi=ilp32 -mcmodel=medany
+ASFLAGS = -march=rv64imafdc -mabi=lp64d -mcmodel=medany
LDFLAGS = -nostartfiles -Tfake_rom.lds \
    -Xlinker --gc-sections \
    -Xlinker --defsym=__stack_size=300
@@ -53,8 +53,14 @@ ASMS = start.S \
OBJS = $(SRCS:%.c=$(BUILD_DIR)/%.o) $(ASMS:%.S=$(BUILD_DIR)/%.o)
DEPS = $(SRCS:%.c=$(BUILD_DIR)/%.d) $(ASMS:%.S=$(BUILD_DIR)/%.d)

-$(BUILD_DIR)/RTOSDemo.axf: $(OBJS) fake_rom.lds Makefile
-$(CC) $(LDFLAGS) $(OBJS) -o $@
+TARGET = RTOSDemo.bin
+ELF_IMAGE = RTOSDemo.elf
+
+$(BUILD_DIR)/$(TARGET):$(BUILD_DIR)/$(ELF_IMAGE)
+$(OBJCOPY) $(BUILD_DIR)/$(ELF_IMAGE)  -O binary $(BUILD_DIR)/$(TARGET)
+
+$(BUILD_DIR)/$(ELF_IMAGE): $(OBJS) fake_rom.lds Makefile
+$(CC) $(LDFLAGS) $(OBJS) -o $@

最终得到补丁文件 0001-modify-Makefile-to-adapt-riscv64.patch

定制 RISC-V 64 FreeRTOS Layer

  1. 获取 meta-freertos

为了能够在 Poky 工程下使用 QEMU 运行 FreeRTOS ,以 “OpenEmbedded layer index” 内现存的 meta-freertos 为基础进行 RISC-V64 架构的适配。首先在已搭建的 Poky 工程目录下获取 meta-freertos :

sudo git clone https://github.com/aehs29/meta-freertos.git

为了精简 meta-freertos ,删除本地测试 BB 文件和 CI 文件:

sudo rm CI/ -rf
sudo rm recipes-freertos/freertos-demo-local/ -rf
  1. 修改 classes

接下来需要修改 classes 文件夹下的类文件 freertos-image.bbclassfreertos-armv5.bbclass ,由于更改了 FreeRTOS 源且计划在 QEMU 下运行基于 RISC-V64 架构的 Demo RISC-V-Qemu-virt_GCC ,因此需要进行相应的修改适配,freertos-image.bbclass 修改后的 git diff 输出如下:

diff --git a/classes/freertos-image.bbclass b/classes/freertos-image.bbclass
old mode 100644
new mode 100755
index c3cf49f..a3a63c4
--- a/classes/freertos-image.bbclass
+++ b/classes/freertos-image.bbclass
@@ -13,26 +13,26 @@
# And we get the app code from a different repo (app recipe)

# FreeRTOS kernel version (FreeRTOS.h)
-FREERTOS_VERSION = "FreeRTOSv10.4.3"
-SRCBRANCH = "202012-LTS"
+FREERTOS_VERSION = "FreeRTOSv10.4.6"
+SRCBRANCH = "release-candidate"

LICENSE = "MIT"

# FreeRTOS License, careful here, the gitsm fetcher does not work properly with license checking
# double check this manually after an upgrade
-LIC_FILES_CHKSUM = "file://../freertos/LICENSE;md5=7ae2be7fb1637141840314b51970a9f7"
+LIC_FILES_CHKSUM = "file://${S}/git/LICENSE.md;md5=7ae2be7fb1637141840314b51970a9f7"
FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"

SRC_URI = " \
-    gitsm://github.com/FreeRTOS/FreeRTOS-LTS.git;name=freertos;destsuffix=freertos;branch=${SRCBRANCH};protocol=https \
+         gitsm://github.com/FreeRTOS/FreeRTOS.git;branch=${SRCBRANCH};protocol=https \
"

SRCREV_FORMAT ?= "freertos_bsp"
-SRCREV_freertos ?= "1bb18c8dfbf8f0445e873b20cec7d6091771f9e9"
+SRCPV ?= "4a4abbfe207e83f615ea629a5ce7f2c9fc52eafb"

PV = "${FREERTOS_VERSION}+git${SRCPV}"

-FREERTOS_KERNEL_SRC = "${WORKDIR}/freertos/FreeRTOS/FreeRTOS-Kernel/"
+FREERTOS_SRC = "${S}/git"

IMAGE_BASENAME = "freertos-image"
BAREMETAL_BINNAME ?= "${IMAGE_BASENAME}"
@@ -43,20 +43,27 @@ IMAGE_LINK_NAME ?= "${IMAGE_BASENAME}-${MACHINE}"
CFLAGS:remove = "-O2"

# Extra CFLAGS required for FreeRTOS include files
-CFLAGS:append = " -I${FREERTOS_KERNEL_SRC} -I${FREERTOS_KERNEL_SRC}/include/"
+CFLAGS:append = " -I${FREERTOS_SRC}/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC   -I${FREERTOS_SRC}/FreeRTOS/Source/include    -I${FREERTOS_SRC}/FreeRTOS/Demo/Common/include -I${FREERTOS_SRC}/FreeRTOS/Source/portable/GCC/RISC-V/chip_specific_extensions/RV32I_CLINT_no_extensions -I${FREERTOS_SRC}/FreeRTOS/Source/portable/GCC/RISC-V"

# We need to define the FreeRTOS source code location, the port we'll be using
# should be defined on the specific bsp class
-EXTRA_OEMAKE = " FREERTOS_SRC=${FREERTOS_KERNEL_SRC} 'CFLAGS=${CFLAGS}'"
+EXTRA_OEMAKE = "FREERTOS_SRC=${FREERTOS_SRC} 'CFLAGS=${CFLAGS}'"
+
+cmake_do_configure(){
+  -DCMAKE_NO_SYSTEM_FROM_IMPORTED=1 \
+  ${EXTRA_OECMAKE} \
+  -Wno-dev
+}

do_compile(){
-  oe_runmake ${EXTRA_OEMAKE}
+  cd ${FREERTOS_SRC}
+  make -C ./FreeRTOS/Demo/RISC-V-Qemu-virt_GCC
}

do_install(){
-  install -d ${D}/${base_libdir}/firmware
-  install -m 755 ${B}/image.bin ${D}/${base_libdir}/firmware/${BAREMETAL_BINNAME}.bin
-  install -m 755 ${B}/image.elf ${D}/${base_libdir}/firmware/${BAREMETAL_BINNAME}.elf
+  install -d ${D}${base_libdir}/firmware
+  install -m 755 ${S}/git/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/build/RTOSDemo.bin   ${D}${base_libdir}/firmware/${BAREMETAL_BINNAME}.bin
+  install -m 755 ${S}/git/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/build/RTOSDemo.elf   ${D}${base_libdir}/firmware/${BAREMETAL_BINNAME}.elf
}


@@ -66,10 +73,12 @@ FILES:${PN}:append = " \
"

# QEMU generic FreeRTOS parameters
-QB_DEFAULT_KERNEL = "${IMAGE_LINK_NAME}.bin"
-QB_MEM = "-m 128"
+QB_DEFAULT_KERNEL = "${IMAGE_LINK_NAME}.elf"
+QB_MEM = "-m 256"
QB_OPT_APPEND = "-nographic"
-QB_DEFAULT_FSTYPE = "bin"
+QB_DEFAULT_FSTYPE = "elf"
+QB_SMP = "-smp 8"
+QB_SERIAL_OPT = "-serial chardev:con"

inherit baremetal-image

freertos-armv5.bbclass 文件名修改为 freertos-riscv.bbclass

sudo mv freertos-armv5.bbclass freertos-riscv.bbclass

修改后的 freertos-riscv.bbclass 内容如下:

inherit freertos-image

BSP_REPO ?= "../bsp"

SRC_URI = " \
     gitsm://github.com/FreeRTOS/FreeRTOS.git;branch=release-candidate;protocol=https \
"

# BSP repo License
LIC_FILES_CHKSUM = "file://${S}/git/LICENSE.md;md5=7ae2be7fb1637141840314b51970a9f7"

SRCREV = "${AUTOREV}"

S="${WORKDIR}"

# QEMU parameters specific for this PORT
QB_SYSTEM_NAME = "qemu-system-riscv64"
QB_MACHINE = "-machine virt"
QB_DTB = ""
  1. 修改 recipes-freertos

为了引入上面移植好的 FreeRTOS ,使用补丁文件 0001-modify-Makefile-to-adapt-riscv64.patch 替换 recipes-freertos/freertos-demo/files/ 路径下的补丁文件 use-newlib-as-libc.patch ,修改后的 freertos-demo_git.bb 如下所示:

inherit freertos-riscv
# App can be replaced by using a different repo

FILESEXTRAPATHS:prepend := "${THISDIR}/file:"
SRC_URI = " gitsm://github.com/FreeRTOS/FreeRTOS.git;branch=release-candidate;protocol=https \
     file://0001-modify-Makefile-to-adapt-riscv64.patch \
"
SRCREV = "${AUTOREV}"
S="${WORKDIR}"
SRCREV_app = "4a4abbfe207e83f615ea629a5ce7f2c9fc52eafb"

EXTRA_OEMAKE += "APP_SRC=${S}/git/FreeRTOS/ 'STAGING_LIBDIR=${STAGING_LIBDIR}'"

经过上述的更改,已完成了基于 RISC-V 64 架构的 FreeRTOS Layer 定制,为了与原 meta-freertos 区分开,将定制好的 meta-freertos 更名为 meta-freertos-riscv

构建 Poky

在满足构建环境后,需要获取 Poky 工程,使用下面命令获取 Poky:

git clone git://git.yoctoproject.org/poky

切换至 kirkstone 分支,选择与最新稳定版本兼容的版本:

cd poky
git checkout -t origin/kirkstone -b my-kirkstone

上面的 git checkout 命令创建了一个名为 my-kirkstone 的分支。该分支中可供您使用的文件与 kirkstone 发布分支中的文件完全匹配。

测试 meta-freertos-riscv

现在,我们将在 Poky 工程下测试运行 meta-freertos-riscv,首先,需要初始化构建环境,进入 poky 目录,运行 oe-init-build-env 环境设置脚本,自动创建并进入 build 目录:

cd poky
source oe-init-build-env

增加图层 meta-freertos-riscv 到配置文件,并启动构建:

bitbake-layers add-layer ../meta-freertos-riscv
echo "MACHINE = \"qemuriscv64\"" >> ./conf/local.conf
echo "DISTRO = \"freertos\"" >> ./conf/local.conf
bitbake freertos-demo

最后使用 QEMU 模拟器来运行生成的镜像:

runqemu nographic

启动后的结果如下图所示,在 FreeRTOS 系统下运行了发送和接收任务:

../../_images/freertos_run.png

RISC-V 64 架构的 FreeRTOS Layer 运行结果

至此,完成了基于RISC-V 64 的 FreeRTOS Layer 的定制,为后续集成 TensorFlow Lite 功能提供了基础。