Running FreeRTOS on the VEGA RISC-V Board
In “Debugging the RV32M1-VEGA RISC-V with Eclipse and MCUXpresso IDE” I described how to build and debug applications for the VEGA RISC-V…
In “Debugging the RV32M1-VEGA RISC-V with Eclipse and MCUXpresso IDE,” I described how to build and debug applications for the VEGA RISC-V board. In this article, I describe how to enable FreeRTOS for RISC-V, based on the latest FreeRTOS V10.2.0 release.
The latest FreeRTOS V10.2.0 release comes with basic support for the RISC-V ISA. This post describes how to add FreeRTOS to a VEGA SDK application and run it with the NXP MCUXpresso IDE or any other Eclipse IDE using the GNU MCU Eclipse plugins:
Here is what you need:
- The VEGA RISC-V board with MCUXpresso IDE (see Debugging the RV32M1-VEGA RISC-V with Eclipse and MCUXpresso IDE).
- McuLib and FreeRTOS port from GitHub.
Note: I have published my project on GitHub.
Below are the steps if you want to enable an existing VEGA project with FreeRTOS:
- Create a project for the VEGA RISC-V (RI5CY) in MCUXpresso IDE, or use an existing project from the VEGA SDK for the RISC-V
- Add the following includes to the compiler includes for the library:
../McuLib/config ../McuLib/config/fonts ../McuLib/fonts ../McuLib/src ../McuLib/FreeRTOS/Source/include ../McuLib/FreeRTOS/Source/portable/GCC/RISC-V ../McuLib/SEGGER_RTT ../McuLib/SEGGER_Sysview ../McuLib/TraceRecorder/config ../McuLib/TraceRecorder/include ../McuLib/TraceRecorder/streamports/Jlink_RTT/include ../McuLib/HD44780
- Add a global include with -include (see “Different Ways of Software Configuration”):
"${ProjDirPath}/src/IncludeMcuLibConfig.h"
In that header file used with -include, specify to use FreeRTOS with RISC-V, together with the SDK used:
#define McuLib_CONFIG_SDK_VERSION_USED McuLib_CONFIG_SDK_MCUXPRESSO_2_0 #define McuLib_CONFIG_CPU_IS_ARM_CORTEX_M (0) #define McuLib_CONFIG_CPU_IS_RISC_V (1) #define McuLib_CONFIG_SDK_USE_FREERTOS (1) #define McuLib_CONFIG_SDK_VERSION_MAJOR (2) #define McuLib_CONFIG_SDK_VERSION_MINOR (2) #define McuLib_CONFIG_SDK_VERSION_BUILD (0)
In that same file the pins for the LEDs on the VEGA board can be configured too:
/* red LED */ #define McuLED1_CONFIG_IS_LOW_ACTIVE (0) #define LEDpin1_CONFIG_GPIO_NAME GPIOA #define LEDpin1_CONFIG_PORT_NAME PORTA #define LEDpin1_CONFIG_PIN_NUMBER 24u #define LEDpin1_CONFIG_DO_PIN_MUXING 1 /* green LED */ #define McuLED2_CONFIG_IS_LOW_ACTIVE (0) #define LEDpin2_CONFIG_GPIO_NAME GPIOA #define LEDpin2_CONFIG_PORT_NAME PORTA #define LEDpin2_CONFIG_PIN_NUMBER 23u #define LEDpin2_CONFIG_DO_PIN_MUXING 1 /* blue LED */ #define McuLED3_CONFIG_IS_LOW_ACTIVE (0) #define LEDpin3_CONFIG_GPIO_NAME GPIOA #define LEDpin3_CONFIG_PORT_NAME PORTA #define LEDpin3_CONFIG_PIN_NUMBER 22u #define LEDpin3_CONFIG_DO_PIN_MUXING 1 /* sts LED */ #define McuLED4_CONFIG_IS_LOW_ACTIVE (0) #define LEDpin4_CONFIG_GPIO_NAME GPIOE #define LEDpin4_CONFIG_PORT_NAME PORTE #define LEDpin4_CONFIG_PIN_NUMBER 0u #define LEDpin4_CONFIG_DO_PIN_MUXING 1
Replace the standard startup code with the version specific for FreeRTOS. This version uses freertos_risc_v_trap_handler as the default trap handler. This version is taken from the FreeRTOS distribution for V10.2.0:
/* ------------------------------------------------------------------------- */ /* @file: startup_RV32M1_ri5cy.s */ /* @purpose: RI5CY Core Device Startup File */ /* RV32M1_ri5cy */ /* @version: 1.0 */ /* @date: 2018-10-2 */ /* @build: b180926 */ /* ------------------------------------------------------------------------- */ /* */ /* Copyright 1997-2016 Freescale Semiconductor, Inc. */ /* Copyright 2016-2018 NXP */ /* All rights reserved. */ /* */ /* SPDX-License-Identifier: BSD-3-Clause */ // Copyright 2017 ETH Zurich and University of Bologna. // Copyright and related rights are licensed under the Solderpad Hardware // License, Version 0.51 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law // or agreed to in writing, software, hardware and materials distributed under // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. .extern freertos_risc_v_trap_handler #define EXCEPTION_STACK_SIZE 0x58 .text .section .vectors, "ax" .option norvc; jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler jal x0, freertos_risc_v_trap_handler // reset vector jal x0, Reset_Handler // Illegal instrution exception jal x0, IllegalInstruction_Handler // ecall handler jal x0, freertos_risc_v_trap_handler // LSU error jal x0, LSU_Handler .section .startup /* Reset Handler */ Reset_Handler: # Disable global interrupt. */ csrci mstatus, 8 # initialize stack pointer la sp, __StackTop # initialize global pointer la gp, __global_pointer #ifndef __NO_SYSTEM_INIT jal SystemInit #endif call __libc_init_array # Enable global interrupt. */ csrsi mstatus, 8 jal main ebreak .size Reset_Handler, . - Reset_Handler .global _init .global _fini _init: _fini: ret // saves all caller-saved registers (except return address) store_regs: sw x3, 0x00(x2) // gp sw x4, 0x04(x2) // tp sw x5, 0x08(x2) // t0 sw x6, 0x0c(x2) // t1 sw x7, 0x10(x2) // t2 sw x10, 0x14(x2) // a0 sw x11, 0x18(x2) // a1 sw x12, 0x1c(x2) // a2 sw x13, 0x20(x2) // a3 sw x14, 0x24(x2) // a4 sw x15, 0x28(x2) // a5 sw x16, 0x2c(x2) // a6 sw x17, 0x30(x2) // a7 csrr a0, 0x7B0 csrr a1, 0x7B1 csrr a2, 0x7B2 sw a0, 0x34(x2) // lpstart[0] sw a1, 0x38(x2) // lpend[0] sw a2, 0x3c(x2) // lpcount[0] csrr a0, 0x7B4 csrr a1, 0x7B5 csrr a2, 0x7B6 sw a0, 0x40(x2) // lpstart[1] sw a1, 0x44(x2) // lpend[1] sw a2, 0x48(x2) // lpcount[1] csrr a0, 0x341 sw a0, 0x4c(x2) // mepc csrr a1, 0x300 sw a1, 0x50(x2) // mstatus jalr x0, x1 // load back registers from stack end_except: lw a1, 0x50(x2) // mstatus csrrw x0, 0x300, a1 lw a0, 0x4c(x2) // mepc csrrw x0, 0x341, a0 lw a0, 0x40(x2) // lpstart[1] lw a1, 0x44(x2) // lpend[1] lw a2, 0x48(x2) // lpcount[1] csrrw x0, 0x7B4, a0 csrrw x0, 0x7B5, a1 csrrw x0, 0x7B6, a2 lw a0, 0x34(x2) // lpstart[0] lw a1, 0x38(x2) // lpend[0] lw a2, 0x3c(x2) // lpcount[0] csrrw x0, 0x7B0, a0 csrrw x0, 0x7B1, a1 csrrw x0, 0x7B2, a2 lw x3, 0x00(x2) // gp lw x4, 0x04(x2) // tp lw x5, 0x08(x2) // t0 lw x6, 0x0c(x2) // t1 lw x7, 0x10(x2) // t2 lw x10, 0x14(x2) // a0 lw x11, 0x18(x2) // a1 lw x12, 0x1c(x2) // a2 lw x13, 0x20(x2) // a3 lw x14, 0x24(x2) // a4 lw x15, 0x28(x2) // a5 lw x16, 0x2c(x2) // a6 lw x17, 0x30(x2) // a7 lw x1, 0x54(x2) addi x2, x2, EXCEPTION_STACK_SIZE mret .weak IRQ_Handler .type IRQ_Handler, %function IRQ_Handler: addi x2, x2, -EXCEPTION_STACK_SIZE sw x1, 0x54(x2) jal x1, store_regs la x1, end_except csrr a0, mcause jal x0, SystemIrqHandler .size IRQ_Handler, . - IRQ_Handler .macro define_exception_entry entry_name handler_name .weak \entry_name \entry_name: addi x2, x2, -EXCEPTION_STACK_SIZE sw x1, 0x54(x2) jal x1, store_regs la x1, end_except jal x0, \handler_name .endm define_exception_entry IllegalInstruction_Handler IllegalInstruction_HandlerFunc define_exception_entry Ecall_Handler Ecall_HandlerFunc define_exception_entry LSU_Handler LSU_HandlerFunc .weak IllegalInstruction_HandlerFunc .type IllegalInstruction_HandlerFunc, %function IllegalInstruction_HandlerFunc: j . .size IllegalInstruction_HandlerFunc, . - IllegalInstruction_HandlerFunc .weak Ecall_HandlerFunc .type Ecall_HandlerFunc, %function Ecall_HandlerFunc: j . .size Ecall_HandlerFunc, . - Ecall_HandlerFunc .weak LSU_HandlerFunc .type LSU_HandlerFunc, %function LSU_HandlerFunc: j . .size LSU_HandlerFunc, . - LSU_HandlerFunc
In the linker file, add a symbol to mark the end of the IRQ stack:
__freertos_irq_stack_top = .;
That’s it! With this I can use FreeRTOS with the RISC-V on the VEGA board!
Demo ApplicationI have put on GitHub a simple ‘blinky’ application. Code pasted below:
/* * Application.c * * Author: Erich Styger */ #include "Application.h" #include "McuLib.h" #include "McuWait.h" #include "McuLED1.h" #include "McuLED2.h" #include "McuLED3.h" #include "McuLED4.h" #include "McuRTOS.h" #include "FreeRTOS.h" #include "task.h" static void AppTask(void *pv) { for(;;) { McuLED1_On(); vTaskDelay(pdMS_TO_TICKS(100)); McuLED1_Off(); McuLED2_On(); vTaskDelay(pdMS_TO_TICKS(100)); McuLED2_Off(); McuLED3_On(); vTaskDelay(pdMS_TO_TICKS(100)); McuLED3_Off(); McuLED4_On(); vTaskDelay(pdMS_TO_TICKS(100)); McuLED4_Off(); vTaskDelay(pdMS_TO_TICKS(500)); McuLED4_Neg(); } } void APP_Run(void) { /* initialize McuLib drivers */ McuLib_Init(); McuRTOS_Init(); McuWait_Init(); McuLED1_Init(); /* red */ McuLED2_Init(); /* green */ McuLED3_Init(); /* blue */ McuLED4_Init(); /* red status */ if (xTaskCreate(AppTask, "App", 500/sizeof(StackType_t), NULL, tskIDLE_PRIORITY+1, NULL) != pdPASS) { for(;;){} /* error */ } vTaskStartScheduler(); /* shoul not end up here... */ for(;;) { } }
Below a screenshot with the NXP MCUXpresso IDE 10.3.1 debugging the application on the VEGA board:
The port is still ‘work in progress’:
- I have it running so far only on the RV32M1 RI5CY.
- As tick timer, it uses the LPIT0 timer.
- Tickless Idle mode has not been implemented yet.
- Interrupt nesting is not supported yet.
- SEGGER SystemViewer is not tested yet.
- Percepio Tracealizer is not tested yet.
- Currently I have to use OpenOCD to debug the VEGA board, and OpenOCD is not rather slow (compared to a true J-Link). If debugging fails, try to re-power to board and debug probe. If this does not help, try reboot the host machine helps.
- OpenOCD replaces the Windows USB drivers which obviously affects working with a J-Link in a normal way. Try reverting the USB drivers back to the original ones and reboot your host machine.
- Debugging task code with OpenOCD usually steps into the interrupt service routine: the workaround is to set a breakpoint and run to it.
- FreeRTOS V10.2.0 has changed (again :-( ) the internal data structure, potentially breaking RTOS debug awareness. For example the FreeRTOS timers might not be shown properly in the debugger. As a workaround, I have added a define to FreeRTOS timers.c to support the previous API:
#define TIMER_LEGACY_API (1) /* << EST: needed to have TAD working */
- Proper FreeRTOS thread awareness is not any more possible with OpenOCD (it was possible with earlier OpenOCD versions, see https://mcuoneclipse.com/2016/04/09/freertos-thread-debugging-with-eclipse-and-openocd and https://forums.embarc.org/discussion/122/debugging-freertos-using-openocd If trying to use FreeRTOS awareness, I only get an error message:
Error: Could not find target in FreeRTOS compatibility listSummary
I’m now able to run FreeRTOS V10.2.0 on the VEGA board, using Eclipse (MCUXpresso IDE). The IDE has very useful FreeRTOS debugging views. The biggest Achilles heel is OpenOCD. It works, but it is slow and very limiting when using an RTOS. I might have to explore other options than OpenOCD to make debugging a better experience. I hope I can report on this soon in a next article.
Happy FreeRISC-Ving!
Links- Project used in this article: https://github.com/ErichStyger/mcuoneclipse/tree/master/Examples/MCUXpresso/VEGA/VEGA_Blinky
- Setup IDE and toolchain: Debugging the RV32M1-VEGA RISC-V with Eclipse and MCUXpresso IDE
- FreeRTOS on RISC-V: https://www.freertos.org/Using-FreeRTOS-on-RISC-V.html
- FreeRTOS demo for PULP/VEGA: https://www.freertos.org/RTOS-RISC-V-Vegaboard_Pulp.html
- OpenOCD user’s guide: http://openocd.org/doc/html/GDB-and-OpenOCD.html
- Not able to debug FreeRTOS with OpenOCD: https://forums.embarc.org/discussion/122/debugging-freertos-using-openocd
- FreeRTOS debugging with OpenOCD: https://mcuoneclipse.com/2016/04/09/freertos-
Originally published at mcuoneclipse.com on March 16, 2019.