About the "configKERNEL INTERRUPT PRIORITY" value

(For some reason, configKERNEL_INTERRUPT_PRIORITY is considered too long a word to be in the title)

Hi all,

Sorry for the noise, but I am feeling really thick at the moment and struggling to get a grasp of something which seems to be documented everywhere and explained multiple times, but somehow is not getting through to me. Things have a habit of clicking with me when I try to explain my thoughts, so bear with me. :slight_smile:

I have also read through this thread but still I dont seem to be getting it :roll_eyes:: What is config KERNEL INTERRUPT PRIORITY?

In my m68k port I have defined both configKERNEL_INTERRUPT_PRIORITY and configMAX_API_CALL_INTERRUPT_PRIORITY.

On the m68k, IPL 0 means “all interrupts are enabled”, but is not an IRQ level itself. The minimum IRQ is 1, so in my current project my tick interrupt is configured for IRQ 1 as it is recommended to be the lowest available IRQ level. (c1600_freertos_tcp/FreeRTOSConfig.h at master · tomstorey/c1600_freertos_tcp · GitHub)

The problem is arising when I look at my implementation of portENABLE_INTERRUPTS (FreeRTOS-Kernel/portmacro.h at new_task_switch · tomstorey/FreeRTOS-Kernel · GitHub). My fuzzy understanding was that this should fully re-enable all interrupts, i.e. configure the IPL to 0. However, if I do this, I get a lot of crashes due to null pointers. Configuring IPL 1 is generally leaps and bounds more stable (although I still seem to be getting crashes due to null pointers for some reason right now… this has been very temperamental)

But it doesnt seem right to “re-enable” interrupts at a priority above 0? Surely if portDISABLE_INTERRUPTS is setting the IPL to configMAX_API_CALL_INTERRUPT_PRIORITY to prevent API-calling ISRs from interrupting the kernel, then portENABLE_INTERRUPTS should restore the IPL to 0 to re-enable all interrupts as the name suggests?

What am I missing?


Actually, in my understanding, the kernel interrupt priority must be lowest. This is because a context switch happens at kernel interrupt time, and in order to save a valid task context to which to switch back to orderly, the kernel interrupt must never interrupt ISRs, only tasks.

And yes, enabling interrupts will (must) also enable the kernel interrupt.

Not quire sure I follow what you said there… To clarify, on the 68k, interrupt priority level 0 would permit IRQ1 plus all higher priority interrupts. My tick interrupt is at IRQ1 and the only peripherals I am using at present (Ethernet controller) are at IRQ2.

Raising the priority level to 1 would mask IRQ1, but still allow IRQ2 and above. And so forth until level 7 which cannot be masked and is effectively the NMI.

So in my ISRs (such as the tick interrupt, yield, and my peripherals) I raise the priority level to the value of configMAX_API_CALL_INTERRUPT_PRIORITY (4 in my project).

Is this what you were saying?

The problem is if I set the priority back to level 0 in portENABLE_INTERRUPTS, which would seem like the logical thing to do, I very quickly get bus errors as some code within FreeRTOS tries to dereference a null pointer.

For example (I have no memory mapped at address 0, hence bus error):

Bus Error   (08)

D0 0x00000002   D4 0x00000001   A0 0x00000000   A4 0x00000000   SR 0x2400 (EH 0x2700)
D1 0x00000000   D5 0xA5A5A5A5   A1 0x0220C21C   A5 0x02230CA8   PC 0x02002F7A
D2 0x00000008   D6 0xA5A5A5A5   A2 0x02230CC8   FP 0x02230BFC  USP 0x00000000
D3 0x02230CCC   D7 0xA5A5A5A5   A3 0x00000015  SSP 0x02230B60

the code at the PC:

                    listREMOVE_ITEM( &( pxTCB->xStateListItem ) );
 2002f5e:       206a 0014       moveal %a2@(20),%a0
 2002f62:       226a 0008       moveal %a2@(8),%a1
 2002f66:       236a 000c 0008  movel %a2@(12),%a1@(8)
 2002f6c:       226a 000c       moveal %a2@(12),%a1
 2002f70:       236a 0008 0004  movel %a2@(8),%a1@(4)
 2002f76:       260a            movel %a2,%d3
 2002f78:       5883            addql #4,%d3
 2002f7a:       b6a8 0004       cmpl %a0@(4),%d3       <<<<<<<<<<<<<<

which is this line FreeRTOS-Kernel/tasks.c at new_task_switch · tomstorey/FreeRTOS-Kernel · GitHub

The A2 register would appear to correspond with the TaskHandle_t xTaskToNotify parameter of xTaskGenericNotifyFromISR which becomes the pxTCB variable.

If, instead of changing the IPL to 0 I change it to 1 instead, I am far less likely to crash, and it would be up to a context restore to set IPL to 0 at the end of a task switch.

Its really confusing behaviour, and the bus errors arent even consistent. Last night I ran for 10+ minutes without a crash (1.1 million pings responded to with only 2 drops, I only stopped it to see how many packets had been sent), today I’d be lucky to get a couple of seconds worth of pings through before a crash. :confused:

Don’t try to look too close at the immediate fault cause. In an RTOS, typically the code that caused that problem took place long before something else crashes.

I’m not familiar with the 68k port (though I coded for the 68332 and a ColdFire POD many many years).

What I’m interested in is the context switching mechanism. Normally this goes through a software interrupt. Which one is that and what priority is that one assigned to?

Coincidentally, the only issue I ever created in this forum actually dealt with an 68k derivate (ColdFire). Maybe some of the info in there is useful to you (the fault cause actually was exactly that - a problem in the context switch interrupt priority):

ColdFire and 68k are somewhat similar as I understand it. I am using a 68k port that I created myself, with some inspiration from the supplied ColdFire port and others.

There is no software interrupt mechanism on the CPU I am using I believe (68EN360, a bigger brother of the 68332 I believe - it uses a CPU32 core but that is also quite similar, moreso to a 68020 as I recall), so I am using trap #15 to do a yield.

Task switch code is here for a yield: FreeRTOS-Kernel/portasm.S at new_task_switch · tomstorey/FreeRTOS-Kernel · GitHub

That is callable via: FreeRTOS-Kernel/portmacro.h at new_task_switch · tomstorey/FreeRTOS-Kernel · GitHub

And here is my projects tick interrupt which is using a timer that is configured to generate interrupts at IRQ1: c1600_freertos_tcp/PITInterruptHandler.S at master · tomstorey/c1600_freertos_tcp · GitHub

Timer setup code: c1600_freertos_tcp/main.c at master · tomstorey/c1600_freertos_tcp · GitHub

I guess the problem with doing a yield is that it happens at what ever IPL the code that calls it is running in, since it isnt possible to perform a trap at a specified IPL (that I know of). So I raise it myself in the trap handler, and the RTE instruction at the end will restore the previous IPL.

I almost had a thought that maybe IRQs were only masked below the IPL, but the datasheet confirms it is at and below the IPL. So e.g. IPL4 means IRQ1-4 are masked, 5-7 can still happen.

Thanks for lending an ear. :slight_smile:

well, a TRAP IS a software interrupt handler. There must also be an entry in your IVT for trap 15, and it is subject to masking.

I don’t remember what the priority and masking rules for your POD is, but if the priority of your TRAP handler is not the lowest interrupt priority, it may well explain your problems.

EDIT: I just checked with the 68332 Reference Manual, which of course may differ in details from your POD, but the basic operation should be compatible. The TRAP vectors are in locations 32-47 of your IVT, and it seems as if all traps have a higher priority than all interrupts, which in my understanding is not FreeRTOS architecture compliant. You should try rewriting your context switch architecture.

Editedit: Maybe this here link provides more useful info:, even if it already quite old: trap instruction for taskYIELD

The traps dont have any assignable priority and cant be masked - they just execute “in-line” with the current executing code.

That is to say, if I want to cause an interrupt that will be deferred and run after the current code completes and drops back to a lower IPL, that isnt possible with a trap. You call the trap and it runs immediately.

There is indeed an entry in the IVT for my trap #15 handler, and it does work like this - I have 5 tasks that run and blink some LEDs, and they are definitely running.

The issue is under load I suppose, when I ping flood the device, this consumes quite a substantial amount of CPU as can be evidenced when the blinking LEDs slow to a crawl. And maybe something is out of whack between interrupts at that time.

Im having an idea that perhaps a task switch needs to be a bit more tightly coupled with enabling/disabling interrupts…

Say I call trap #15 and the IPL == the API priority, I set a flag somewhere to say a task switch is required, and then exit the trap handler without effecting a task switch.

Then in something like portENABLE_INTERRUPTS I check this flag, and if it is set - after lowering the IPL - then I execute a task switch.

That then kind of emulates the deferred interrupt behaviour. Does that sound reasonable?

I dont know if FreeRTOS requires that a task switch doesnt happen in the middle of a critical section, but this sounds like it would prevent that from happening.

ok, just make sure to observe Richard’s advice in his last post in the thread I added in my most recent response - if nested critical sections are required, make sure to record the most recent mask when reentering masking so your crit section management is fully “unwinding at the right levels.”

A trap instruction will push the status register (which holds the IPL) onto the stack along with the return address, and an RTE (return from exception) then pops that status register value back (thus restoring any previous IPL and flags), so it kind of nicely keeps track of CPU state.

As for nesting critical sections, the existing port functions (vPortEnterCritical and vPortExitCritical) do not seem to record the current IPL, instead just enable or disable according to the API and kernel IPL values in your config file while keeping track of what to do with a counter (i.e. only re-enable once the counter hits 0).

There are two macros (portSET_INTERRUPT_MASK_FROM_ISR and portCLEAR_INTERRUPT_MASK_FROM_ISR) that call ulPortSetIPL which allows the current IPL to be exchanged, so I suppose in a situation where it needs to be maintained, that is used instead.

Soooo, if I understand everything correctly, then the IPL should be maintained appropriately as long as the correct combinations of macros were used.

Only one way to find out though I guess. :slight_smile:

Holy balls what a difference that made! :star_struck:

The LED blinking tasks are actually managing to run at a half reasonable rate as well.

I guess that was it, or at least a significant part of it.

No bus errors so far either, so fingers crossed…

Changes for reference: Major improvement to task switching · tomstorey/FreeRTOS-Kernel@c04a7db · GitHub

Many thanks for your input and guidance @RAc !