So my guess would be that the code doesn't need disabling the interrupts because the task switch timer interrupt has a much lower prority.
The scenario is a foreground program (which is an RTOS task, like everything in this product after main() completes) puts TX data into that buffer, and then a TXE interrupt takes data out of it. And similarly, on the RX side, I have an RXNE interrupt putting data into the buffer and an RTOS task taking it out.
Accompanying this I have functions like opqspace() and ipqcount() which do the obvious thing. Because my buffers don't have a "quesize" value (the difference between the pointers is the queue size) these functions do the pointer subtraction.
This scheme wastes one byte compared to the one where you maintain a separate "quesize" value. These functions also need to do the job atomically, because the pointers chase each other round the buffer - example:
// Get the number of bytes available in the receive buffer for this port
// Port is numbered 0 to 4, with 0 being the USB VCP
uint16_t serial_get_ipqcount(uint8_t port)
{
int32_t count;
portENTER_CRITICAL(); // prevent interrupts while updating the index
// Port number is 1 to 4, so -1 to offset from 0
count = (int32_t)serial_receive_buffer_rx_index[port-1] - (int32_t)serial_receive_buffer_get_index[port-1];
if (count < 0)
{
// rx index has wrapped and is now before the get index
count = SERIAL_RX_BUFFER_SIZE + count;
}
if (count > (SERIAL_RX_BUFFER_SIZE-1))
{
// This should never be allowed to happen
count = 0;
serial_receive_buffer_rx_index[port-1] = 0;
serial_receive_buffer_get_index[port-1] = 0;
}
portEXIT_CRITICAL();
}
return (uint16_t) count;
}
I am not calling any bit of FreeRTOS from an ISR, even though this is supposedly possible.I have changed the portENTER_CRITICAL() and portEXIT_CRITICAL() to block ints with a priority of >0 (was >5 previously as default on FreeRTOS, because the intention of these macros is
only to stop an RTOS task switch, while allowing most/all interrupts). This in effect does a DI/EI (because I have no priority 0 interrupts) but will look into:
ARM does have DI/EI equivalent: CPSID I/CPSIE I
These appear to need
__asm("CPSID i");
// ...some code which needs to be protected from interrupts...
__asm("CPSIE i");Does that look right? I have seen other and much longer variations of the asm constructs. I don't think the above work on GCC v10. Like this
/**
\brief Enable IRQ Interrupts
\details Enables IRQ interrupts by clearing the I-bit in the CPSR.
Can only be executed in Privileged modes.
*/
__attribute__( ( always_inline ) ) __STATIC_INLINE void __enable_irq(void)
{
__ASM volatile ("cpsie i" : : : "memory");
}
/**
\brief Disable IRQ Interrupts
\details Disables IRQ interrupts by setting the I-bit in the CPSR.
Can only be executed in Privileged modes.
*/
__attribute__( ( always_inline ) ) __STATIC_INLINE void __disable_irq(void)
{
__ASM volatile ("cpsid i" : : : "memory");
}Without volatile, compiler is free to assume nothing in the buffer changed (both data and indices) and optimize out related code entirely.
And without volatile, compiler is free to reorder accesses, e.g "write data first then increment index" may happen in the opposite order than written in source.
We did this in that long "is compiler smart enough" thread, etc, but the reality is that practically nobody uses "volatile" on buffers

I guess that's because most are declared "static", or "extern" at some place. But even where I have a buffer as a local variable in a function, it is still not getting removed.
Makes me wonder how anybody's code ever worked... My suspicion is that compiler writers don't just want to break everybody's code.