The right way to clear an interrupt flag on STM32 - arm

I'm developping a bare-metal project on a STM32L4 and I'm starting from an existing code base.
The ISRs have been implemented the following way:
read interrupt status in the peripheral to know what event(s) provoked the interrupt
do something
clear the flags that have read at the beginning.
Is it the right way to clear the flag ? Shouldn't the flags be cleared at the very beginning of the ISR ? My understanding is that, if the same peripheral event is happening a second time during step 2, it will not provoke a second IRQ so it would be lost. On the other hand if you clear the flag as soon as you can, this second event would pulse the interrupt whose state in the CPU would change to "pending and active": a second IRQ would happen.
PS: From STM32 Processor Programming Manual I read: "STM32 interrupts are both level-sensitive and pulse-sensitive".

Definitely at the beginning (unless you have special reasons in the program logic) as some time is needed the for actual write to the flag clear register to propagate through the buses.
If you decide for some reason to put it at the end of the interrupt you should leave some instructions, place the barrier instruction or read back the register before the interrupt routine return to make sure that the clear operation has propagated across the buses. Otherwise you may have a "phantom" duplicate routine calls.

Related

Avoiding Race Condition with event queue in event driven embedded system

I am trying to program stm32 and use event driven architecture. For example I am going to toggle a pin when timer interrupt occurs and transfer some data to external flash when ADC DMA buffer full interrupt occurs and so on..
There will be multiple interrupt sources each with same priority which disables nesting.
I will use the interrupts to set a flag to signal my main that interrupt occured and process data inside main. There will be no processing/instruction inside ISRs.
What bothers me is that accessing a variable(flags in this case) in main and ISRs may cause race condition bug in the long run.
So I want to use an circular event queue instead of flags.
Only ISRs will be able to write to event queue buffer and increment "head".
Only main will be able to read the event queue(and execute instructions according to event) and increment "tail".
Since ISR nesting is disabled and each ISR will access different element of event queue array and main function will only react when there is new event on event queue, race condition is avoided right? or am I missing something?
Please correct me if I am doing something wrong.
Thank you.
If the interrupt only sets a variable and nothing gets done until main context is ready to do it then there is really no reason to have an interrupt at all.
For example: if you get a DMA complete hardware interrupt and set a variable then all you have achieved is to copy one bit of information from a hardware register to a variable. You could have much simpler code with identical performance and less potential for error by instead of polling a variable just not enabling the interrupt and polling the hardware flag directly.
Only enable the interrupt if you are actually going to do something in interrupt context that cannot wait, for example: reading a UART received data register so that the next character received doesn't overflow the buffer.
If after the interrupt has done the thing that cannot wait it then needs to communicate something with main-context then you need to have shared data. This will mean that you need some way of preventing race-conditions. The simplest way is atomic access with only one side writing to the data item. If that is not sufficient then the old-fashioned way is to turn off interrupts when main context is accessing the shared data. There are more complicated ways using LDREX/STREX instructions but you should only explore these once you are sure that the simple way isn't good enough for your application.

How Can I save some data before hardware reset of microcontroller?

I'm working on one of Freesacle micro controller. This microcontroller has several reset sources (e.g. clock monitor reset, watchdog reset and ...).
Suppose that because of watchdog, my micro controller is reset. How can I save some data just before reset happens. I mean for example how can I understand that where had been the program counter just before watchdog reset. With this method I want to know where I have error (in another words long process) that causes watchdog reset.
Most Freescale MCUs work like this:
RAM is preserved after watchdog reset. But probably not after LVD reset and certainly not after power-on reset. This is in most cases completely undocumented.
The MCU will either have a status register where you can check the reset cause (for example HCS08, MPC5x, Kinetis), or it will have special reset vectors for different reset causes (for example HC11, HCS12, Coldfire).
There is no way to save anything upon reset. Reset happens and only afterwards can you find out what caused the reset.
It is however possible to reserve a chunk of RAM as a special segment. Upon power-on reset, you can initialize this segment by setting everything to zero. If you get a watchdog reset, you can assume that this RAM segment is still valid and intact. So you don't initialize it, but leave it as it is. This method enables you to save variable values across reset. Probably - this is not well documented for most MCU families. I have used this trick at least on HCS08, HCS12 and MPC56.
As for the program counter, you are out of luck. It is reset with no means to recover it. Meaning that the only way to find out where a watchdog reset occurred is the tedious old school way of moving a breakpoint bit by bit down your code, run the program and check if it reached the breakpoint.
Though in case of modern MCUs like MPC56 or Cortex M, you simply check the trace buffer and see what code that caused the reset. Not only do you get the PC, you get to see the C source code. But you might need a professional, Eclipse-free tool chain to do this.
Depending on your microcontroller you may get Reset Reason, but getting previous program counter (PC/IP) after reset is not possible.
Most of modern microcontrollers have provision for Watchdog Interrupt Instead of reset.
You can configure watchdog peripheral to enable interrupt , In that ISR you can check stored context on stack. ( You can take help from JTAG debugger to check call stack).
There are multiple debugging methods available if your micro-controller dosent support above method.
e.g
In simple while(1) based architecture you can use a HW timer and restart it after some section of code. In Timer ISR you will know which code section is consuming long enough than the timer.
Two things:
Write a log! And rotate that log to keep the last 30 min. or whatever reasonable amount of time you think you need to reproduce the error. Where the log stops, you can see what happened just before that. Even in production-level devices there is some level of logging.
(Less, practical) You can attach a debugger to nearly every micrcontroller and step through the code. Probably put a break-point that is hit just before you enter the critical section of code. Some IDEs/uCs allow having "data-breakpoints" that get triggered when certain variables contain certain values.
Disclaimer: I am not familiar with the exact microcontroller that you are using.
It is written in your manual.
I don't know that specific processor but in most microprocessors a watchdog reset is a soft reset, meaning that certain registers will keep information about the reset source and sometimes reason.
You need to post more specific information on your Freescale μC for this be answered properly.
Even if you could get the Program Counter before reset, it wouldn't be advisable to blindly set the program counter to another after reset --- as there would likely have been stack and heap information as well as the data itself may also have changed.
It depends on what you want to preserve after reset, certain behaviour or data? Volatile memory may or may not have been cleared after watchdog (see your uC datasheet) and you will be able to detect a reset after checking reset registers (again see your uC datasheet). By detecting a reset and checking volatile memory you may be able to prepare your uC to restart in a way that you'd prefer after the unlikely event of a reset occurring. You could create a global value and set it to a particular value in global scope, then if it resets, check the value against it when a reset event occurs -- if it is the same, you could assume other memory may also be the same. If volatile memory is not an option you'll need to have a look at the datasheet for non-volatile options, however it is also advisable not to continually write to non-volatile memory due to writing limitations.
The only reliable solution is to use a debugger with trace capability if your chip supports embedded instruction trace.
Some devices have an option to redirect the watchdog timeout to an interrupt rather then a reset. This would allow you to write the watchdog timeout handler much like an exception handler and dump or store the stack information including the return address which will indicate the location the interrupt occurred.
However in some cases, neither solution is a reliable method of achieving your aim. In a multi-tasking environment or system with interrupt handlers, the code running when the watchdog timeout occurs may not be the process that is causing the problem.

Changing priority of current interrupt in NVIC

I have a conundrum. The part I am using (NXP KL27, Cortex-M0+) has an errata in its I2C peripheral such that during receive there is no flow control. As a result, it needs to be a high priority interrupt. I am also using a UART that, by its asynchronous nature, has no flow control on its receive. As a result, it needs to be a high priority interrupt.
Circular Priority
The I2C interrupt needs to be higher priority than the UART interrupt, otherwise an incoming byte can get demolished in the shift register before being read. It really shouldn't work this way, but that's the errata, and so it needs to be higher priority.
The UART interrupt needs to be higher priority than the I2C interrupt, because to close out an I2C transaction the driver (from NXP's KSDK) needs to set a flag and wait for a status bit. During this wait incoming characters on the UART can overflow the non-FIFO'd shift register.
In trying to solve an issue with the UART, I discovered this circular dependency. The initial issue saw characters disappearing from the UART receive and the overrun flag being set. When swapping priorities, the UART was rock solid, never missing a character, but I2C transactions ended up stalling due to overruns.
Possible Solution
The solution I came up with involves changing interrupt priorities on the fly. When the I2C driver is closing out a transaction, it is not receiving, which means the errata that causes bytes to flow in uncontrolled is not an issue. I would like to demote the I2C interrupt priority in the NVIC during this time so that the UART is able to take priority over it, thus making the UART happy (and not missing any characters).
Question
I haven't been able to find anything from ARM that states whether changing the interrupt priority while executing that interrupt will take effect immediately, or if the priority of the current interrupt was latched in when it started executing. I am hoping someone can definitely save from the depths of their knowledge of the architecture or from experience that changing the priority will take effect immediately, or not.
Other Possible Solutions
There are a number of other possible solutions and reasons why they are undesirable. Refactoring the I2C driver to handle the loop in the process context rather than interrupt context would be a significant effort digging into the vendor code and affects the application code that calls into it. Using DMA for either of these peripherals uses up a non-trivial amount of the DMA channels available and incurs the overhead of setting up DMA for each transaction (and also affects the application code that calls into the drivers).
I am open to other solutions, but hesitant to go down any path that causes significant changes to the vendor code.
Test
I have an idea for an experiment to test how the NVIC works in this regard, but I thought I would check here first. If I get to the experiment, I will post a follow-up answer with the results.
Architecturally, this appears to be UNPREDICTABLE (changing the priority of a currently active exception). There seems to be no logic in place to enforce more consistent behavior (i.e. the registration logic you are concerned about is not obviously present in M0/M0+).
This means that if you test for the effectiveness of your workaround, it will probably appear to work - and in your constrained scenario it might be effective. However, there is no guarantee that the same code will work on M3, or that it works reliably in all scenarios (for example any interaction with debug). You might even observe some completely unpredictable corner case behavior, but the area-constrained
This is specified as unpredictable in section B1.5.4 of the ARM v6-M ARM.
For v7-M (B1.5.4, Exception Priorities and preemption)
This definition of execution priority means that an exception handler
can be executing at a priority that is higher than the priority of the
corresponding exception. In particular, if a handler reduces the
priority of its corresponding exception, the execution priority falls
only to the priority of the highest-priority preempted exception.
Therefore, reducing the priority of the current exception never
permits:
A preempted exception to preempt the current exception handler.
Inversion of the priority of preempted exceptions.
The v7-M aspect clarifies some of the complex scenarios which must be avoided if you attempt to make use of the unpredictable behavior which you have identified as useful with the M0+ part.
Experiment
I coded up a quick experiment today to test this behavior on my particular variant of the Cortex M0+. I am leaving this as an unaccepted answer, and I believe #Sean Houlihane's answer is the most correct (i.e. it is unpredictable). I still wanted to test the behavior and report in under the specific circumstances for while I am using it.
The experiment was performed on a FRDM-KL43Z board. It has a red LED, a green LED, and two push buttons. The application performed some setup of the GPIO and interrupts and then sat in an infinite loop.
Button 1: Button 1's interrupt handler was initialized to midscale priority (0x80). On every falling edge of button 1 it would pend the interrupt. This interrupt would toggle the green LED's state.
Button 2: Button 2's interrupt handler was initialized to midscale priority (0x80), but would be changed as a part of execution. The button 2 interrupt handler would run a loop that lasted approximately 8 seconds (two phases of four), repeating indefinitely. It would turn on the red LED and decrease it's own priority below that of button 1. After the four seconds, it would turn off the red LED and increase it's own priority above that of button 1. After four seconds it would repeat.
Expected Results
If the hypothesis proves to be true, when the red LED is on, pressing button 1 will toggle the green LED, and when the red LED is off, pressing button 1 will have no effect until the red LED turns off. The button 1 interrupt would not execute until the forever looping button 2 interrupt is of a lower priority.
Results
This is the boring section. Everything I expected in the previous section happened.
Conclusion
For the experimental setup (NXP KL43Z Cortex M0+), changing the interrupt priority of the currently executing interrupt takes effect while the interrupt is running. As a result, my hacky workaround of demoting priority during the busy wait and restoring it after should function for what I need.
Edit:
Later Results
Though the experiment was successful, problems started occurring once the workaround for the original issue was implemented. The interaction between the UART and I2C handlers was relatively consistent, but a third peripheral started having very odd behavior in its interrupt handler. Take heed of the warning of UNPREDICTABLE.
One alternative solution could be to defer to another, lower priority, interrupt for the second half of your processing. A good candidate is the PendSV interrupt (if not already in use), which can (only) be triggered from software.
For a more detailed explanation, see this answer to a similar question and this answer about PendSV in general.

DSB on ARM Cortex M4 processors

I have read the ARM documentation and it appears that they say in some places that the Cortex M4 can reorder memory writes, while in other places it indicates that M4 will not.
Specifically I am wondering if the DBM instruction is needed like:
volatile int flag=0;
char buffer[10];
void foo(char c)
{
__ASM volatile ("dbm" : : : "memory");
__disable_irq(); //disable IRQ as we use flag in ISR
buffer[0]=c;
flag=1;
__ASM volatile ("dbm" : : : "memory");
__enable_irq();
}
Uh, it depends on what your flag is, and it also varies from chip to chip.
In case that flag is stored in memory:
DSB is not needed here. An interrupt handler that would access flag would have to load it from memory first. Even if your previous write is still in progress the CPU will make sure that the load following the store will happen in the correct order.
If your flag is stored in peripheral memory:
Now it gets interesting. Lets assume flag is in some hardware peripheral. A write to it may make an interrupt pending or acknowledge an interrupt (aka clear a pending interrupt). Contrary to the memory example above this effect happens without the CPU having to read the flag first. So the automatic ordering of stores and loads won't help you. Also writes to flag may take effect with a surprisingly long delay due to different clock domains between the CPU and the peripheral.
So the following szenario can happen:
you write flag=1 to clear an handled interrupt.
you enable interrupts by calling __enable_irq()
interrupts get enabled, write to flag=1 is still pending.
wheee, an interrupt is pending and the CPU jumps to the interrupt handler.
flag=1 takes effect. You're now in an interrupt handler without anything to do.
Executing a DSB in front of __enable_irq() will prevent this problem because whatever is triggered by flag=1 will be in effect before __enable_irq() executes.
If you think that this case is purely academic: Nope, it's real.
Just think about a real-time clock. These usually runs at 32khz. If you write into it's peripheral space from a CPU running at 64Mhz it can take a whopping 2000 cycles before the write takes effect. Now for real-time clocks the data-sheet usually shows specific sequences that make sure you don't run into this problem.
The same thing can however happen with slow peripherals.
My personal anecdote happened when implementing power-saving late in a project. Everything was working fine. Then we reduced the peripheral clock speed of I²C and SPI peripherals to the lowest possible speed we could get away with. This can save lots of power and extend battery live. What we found out was that suddenly interrupts started to do unexpected things. They seem to fire twice each time wrecking havoc. Putting a DSB at the end of each affected interrupt handler fixed this because - you can guess - the lower clock speed caused us to leave the interrupt handlers before clearing the interrupt source was in effect due to the slow peripheral clock.
This section of the Cortex M4 generic device user guide enumerates the factors which can affect reordering.
the processor can reorder some memory accesses to improve efficiency, providing this does not affect the behavior of the instruction sequence.
the processor has multiple bus interfaces
memory or devices in the memory map have different wait states
some memory accesses are buffered or speculative.
You should also bear in mind that both DSB and ISB are often required (in that order), and that C does not make any guarantees about the ordering (except in-thread volatile accesses).
You will often observe that the short pipeline and instruction sequences can combine in such a way that the race conditions seem unreachable with a specific compiled image, but this isn't something you can rely on. Either the timing conditions might be rare (but possible), or subsequent code changes might change the resulting instruction sequence.

Cortex: NVIC, please demostrate how to make it level or edge detects by software

I have read the ARM document about Cortex-M3 (or M0) and it say it can be used as level sensetive or pulse (edge) interrupt within the NVIC controller. The problem that it rather vague on how to do this, if this is done by software.
I fails to see any kind of register within the NVIC or such that control the type of the interrupt (to select edge or level by adjusting the register bits). So something must be done by software within handler but again it vague in this field.
I like to hear anyone having a way to make it edge or level trigger interrupt by software.
Please demonstrate within the handler code (if this control it) that the make it detect for level or pulse.
If this is level detect, I can hold interrupt active and disable by the handler, until restore by external code for which it re-excute the interrupt. This is what I'm trying to do, but it will not work if this is pulse detect type.
Thx
A document that describes how the Cortex-M3 NIVC handles level or edge (pulse) triggered interrupts can be found here:
Cortex-M3 Devices Generic User Guide, 4.2.9. Level-sensitive and pulse interrupts
This may well be the document you refer to in your question. Joseph Yiu's book, "The Definitive Guide to the ARM Cortex-M3" also has a pretty good description.
There is no particular configuration of the NVIC for these two interrupt signal types - it handles either kind. Essentially, when an interrupt is asserted (whterh level-based or edge triggered) the NVIC latches that status in the SETPENDx register. When the ISR for that interrupt is vectored to, the corresponding bit in the ACTIVEx register will be set and the bit in the SETPENDx register will be cleared.
While the interrupt is active, if the interrupt line transitions from inactive to active, the pending bit will be turned on again, and upon return from the current active ISR instance, the interrupt will be handled again. This handles the edge triggered interrupt case.
Also, when the ISR returns (and the NVIC clears the 'active' bit), the NIVC will reexamine the state of the interrupt line - if it's still asserted it will set the pending bit again (even if there hasn't been a a transition from inactive to active). This handles the case where an interrupt is level triggered, and the ISR didn't manage to cause the interrupt to be de-asserted (maybe a second device on a shared IRQ line asserted its interrupt at just the critical moment so there was no time when the interrupt line was inactive).
If this is level detect, I can hold interrupt active and disable by the handler, until restore by external code for which it re-execute the interrupt.
I'm not sure I really understand what you're after here, but I think that you might be able to do what you want using the NVIC's SETENAx and CLRENAx registers to enable/disable the interrupt. These work independently of the pending bits, so an interrupt can be pending (or become pending) even if the interrupt is disabled. So you can hold off handling an interrupt for as long as you want.
If that's not quite enough, also note that you can cause an interrupt to pend via software by simply setting the pending bit in the corresponding SETPENDx register - the CPU will vector to the ISR just as if a hardware interrupt were asserted (assuming the interrupt is enabled in the SETENAx register). you can also use the "Software Trigger Interrupt Register" (STIR) to trigger an interrupt by software.

Resources