2

For my application (running on an STM32L082) I need accurate (relative) timestamping of a few types of interrupts. I do this by running a timer at 1 MHz and taking its count as soon as the ISR is run. They are all given the highest priority so they pre-empt less important interrupts. The problem I'm facing is that they may still be delayed by other interrupts at the same priority and by code that disables interrupts, and there seems to be no easy way to know this happened. It is no problem that the ISR was delayed, as long as I know that the particular timestamp is not accurate because of this.

My current approach is to let each ISR and each block of code with interrupts disabled check whether interrupts are pending using NVIC->ISPR[0] and flagging this for the pending ISR. Each ISR checks this flag and, if needed, flags the timestamp taken as not accurate.

Although this works, it feels like it's the wrong way around. So my question is: is there another way to know whether an IRQ was served immediately?

The IRQs in question are EXTI4-15 for a GPIO pin change and RTC for the wakeup timer. Unfortunately I'm not in the position to change the PCB layout and use TIM input capture on the input pin, nor to change the MCU used.

update

The fundamental limit to accuracy in the current setup is determined by the nature of the internal RTC calibration, which periodically adds/removes 32kHz ticks, leading to ~31 µs jitter. My goal is to eliminate (or at least detect) additional timestamping inaccuracies where possible. Having interrupts blocked incidentally for, say, 50+ µs is hard to avoid and influences measurements, hence the need to at least know when this occurs.

update 2

To clarify, I think this is a software question, asking if a particular feature exists and if so, how to use it. The answer I am looking for is one of: "yes it is possible, just check bit X of register Y", or "no it is not possible, but MCU ... does have such a feature, called ..." or "no, such a feature is generally not available on any platform (but the common workaround is ...)". This information will guide me (and future readers) towards a solution in software, and/or requirements for better hardware design.

mvds
  • 45,755
  • 8
  • 102
  • 111
  • What kind of accuracy do you need for the timestamp? I fear you will struggle without using a TIM capture. Also the M0 does not have a BASEPRI register like the M3, so you can't mask interrupts based on priority. – Realtime Rik Nov 10 '21 at 12:48
  • @RealtimeRik somewhere between 1 and 30 µs, see my updated question. The question is really about knowing whether an IRQ was delayed, given the hardware available at this moment. – mvds Nov 10 '21 at 13:05
  • Also note that timestamping of the RTC wakeup will always have to be done using the RTC IRQ, as there is no timer capture based on RTC events (at least in the STM32L082). – mvds Nov 10 '21 at 13:07
  • 1
    this is a software question not a hardware question, you need to design your overall system so that you can either detect or so that you cannot delay. You have not described the system level design and why it is possible to be delayed, what you have done to control that, etc. – old_timer Nov 10 '21 at 13:51
  • @old_timer I have eliminated most blocking code, I run most other IRQs at priorities below those of the particular EXTI and RTC interrupt, but still there remains the small chance that one of my timing IRQs is delayed by another interrupt or by periods in which interrupts are disabled. Again: the delay is not an issue, I can ignore measurements that are affected by delays, but to do that I do need to know it was delayed. At this time I'm signalling the fact there was a delay from the delaying code, which makes me feel like I'm missing a more straightforward method to know this. – mvds Nov 10 '21 at 14:02
  • A simple way is with a scope. Use the trigger GPIO and a free GPIO to toggle while servicing. The IRQ itself may delay itself. Ie, at the start of the IRQ the condition can be cleared, but while servicing it another event occurs. With the free GPIO, toogle high on IRQ entry and low on exit. Some processor have pin muxing of IRQ registers like `ISPR`, but usually not on Cortex-M type devices. There may be tracing facilities with JTAG/SWO that a connected system can diagnose things like this. I don't think you can account for it 'in system'. **NO** is a hard answer to be authoritative on. – artless noise Nov 16 '21 at 15:12
  • Personally I'm 99% sure NO is the correct answer. You can do some software tricks to get if your interrupt is prehempted but you cannot detect interrupt disabing by primask/faultmask. – Damiano Nov 18 '21 at 11:08
  • @Damiano after hours of googling I ended at a 95% confidence level ;-) And given the reactions here, now at 99% as well. So 500 internet points for whoever can close the gap to 100%! – mvds Nov 18 '21 at 12:44

1 Answers1

2

In general

The ideal solution for accurate timestamping is to use timer capture hardware (built-in to the microcontroller, or an external implementation). Aside from that, using a CPU with enough priority levels to make your ISR always the highest priority could work, or you might be able to hack something together by making the DMA engine sample the GPIO pins (specifics below).

Some microcontrollers have connections between built-in peripherals that allow one peripheral to trigger another (like a GPIO pin triggering timer capture even though it isn't a dedicated timer capture input pin). Manufacturers have different names for this type of interconnection, but a general overview can be found on Wikipedia, along with a list of the various names. Exact capabilities vary by manufacturer.

I've never come across a feature in a microcontroller for indicating if an ISR was delayed by a higher priority ISR. I don't think it would be a commonly-used feature, because your ISR can be interrupted by a higher priority ISR at any moment, even after you check the hypothetical was_delayed flag. A higher priority ISR can often check if a lower priority interrupt is pending though.

For your specific situation

A possible approach is to use a timer and DMA (similar to audio streaming, double-buffered/circular modes are preferred) to continuously sample your GPIO pins to a buffer, and then you scan the buffer to determine when the pins changed. Note that this means the CPU must scan the buffer before it is overwritten again by DMA, which means the CPU can only sleep in short intervals and must keep the timer and DMA clocks running. ST's AN4666 is a relevant document, and has example code here (account required to download example code). They're using a different microcontroller, but they claim the approach can be adapted to others in their lineup.

Otherwise, with your current setup, I don't think there is a better solution than the one you're using (the flag that's set when you detect a delay). The ARM Cortex-M0+ NVIC does not have a feature to indicate if an ISR was delayed.

A refinement to your current approach might be making the ISRs as short as possible, so they only do the timestamp collection and then put any other work into a queue for processing by the main application at a lower priority (only applicable if the work is more complex than the enqueue operation, and if the work isn't time-sensitive). Eliminating or making the interrupts-disabled regions short should also help.

camerondm9
  • 1,005
  • 15
  • 22
  • Thanks for your answer. AN4666 is an interesting read, but GPIO DMA is not available on my MCU unfortunately. I agree with your consideration about the usefulness of a feature telling an ISR was being delayed by another ISR - but it would still be useful to know whether interrupts were blocked completely, especially on my Cortex-M0+ which does not have a priority mask to disable interrupts selectively. – mvds Nov 24 '21 at 10:03
  • Oh, that is unfortunate. I thought it would work, but I see the problem now: Section 2.1 of the reference manual shows that only the CPU has access to the IOPORT bus on the STM32L082. I suppose you could read the EXTI pending register instead, since the DMA controller has access to the AHB and APB buses. Note that accessing the EXTI registers has to be done using a 32-bit transfer. This would let you sample the RTC wakeup interrupt in the same way as the GPIO-triggered interrupts. – camerondm9 Nov 25 '21 at 18:15
  • That's a really interesting approach. Until now I only ever considered peripheral-to-memory and memory-to-peripheral DMA for peripherals listed in Table 51 of the reference manual, that specified the magic values for `DMA.CSELR` (also 'magic' in the sense that they are not even `#defined` anywhere in autogenerated CMSIS or HAL_Driver code). But do I understand correctly you are suggesting to do a memory-to-memory transfer from EXTI.PR to a buffer? Could that work? – mvds Nov 25 '21 at 19:52
  • After a bit of reading, I now understand that memory-to-memory DMA only says to perform the transfer as fast as possible, and that I should be doing peripheral-to-memory DMA with a timer as peripheral - the peripheral is then only dictating the sampling clock, and `DMA_Channel.CPAR` determines the actual peripheral being read out, which could then be any memory mapped address that the DMA controller can read. – mvds Nov 25 '21 at 20:04
  • Right, the timer is the peripheral that requests the DMA transfer, but you use peripheral-to-memory mode with the EXTI (or GPIO, if it were possible) register as the source address. – camerondm9 Nov 25 '21 at 20:10