Confusing problem with timer alarm on RP2040

Hi,
I discovered a weired behaviour when using timer alarms on RP2040. I used it with low level functions of the SDK and could even reproduce it with the low level timer example of the SDK.
The first call of the ISR after initialising the timer alarm needs more than 10 times execution time than all consecutive calls.
Did anyone already have the same experience? I tried on pi pico 1 and 2 and on Nano RP2040 connect.
It's the same everywhere.
I used the core of Earle Philhower, which includes the standard RP2040 SDK.
This is the example code from the SDK. It's slightly changed to Arduino needs and an added GPIO output to measure the execution time via an oscilloscope
The first alarm match needs 1.2 µs within the ISR, while all subsequent alarms need less than 100ns. With a more complex ISR it is the same - the first call needs about 10 times as much execution time as all subsequent calls.

/**
   Copyright (c) 2020 Raspberry Pi (Trading) Ltd.

   SPDX-License-Identifier: BSD-3-Clause
*/

#include "pico/stdlib.h"
#include "hardware/timer.h"
#include "hardware/irq.h"

constexpr uint8_t TP1 = 25; // timing measurement

// Note on RP2350 timer_hw is the default timer instance (as per PICO_DEFAULT_TIMER)

/// \tag::get_time[]
// Simplest form of getting 64 bit time from the timer.
// It isn't safe when called from 2 cores because of the latching
// so isn't implemented this way in the sdk
static uint64_t get_time(void) {
  // Reading low latches the high value
  uint32_t lo = timer_hw->timelr;
  uint32_t hi = timer_hw->timehr;
  return ((uint64_t) hi << 32u) | lo;
}
/// \end::get_time[]

/// \tag::alarm_standalone[]
// Use alarm 0
#define ALARM_NUM 0
#define ALARM_IRQ timer_hardware_alarm_get_irq_num(timer_hw, ALARM_NUM)

// Alarm interrupt handler
static volatile bool alarm_fired;

static void alarm_irq(void) {
  // Clear the alarm irq
  gpio_set_mask( 1<<TP1 );
  hw_clear_bits(&timer_hw->intr, 1u << ALARM_NUM);

  // Assume alarm 0 has fired
  //Serial.printf("Alarm IRQ fired\n");
  alarm_fired = true;
  gpio_clr_mask( 1<<TP1 );
}

static void alarm_in_us(uint32_t delay_us) {
  // Enable the interrupt for our alarm (the timer outputs 4 alarm irqs)
  hw_set_bits(&timer_hw->inte, 1u << ALARM_NUM);
  // Set irq handler for alarm irq
  irq_set_exclusive_handler(ALARM_IRQ, alarm_irq);
  // Enable the alarm irq
  irq_set_enabled(ALARM_IRQ, true);
  // Enable interrupt in block and at processor

  // Alarm is only 32 bits so if trying to delay more
  // than that need to be careful and keep track of the upper
  // bits
  uint64_t target = timer_hw->timerawl + delay_us;

  // Write the lower 32 bits of the target time to the alarm which
  // will arm it
  timer_hw->alarm[ALARM_NUM] = (uint32_t) target;
}

void setup() {
  //Serial.begin(115200);
  //while (!Serial);
  delay(3000);
  //Serial.printf("Timer lowlevel!\n");
  pinMode( TP1, OUTPUT );
}


// Set alarm every 2 seconds
void loop() {
  alarm_fired = false;
  alarm_in_us(1000000 * 2);
  // Wait for alarm to fire
  while (!alarm_fired);
}

/// \end::alarm_standalone[]

Code is stored in QSPI flash memory and pulled into a small XIP cache on the first use (and possibly later ones if it was evicted from cache). So the first pass in many functions will be much slower than subsqeuent ones.

If that's the root cause here, you can mark certain routines to run from RAM instead void __not_in_flash_func(alarm_irq)(...). See the SDK docs for more info.

Thanks a lot, that makes sense. I tried it, and now the execution time is as expected from the first IRQ on.