Delay problem

I’m working on RTG4 FPGA which uses a RISCV processor and trying to operate it through a freeRTOS. but each time i’m suffering from delay related issues like in my case

configTICK_RATE_HZ is 1000Hz;

vTaskDelay(1000);

means i have to got a delay of 1000ms (1 sec) by calculation. but in my case i got a delay of 100sec. i had tried

pdMS_TO_TICKS(1000);

but in this case also i got a delay of 100sec instead of 1 sec.

means each time i got a delay with multiply by 100 with it’s original value.( e.g. 1000ms *100 = 100sec.)

i had try all the things to solve this problem but didn’t got any right solution. please provide me the correct way.

Two things:

  • did you set configCPU_CLOCK_HZ correctly? That is the speed of the clock that feeds the timer, which runs the system clock.

  • When your port.c is being compiled, are you sure that configTICK_RATE_HZ has a value of 1000? Maybe you check this with a #if statement?

yes. i had set configCPU_CLOCK_HZ correctly. My RTG4 FPGA kit runs at 50 MHz. so i had set the value as 50 MHz.

yes.i’m sure that when main.c file is compiled , configTICK_RATE_HZ has the value of 1000.

Despite its historic name, configCPU_CLOCK_HZ has to be set to the frequency of the clock used to generate the tick interrupt, not the frequency of the clock used to driver the microcontroller core. Which clock are you using? If the machine timer, then I’ve used RISC-V cores where the machine time is running many times slower than the RISC-V core.

yes.i’m sure that when main.c file is compiled,
configTICK_RATE_HZ has the value of 1000

More importantly is to check this within port.c. There the 2 macro’s are used to calculate a clock divisor for the timer.

In a FreeRTOS project, there should be only 1 single copy of FreeRTOSConfig.h within the -I include path. Sometimes an old copy goes unnoticed, that’s why I ask it. This has happened to me.

please tell me that configCPU_CLOCK_HZ will affect the vTaskDelay??? Because as per i know delay is dependent on number of tick interrupt generated and the time at which tick interrupt is generated is decided by the value of time slice whom value is given in configTICK_RATE_HZ.

I didn’t got an idea that is there any correlation between configCPU_CLOCK_HZ and configTICK_RATE_HZ??

The initialization code for the timer needs to know how many clock ticks into the counter there are for every system tick interrupt to be generated. configCPU_CLOCK_HZ provides the information needed for that (or more precisely configCPU_CLOCK_HZ / configTICK_RATE_HZ)

If you provide your own timer initialization code, over riding that provided in port.c, then the isn’t used, but if you don’t, then port.c generally will be built to use that value.

i’m uploading my port.c file here. Can you please explain me a "void vPortSetupTimer(void); " function. because this function itself setups the timer to generate the tick interrupts. but I didn’t got an idea that which subroutines are performed within this function. please kindly explain.

/* Scheduler includes. */
#include “FreeRTOS.h”
#include “task.h”
#include “portmacro.h”

#include “riscv_hal.h”

#ifdef __riscv64

define STORE sd

define LOAD ld

define REGBYTES 8

#else

define STORE sw

define LOAD lw

define REGBYTES 4

#endif
/* A variable is used to keep track of the critical section nesting. This
variable has to be stored as part of the task context and must be initialized to
a non zero value to ensure interrupts don’t inadvertently become unmasked before
the scheduler starts. As it is stored as part of the task context it will
automatically be set to 0 when the first task is started. */
static UBaseType_t uxCriticalNesting = 0xaaaaaaaa;

/* Contains context when starting scheduler, save all 31 registers */
#ifdef __gracefulExit
BaseType_t xStartContext[31] = {0};
#endif

typedef struct
{
uint32_t val_low;
uint32_t val_high;
}riscv_machine_timer_t;

static volatile riscv_machine_timer_t *mtime = (riscv_machine_timer_t *)0x4400BFF8;

static volatile riscv_machine_timer_t *mtimecmp = (riscv_machine_timer_t *)0x44004000;

/*

  • Setup the timer to generate the tick interrupts.
    */
    void vPortSetupTimer( void );

/*

  • Set the next interval for the timer
    */
    static void prvSetNextTimerInterrupt( void );

/*

  • Used to catch tasks that attempt to return from their implementing function.
    */
    static void prvTaskExitError( void );

void vPortEnterCritical( void )
{
portDISABLE_INTERRUPTS();
uxCriticalNesting++;
}
/-----------------------------------------------------------/

void vPortExitCritical( void )
{
uxCriticalNesting–;
if( uxCriticalNesting == 0 )
{
portENABLE_INTERRUPTS();
}
}
/-----------------------------------------------------------/

/* Sets the next timer interrupt

  • Reads previous timer compare register, and adds tickrate */
    static void prvSetNextTimerInterrupt(void)
    {
    uint64_t time;

    time = mtime->val_low;
    time |= ((uint64_t)mtime->val_high << 32);

    time += (configCPU_CLOCK_HZ / configTICK_RATE_HZ);

    mtimecmp->val_low = (uint32_t)(time & 0xFFFFFFFF);
    mtimecmp->val_high = (uint32_t)((time >> 32) & 0xFFFFFFFF);

    /* Enable timer interrupt /
    __asm volatile(“csrs mie,%0”::“r”(0x80));
    }
    /
    -----------------------------------------------------------*/

/* Sets and enable the timer interrupt */
void vPortSetupTimer(void)
{
uint64_t time;

time = mtime->val_low;
time |= ((uint64_t)mtime->val_high << 32);

time += (configCPU_CLOCK_HZ / configTICK_RATE_HZ);

mtimecmp->val_low = (uint32_t)(time & 0xFFFFFFFF);
mtimecmp->val_high = (uint32_t)((time >> 32) & 0xFFFFFFFF);


/* Enable timer interrupt */
__asm volatile("csrs mie,%0"::"r"(0x80));

}
/-----------------------------------------------------------/

void prvTaskExitError( void )
{
/* A function that implements a task must not exit or attempt to return to
its caller as there is nothing to return to. If a task wants to exit it
should instead call vTaskDelete( NULL ).

Artificially force an assert() to be triggered if configASSERT() is
defined, then stop here so application writers can catch the error. */
configASSERT( uxCriticalNesting == ~0UL );
portDISABLE_INTERRUPTS();
for( ;; );

}
/-----------------------------------------------------------/

/* Clear current interrupt mask and set given mask /
void vPortClearInterruptMask(int mask)
{
__asm volatile(“csrw mie, %0”::“r”(mask));
}
/
-----------------------------------------------------------*/

/* Set interrupt mask and return current interrupt enable register */
int vPortSetInterruptMask(void)
{
int ret;
__asm volatile(“csrr %0,mie”:"=r"(ret));
__asm volatile(“csrc mie,%0”::“i”(7));
return ret;
}

/*

  • See header file for description.
    */
    StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void pvParameters )
    {
    /
    Simulate the stack frame as it would be created by a context switch
    interrupt. */
    register int *tp asm(“x3”);
    pxTopOfStack–;
    pxTopOfStack = (portSTACK_TYPE)pxCode; / Start address */
    pxTopOfStack -= 22;
    pxTopOfStack = (portSTACK_TYPE)pvParameters; / Register a0 */
    pxTopOfStack -= 6;
    pxTopOfStack = (portSTACK_TYPE)tp; / Register thread pointer */
    pxTopOfStack -= 3;
    pxTopOfStack = (portSTACK_TYPE)prvTaskExitError; / Register ra */

    return pxTopOfStack;
    }
    /-----------------------------------------------------------/

void vPortSysTickHandler( void )
{
/Save Context/
{
__asm volatile(“lw t0, pxCurrentTCB”);
__asm volatile(“sw a2, 0x0(t0)”);
}

/* Increment the RTOS tick. */
prvSetNextTimerInterrupt();

/*Switch task */
if( xTaskIncrementTick() != pdFALSE )
{
	vTaskSwitchContext();
}

/*Restore Context*/
{
	__asm volatile("lw	sp, pxCurrentTCB");
	__asm volatile("lw	sp, 0x0(sp)");

	__asm volatile("lw	t0, 31 * 4(sp)");
	__asm volatile("csrw	mepc, t0");

	__asm volatile("lw	x1, 0x0(sp)");
	__asm volatile("lw   x4, 3 * 4(sp)");
	__asm volatile("lw   x5, 4 * 4(sp)");
	__asm volatile("lw   x6, 5 * 4(sp)");
	__asm volatile("lw   x7, 6 * 4(sp)");
	__asm volatile("lw   x8, 7 * 4(sp)");
	__asm volatile("lw   x9, 8 * 4(sp)");
	__asm volatile("lw   x10, 9 * 4(sp)");
	__asm volatile("lw   x11, 10 * 4(sp)");
	__asm volatile("lw   x12, 11 * 4(sp)");
	__asm volatile("lw   x13, 12 * 4(sp)");
	__asm volatile("lw   x14, 13 * 4(sp)");
	__asm volatile("lw   x15, 14 * 4(sp)");
	__asm volatile("lw   x16, 15 * 4(sp)");
	__asm volatile("lw   x17, 16 * 4(sp)");
	__asm volatile("lw   x18, 17 * 4(sp)");
	__asm volatile("lw   x19, 18 * 4(sp)");
	__asm volatile("lw   x20, 19 * 4(sp)");
	__asm volatile("lw   x21, 20 * 4(sp)");
	__asm volatile("lw   x22, 21 * 4(sp)");
	__asm volatile("lw   x23, 22 * 4(sp)");
	__asm volatile("lw   x24, 23 * 4(sp)");
	__asm volatile("lw   x25, 24 * 4(sp)");
	__asm volatile("lw   x26, 25 * 4(sp)");
	__asm volatile("lw   x27, 26 * 4(sp)");
	__asm volatile("lw   x28, 27 * 4(sp)");
	__asm volatile("lw   x29, 28 * 4(sp)");
	__asm volatile("lw   x30, 29 * 4(sp)");
	__asm volatile("lw   x31, 30 * 4(sp)");

	__asm volatile("addi	sp, sp, 4 * 32");

	__asm volatile("mret");
}

}
uint32_t g_startscheduler = 0;
BaseType_t xPortStartScheduler( void )
{
vPortSetupTimer();
uxCriticalNesting = 0;
g_startscheduler = 1;
__enable_irq();

raise_soft_interrupt();

/*Should not get here*/
return pdFALSE;

}

void Software_IRQHandler(void)
{
if(1 == g_startscheduler)
{
g_startscheduler = 2; //skip the save n switch context first time when scheduler is starting.
}
else
{
/Save Context/
{
__asm volatile(“lw t0, pxCurrentTCB”);
__asm volatile(“sw a2, 0x0(t0)”);
}

	vTaskSwitchContext();
}

/*Restore Context*/
{
	__asm volatile("lw	sp, pxCurrentTCB");
	__asm volatile("lw	sp, 0x0(sp)");

	__asm volatile("lw	t0, 31 * 4(sp)");
	__asm volatile("csrw	mepc, t0");

	__asm volatile("lw	x1, 0x0(sp)");
	__asm volatile("lw   x4, 3 * 4(sp)");
	__asm volatile("lw   x5, 4 * 4(sp)");
	__asm volatile("lw   x6, 5 * 4(sp)");
	__asm volatile("lw   x7, 6 * 4(sp)");
	__asm volatile("lw   x8, 7 * 4(sp)");
	__asm volatile("lw   x9, 8 * 4(sp)");
	__asm volatile("lw   x10, 9 * 4(sp)");
	__asm volatile("lw   x11, 10 * 4(sp)");
	__asm volatile("lw   x12, 11 * 4(sp)");
	__asm volatile("lw   x13, 12 * 4(sp)");
	__asm volatile("lw   x14, 13 * 4(sp)");
	__asm volatile("lw   x15, 14 * 4(sp)");
	__asm volatile("lw   x16, 15 * 4(sp)");
	__asm volatile("lw   x17, 16 * 4(sp)");
	__asm volatile("lw   x18, 17 * 4(sp)");
	__asm volatile("lw   x19, 18 * 4(sp)");
	__asm volatile("lw   x20, 19 * 4(sp)");
	__asm volatile("lw   x21, 20 * 4(sp)");
	__asm volatile("lw   x22, 21 * 4(sp)");
	__asm volatile("lw   x23, 22 * 4(sp)");
	__asm volatile("lw   x24, 23 * 4(sp)");
	__asm volatile("lw   x25, 24 * 4(sp)");
	__asm volatile("lw   x26, 25 * 4(sp)");
	__asm volatile("lw   x27, 26 * 4(sp)");
	__asm volatile("lw   x28, 27 * 4(sp)");
	__asm volatile("lw   x29, 28 * 4(sp)");
	__asm volatile("lw   x30, 29 * 4(sp)");
	__asm volatile("lw   x31, 30 * 4(sp)");

	__asm volatile("addi	sp, sp, 4 * 32");

	//PRCI->MSIP[0] = 0x00;

	__asm volatile("addi sp, sp, -1*4");
	__asm volatile("sw t0, 0(sp)");
	__asm volatile("li t0, 0x44000000");	// address of PRCI->MSIP[0]
	__asm volatile("sw zero,0(t0)");
	__asm volatile("lw t0, 0(sp)");
	__asm volatile("addi sp, sp, 1*4");

	__asm volatile("mret");
}

}

void vPortYield( void )
{
raise_soft_interrupt();
}

I had a similar problem when I started trying to get FreeRTOS working on a RISCV embedded in a PolarFire FPGA. What I found is that there is that there appears to be a prescaler that divides the system clock by a factor of 100 and it is this divided clock that is used for the system time.

/* MTIME register is clocked by ‘rtc_toggle’ which is required to be at least

  • 1/2 of configCPU_CLK_HZ. In actual fact it appears to be less by a factor

  • of 100. The file ‘hal_irq.c’ defines RTC_PRESCALER as 100UL, but then fails

  • to use the value anywhere. Presumably this value is embedded deep within

  • the RISCV core.

  • In newer versions of the hal for PolarFire SoC, ‘hw_mss_clks.h’ defines

  • ‘LIBERO_SETTING_MSS_RTC_TOGGLE_CLK’ as 1000000, which is consistent with

  • dividing the system clock by a factor of 100. This value is used to set

  • the RTC divider register SYSREG->RTC_CLOCK_CR in the ‘mss_pll.c’.
    */

**#define RTC_PRESCALER 100UL