nRF5340 / Cortex-M33, power consumption questions

Hello!

We want to use nRF5340 in our future products and now I’m in the middle of experimenting with this device (using an nRF5340DK). Nordic Connect SDK and Zephyr are out of our scope. I’ve already successfully built a base system in which I can put custom programs (FWs) to either core (APP/NET). The next step would be putting FreeRTOS on the MCU. Power consumption in idle mode is also very important. I’ve a few questions, could you please help me out?
-1) I don’t want to play with secure/nonsecure modes, SPU, TrustZone etc, I simply need both the APP and NET cores without any “protection” etc (all secure mode). As I’ve seen in the most recent FreeRTOS release (202112.00) secure mode is also supported. I don’t know how mature the port (Cortex-M33) is?
-2) can I put the latest release both on APP and NET cores? At first glance I haven’t seen anything that’d prevent FreeRTOS from running on the NET core too;
-3) energy consumption is crucial on APP core, there’ll be days the APP processor is doing nothing. Power save modes that cause a reset when exit are not suitable, I believe, because they’d reset FreeRTOS itself too. Some kind of WFI/WFE sleep is necessary, I’ve seen traces of it in port.c. If all other conditions are met (unused peripherals, pins etc are all off by the firmware etc), is tickless sleep or the regular sleep better? The FW has to preserve the correct system time during the entire sleep;

UPDATE1: I think I don’t need TrustZone, but need the entire APP CPU run in secure mode to have access to everything. Under FreeRTOS/Source/portable/GCC/ARM_CM33_NTZ/ there’s no ‘secure’ directory, only non_secure. I’ve just compiled with this and doesn’t seem to run, but had no time to debug it yet, maybe I’m doing wrong (e.g. my malloc can be faulty). Nevertheless it’s suspicious to me, I want secure but I’m compiling non_secure :wink: Some lines from the config:

#define configENABLE_MPU								0
#define configENABLE_FPU								1
#define configENABLE_TRUSTZONE							0
#define configRUN_FREERTOS_SECURE_ONLY                  1

Thanks, regards,

If you do not want TrustZone, you should use the non-trust zone port and define configENABLE_TRUSTZONE to 0. If you are running all secure, you also need to set configRUN_FREERTOS_SECURE_ONLY to 1.

#define configENABLE_TRUSTZONE 0
#define configRUN_FREERTOS_SECURE_ONLY 1

This page provides more details - Using FreeRTOS on ARMv8-M Microcontrollers - FreeRTOS

Yes, you should be able to as long as you partition the memory correctly.

Tickless idle support has macros that you can override to put your MCU in whatever sleep mode you want. This page provides more details on tickless idle support - Tickless Low power features in FreeRTOS

There are 2 ways to not use TrustZone-

  1. Disable the TrustZone support in hardware - the MCU boots as non-secure. In this case, you just use the non trustzone port as is.
  2. Do not disable TrustZone in hardware - the MCU boots secure and always remains on the secure side. In this case you need to additionally define configRUN_FREERTOS_SECURE_ONLY .

If your use case is the first one, the terminology would probably make more sense.

Can you please break in the debugger and see what the code is doing?

Thanks.

Thank you for the two articles, I’ll read them carefully.

Can you please break in the debugger and see what the code is doing?

Of course, it’s the first thing I’m going to do tomorrow morning.

A few general comments about your problems.

If you really want the lowest power mode, do not try to use the FreeRTOS tick as your clock time time-base, but the processor’s RTC to provide that capability. This allows you to put the processor into the lighter sleep modes that restart the processor where it left off.

Don’t be afraid of the reset exiting modes if you can afford the longer response time for rebooting the system. This might be true for one of the cores, letting the other handle the quick response, and the other coming up slower to handle a bigger load if needed.

Tickless idle tends to be better for shorter sleeps, where you are expecting to be woken up in the near future, you want to wake up quick, but don’t need to handle every tick on the way, and can accept the timing error of tickless idle (or use a different counter that lets it stay accurate)

For a longer sleep, you can go into a “real” sleep mode, (which will also be ‘tick-less’, but more than just idle), these will incur more delay to wake up (and often a power hit if it happens too soon). This becomes the tuning problem.

Hello Richard!

Thanks for your thoughts.

So your suggestion is:

  1. keep M33’s SysTick peripheral as kernel systick which performs task switching;
    1.1 (my idea) set configTICK_RATE_HZ to a reasonbly low value (e.g. 100) to prevent too frequent wakeups from WFI/WFE sleeps;
  2. set up an RTC which is always running to keep proper absolute system time
    (by the way, this is what we do in our 8-bit MCU project, those RTCs with an external crystal can go very low in terms of energy consumption);
  3. our FW must preserve its internal state during sleep, so using a reset-exiting sleep would clear the SRAM. nRF5340 has ‘RAM retention’ feature in its VMC peripheral. I guess if I specify RAM blocks, they can preserve precious state variables between two resets, am I right?

APP core.

Without debugger:

  • call to xTaskCreate(mainloop_task, …) succeeds to prepare mainloop_task(…) task function;
  • execution blocks somewhere (I haven’t digged deep yet) right after vTaskStartScheduler() → mainloop_task(…) isn’t running;

With debugger (Ozone):

  • call to xTaskCreate(mainloop_task, …) succeeds to prepare mainloop_task(…) task function;
  • mainloop_task(…) is running;
  • with the very same .elf as in the debugger-less case;

So some kind of APP core init may be still missing on my side… any idea?

Tickless idle was not disabled, changed it:

#define configUSE_TICKLESS_IDLE							0

The generated code being burnt with nrfjprog, and it is running. Re-enabling tickless idle, the generated code is running only within Ozone debugger.

For 1, you don’t NEED to keep using SysTick, but you can.

For 2, YES, an always-running RTC is normally the best solution to keeping track of “clock” time, especially if your processor is going to use sleep modes.

For 3, the key is to identify the state that needs to be preserved and save that through a reset, assuming you have determined you can afford the delay of a reset/reboot for that processor.

Can you try to use some physical indication like toggling an LED to see till what point the control reaches when the code appears to be not working? Is it possible that the MCU is entering sleep mode and never waking up?

This is my assumption too, but will investigate better in the upcoming days.

Thanks. Please keep us posted on whatever you find.

What I see now in portable/GCC/ARM_CM33_NTZ/non_secure/port.c in function void vPortSuppressTicksAndSleep(...): there’s an if statement after WFI:

if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 )

And this never executes, therefore ulCompleteTickPeriods remains zero and the scheduler will start again the sleep (I believe). The else branch executes once in a second, but it’s okay, since my RTC is programmed to generate 1 IRQ/s, so in these cases the sleep is interrupted by my RTC and ulCompleteTickPeriods seems to be fine.

The APP core is running at 64MHz.

At the moment I don’t have any idea why portNVIC_SYSTICK_COUNT_FLAG_BIT flag isn’t set, and why does it get set if being run in a debugger (Segger Ozone)?

Will go on with this tomorrow.

I think I’ve got it.

nRF5340 shuts down the HFCLK clock during WFI, so the MCU (including SysTick peripheral) doesn’t get clock, therefore stops. Nordic Devzone topics suggest to use an RTC instead of SysTick to work around the situation.

(I suppose other Cortex M33-based MCUs keep the SysTick powered on even in WFI, at least this is what I’ve learnt from vPortSuppressTicksAndSleep implementation. I also suppose a lot of tests were performed on other Cortex M33 machines and they passed.)

I’m going to patch my FreeRTOS in the future to use RTC for systick instead of SysTick peripheral. And the whole tickless idle logic also has to be recreated…

Here are some links:

Thank you for sharing your findings.

You do not need to patch the kernel. You just need to supply an implementation of vPortSetupTimerInterrupt which will override the default weak implementation - FreeRTOS-Kernel/port.c at main · FreeRTOS/FreeRTOS-Kernel · GitHub

Thanks.

Sure. And vPortSuppressTicksAndSleep must also rewritten, because RTC works a bit differently than SysTick. (Or create a new function and use by portSUPPRESS_TICKS_AND_SLEEP). Oh, vPortSuppressTicksAndSleep is also weak. So many ways to do the task :wink:

By the way, what are your experiences with other Cortex M33 MCUs? Is this SysTick issue specific to nRF5340?

Yes, that seems to be the case. I have not seen it before.

The ST Implementations of the M33 have additional registers to decide how much is shut down on a WFI instruction. On default it is only a CPU halt and nothing more, but you can put it up to shutting down everything and only recover on RTC interrupt via reset vector. I expect this is how most manufacturers have done it.
I am surprised to hear that Nordic does not seem to have a CPU halt only configuration.