Undefined reference to xTaskCreate() error when using FreeRTOS with RISC-V environment

I’m using PlatformIO to run a FreeRTOS code on the Nexys RISC-V framework. Shown below is the platformio.ini file:

[env:swervolf_nexys]
platform = chipsalliance
board = swervolf_nexys
framework = wd-riscv-sdk, freertos
debug_tool = whisper

Here’s my program:

#include "FreeRTOS.h"

#include "task.h"

#include <bsp_printf.h>

#include <bsp_mem_map.h>

#include <bsp_version.h>

void vTask1(void *pvParameters);

void vTask2(void *pvParameters);

int main(void)

{

    

    xTaskCreate(&vTask1, "Task 1", 1024, NULL, 1, NULL);

    xTaskCreate(&vTask2, "Task 2", 1024, NULL, 1, NULL);

    vTaskStartScheduler();

    return 0;

}

void vTask1(void *pvParameters)

{

    for (;;)

    {

        printfNexys("Task 1\r\n");

        vTaskDelay(pdMS_TO_TICKS(1000));

    }

    vTaskDelete(NULL);

}

void vTask2(void *pvParameters)

{

    for (;;)

    {

        printfNexys("Task 2\r\n");

        vTaskDelay(pdMS_TO_TICKS(1000));

    }

    vTaskDelete(NULL);

}

I’m getting the below error:
task_code.c:14: undefined reference to `xTaskCreate’

Please help me resolve this issue. The demo present for FreeRTOS seemed to be working fine. However, when I trie dto compile my own code, error was observed.

First of all: One is not supposed to use the FreeRTOS functions like xTaskCreate directly. The SDK has the RTOSAL (RTOS-abstraction-layer) which provides you with functions like

under the hood, they call the right function for the selected underlying RTOS (FreeRTOS, ThreadX, etc.)

So you should really be using rtosalTaskCreate instead of xTaskCreate(). All other direct FreeRTOS calls should also be replaced by the rtosal calls.

Code:


#include "common_types.h"
#include "rtosal_task_api.h"
#include "rtosal_time_api.h"
#include <bsp_printf.h>
#include <bsp_mem_map.h>
#include <bsp_version.h>

void vTask1(void *pvParameters);
void vTask2(void *pvParameters);

static rtosalTask_t stTask1;
static rtosalTask_t stTask2;
static rtosalStackType_t uiRxTaskStackBuffer1[1024];
static rtosalStackType_t uiRxTaskStackBuffer2[1024];
void create_tasks();

int main(void)
{
    //will call into create_tasks(NULL) and then vTaskStartScheduler()
    rtosalStart(create_tasks);
    return 0;
}

void create_tasks(void* params) {
    /* Disable the timer interrupts until setup is done. */
    pspDisableInterruptNumberMachineLevel(D_PSP_INTERRUPTS_MACHINE_TIMER);
    rtosalTaskCreate(&stTask1, (s08_t*)"Task 1", E_RTOSAL_PRIO_29, &vTask1, 0, 1024, uiRxTaskStackBuffer1, 0, D_RTOSAL_AUTO_START, 0);
    rtosalTaskCreate(&stTask2, (s08_t*)"Task 2", E_RTOSAL_PRIO_30, &vTask2, 0, 1024, uiRxTaskStackBuffer2, 0, D_RTOSAL_AUTO_START, 0);
    u32_t uiTimerPeriod = 0;
    #if (0 == D_CLOCK_RATE) || (0 == D_TICK_TIME_MS)
    #error "Core frequency values definitions are missing"
    #endif
    uiTimerPeriod = (D_CLOCK_RATE * D_TICK_TIME_MS / D_PSP_MSEC);
    /* Store calculated timerPeriod for future use */
    rtosalTimerSetPeriod(uiTimerPeriod);
}

void vTask1(void *pvParameters)
{

    for (;;)
    {
        printfNexys("Task 1\r\n");
        rtosalTaskSleep(1000 / D_TICK_TIME_MS);
    }
    rtosalTaskDestroy(&stTask1);
}

void vTask2(void *pvParameters)
{
    for (;;)
    {
        printfNexys("Task 2\r\n");
        rtosalTaskSleep(1000 / D_TICK_TIME_MS);
    }
    rtosalTaskDestroy(&stTask2);
}

with demo_platform_al.h at least having

#include "psp_api.h"

#ifdef D_HI_FIVE1
   #include "encoding.h"
   #include "platform.h"
#endif


#ifdef D_HI_FIVE1
   void demoOutputMsg(const void *pStr, u32_t uiSize);
#elif defined(D_SWERV_EH1) || defined(D_SWERV_EH2) || defined(D_SWERV_EL2)
   #include "bsp_printf.h"
   #define demoOutputMsg(f_,...)  printfNexys((f_), ##__VA_ARGS__)
#else
   #define demoOutputMsg(f_,...)
#endif

And lastly: Due to the FreeRTOS configuration that is done in the SDK

The function xTaskCreate is not available since it dynamically allocates the stack memory of the task. That means you can use xTaskCreateStatic() as in

#include "FreeRTOS.h"
#include "task.h"
#include <bsp_printf.h>
#include <bsp_mem_map.h>
#include <bsp_version.h>

void vTask1(void *pvParameters);
void vTask2(void *pvParameters);

StackType_t vTask1Stack[1024];
StackType_t vTask2Stack[1024];
StaticTask_t xTaskBufferTask1;
StaticTask_t xTaskBufferTask2;

int main(void)
{
    xTaskCreateStatic(&vTask1, "Task 1", 1024, NULL, 1, vTask1Stack, &xTaskBufferTask1);
    xTaskCreateStatic(&vTask2, "Task 2", 1024, NULL, 1, vTask2Stack, &xTaskBufferTask2);
    vTaskStartScheduler();
    return 0;
}

void vTask1(void *pvParameters)
{

    for (;;)
    {
        printfNexys("Task 1\r\n");
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
    vTaskDelete(NULL);
}

void vTask2(void *pvParameters)
{
    for (;;)
    {
        printfNexys("Task 2\r\n");
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
    vTaskDelete(NULL);
}

However, above is untested and might not work because it does not do the correct interrupt handler initialization as shown in the original implementation as well the timer period calculation per demo code.

I have tried to active the dynamic allocation support (build_flags = -DconfigSUPPORT_DYNAMIC_ALLOCATION=1 in the platformio.ini plus changing the above config file), but that one gives just errors about the heap functions not being available (from e.g. heap_3.c)

c:/users/max/.platformio/packages/toolchain-riscv/bin/../lib/gcc/riscv64-unknown-elf/8.3.0/../../../../riscv64-unknown-elf/bin/ld.exe: .pio\build\swervolf_nexys\libFreeRTOS.a(tasks.o): in function `xTaskCreate':     
tasks.c:(.text.xTaskCreate+0x1a): undefined reference to `pvPortMalloc'
c:/users/max/.platformio/packages/toolchain-riscv/bin/../lib/gcc/riscv64-unknown-elf/8.3.0/../../../../riscv64-unknown-elf/bin/ld.exe: tasks.c:(.text.xTaskCreate+0x2a): undefined reference to `pvPortMalloc'
c:/users/max/.platformio/packages/toolchain-riscv/bin/../lib/gcc/riscv64-unknown-elf/8.3.0/../../../../riscv64-unknown-elf/bin/ld.exe: tasks.c:(.text.xTaskCreate+0x62): undefined reference to `vPortFree'

And that would need more additions to work. I wouldn’t recommend changing the SDK files, just use what the SDK provides you.