CM40x has multiple clock domains/buses: AHB, PAB, APB, AXI. The necessary bus conversions add latency as MMR reads/writes traverse through the fabric (typically when a peripheral register is accessed from the core domain). In other words, each MMR write would need to stall code execution until the operation made the round-trip through the fabric This can be over 10 SYSCLKs per write.
For most MMR writes however, it's acceptable to do the write and proceed code execution right away. There is no need to wait until the write has actually reached the peripheral. If this mode is acceptable CM40x supports "Posted Writes" which immediately acknowledge writes before the peripherals knows if they were successful. This brings down the latency considerably but is some cases it requires special attention from the programmer.
Consider an ISR with only a few instructions in it. For this example, we look at ISR for PWM0 SYNC but it applies to any interrupt:
void PWM0_SYNC_ISR(uint32_t iid, void* handlerArg){
*pREG_PWM0_STAT = BITM_PWM_STAT_TMR0PER;
*pREG_PWM0_ILAT = BITM_PWM_ILAT_TMR0PER;
}
Here the ISR only contains a few instructions - clear of STAT and ILAT register. These writes has to transverse across clock domains and assuming Posted Writes is enabled, it means the clear of IALT has not taken effect before the end of the ISR. To the interrupt handler it appears like IALT is set again as soon as the ISR is exited, leading to a new execution of the interrupt - Double Execution. This is an unwanted consequence of using Posted Writes.
To avoid this, Posted Writes can be disabled. However, for the most part, Posted Writes is desirable and significantly improves code performance. Therefore, it is better to leave Posted Writes enabled and deal with the special cases.
Another work-around is to clear ILAT at the top of the ISR. If the ISR has several instructions in it, the execution time will allow for the write to go though before exiting the ISR.
In cases where the ISR only has a few instructions in it (like the example above), the programmer must take special care to make sure the write goes through. For that the assembler instructions sev and isb are helpful. This ensures synchronization across clock domains and flushes the pipeline to the processor.
With synchronization, the ISR looks like this:
void PWM0_SYNC_ISR(uint32_t iid, void* handlerArg){
*pREG_PWM0_STAT = BITM_PWM_STAT_TMR0PER;
*pREG_PWM0_ILAT = BITM_PWM_ILAT_TMR0PER;
asm("sev;isb;");
}