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…

Erich Styger
5 years ago

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:

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
"${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 Application

I 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:

Limitations

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.
Troubleshooting
  • 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 */
Error: Could not find target in FreeRTOS compatibility list
Summary

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

Originally published at mcuoneclipse.com on March 16, 2019.

Latest articles
Sponsored articles
Related articles
Latest articles
Read more
Related articles