FreeRTOS 的 TensorFlow Lite 移植

根据 FreeRTOS 特性,移植的 TensorFlow Lite 是适用于微控制器的 TensorFlow Lite ,即 TensorFlow Lite for Microcontrollers (TFLM)。它专门用于在微控制器和其他只有几千字节内存的设备上运行机器学习模型。核心运行时可以放入 Arm Cortex M3 上 16 KB 的存储空间中,并且可以运行许多基本模型。它不需要操作系统支持、任何标准 C/C++ 库或动态内存分配。

构建并生成支持 RISC-V 64 的 TFLite 静态库

  1. 获取 TFLM

TFLM 是 TensorFlow Lite的一部分,旨在在 DSP、微控制器和其他内存有限的设备上运行机器学习模型。首先需要从官方获取 TFLM 存储库并创建一个本地分支:

sudo git clone https://github.com/tensorflow/tflite-micro.git
cd tflite-micro
sudo git checkout -b freertos remotes/origin/HEAD
  1. 构建支持 RISC-V 64 架构的编译文件

目前 TFLM 仅提供了支持 RISC-V 32 的构建目标,需要在其基础上进行 RISC-V 64 架构的适配。进入 tensorflow/lite/micro/ ,参考 riscv32_mcu 创建支持 RISC-V 64 的 riscv64_mcu 文件及内容:

cd tensorflow/lite/micro/
sudo cp riscv32_mcu/ riscv64_mcu/ -rf

进入 tensorflow/lite/micro/tools/make/targets ,修改 riscv32_mcu_makefile.inc 并创建支持 RISC-V 64 的 riscv64_mcu_makefile.inc

cd tensorflow/lite/micro/tools/make/targets
sudo cp riscv32_mcu_makefile.inc/ riscv64_mcu_makefile.inc/

其中 riscv32_mcu_makefile.inc 修改的 git diff 输出如下:

diff --git a/tensorflow/lite/micro/tools/make/targets/riscv32_mcu_makefile.inc b/tensorflow/lite/micro/tools/make/targets/riscv32_mcu_makefile.inc
index b59065c..51aa5e0 100644
--- a/tensorflow/lite/micro/tools/make/targets/riscv32_mcu_makefile.inc
+++ b/tensorflow/lite/micro/tools/make/targets/riscv32_mcu_makefile.inc
@@ -1,5 +1,5 @@
-# Settings for RISCV 32-bit MCU toolchain.
-TARGET_ARCH := riscv32_mcu
+# Settings for RISCV 64-bit MCU toolchain.
+TARGET_ARCH := riscv64_mcu
 TARGET_TOOLCHAIN_PREFIX := riscv64-unknown-elf-

  export PATH := $(MAKEFILE_DIR)/downloads/riscv_toolchain/bin/:$(PATH)
@@ -17,8 +17,8 @@ ifneq ($(DOWNLOAD_RESULT), SUCCESS)
  endif

PLATFORM_FLAGS = \
-  -march=rv32imac \
-  -mabi=ilp32 \
+  -march=rv64imafdc \
+  -mabi=lp64d \
   -mcmodel=medany \
   -mexplicit-relocs \
   -fno-builtin-printf \
@@ -55,7 +55,7 @@ MICRO_FE310_LIBWRAP_SRCS := \
    $(MAKEFILE_DIR)/downloads/sifive_fe310_lib/bsp/libwrap/stdlib/malloc.c

 MICROLITE_CC_SRCS += \
-  $(wildcard tensorflow/lite/micro/riscv32_mcu/*.cc) \
+  $(wildcard tensorflow/lite/micro/riscv64_mcu/*.cc) \
   $(MICRO_FE310_BSP_ENV_SRCS) \
   $(MICRO_FE310_LIBWRAP_SRCS)

@@ -112,13 +112,13 @@ EXCLUDED_EXAMPLE_TESTS := \

 MICRO_LITE_EXAMPLE_TESTS := $(filter-out $(EXCLUDED_EXAMPLE_TESTS), $(MICRO_LITE_EXAMPLE_TESTS))

-TEST_SCRIPT := tensorflow/lite/micro/testing/test_with_renode.sh
+#TEST_SCRIPT := tensorflow/lite/micro/testing/test_with_renode.sh

 # We are setting this variable to non-zero to allow us to have a custom
 # implementation of `make test` for our target
 TARGET_SPECIFIC_MAKE_TEST := 1

-TEST_TARGET_BINARIES = $(shell ls -1 $(BINDIR)/*_test)
+#TEST_TARGET_BINARIES = $(shell ls -1 $(BINDIR)/*_test)

-test: build
-       $(TEST_SCRIPT) "$(TEST_TARGET_BINARIES)" $(TEST_PASS_STRING) $(TARGET)
+#test: build
+#      $(TEST_SCRIPT) "$(TEST_TARGET_BINARIES)" $(TEST_PASS_STRING) $(TARGET)

针对 RISC-V 64 架构,修改了对应的编译选项,由于 TFLM 自带了 renode 模拟器环境,在生成相关的 C++ 库文件后会进行 RISC-V 32 环境的测试仿真,在此暂时屏蔽 renode 相关的脚本。根据上述 git diff 生成补丁文件 0001-ADD-RISCV64-FreeRTOS-SUPPORT.patch,为后续创建 TFLM 的 recipe 提供支持。

  1. 生成支持RISC-V 64架构的编译目标

在完成上述构建后,进入 tflite-micro 目录,执行以下指令下载所需的依赖文件并生成 TFLM 静态库:

sudo make -f tensorflow/lite/micro/tools/make/Makefile TARGET=riscv64_mcu

注意

若编译时出现 ModuleNotFoundError:No module named 'PIL' Python第三方库PIL(Python Image Library) 报错时,可执行指令 sudo pip install pillow 进行安装,指令前需要加上 sudo ,否则PIL将会被安装到非默认版本Python路径下。

执行完在 tensorflow/lite/micro/tools/make/ 目录下生成了 downloadsgen 文件,其中 downloads 包含了构建 RISC-V 64 架构目标所需的依赖库和编译工具链,为了创建后续 TFLM 的 recipe ,将其打包压缩成 .tar.xz 格式:

sudo tar cvf downloads.tar downloads
sudo xz  -z  downloads.tar

gen 下生成了 riscv64_mcu_x86_64_default 文件,它包含了支持 RISC-V 64 架构的静态库、模型转化的 .cc 文件、支持 TFLM 功能的目标文件。

至此,根据修改生成了补丁文件 0001-ADD-RISCV64-FreeRTOS-SUPPORT.patch 和依赖包 downloads.tar.gz ,完成了支持 RISC-V 64 的 TFLM 目标的构建。

创建 TFLM recipe

为了在我们移植好的 FreeRTOS 系统下运行 TensorFlow Lite ,我们在之前创建的 meta-freertos-riscv Layer 下创建 recipes-tflite-micro 及其子目录:

cd poky/meta-freertos-riscv/
sudo mkdir -p recipes-tflite-micro/tflite-demo/files/

将上述生成的补丁文件 0001-ADD-RISCV64-FreeRTOS-SUPPORT.patch 和依赖包 downloads.tar.gz 放在 recipes-tflite-micro/tflite-demo/files/ 目录下,作为构建 TFLM 的本地文件。在 recipes-tflite-micro/tflite-demo/ 文件下创建 TFLM recipe tflite-demo_git.bb

 DESCRIPTION = "TensorFlow Lite Micro "
 LICENSE = "Apache-2.0"

 LIC_FILES_CHKSUM = "file://LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57"

 SRC_URI = " git://github.com/tensorflow/tflite-micro.git;branch=main;protocol=https \
          file://0001-ADD-RISCV64-FreeRTOS-SUPPORT.patch \
 "

 SRC_URI += "file://downloads.tar.xz \
 "

 SRCREV = "60d83223d39fa8660287766edb4533a292f59eb8"

 S = "${WORKDIR}/git"
 MakeDir   = "${S}/tensorflow/lite/micro/tools/make"
 DependDir = "${WORKDIR}/downloads"
 GenDir    = "${MakeDir}/gen/riscv64_mcu_x86_64_default"
 TFLite_HELLO_WORLD_MODEL_DIR = "${GenDir}/genfiles/tensorflow/lite/micro/examples/hello_world"
 TFLite_HELLO_WORLD_DEMO_DIR  = "${S}/tensorflow/lite/micro/examples/hello_world"

do_compile(){
   cp -r "${DependDir}" "${MakeDir}"
   cd ${S}
   make -f tensorflow/lite/micro/tools/make/Makefile TARGET=riscv64_mcu
 }

 do_install(){
   install -m 644 ${TFLite_HELLO_WORLD_MODEL_DIR}/*.cc ${TFLite_HELLO_WORLD_DEMO_DIR}
   install -m 644 ${TFLite_HELLO_WORLD_MODEL_DIR}/*.h  ${TFLite_HELLO_WORLD_DEMO_DIR}
 }

由于计划验证的是 TFLM 提供的 hello_world 用例,所以 tflite-demo_git.bb 除了编译生成目标 TensorFlow Lite 库,还将生成的模型数据 hello_world_model_data.cchello_world_model_data.h 放入用例 hello_world 文件下供程序调用。

最终, recipes-tflite-micro 的目录结构如下图所示:

../../_images/recipes-tflite-micro.png

recipes-tflite-micro 目录结构

移植 FreeRTOS

之前已经适配了官方的 FreeRTOS ,目前根据补丁文件 0001-modify-Makefile-to-adapt-riscv64.patch 可以实现在 RISC-V 64 架构的 QEMU 上运行官方 FreeRTOS 提供的 Demo RISC-V-Qemu-virt_GCC ,为了实现在 FreeRTOS 上移植 TensorFlow Lite 功能,以 Demo RISC-V-Qemu-virt_GCC 为基础,添加 TFLM 验证用例 hello_world ,重新制作补丁文件 0001-modify-Makefile-to-adapt-riscv64.patch

由于 TFLM 提供的是 C++ 库,而 RISC-V-Qemu-virt_GCC 内的功能实现是基于 C 语言的,所以若想调用 TFLM 功能,就必须进行 C++ 和 C 语言的混合编程。在使用混合编程时将 main.c 改为 main.cc 便于对 TFLM 功能函数的调用, C++ 对于 C 语言的调用使用 extern “C” 语法, main.c 修改后的 git diff 如下:

diff --git a/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/main.c b/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/main.c
old mode 100644
new mode 100755
index 5f867c2f7..8289236bf
--- a/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/main.c
+++ b/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/main.c
@@ -26,18 +26,29 @@
*/

/* FreeRTOS kernel includes. */
+
+#include <stdio.h>
+
+extern "C" {
#include <FreeRTOS.h>
+#include "riscv-virt.h"
+#include "ns16550.h"
#include <task.h>
+#include <queue.h>
+}
+
+#include "main_functions.h"
+extern "C"{ void * __dso_handle = 0 ;}

/* Run a simple demo just prints 'Blink' */
#define DEMO_BLINKY    1

-void vApplicationMallocFailedHook( void );
-void vApplicationIdleHook( void );
-void vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName );
-void vApplicationTickHook( void );
+extern "C" void vApplicationMallocFailedHook( void );
+extern "C" void vApplicationIdleHook( void );
+extern "C" void vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName );
+extern "C" void vApplicationTickHook( void );

-int main_blinky( void );
+extern "C" int main_blinky( void );

进入 FreeRTOS/Demo/RISC-V-Qemu-virt_GCC 目录:

sudo mv main.c main.cc

由于 main 函数内主要执行的是 main_blinky() 函数,因此还需要修改 main_blinky.c ,在 main_blinky.c 中创建并启动了发送和接收任务,将 hello_world 功能函数放在发送任务中执行,更改后的 git diff 示例如下:

diff --git a/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/main_blinky.c b/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/main_blinky.c
index 67ff9e47b..2c03d67e8 100644
--- a/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/main_blinky.c
+++ b/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/main_blinky.c
@@ -34,6 +34,7 @@

#include "riscv-virt.h"
#include "ns16550.h"
+#include "main_functions.h"

/* Priorities used by the tasks. */
#define mainQUEUE_RECEIVE_TASK_PRIORITY                ( tskIDLE_PRIORITY + 2 )
@@ -65,7 +66,9 @@ const unsigned long ulValueToSend = 100UL;
const char * const pcMessage1 = "Transfer1";
const char * const pcMessage2 = "Transfer2";
int f = 1;
-
+int count =0 ;
+       vSendString( "TensorFlow Lite Start test!\n" );
+       setup();
        /* Remove compiler warning about unused parameter. */
        ( void ) pvParameters;

@@ -82,6 +85,15 @@ int f = 1;
                vSendString( buf );
                f = !f;

+               if (count < 50)
+               {
+                       loop(count);
+                       count++;
+               }
+               else
+               {
+                       count = 0;
+               }
                /* Place this task in the blocked state until it is time to run again. */
                vTaskDelayUntil( &xNextWakeTime, mainQUEUE_SEND_FREQUENCY_MS );

@@ -125,7 +137,6 @@ int f = 1;
                                        ( f ) ? pcMessage1 : pcMessage2 );
                        vSendString( buf );
                        f = !f;
-
                        ulReceivedValue = 0U;
                }
                else

其中 setup()loop() 功能函数是定义在 TFLM 工程 tensorflow/lite/micro/examples/hello_world 路径下的 main_functions.cc 文件中,修改 main_functions.ccmain_functions.h 并把它们放在FreeRTOS工程 FreeRTOS/Demo/RISC-V-Qemu-virt_GCC 目录下,其中 main_functions.cc 修改后的git diff如下:

diff --git a/tensorflow/lite/micro/examples/hello_world/main_functions.cc b/tensorflow/lite/micro/examples/hello_world/main_functions.cc
old mode 100644
new mode 100755
index 3cc66e5..29e5054
--- a/tensorflow/lite/micro/examples/hello_world/main_functions.cc
+++ b/tensorflow/lite/micro/examples/hello_world/main_functions.cc
@@ -13,8 +13,7 @@ See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

-#include "tensorflow/lite/micro/examples/hello_world/main_functions.h"
-
+#include "main_functions.h"
#include "tensorflow/lite/micro/all_ops_resolver.h"
#include "tensorflow/lite/micro/examples/hello_world/constants.h"
#include "tensorflow/lite/micro/examples/hello_world/hello_world_model_data.h"
@@ -23,7 +22,12 @@ limitations under the License.
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/micro/system_setup.h"
#include "tensorflow/lite/schema/schema_generated.h"
+#include <math.h>
+#include <stdio.h>

+extern "C" {
+#include "riscv-virt.h"
+}
// Globals, used for compatibility with Arduino-style sketches.
namespace {
tflite::ErrorReporter* error_reporter = nullptr;
@@ -83,12 +87,14 @@ void setup() {
}

// The name of this function is important for Arduino compatibility.
-void loop() {
+void loop(int count) {
// Calculate an x value to feed into the model. We compare the current
// inference_count to the number of inferences per cycle to determine
// our position within the range of possible x values the model was
// trained on, and use this to calculate a value.
-  float position = static_cast<float>(inference_count) /
+  char buf[40];
+
+  float position = static_cast<float>(count) /
                    static_cast<float>(kInferencesPerCycle);
float x = position * kXrange;

@@ -110,12 +116,20 @@ void loop() {
// Dequantize the output from integer to floating-point
float y = (y_quantized - output->params.zero_point) * output->params.scale;

+  //add freertos output
+  int postx = static_cast< int >(x*10000000);
+  int posty = static_cast< int >(y*10000000);
+
+  sprintf(buf, "count:%d x:%d sinx:%d",count,postx,posty);
+  vSendString( buf );
+
+  memset(buf,0,sizeof(buf));
// Output the results. A custom HandleOutput function can be implemented
// for each supported hardware target.
-  HandleOutput(error_reporter, x, y);
+  // HandleOutput(error_reporter, x, y);

// Increment the inference_counter, and reset it if we have reached
// the total number per cycle
-  inference_count += 1;
-  if (inference_count >= kInferencesPerCycle) inference_count = 0;
+  // inference_count += 1;
+  // if (inference_count >= kInferencesPerCycle) inference_count = 0;
}

同样,main_functions.h 修改的git diff如下:

diff --git a/tensorflow/lite/micro/examples/hello_world/main_functions.h b/tensorflow/lite/micro/examples/hello_world/main_functions.h
old mode 100644
new mode 100755
index a1ea715..7d30684
--- a/tensorflow/lite/micro/examples/hello_world/main_functions.h
+++ b/tensorflow/lite/micro/examples/hello_world/main_functions.h
@@ -13,8 +13,8 @@ See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

-#ifndef TENSORFLOW_LITE_MICRO_EXAMPLES_HELLO_WORLD_MAIN_FUNCTIONS_H_
-#define TENSORFLOW_LITE_MICRO_EXAMPLES_HELLO_WORLD_MAIN_FUNCTIONS_H_
+#ifndef MAIN_FUNCTIONS_H_
+#define MAIN_FUNCTIONS_H_

// Expose a C friendly interface for main functions.
#ifdef __cplusplus
@@ -28,7 +28,7 @@ void setup();
// Runs one iteration of data gathering and inference. This should be called
// repeatedly from the application code. The name needs to be loop() for Arduino
// compatibility.
-void loop();
+void loop(int count);

#ifdef __cplusplus
}

FreeRTOS/Demo 新建文件 RISC-V-RV64-TFLite_Micro ,用于存放编译构建好的 TFLM 工程。

最后引用 TFLM 生成的静态库 libtensorflow-microlite.a 以及使用 RISC-V 64 编译工具链编译 C++ 文件生成可执行文件,修改 Makefile 文件, git diff 如下:

diff --git a/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/Makefile b/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/Makefile
old mode 100644
new mode 100755
index 4d806bf49..9cde551b7
--- a/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/Makefile
+++ b/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/Makefile
@@ -1,7 +1,8 @@
-CROSS   = riscv64-unknown-elf-
+CROSS   ?= /opt/riscv-new/bin/riscv64-unknown-elf-
CC      = $(CROSS)gcc
OBJCOPY = $(CROSS)objcopy
ARCH    = $(CROSS)ar
+CXX     = $(CROSS)g++

BUILD_DIR       = build
RTOS_SOURCE_DIR = $(abspath ../../Source)
@@ -14,13 +15,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
@@ -31,7 +32,31 @@ else
    CFLAGS += -O2
endif

-SRCS = main.c main_blinky.c riscv-virt.c ns16550.c \
+TFLITE_SOURCE_DIR = $(abspath ../RISC-V-RV64-TFLite_Micro)
+MAKEFILE_DIR := $(TFLITE_SOURCE_DIR)/tensorflow/lite/micro/tools/make
+HELLO_WORLD_DIR := $(TFLITE_SOURCE_DIR)/tensorflow/lite/micro/examples/hello_world
+GENDIR := $(MAKEFILE_DIR)/gen/riscv64_mcu_x86_64_default/
+LIBDIR := $(GENDIR)lib
+MICROLITE_LIB_NAME := libtensorflow-microlite.a
+MICROLITE_LIB_PATH := $(LIBDIR)/$(MICROLITE_LIB_NAME)
+MICROLITE_LIBS := -lm
+
+INCLUDES := \
+-I. \
+-I $(TFLITE_SOURCE_DIR) \
+-I $(MAKEFILE_DIR)/downloads/gemmlowp \
+-I $(MAKEFILE_DIR)/downloads/flatbuffers/include \
+-I $(MAKEFILE_DIR)/downloads/ruy
+
+CXXFLAGS := -O3 -DNDEBUG
+CXXFLAGS += --std=c++11 -g -DTF_LITE_STATIC_MEMORY
+CCFLAGS := -DNDEBUG -g -DTF_LITE_STATIC_MEMORY
+SRCXX = main.cc main_functions.cc \
+       $(HELLO_WORLD_DIR)/hello_world_model_data.cc \
+       $(HELLO_WORLD_DIR)/constants.cc \
+       $(HELLO_WORLD_DIR)/output_handler.cc
+
+SRCS = main_blinky.c riscv-virt.c ns16550.c \
        $(DEMO_SOURCE_DIR)/EventGroupsDemo.c \
        $(DEMO_SOURCE_DIR)/TaskNotify.c \
        $(DEMO_SOURCE_DIR)/TimerDemo.c \
@@ -50,12 +75,21 @@ SRCS = main.c main_blinky.c riscv-virt.c ns16550.c \
ASMS = start.S \
        $(RTOS_SOURCE_DIR)/portable/GCC/RISC-V/portASM.S

-OBJS = $(SRCS:%.c=$(BUILD_DIR)/%.o) $(ASMS:%.S=$(BUILD_DIR)/%.o)
-DEPS = $(SRCS:%.c=$(BUILD_DIR)/%.d) $(ASMS:%.S=$(BUILD_DIR)/%.d)
+OBJS = $(SRCS:%.c=$(BUILD_DIR)/%.o) $(ASMS:%.S=$(BUILD_DIR)/%.o) $(SRCXX:%.cc=$(BUILD_DIR)/%.o)
+DEPS = $(SRCS:%.c=$(BUILD_DIR)/%.d) $(ASMS:%.S=$(BUILD_DIR)/%.d) $(SRCXX:%.cc=$(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) $(MICROLITE_LIB_PATH) fake_rom.lds Makefile
+       $(CXX) $(CXXFLAGS) $(CFLAGS) $(LDFLAGS) $(OBJS) $(INCLUDES) $(CPPFLAGS) $(MICROLITE_LIB_PATH) -o $@
+
+$(BUILD_DIR)/%.o: %.cc Makefile
+       @mkdir -p $(@D)
+       $(CXX) $(CXXFLAGS) $(CFLAGS) $(INCLUDES) $(CPPFLAGS) -MMD -MP -c $< -o $@
$(BUILD_DIR)/%.o: %.c Makefile
        @mkdir -p $(@D)
        $(CC) $(CPPFLAGS) $(CFLAGS) -MMD -MP -c $< -o $@

至此,在 FreeRTOS 下引用 TFLM C++ 静态库,并测试其 hello_world 用例的修改基本完成,根据 git diff 重新生成补丁文件 0001-modify-Makefile-to-adapt-riscv64.patch

适配 meta-freertos-riscv

为了引入和支持 TFLM 库,需要对定制的 meta-freertos-riscv Layer 进行修改,前面已经创建了 TFLM recipe ,现在首先更新 recipes-freertos/freertos-demo/files/ 目录下的补丁文件 0001-ADD-RISCV64-FreeRTOS-SUPPORT.patch 。然后修改 classes 文件下的 freertos-image.bbclass , git diff 输出如下:

diff --git a/classes/freertos-image.bbclass b/classes/freertos-image.bbclass
index ead30f2..6e9efce 100644
--- a/classes/freertos-image.bbclass
+++ b/classes/freertos-image.bbclass
@@ -39,6 +39,7 @@ IMAGE_BASENAME = "freertos-image"
BAREMETAL_BINNAME ?= "${IMAGE_BASENAME}"
IMAGE_LINK_NAME ?= "${IMAGE_BASENAME}-${MACHINE}"

+TFLITE_RV64_MICRO_DIR = "${TMPDIR}/work/riscv64-oe-elf/tflite-demo/git-r0/git/*"

# QEMU crashes when FreeRTOS is built with optimizations, disable those for now
CFLAGS:remove = "-O2"
@@ -57,7 +58,11 @@ cmake_do_configure(){
-Wno-dev
}

+do_compile[depends] = "tflite-demo:do_compile"
+do_compile[depends] = "tflite-demo:do_install"
+
do_compile(){
+  cp ${TFLITE_RV64_MICRO_DIR} ${FREERTOS_SRC}/FreeRTOS/Demo/RISC-V-RV64-TFLite_Micro -rf
cd ${FREERTOS_SRC}
make -C ./FreeRTOS/Demo/RISC-V-Qemu-virt_GCC
}

在此主要是将 recipe tflite-demo_git.bb 获取、解压、配置软件源码后的 TFLM 工程安装的 RISC-V-RV64-TFLite_Micro 目录下。

测试验证 hello_world

build_portal环境搭建及使用

最后启动 QEMU 模拟器运行:

假设编译路径为 : ~/build_portal

export PATH=~/build_portal/poky/scripts:~/build_portal/poky/bitbake/bin:$PATH

cd  ~/build_portal/build

runqemu nographic

启动后的结果如下图所示,该用例通过加载解析定义在 tensorflow/lite/micro/examples/hello_world/hello_world_model_data.cc 内的字符数组实例化模型,成功运行模型后获得输出,其中输入为 x ,输出为模型运算后得到的sinx值,为了便于展示,它们均被放大了 10000000 倍。

../../_images/tflite_result.png

FreeRTOS 运行 hello_world 结果

至此,已完成移植 TensorFlow Lite 到基于RISC-V 64 架构下的 FreeRTOS 。