cancel
Showing results for 
Search instead for 
Did you mean: 

Timing issue when TIM10 interrupts HAL IWDG macro

RBack.1
Senior

I am using a STM32F746VET6 to drive time critical signals on up to 14 pins. I am bit banging these signals in the ISR. I was surprised to find after a benign change I started to have jitter in these signals. I tracked it down to the ISR firing during the __HAL_IWDG_RELOAD_COUNTER(&hiwdg) macro.

The ISR itself isn't being delayed as my scope shows the periodicity is still correct. The strange thing is that the timing *inside* the ISR is inconsistent even though it consists of little beyond writing to ports and nops. I have included the ISR at the bottom of this post.I have also attached scope shots with lines 5, 7, and 8 both active (working correctly) and commented out (timing issue).

I have a solution and am moving my project forward, but I really don't understand the symptom. What is it about resetting the IWDG that doesn't delay the ISR, but screws up the timing inside the ISR instead?

void __attribute__((optimize("O0"))) __attribute__((section(".itcm_text"))) bobbywait(Sysmgr_Task_Struct* task_struct_ptr) {
    while(1) {
        // !!! Unless I ensure the ISR does not fire during this macro the ISR timing is inconsistent!!!
        extern IWDG_HandleTypeDef hiwdg;
        if(impulse.counter != impulse.counter_last) {
            __HAL_IWDG_RELOAD_COUNTER(&hiwdg);
            impulse.counter_last = impulse.counter;
        }
    }
    TIM10->DIER &= ~(TIM_IT_UPDATE);
}
 
void __attribute__((optimize("O0"))) __attribute__((section(".itcm_text"))) TIM1_UP_TIM10_IRQHandler(void)
{
    TIM10->SR = 0;
 
    // Create the pulse
    impulse.TxPort->ODR = impulse.Step1; // First clamp output channel
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    impulse.TxPort->ODR = impulse.Step2; // Then create pulse, postive or negative depending upon pos or neg pulse configuration
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    //    nop_delay(impulse.Period2);
    impulse.TxPort->ODR = impulse.Step3; // Then either clamp for unipolar or create opposite pulse for bipolar
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    impulse.TxPort->ODR = impulse.Step4; // Then clamp again.
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    asm("nop"); // 11
    impulse.RxPort->ODR = impulse.Mask;    // Activate LVOUT to send ultrasound signal to ADC
 
    ADC1->CR2 |= impulse.ADCStartMask; // Activate the ADC acquisition mechanism
    Capture_State = CAPTURE_SEQUENCE_STARTED;
    impulse.counter++;
 
    return;
}

4 REPLIES 4

Below I preserve the text of an obviously AI-generated spam post, which will be probably removed by moderators, as it's mostly correct; although - being AI generated - obviously does not contain the understanding of the root of problem, which is probably contention on processor's AHBP bus.

As an excercise, in the "jittery" state, you could try to swap first write to GPIO with write to TIM10 SR

impulse.TxPort->ODR = impulse.Step1; // First clamp output channel
TIM10->SR = 0;
... and here come the nops...

although I'm not sure if it helps and/or not introduces any other problem.

On a general note, the AI is right and nop-timed events are prone to timing issues, especially in a (mildly) superscalar processor as the Cortex-M7 is. You may want to strive to use hardware for strictly timed events, e.g. timers.

Also, even if it's not the issue here, but generally, don't use GPIO_ODR to change pins' state, use GPIO_BSRR instead, that is guaranteed to be atomic.

JW

--------:

It seems that your ISR timing is being affected by resetting the IWDG in the main loop. This may be happening because the IWDG reset is causing a delay in the execution of the instructions in the main loop, which in turn is causing the ISR to be delayed.

When the IWDG counter is reset, the CPU has to perform several operations to reload the counter and restart the watchdog. These operations take a certain amount of time to execute, during which the CPU cannot execute any other instructions. If the main loop is executing instructions during this time, those instructions will be delayed by the IWDG reset.

One way to avoid this issue is to disable interrupts while resetting the IWDG in the main loop. This will prevent the ISR from being delayed by the IWDG reset. Another way is to increase the priority of the ISR so that it is not delayed by other interrupts.

It's also worth noting that the use of nops in the ISR is not a reliable way to generate precise timings, as the number of cycles required for each nop instruction can vary depending on the CPU clock frequency and other factors. Instead, you could use a hardware timer to generate the required timing signals, which would be more accurate and less affected by other factors.

Heh...thanks for letting me know that was an AI generated post. It actually wasn't obvious to me.

And definitely thank you for letting me know about the BSRR register. With respect to nops being bad for timing, I'd love to use timers with output compares for driving all of my I/O, but in my actual application (not my simplified one for this post) I need to drive 12 I/O in six states. The time between these states can be very short (minimum 5 nops @ 216MHz). I'd like to strive to only use timers but I'm not sure it's possible in this case.

Sounds difficult indeed, but it would be an interesting challenge, would you be so inclined to draw a timing diagram... 🙂 No pressure, it may be lost time too.

JW

I'd be happy to provide a diagram and appreciate any help! Maybe I could start with a scope shot that only shows 4 I/O but does show all six of the states? State one drives all I/O low for 10us, state two is variable width and drives some of the I/O high for a brief period (as low as ~20ns but typically ~80ns), state three is similar to state three but a different set of pins, state four has all pins low again, state five has all pins high except 4, and then state six has all pins high. If this is insufficient I could draw all 12 I/O in a diagram but this is obviously difficult to record with a scope. Also, I did notice I forgot to mention a 13th I/O which is the trigger for the scope.
_legacyfs_online_stmicro_images_0693W00000biR8hQAE.png