KB: What's the mechanism of Sys_Delay_ProgramROM()?

Question

The Sys_Delay_ProgramROM() function can be used to delay the program, but if there are two tasks, A and B, and if we call Sys_Delay_ProgramROM() in task A, what will happen? Will the two tasks hang? Or just task A?

Recommendation

In this scenario, only task A will hang. When we call Sys_Delay_ProgramROM() in task A, task A surrenders CPU usage rights, then task B runs. When the delay time is over, task A reclaims the CPU usage rights and continues its work.

Sys_Delay_ProgramROM calls a function inside Program ROM, so we can’t see the code, but as far as I know it performs an “active wait”: thus, no other function can execute (not even task B)!

Could anybody confirm this?

Hi, @rvs . could you show me your project to check if there is any misunderstanding? Thanks

The description provided by the RSL10 Firmware Reference is:

Internally, this function calls the “System Delay” function in ROM (see rsl10_romvect.h):

/* ----------------------------------------------------------------------------
 * Function      : void Sys_Delay_ProgramROM(uint32_t cycles)
 * ----------------------------------------------------------------------------
 * Description   : Delay by at least the specified number of clock cycles
 * Inputs        : cycles   - Number of system clock cycles to delay
 * Outputs       : None
 * Assumptions   : - The requested delay is at least 32 cycles (32 us at 1 MHz)
 *                   and fits in a uint32_t (0xFFFFFFFF cycles is approximately
 *                   214.75 s at 20 MHz).
 *                 - A delay between cycles and (cycles + 3) provides a
 *                   sufficient delay resolution.
 *                 - The requested delay does not exceed the watchdog timeout.
 *                 - If the delay resolution is required to be exact, disable
 *                   interrupts.
 * ------------------------------------------------------------------------- */
__STATIC_INLINE void Sys_Delay_ProgramROM(uint32_t cycles)
{
    /* The overhead for this version of the function is greater than that of a
     * direct call to Sys_Delay. Decrement the cycle count before calling the
     * underlying function to account for this added delay. */
    if (cycles > 17)
    {
        (*((uint32_t (**)(uint32_t))
           ROMVECT_PROGRAMROM_SYS_DELAY))(cycles - 17);
    }
}

The ROM code is described by:

As simple user, we have no further information (unless we try reverse engineering on the ROM code at address 0x0000002C).
I believe Sys_Delay_ProgramROM performs an active wait (i.e.: wasting CPU cycles) for the following reasons:

  • You can call it from bootloader (without kernel),
  • You can call it from an application linked with the kernel,
  • You can even call it from FreeRTOS (which wouldn’t be efficient as vTaskDelay would provide a similar result without wasting CPU cycles)

If the description of Sys_Delay_ProgramROM indicated it performs “an active wait” everything would be clear. But as it isn’t indicated and as you described it can run another task (the way vTaskDelay could do it) I was surprised…

So, my question is: can you confirm another task can be run while a task is executing Sys_Delay_ProgramROM? If so, could the description of Sys_Delay_ProgramROM be updated as I believe it is important to be aware of this behavior.

Hi, RVS:
you can verify my point with our image sample, you just need add an DIO as test dio and then implement the delay function in mainloop. you will find this delay function in the mainloop (Task A) doesn’t effect the timer0 (TaskB);

image


if change the delay period to Sys_Delay_ProgramROM(0.01 * SystemCoreClock);
the timero will also the same

@snow.yang The example you provide shows that timer0 continues to run at the same frequency while the main loop toggles Test_DIO.

In practice the LED is toggled under TIMER0_IRQHandler, not under a task context.

If you replace the call of Sys_Delay_ProgramROM by a simple wait loop such as:

for (int i=0; i<1000000; i++);

…you would also notice that both Test_DIO and the LED would continue to work simultaneously.

Hi, @rvs . sorry I’m not very clear what’s your point. if you could send your test code and figure your point out will more helpful for me to understand.
back to this topic, we talk about if there are two tasks, A and B, and if we call Sys_Delay_ProgramROM() in task A, then just task A be hanged and no impact with Task B. so I show you when delay in mainloop there is no any change in the Timer0 interrupt.

Because timer_free_run is based on a hardware timer, adding a call to Sys_Delay_ProgramROM in the main loop doesn’t impact it because interrupts can still execute while performing the active wait…

Let’s take a similar example based on a software timer: the sample application kernel_timer is a good candidate for that.
image

Just make a simple change in main(): add the code between lines 44 and 47 calling Sys_Delay_ProgramROM:
image

While keeping #if 0 ... #endif we still have the native behavior of the application: it blinks the LED toggling its state every 2s.

If we replace #if 0 by #if 1 the LED toggling won’t be every 2s anymore but every 5s because Kernel_Schedule isn’t called while inside Sys_Delay_ProgramROM. What is interesting in this example is that the external behavior (LED blinking) depends on the input value of Sys_Delay_ProgramROM:

  • If the value is below or equal to 2*SystemCoreClock the LED will continue to toggle every 2s
  • If the value is above 2*SystemCoreClock, the argument of Sys_Delay_ProgramROM will become the visible toggling period

Conclusion

The function Sys_Delay_ProgramROM doesn’t allow another task to run (illustrated by the kernel_timer sample application) but still allows interrupt service routine to executed (illustrated by the timer_free_run sample application).

1 Like