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 board. In this article I describe how to enable FreeRTOS for RISC-V, based on the latest FreeRTOS V10.2.0 release.

Blinky with FreeRTOS on the VEGA RISC-V Board

Blinky with FreeRTOS on the VEGA RISC-V Board

The latest FreeRTOS V10.2.0 release comes with basic support for the RISC-V ISA. This article 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:

FreeRTOS on VEGA RISC-V Board

FreeRTOS on VEGA RISC-V Board

Here is what you need:

  1. The VEGA RISC-V board with MCUXpresso IDE (see Debugging the RV32M1-VEGA RISC-V with Eclipse and MCUXpresso IDE)
  2. McuLib and FreeRTOS port from GitHub: https://github.com/ErichStyger/McuOnEclipseLibrary

I have published my project on GitHub: https://github.com/ErichStyger/mcuoneclipse/tree/master/Examples/MCUXpresso/VEGA/VEGA_Blinky

Below are the steps if you want to enable an existing VEGA project with FreeRTOS:

  1. 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
  2. Add the McuLib and FreeRTOS Port https://github.com/ErichStyger/McuOnEclipseLibrary

    McuLibrary

    McuLibrary

  3. 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

    McuLib Includes

    McuLib Includes

  4. Add a global include with -include (see “Different Ways of Software Configuration“):
    "${ProjDirPath}/src/IncludeMcuLibConfig.h"

    -include setting

    -include setting

  5. 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)
  6. 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
  7. 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
  8. In the linker file, add a symbol to mark the end of the IRQ stack:
    __freertos_irq_stack_top = .;

    __freertos_irq_stack_top

    __freertos_irq_stack_top

  9. 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:

Debugging RISC-V FreeRTOS Applicaiton with MCUXpresso IDE on

Debugging RISC-V FreeRTOS Applicaiton with MCUXpresso IDE

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.
  • SEGGER SystemViewer is not tested yet
  • Percepio Tracealizer is not tested yet

Trouble Shooting

  • 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 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

Advertisements

1 thought on “Running FreeRTOS on the VEGA RISC-V Board

  1. You’re seriously blaming OpenOCD for Windows being crap and requiring a reboot? Come on, you should know better than that.

    Like

What do you think?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.