portable/GCC/ARM_CM3/port.c:vPortValidateInterruptPriority requires that SCB_AIRCR.PRIGROUP be zero. It assumes that zero means that all the bits are used for group/preemption priority and none for a subpriority.
On STM32F2/4, only the values 3 through 7 are documented for PRIGROUP, with 3 meaning that there are no subpriority bits. I tried setting PRIGROUP to zero in an attempt to please vPortValidateInterruptPriority, but this causes great confusion in interrupt handling (I still get some interrupts, but others fire just once, never to be seen again).
Setting PRIGROUP to 3 and adapting the test in vPortValidateInterruptPriority accordingly makes things work as expected.
I suppose this has something to do with the STM32 only having four priority bits. Perhaps the correct value for PRIGRP should be calculated as (configPRIO_BITS == 8 ? 0 : 7-configPRIO_BITS) ?
The check for 8 is necessary because - according to Table 4.18 on http://infocenter.arm.com/help/topic/com.arm.doc.dui0552a/Cihehdge.html - even 0 still leaves one subpriority bit, so on a core that actually implemented all 8 bits, the calculation would wrap around 0.
I've seen this on an STM32F207 (CM3) and it seems that the same should also apply to STM32F4xxx (CM4F), and possibly others.
Thanks for your report. I can replicate this on an STM32 and will investigate further. The function was tested on Cortex-M parts from three different manufacturers (two with 4 priority bits, one with three priority bits), but not unfortunately the STM32, so it might be STM32 specific.
I will report back here when I have more information.
This looks like it might be related to a persistent incompatibility between the ST peripheral driver library and generic Cortex-M code.
The binary point (priority group) bits are actually defaulted to zero when the Cortex-M powers up, so on all implementations of the Cortex-M 0 is a valid number as that is Cortex-M specific, not STM32 specific. Zero is a generic value that will work on all chips, and is normally never changed during the lifetime of an application. vPortValidateInterruptPriority() checks that the value is zero. However….
Due to the implementation of the STM32 peripheral library we have always had to provide specific instruction to STM32 users (note numerous support request answers and the bold red text on FreeRTOS.org web pages specific to Cortex-M) to ensure they call NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 ) before starting the scheduler. This is an extra step necessary for STM32 applications only. That call changes the binary point setting, and causes vPortValidateInterruptPriority() to trigger a failure, but is necessary in order to use other STM32 peripheral driver functions. A colleague of mine has just pointed out/reminded me as to why this is.
If you look at the implementation of the function NVIC_Init() library function you will see the following code:
tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))>> 0x08;
tmppre = (0x4 - tmppriority);
Now assume you pass the priority in as 15 when the binary point register is 0. tmppriority will then be calculated as:
tmppriority = (0x700 - 0) >> 8;
On the next line the 4 is hard coded, resulting in tmppre being calculated as:
tmppre = 4 - 7;
… which is negative (actually as this is unsigned maths it is a huge positive), and you have a problem.
The question now is, what to do about this? We can't have a portable layer that is specific to the STM32, nor can we force users of all other chips to set a binary point value. I suspect we will have to issue a maintenance release that implements
vPortValidateInterruptPriority() to check for either 0 or an acceptable value given the number of implemented priority bits. That will naturally add some overhead.
I appreciate you pointed this out so quickly.
Thanks for the amazingly quick analysis ! It is indeed unfortunate that this ST quirk produces a regression in FreeRTOS.
I think that allowing all values that produce the desired behaviour would be the best choice. With a decent compiler, a test for PRIGROUP >= (configPRIO_BITS == 8 ? 0 : 7-configPRIO_BITS) should have negligible overhead (if any) compared to the current check, since all the calculations are with constants anyway.
I have updated the code to perform a "less than or equal to a maximum permissible value" check in place of the "equals zero" check. I have tested this on the STM32 first this time :o)
Currently I have it in GCC only. Which compiler are you using? If you are using GCC I can send you the file to try. If you are using a different compiler then I will update the port layer for that compiler next so you can try it.
If you want to try it then send an email to the email address on the Real Time Engineers Contact page so I have your address. Please don't post your email address to this forum.
Yes, I use GCC. I sent a mail to the info address (there's no openly visible e-mail address on the contact page). BTW, my e-mail address isn't exactly a secret, so getting an extra serving of spam wouldn't be a concern :)
I assume this problem is also occuring with my setup: I am using an STM32F4, since the latest FreeRTOS 7.5.0 I get stuck with vPortValidateInterruptPriority(). The priority settings are right, as the documentation of FreeRTOS instructs it: (PRIORITY_GROUP to 4 and all my ISRs which use FreeRTOS API calls have a priority lower than configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY = 5). In my case the USART2 Priority is set to 9.
Can you please post your solution here, so that I can try it, or are the chages too much to post it here? I am also using GCC.
Is there any chance to get an answer on this?
thanks in advance, regards
Sorry - I missed your email. The solution is already provided in the FreeRTOS V7.5.2 maintenance release available from Sourceforge.
Thanks! I will try it!