xPortStartScheduler fails on configASSERT, STM32F446, IAR8.50

I am bringing up STM32F446 in IAR8.50, FreeRTOS 10.2.0
I have numerous projects running in this environment using STM32F401, F412, F427. This is the first time for F446.
configASSERT in xPortStartScheduler fails verifying configPRIO_BITS.
I see a similar problem described here :
[Bringing up a SAM4S with Keil and FreeRTOS, Failure in XPortStartScheduler with configPRIO_BITS check]
I can clearly see where the problem comes from. The sequence that writes 0xFF to NVIC_IPR0 and then reads back the value, that is supposed to show only the active bits, does not work as intended, the read returns the same 0xFF:

	/* Determine the number of priority bits available.  First write to all
	possible bits. */
	*pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE;

	/* Read the value back to see how many bits stuck. */
	ucMaxPriorityValue = *pucFirstUserPriorityRegister;

As a result the ‘while’ loop below wraps around and the assert that follows it fails.
This seems to be a hardware problem. My question is - am I the only one who sees this, has anybody seen this before?


This may be an optimization issue; the compiler may believe that input and output must be the same and therefore “optimize” away the read. Can you post the generated assembly code?

That was my first guess, but no, this is IAR we are talking about, it does not optimize “volatile” data access.

//  245 		volatile uint32_t ulOriginalPriority;
//  246 		volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER );
        LDR.N    R4,??DataTable7_4  ;; 0xe000e400
//  247 		volatile uint8_t ucMaxPriorityValue;
//  248 
//  249 		/* Determine the maximum priority from which ISR safe FreeRTOS API
//  250 		functions can be called.  ISR safe functions are those that end in
//  251 		"FromISR".  FreeRTOS maintains separate thread and ISR API functions to
//  252 		ensure interrupt entry is as fast and simple as possible.
//  253 
//  254 		Save the interrupt priority value that is about to be clobbered. */
//  255 		ulOriginalPriority = *pucFirstUserPriorityRegister;
        LDRB     R0,[R4, #+0]
        STR      R0,[SP, #+4]
//  256 
//  257 		/* Determine the number of priority bits available.  First write to all
//  258 		possible bits. */
//  259 		*pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE;
        MOVS     R0,#+255
        STRB     R0,[R4, #+0]
//  260 
//  261 		/* Read the value back to see how many bits stuck. */
//  262 		ucMaxPriorityValue = *pucFirstUserPriorityRegister;
        LDRB     R0,[R4, #+0]
        STRB     R0,[SP, #+0]

It might be a bus timing issue, a read access to the IPR register follows the write immediately.

I’ve only seen this behaviour in QEMU - which doesn’t seem to model the NVIC correctly.

As an experiment, update the line:

*pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE;


__asm volatile( "isb" ::: "memory" );
__asm volatile( "dsb" ::: "memory" );
*pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE;
__asm volatile( "isb" ::: "memory" );
__asm volatile( "dsb" ::: "memory" );

That adds barriers to ensure the write to the register takes before moving on to reading back.

Also, does this part use a cache?

I think there is a problem elsewhere. STM32F446 is just another variant of a STM32F4 as your other STM32F4 MCUs . They should be equal with regards to NVIC.
Did you check the erratas and the basic (clock) MCU setup ?

Along the lines of what Hartmut wrote: Is this the first register access in your startup sequence? If should not be because normally there is a lot of MCU initialization going on before that. Do all other registers read back correctly?

Richard, that was a false alarm after all. That behavior was found on a remote setup. I can not reproduce it on my local setup and after some careful cleaning the remote setup now builds and runs correctly.
Thank you so much