FreeRTOS - where should the CPU be spending most of its time? - Page 1
Forum section: electronics / general

author: peter-h
author_rank: Super Contributor
timestamp: July 17, 2023, 02:21:11 pm
content: This seems about right, for co-operative multitasking i.e. a task yielding to RTOS when it has nothing to doAs I see it, the CPU has to spend timesomewhereand that should be mostly in the RTOS.The hang_around() function is a dumb wait used for LEDs at startup and it wastes a couple of seconds so features significantly over the sample interval.

author: strawberry
author_rank: Super Contributor
timestamp: July 17, 2023, 04:30:09 pm
content: not expert. could it be that it uses 60%CPU just to switch between two idle tasks(1000x/s collecting some garbage)?is it possible to estimate exact CPU usage or what exactly going on in MCU?

author: Jeroen3
author_rank: Super Contributor
timestamp: July 17, 2023, 06:11:22 pm
content: The cpu should be in a wfi

author: rounin
author_rank: Regular Contributor
timestamp: July 17, 2023, 06:43:08 pm
content: I call some sleep function from vApplicationIdleHook. Define configUSE_IDLE_HOOK to 1.Eg WFI, HAL_PWR_EnterSLEEPMode, ...

author: jc101
author_rank: Frequent Contributor
timestamp: July 17, 2023, 09:14:16 pm
content: Weird as prvCheckTasksWaitingTermination is only ever called by Idle, so I'd lump those two together. FreeRTOS will drop into Idle if nothing else needs to be done, plus associated housekeeping.

author: brucehoult
author_rank: Super Contributor
timestamp: July 17, 2023, 10:12:51 pm
content: Quote from: jc101 on July 17, 2023, 09:14:16 pmSo that's over 88% of the time.Note also that if a sampling profiler finds you at an WFI that counts as "all the time is being spent in that function".If it can even make a sample without waking up the CPU. (Some cores/ISAs have a completely separate hardware debug unit)

author: peter-h
author_rank: Super Contributor
timestamp: July 18, 2023, 02:14:04 pm
content: This time I have run it overnightWhat is a WFI?QuoteSo the timings look about right. We are looking at 92% spent in the RTOS, so the CPU is spending just 10% of the time running this lot (RTOS tasks)which is remarkable, if not surprising to me, having been working on this thing for 2-3 years and being constantly amazed at the speed.SPI GPS processingETH http server updating a status page at 1HzFast USB debugsTLS session every 60 secs (crypto takes 3 secs)etcMaybe I could do some bitcoin mining?

author: AndyC_772
author_rank: Super Contributor
timestamp: July 18, 2023, 02:24:22 pm
content: WFI = wait for interrupt

author: peter-h
author_rank: Super Contributor
timestamp: July 18, 2023, 03:23:44 pm
content: Right - but surely every task which is1 - stuck in a loop of its own e.g. for (;{}2 - has called osDelay(100);is waiting for an interrupt: the 1kHz Systick which drives the RTOS.The 2nd case is much more efficient.The 1st case is what happens in 3rd party code which just waits somewhere. In my project it is probably just MbedTLS which does that. It spends about 3 seconds doing crypto for the TLS setup. AIUI this involves a "bignum" library which is the last two RTOS tasks in that list above. However it is likely that nothing can be done about this, because the CPU is not waiting for something external.But MbedTLS illustrates the difference between case 1 and case 2 above, because during those 3 secs the RTOS pretty well grinds to a halt. It still runs but much slower.I don't think FreeRTOS has detection of "time wasting loops" which I think some have. It is perhaps done by dropping the priority of such tasks if the variation in program counter value (code address) is less than say 100 bytes.

author: abyrvalg
author_rank: Frequent Contributor
timestamp: July 19, 2023, 12:31:20 pm
content: WFI is not some abstract task state, it’s a CPU instruction, so it will be a part of some specific function.

author: peter-h
author_rank: Super Contributor
timestamp: July 19, 2023, 12:38:21 pm
content: The only time I used WFI was during a CPU shutdown procedure, where - upon VCC loss detection - I want VCC to last as long as possible to protect a FLASH write. I shut down the ETH PHY (that does a lot), other stuff, then CPU shutdown, and then WFI in a loop because an interrupt can wake it all up again. I posted all the shutdown details in a thread here.Does FreeRTOS use WFI? Not AFAIK. It would not do much anyway because other interrupts are running all the time anyway. I would expect the RTOS to be continually stepping through the task list.

author: JPortici
author_rank: Super Contributor
timestamp: July 19, 2023, 01:20:55 pm
content: Quote from: peter-h on July 19, 2023, 12:38:21 pmYou're missing the point. If most of the time the application looping through the idle task (and you're not doing anything in the idle task) you way just want to WFI (or enter a lower power state and WFI). Any interrupt will resume operation. The interrupt may be the task scheduler timer interrupt, or another interrupt that will/should/could wake another task

author: peter-h
author_rank: Super Contributor
timestamp: July 19, 2023, 02:17:09 pm
content: I don't understand the benefit, except some very marginal benefit in power consumption, but there will be all kinds of potential issues e.g. with the ETH controller going to sleep, etc.The key here may be that the CPU spends ~90% of its time in the RTOS, so while a 32F4 has only a slight potential for power saving (I found in my tests that by far the biggest savings come from ETH PHY shutdown) one may still get something useful.Unfortunately I have never really dug properly into the sleep modes, in the context of an instant wakeup from one.If there were no async interrupts, the RTOS could be modded to sleep when it has finished its processing (after each 1ms tick) but it would have to be something which has an immediate wakeup.

author: eutectique
author_rank: Frequent Contributor
timestamp: July 19, 2023, 05:43:37 pm
content: Quote from: peter-h on July 19, 2023, 12:38:21 pmYou can see it for yourself. ExamineSource/tasks.cand go straight tostatic portTASK_FUNCTION( prvIdleTask, pvParameters )and especiallyCode:#if ( configUSE_IDLE_HOOK == 1 ){extern void vApplicationIdleHook( void );/* Call the user defined function from within the idle task. This* allows the application designer to add background functionality* without the overhead of a separate task.* NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES,* CALL A FUNCTION THAT MIGHT BLOCK. */vApplicationIdleHook();}#endif /* configUSE_IDLE_HOOK */Do you define these?To see what is actually being executed, sayarm-none-eabi-objdump -d port.oand search forprvIdleTask.

author: peter-h
author_rank: Super Contributor
timestamp: July 19, 2023, 07:18:03 pm
content: I see these but they are not defined. The idle task is started by vTaskStartScheduler() does various cleanups. The way it is defined (portTASK_FUNCTION) I do not understand but it obviously worksCode:/** -----------------------------------------------------------* The Idle task.* ----------------------------------------------------------** The portTASK_FUNCTION() macro is used to allow port/compiler specific* language extensions. The equivalent prototype for this function is:** void prvIdleTask( void *pvParameters );**/static portTASK_FUNCTION( prvIdleTask, pvParameters ){/* Stop warnings. */( void ) pvParameters;/** THIS IS THE RTOS IDLE TASK - WHICH IS CREATED AUTOMATICALLY WHEN THESCHEDULER IS STARTED. **//* In case a task that has a secure context deletes itself, in which casethe idle task is responsible for deleting the task's secure context, ifany. */portTASK_CALLS_SECURE_FUNCTIONS();for( ;; ){/* See if any tasks have deleted themselves - if so then the idle taskis responsible for freeing the deleted task's TCB and stack. */prvCheckTasksWaitingTermination();#if ( configUSE_PREEMPTION == 0 ){/* If we are not using preemption we keep forcing a task switch tosee if any other task has become available. If we are usingpreemption we don't need to do this as any task becoming availablewill automatically get the processor anyway. */taskYIELD();}#endif /* configUSE_PREEMPTION */#if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ){/* When using preemption tasks of equal priority will betimesliced. If a task that is sharing the idle priority is readyto run then the idle task should yield before the end of thetimeslice.A critical region is not required here as we are just reading fromthe list, and an occasional incorrect value will not matter. Ifthe ready list at the idle priority contains more than one taskthen a task other than the idle task is ready to execute. */if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) 1 ){taskYIELD();}else{mtCOVERAGE_TEST_MARKER();}}#endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) */#if ( configUSE_IDLE_HOOK == 1 ){extern void vApplicationIdleHook( void );/* Call the user defined function from within the idle task. Thisallows the application designer to add background functionalitywithout the overhead of a separate task.NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES,CALL A FUNCTION THAT MIGHT BLOCK. */vApplicationIdleHook();}#endif /* configUSE_IDLE_HOOK *//* This conditional compilation should use inequality to 0, not equalityto 1. This is to ensure portSUPPRESS_TICKS_AND_SLEEP() is called whenuser defined low power modeimplementations requireconfigUSE_TICKLESS_IDLE to be set to a value other than 1. */#if ( configUSE_TICKLESS_IDLE != 0 ){TickType_t xExpectedIdleTime;/* It is not desirable to suspend then resume the scheduler oneach iteration of the idle task. Therefore, a preliminarytest of the expected idle time is performed without thescheduler suspended. The result here is not necessarilyvalid. */xExpectedIdleTime = prvGetExpectedIdleTime();if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ){vTaskSuspendAll();{/* Now the scheduler is suspended, the expected idletime can be sampled again, and this time its value canbe used. */configASSERT( xNextTaskUnblockTime >= xTickCount );xExpectedIdleTime = prvGetExpectedIdleTime();/* Define the following macro to set xExpectedIdleTime to 0if the application does not wantportSUPPRESS_TICKS_AND_SLEEP() to be called. */configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING( xExpectedIdleTime );if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ){traceLOW_POWER_IDLE_BEGIN();portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );traceLOW_POWER_IDLE_END();}else{mtCOVERAGE_TEST_MARKER();}}( void ) xTaskResumeAll();}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_TICKLESS_IDLE */}}portSUPPRESS_TICKS_AND_SLEEP doesn't appear to be defined. Presumably this would be totally system specific, based on what can be put to sleep for a few hundred microseconds.Interesting... what could one do with a 32F417? I had a dig around and found it mentioned herehttps://www.st.com/resource/en/user_manual/um1722-developing-applications-on-stm32cube-with-rtos-stmicroelectronics.pdfbut only in the tickless RTOS mode, which I am not using.The use of sleep mode(s) seems incredibly complex once peripherals, including external ones, and stuff like SPI, are to be accounted for.

author: brucehoult
author_rank: Super Contributor
timestamp: July 19, 2023, 10:04:53 pm
content: Quote from: peter-h on July 19, 2023, 02:17:09 pmIn many applications of RTOS the energy savings of using WFI and/or sleep modes can be 90% to 99.9%.Yes, even if it has to wake up to do something every ms. There are a hell of a lot of ns in a ms.

author: peter-h
author_rank: Super Contributor
timestamp: July 20, 2023, 01:48:26 am
content: How are the peripheral issues handled?I would be really interested in some real-world examples.Obviously if all you are doing is on-chip and GPIO just reads switches and flashes LEDs etc, putting the CPU to sleep is trivial. And for many things you don't need async interrupts, so the next interrupt will always be the RTOS tick, and that can be 100Hz, not the usual 1kHz.But if say you are running USB CDC/MSC then you will be screwing up the USB Host with random response times. The ST implementation of USB is (after init) interrupt driven, so maybe that works. ETH? No chance AFAICS. Talking to things like SPI FLASH?

author: ejeffrey
author_rank: Super Contributor
timestamp: July 20, 2023, 08:18:46 am
content: You can pretty much always do WFI while idle, but whether you can put peripherals in a lower power mode depends greatly on what those peripherals are and whether they have a low power mode that still can detect activity and wake up. There isn't really a way around just reading the manual for the device to see what it can do For instance with slow interfaces you might be able to shut down the peripherals but use a pin change interrupt to wake when a transmission starts.

author: peter-h
author_rank: Super Contributor
timestamp: July 20, 2023, 08:44:34 am
content: Yeah... I can see one spending months chasing around really weird and very hard to track down problems with peripheral behaviour.I guess one would start by making the WFI conditional on some stuffnotbeing active e.g. you will probably already have a mutex on r/w to an SPI FLASH, and you will want tonotdo WFI if that mutex is locked. In fact you will probably have a mutex on an SPI channel as a whole.Maybe use DMA for serial and SPI? That avoids the crap 1-char buffering provided by ST. But that meansnotshutting down peripherals.And lots and lots of other stuff like that.Curiously I have found almost no examples of this online.Looks like only the Sleep mode could be usedNext, the DS suggests Run mode to Sleep mode drops Icc from 102mA to 77mA, only! With all peripherals disabled it drops from 54mA to 27mA. This is at 168MHz, 32F417.Then there are all kinds of other tables showing Icc under lots of conditions, but if one sticks to a mode where the Systick will wake you up, and the HSO (say 25MHz) remained running, I don't see much point in doing this unless your power situation is already critical, in return for the considerable increase in development time and testing required.Maybe other CPUs are much better - the "L" chips?The worst may be compatibility testing e.g. USB. Currently I am implementing the primitive USB client mode which is reportedly used by FLASH sticks i.e. Host activity triggers an ISR which does everything including a 15ms FLASH write, andhangs upthe target for the 15ms.With a 1ms RTOS tick it all still runs but 15x slower; what a surpriseThis is primitive but is 100% compatible across Host USB implementations. Other approaches, sending back a BUSY packet immediately upon starting the FLASH write and hoping the Host will retry, etc, have been tried but are complex, practically nobody knows how to do them (apart from the one guy on ST forum who never actually tells youhow), the ST-provided code hooks for BUSY/NAK do not actually work, and the final code cannot possibly be tested across the Host spectrum.ETH, I don't even want to think about. You probably want to just shut it down totally (for agoodIcc gain) until somebody starts the HTTP serverBut then you won't know they started it, will you?And you still need to have a PSU big enough to run that mode...What am I missing?

author: lucazader
author_rank: Regular Contributor
timestamp: July 20, 2023, 09:24:06 am
content: In an application running F7 and F4 MCUs at work in freeRTOS simply adding __WFI() in the idlehook dropped our system consumption from ~110mA to ~50mA (lots of other things sitll running on the board including a seperate RF comms module etc that aren't sleeping). Huge win for any battery operated device.This device acts as a USB host as well as communication over uart to the RF processor etc.The addition of WFI() was simple, and caused no issues for us with nay of the peripherals.Was a big enough win for us to hit our device battery life targets and enabled us to move onto other more challenging issues in the project.

author: ejeffrey
author_rank: Super Contributor
timestamp: July 20, 2023, 09:49:41 am
content: Quote from: peter-h on July 20, 2023, 08:44:34 amIt shouldn't be that complicated, you shouldn't need to mess with a mutex or anything, at least for basic sleep mode that just shuts off the CPU. Think of it from the perspective of the scheduler: If there are no runnable tasks, then your CPU has nothing to do except wait until a task becomes runnable. Generally that will only happen when an interrupt occurs (either IO or a timer} so the scheduler will call an idle task that executes WFI. After WFI completes, it will return to the scheduler to see if any tasks have been marked runnable from an ISR. If so they are executed until they all are blocked then the idle task runs again.This of course only works if your IO is all interrupt driven or polled slow enough that it makes sense to use a timer. Its ok to have occasional busy waiting: that will be done from a runnable task and have priority over the idle task. But if you need to be constantly polling for work at a high rate then it doesn't make sense.DMA can help reduce how often you wake up but isn't needed. All you need is interrupt driven IO.

author: Tation
author_rank: Regular Contributor
timestamp: July 20, 2023, 09:55:11 am
content: Quote from: peter-h on July 19, 2023, 12:38:21 pmThis is pointless if it is known that there are no tasks able to run. And there will not be runable tasks if all tasks are idling/waiting AND no interrupt arrives.The RTOS should do a WFI when it detects that all tasks are idling/waiting: the processor sleeps, peripherals not (provided that you have not configured any deep sleep configuration that also stops peripherals). Any interrupt will wake the processor, perform the required ISR and then execute the task scheduler. If your ISR has fired any RTOS signal, the related tasks will be invoked by the scheduler, if not, the core will sleep again (thus, direct peripheral management in the ISR will also work). Awakening from this sleep mode usually takes just a handful of clock cycles.If your code is 90 % of the time in the RTOS, expect a reduction in power consumption (of the core, not of the whole chip) of near 90 % when allowing the RTOS to sleep.No need for mutexes or any other mechanism to manage this, it is a well known, out-of-the-box, technique. So put that WFI in the place expected by the RTOS and forget.Regards.

author: Doctorandus_P
author_rank: Super Contributor
timestamp: July 20, 2023, 11:29:45 am
content: I'm not particularly up to date with FreeRTOS internals, but in general it depends on your application.If your application is mostly ISR, event and timer based, then it's likely most of the time is spent in your RTOS "sleep()" or "idle()" function.If your application is doing a lot of (ugly) I/O polling from various tasks, then you can set up your RTOS to divide the time over the most critical polling parts.But overall, the best advise is to learn how the internals of your RTOS work, and then figure out of where it spends it's time makes sense or not.

author: peter-h
author_rank: Super Contributor
timestamp: July 20, 2023, 02:42:48 pm
content: Interesting!No ETH cable:With WFI, 3.3V rail119mAWithout WFI,147mAWith ETH running:With WFI, 3.3V rail165mAWithout WFI,193mA28mA saving. Note there is a lot of stuff running off that 3.3V rail; not just the CPU.And so far all seems to work. More testing tonight.Is there anything worth checking specifically?This is all I did:Code:// This gets called when RTOS idle task has nothing to dovoid vApplicationIdleHook (void){#define __WFI() __asm volatile ("wfi")__WFI();}Interesting that my post above I refer to a 25mA saving in the DS, so I am getting very slightly more but basically that.And instead of sitting mostly in "task waiting" it now shows "unknown function"If the above is the whole story, that is excellent. Digging around online to see what others have done, I see all kinds of other stuff like disable_irq() before WFI but it's not clear what this is for. You would need enough buffering on UARTs to handle the worst case there, etc.QuoteUnless your CPU is very different from mine, you must have been doing more than just the WFI which just sleeps the CPU. Even the DS says you won't get more than about 25mA.I can see one could use a GPIO to drive a MOSFET around the WFI to turn off external stuff but I can't see obvious candidates where a 0-1ms turnoff would be beneficial. Maybe someone can think of something? It was Clive Sinclair who did something like that... LEDs would be obvious but then they will be dimmer so you have to drive the current up

author: Siwastaja
author_rank: Super Contributor
timestamp: July 20, 2023, 04:26:58 pm
content: It would be weird to disable interrupts and then instruct the CPU to Wait For Interrupt. But people suggest the weirdest things.Indeed, if you replace while(1) or while(flag_written_in_isr) with WFI, there is no need for anything special like mutexes, it should be just this simple.For a robust design though, don't make the assumption of having no other spurious wakeups for unexpected reasons. In other words, don't assume that since CPU got past WFI/WFE, a certain interrupt or event did indeed happen; always check for flags (peripheral registers or variables written by your ISR) and if nothing happened, go back to sleep (WFI/WFE).

author: peter-h
author_rank: Super Contributor
timestamp: July 20, 2023, 05:07:55 pm
content: That (last sentence) puts a whole different perspective on it, doesn't it?Interrupts are vectored; no need to check source (unless, obviously, you have combined sources per vector).And if the WFI was occassionally terminated by some other event, that seems harmless, no? Especially if the Icc savingisper the DS.If I was getting spuriousinterruptsthen the system would crash. So what is left?It is interesting because there indeed are some while() loops in my code e.g. checking for DMA completion. But there is no ISR, although to make WFI work properly one should set up an ISR (which does nothing beyond clearing the source as usual). Such loops currently always allow the RTOS to switch tasks, but it could be after as much as 1ms. Normally I don't do that.In practice I have yield=true only when feeding slow SPI devices e.g. an LED controller which uses a 400kHz clock and transfers ~10 bytes. I don't use yield=true for 512 byte transfers to/from SPI FLASH at 21MHz (24us).

author: Siwastaja
author_rank: Super Contributor
timestamp: July 20, 2023, 05:23:34 pm
content: Yeah, I was talking about spurious wakeups, not spurious interrupts.This pattern:Code:while(1){WFI();if(flag) do_something();}just loops back to sleep if something unexpected caused the wakeup, no harm done.Interrupts work as they normally do.

author: peter-h
author_rank: Super Contributor
timestamp: July 20, 2023, 05:55:47 pm
content: OK, but if I understand you right, that yellow loop above will not work with WFI unless I set up an ISR for one of theseNDTR = 0TCIF0 != 0EN = 0and that interrupt will exit WFI.Anyway, spoke too soon: WFI in the idle task broke my RTC setting from GPS time. I wonder if tasks running at idle priority (as most of mine are) are buggered up by the WFI in the idle task?

author: bugnate
author_rank: Regular Contributor
timestamp: July 20, 2023, 07:36:45 pm
content: Quote from: Siwastaja on July 20, 2023, 04:26:58 pmI thought so too the first time I saw it, but has since turned out to be a common idiom on projects that brought me in. Functionally used as a shoddy WFE when the os/app design isn't quite right.

author: ejeffrey
author_rank: Super Contributor
timestamp: July 20, 2023, 07:56:37 pm
content: Quote from: Siwastaja on July 20, 2023, 04:26:58 pmIt's to eliminate a race conditions where an interrupt happens after checking for runnable tasks but before calling WFI. In that case, the ISR may have run and marked a task runnable but you go to sleep anyway.If your latency sensitive work happens in the ISR itself and it's ok to wait for the next timer interrupt to run the backend task you don't need to do this, but if you need to run a task very quickly after the ISR you need to do something like this.

author: peter-h
author_rank: Super Contributor
timestamp: July 20, 2023, 08:04:08 pm
content: QuoteDoes that matter if I never do anything with the RTOS from within an ISR?But presumably there is no harm in doing that.QuoteYeah, I was right!If you use WFI then you should not have any tasks at tskIDLE_PRIORITY (=0). So I am now changing these to osPriorityLow (=8).I think Ejeffrey's point is herehttps://community.arm.com/support-forums/f/architectures-and-processors-forum/1483/cortex-m4-guaranteed-wakeup-from-wfibut that page has every combination possibleMaybe I should use__disable_irq();__WFI();but that doesn't work. The WFI does not re-enable them. Based on that article it should beCode:// This gets called when RTOS idle task has nothing to do.// The disable_irq is needed only to eliminate a race condition where an interrupt happens// after checking for runnable tasks but before calling WFI.// In that case, the ISR may have run and marked a task runnable but you go to sleep anyway.// Currently we are not doing any RTOS actions inside ISRs, anyway.void vApplicationIdleHook (void){__asm volatile ("cpsid i" : : : "memory");// __disable_irq();__asm volatile ("wfi");// __WFI();__asm volatile ("cpsie i" : : : "memory");// __enable_irq();}(I am avoiding doing a #include of a huge .h file with some unwanted stuff in it)

author: bugnate
author_rank: Regular Contributor
timestamp: July 20, 2023, 09:23:15 pm
content: Quote from: peter-h on July 20, 2023, 08:04:08 pmWell, no. You need to stop and think about what is actually happening here and what he said. ejeffrey isn't wrong but he buried the lede.Yes, such a race condition can happen in certain designs but the enable/disables here are meaningless and a complete waste. Think about it... you are disabling IRQs, the MCU sleeps, an interrupt occurs, MCU wakes up, does not immediately execute the IRQ but instead enables IRQs and then executes IRQs. What part of that fixed the race? You need an atomic transition to sleep, not the sleep itself (which doesn't make sense anyway). Yes, if an IRQ hits after __disable_irq() you are potentially better off (won't sleep)... but what if the IRQ hits before __disable_irq()? Same race.For this particular solution, the key is thatyou also have to check the run queue / idle condition after disabling IRQs but before you WFI. If you do this, you will either catch the IRQ in your pre-sleep check (after the irq disable) or if the irq occurs after the check you will just blow though the WFI instead of sleeping (it won't sleep with an interrupt pending).However...Quote from: peter-hAs you are allude to here, I think that really none of this actually matters for you...

author: ejeffrey
author_rank: Super Contributor
timestamp: July 20, 2023, 09:23:52 pm
content: WFI wakes if any interrupts are pending regardless of whether interrupts are disabled but it doesn't reenable them you have to do it yourself.

author: peter-h
author_rank: Super Contributor
timestamp: July 20, 2023, 09:59:11 pm
content: I am reverting back to previous. The WFI breaks a lot of stuff in a subtle way. I think it is to do with task priorities. I wrote most tasks with idle priority (0) and the whole thing is running co-operatively, yielding to RTOS when nothing to do. System tasks run higher e.g. LWIP runs at ~24, ETH runs even higher, etc.I would like the 28mA saving but...

author: ejeffrey
author_rank: Super Contributor
timestamp: July 20, 2023, 11:11:23 pm
content: Quote from: peter-h on July 20, 2023, 09:59:11 pmIf you are not tracking which your tasks are runnable but instead relying on continuous polling I agree, it's not practical. It's not really a question of task priorities: if you are not in a situation where there is nothing to do until an interrupt arrives it doesn't make sense to wait for an interrupt.

author: peter-h
author_rank: Super Contributor
timestamp: July 21, 2023, 05:43:26 am
content: Spent more time on this... It is more subtle.I had some relative task priority issue which didn't cause a problem until using WFI. I have fixed that.A bigger issue is that I am running SPI to talk to a GPS receiver and you have to continually send it dummy data (0x00) to check if there is anything arriving. If no rx data you get an 0xFF back. This stupid polling process (a stupid design of the UBLOX GPS - previous threads on this where I posted my incredulity at the stupid SPI interface on it) needs to be pretty well solid. If I yield to the RTOS during the SPI process (which is always just 1 byte), I get the desired result with WFI (>80% in the idle function) but then I lose data because the yield brings the CPU back to that job after x ms, where 0 < x < 1, and 1ms is too late. One problem is that the SPI tx/rx has to be blocking so I can raise /CS at the end of it. That part of the code could do with a total redesign... but the obvious way (an interrupt driven state machine) can't be used on a shared SPI; it would be fine on a dedicated SPI. Or even having the GPS on a UART would have helped, but I have no spare UARTs (no spare GPIO actually). SPI was simpler and is very fast: a few hundred kbits/sec equivalent. Maybe an SPI UART... there are some with loads of buffering too, but they are expensive and weird chips with no possible replacement. But actually the SPI GPS is an optional feature anyway.I did solve it by yielding to RTOS if there is no data to read (if 0xFF was returned) but not within the SPI DMA wait. And yes using DMA to move 1 byte is silly but I have a very unified SPI API which is thread-safe etc (automatic re-initialisation, etc).This is with a lot of ETH activity but > 80% sleep is still good.I think there is a problem with running a load (or any?) tasks at idle priority (0) with the WFI trick because the Idle task runs at 0 itself also. Can't see why though, given that I have set it to pre-emptive so equal tasks still run, switched on each tick. I have now set all those to Low ( 8 ) and carefully tweaked that to handle cases where one task generates data for another.It seems to run. No disable_irq() was used; I don't have any ISRs interacting with the RTOS.This has been a very useful bit of learning for low power products using these chips, although probably I would not do it with a 32F4 because the saving is not much. A 50% power saving just makes the on-chip temp sensor a little less uselessAnd all the deeper-sleep modes involve oscillator startup.

author: Tation
author_rank: Regular Contributor
timestamp: July 21, 2023, 10:45:43 am
content: Quote from: Siwastaja on July 20, 2023, 04:26:58 pmOn Cortex-M, an (un-masked) incoming interrupt will awake the core, even with interrupts disabled. The ISR will not be invoked, though, until interrupts are re-enabled.Seen similar constructs in bare metal environments, when some check of shared resources is needed before deciding to execute, or not, WFI. All that part is put inside a __disable_irq()/__enable_irq() pair. The idiom I have seen (and used) is:Code:__disable_irq();if (all_tasks_idling()) {__WFI();}__enable_irq();where all_tasks_idling() can take a time to check if all tasks are idle. If an ISR arrives during the execution of all_tasks_idling(), after it has checked that interrupt, the function may, incorrectly, return that it is OK to sleep the core. __WFI() will be executed, but it will not sleep the system (WFI cannot sleep if there are pending interrupts), thus interrupts are immediately re-enabled and the interrupt serviced.I do not see the point in:Code:__disable_irq();__WFI();__enable_irq();instead.

author: peter-h
author_rank: Super Contributor
timestamp: July 21, 2023, 11:11:53 am
content: QuoteIsn't the RTOS supposed to be doing this check, in effect?The relevant bit of the code is here:Code:static portTASK_FUNCTION( prvIdleTask, pvParameters ){/* Stop warnings. */( void ) pvParameters;/** THIS IS THE RTOS IDLE TASK - WHICH IS CREATED AUTOMATICALLY WHEN THESCHEDULER IS STARTED. **//* In case a task that has a secure context deletes itself, in which casethe idle task is responsible for deleting the task's secure context, ifany. */portTASK_CALLS_SECURE_FUNCTIONS();for( ;; ){/* See if any tasks have deleted themselves - if so then the idle taskis responsible for freeing the deleted task's TCB and stack. */prvCheckTasksWaitingTermination();#if ( configUSE_PREEMPTION == 0 ){/* If we are not using preemption we keep forcing a task switch tosee if any other task has become available. If we are usingpreemption we don't need to do this as any task becoming availablewill automatically get the processor anyway. */taskYIELD();}#endif /* configUSE_PREEMPTION */#if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ){/* When using preemption tasks of equal priority will betimesliced. If a task that is sharing the idle priority is readyto run then the idle task should yield before the end of thetimeslice.A critical region is not required here as we are just reading fromthe list, and an occasional incorrect value will not matter. Ifthe ready list at the idle priority contains more than one taskthen a task other than the idle task is ready to execute. */if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) 1 ){taskYIELD();}else{mtCOVERAGE_TEST_MARKER();}}#endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) */#if ( configUSE_IDLE_HOOK == 1 ){//extern void vApplicationIdleHook( void );/* Call the user defined function from within the idle task. Thisallows the application designer to add background functionalitywithout the overhead of a separate task.NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES,CALL A FUNCTION THAT MIGHT BLOCK. */vApplicationIdleHook();}#endif /* configUSE_IDLE_HOOK */But maybe you are referring to checking stuff outside the RTOS e.g. whether some bit of hardware needs attention, but has not yet done an interrupt, but you don't want to wait potentially 1ms for that interrupt to be serviced? I don't understand that, because an interrupt will terminate the WFI within 5 CPU clocks anyway. I have looked online for examples of what might go inside vApplicationIdleHook() but have not found any.This is pretty impressive. No ETH, USB VCP debugs off, no SPI GPS

author: WatchfulEye
author_rank: Regular Contributor
timestamp: July 21, 2023, 08:02:18 pm
content: Quote from: peter-h on July 21, 2023, 11:11:53 amYes. The key issue is that if you depend on tasks which have low latency requirements, correct implementation is essential - and this requires that the Sleep instruction (wfi) must be within the same critical section as the idleness check.The problem with just adding wfi as a hook into the idle task, is that you don't have access to the idleness check, and therefore can't wrap it with the critical section (unless there is an OS API to allow you to perform the check again ,in which case you can wrap it).While I'm not familiar with freeRTOS, some quick searching suggests that it does support CPU sleep modes on cortex-M cores, and has an optional OS feature called "tickless idle" which has a task scheduler which does exactly this.

author: Siwastaja
author_rank: Super Contributor
timestamp: July 22, 2023, 05:26:33 am
content: Indeed all of this is the responsibility of the OS. If the OS does not implement such primitive feature, it's not worth using IMHO. Having to modify the OS and glue such fundamental features to it is absolutely nuts.

author: SiliconWizard
author_rank: Super Contributor
timestamp: July 22, 2023, 05:52:43 am
content: Quote from: Siwastaja on July 22, 2023, 05:26:33 amAgreed.

author: peter-h
author_rank: Super Contributor
timestamp: July 22, 2023, 07:47:00 am
content: QuoteI am trying to work out the meaning of this - since my RTOS just switches at 1kHz - and I reckon it refers to a scenario where one is using an ISR to trigger an RTOS task.I am not doing any of that. My system is simple:- 1kHz task switching- co-operative tasks (doing taskyield, or more often osDelay(1), or osDelay(lots) if there is really nothing to do)- async stuff is all normal interrupts- no interaction between above ISRs and the RTOS- the RTOS code provides mutexes (which I use a lot) and inter-task messages (which I don't use)The RTOS I wrote way back (Z180/Z280) would have done all of the above perfectly well, except it didn't do priorities, but the application didn't need them.I did look at the "tickless" mode but could not see a need. Probably misunderstood it...Why FreeRTOS? It was available as a prepackaged module from ST, in Cube IDE. It also works solidly.

author: hans
author_rank: Super Contributor
timestamp: July 22, 2023, 08:53:47 am
content: Just going to interject my 2 cents after this discussion has been going..If you have access to a JLink then also have a look at a tool from called SystemView. You can also use it to profile your application CPU time, but not only in a % count, but also on a time view graph with all the context switches and IRQs that are bouncing around in the system. It can be useful to detect issues like priority inversion and such. I think there is a FreeRTOS port for it.It runs off Segger's RTT backend, which is a FIFO structure to provide tracing data to the host machine. The application puts in event messages whenever it is doing something, so this is a instrumentation profiler, and the host just empties the buffer as fast as it can. I think there is also a second channel to have printf() messages being redirected, so you can annotate your logs.Unfortunately, because this is a commercial project I presume, it's not free though.. But if you need to dig a lot deeper into this it might be of use. Now a sampling profiler is usually also fine to get a rough idea what is costing CPU time. Even human-based-sampler by running the application and halting it a couple of times with a random distribution is usually good enough to get an idea.If you want to have your application be as robust as possible against timing side effects then having everything event based is ideal. Usually all my RTOS tasks, if I use one, enter an infinite loop and then wait for signals/queue data before they start working on a particular command/action. Those signals are sent by other tasks and/or IRQs. This means that tasks are not time-based sleeping but instead waiting all the time. If they have work they go right into it. Its similar (but more overhead) to an event based framework, which doesnt need a RTOS kernel at all but then looses preemptive switching, of which I may want to transition to in the future (I've some ideas for this that are tailored towards low-power applications, which as you may know is my largest interest).In the contrary, as Doctorandus says, if your application is polling instead with osDelay()s its more prone to falling over when the CPU load changes drastically. Now it doesnt have to be "dramatic" like the application hangs up, but its throughput bound may be less for example.I think that a WFI(); would always be safe to add in a bare idle() task. The scheduler tick of a RTOS is usually ran on a timer (=IRQ), or with some parts on demand (usually also originating from an IRQ or a chain of task executions). The context switching code may also run in its own IRQ for some RTOS designs. E.g. if you have an IRQ that sends data to a task to process further, then the RTOS could check in its osSignalSet() function if that task was waiting for that signal and if it is OK to do a preemptive switch straight away (e.g. based on task priorities). If you write your application completely based on these kinds of signals or other IPC constructions (like queues etc.) then you could disable the scheduler tick, as it wont contribute anything to the scheduling behaviour as all is handled "on demand" in e.g. the osSignalSet (task activation) and osSignalWait (task deactivation) functions. You would probably loose the osDelay() functons though, as thats why the scheduler runs on a fixed time interval (1kHz=1ms ticks)

author: peter-h
author_rank: Super Contributor
timestamp: July 22, 2023, 03:06:28 pm
content: Systemview is €1480 plus €296/year for support. Percepio is similarly priced. That's a bit too much, for what I would hope is a simple RTOS application.QuoteI use that a lot. It is also the primary means to yield to RTOS.

author: mikerj
author_rank: Super Contributor
timestamp: July 22, 2023, 04:51:53 pm
content: If you want to yield to switch context (rather than delay) there is taskYIELD().

author: peter-h
author_rank: Super Contributor
timestamp: July 22, 2023, 04:59:37 pm
content: Indeed; I have used both.taskYIELD is arguably not as useful as osDelay(1) because the former yields only to same or higher priority tasks, whereas the latter lets everything else run.QuoteWhat I was getting at is that vApplicationIdleHook() gets run only if the RTOS idle task has nothing to do. So it is already doing the right thing. All you need to do is add WFI - or some other hardware-specific code.

author: peter-h
author_rank: Super Contributor
timestamp: July 24, 2023, 11:04:28 am
content: Can anyone think of an example of what else can be turned off after WFI i.e. for the remainder of the 1ms tick? A timer could be worth a few mA but stopping a timer is usually a completely useless thing to do.I found no examples online.

author: Tation
author_rank: Regular Contributor
timestamp: July 25, 2023, 10:44:39 am
content: Quote from: peter-h on July 21, 2023, 11:11:53 amQuote from: Siwastaja on July 22, 2023, 05:26:33 amAs I said, I've seen this:Code:__disable_irq();if (all_tasks_idling()) {__WFI();}__enable_irq();inbare-metalscenarios. An RTOS takes care of this, except for the __WFI(), as the user may want or not to activate sleep, or different depths of sleep.My aim was to illustrate when and where use __disable_irq()/__enable_irq() around __WFI().