AON tick generation: selection of the LPOSC as timer source when XOSC is not the reference clock #3107

Open
opened 2026-08-01 02:39:23 -05:00 by antoineomond · 7 comments
antoineomond commented 2026-08-01 02:39:23 -05:00 (Migrated from github.com)

Board: Pico 2 (RP2350)
SDK version: 2.3.0 (current master branch)

In rp2_common/pico_aon_timer/aon_timer.c, the aon_timer_start function always uses the XOSC as reference clock to generate ticks. In some experiments I did, I needed to turn off the XOSC and change the reference clock. The datasheet (12.10.5.1.) mentions the possibility to use the LPOSC instead of the XOSC to generate ticks when the latter isn't in use. The sdk provides a method to this end: powman_timer_set_1khz_tick_source_lposc(). The datasheet (12.10.5.3.) also mentions the CLK_REF_SELECTED register which specifies which clock source is used as reference clock.

In my experiments, I ended up modying the aon_timer_start function like the following:

bool aon_timer_start(const struct timespec *ts) {
#if HAS_RP2040_RTC
    rtc_init();
    return aon_timer_set_time(ts);
#elif HAS_POWMAN_TIMER
    // Check if the XOSC is set as reference clock. If not the case, use the LPOSC to generate ticks
    uint16_t *clk_ref_selected = (uint16_t *)(CLOCKS_BASE+CLOCKS_CLK_REF_SELECTED_OFFSET);
    if(*clk_ref_selected == 0x4) {
    	powman_timer_set_1khz_tick_source_xosc();
    } else {
    	powman_timer_set_1khz_tick_source_lposc();
    }
    bool ok = aon_timer_set_time(ts);
    if (ok) {
        powman_timer_set_ms(timespec_to_ms(ts));
        powman_timer_start();
    }
    return ok;
#else
    panic_unsupported();
#endif
}

The code above was working for me when using the ROSC or LPOSC as reference clocks. The only issue was that when using the LPOSC to generate ticks, the default frequency specified by the sdk (32768) was slightly too fast compared to the one I measured on my board (between 29 and 30kHz). So maybe it would be relevant to let the user specify its own frequency when using the LPOSC as 1khz tick instead of relying on the default.

Note that I haven't found the CLK_REF_SELECTED register in clock_hw so I couldn't use this struct to access the value. This is why I was reading the value directly from the memory without using the clock_hw struct.

Board: Pico 2 (RP2350) SDK version: 2.3.0 (current master branch) In `rp2_common/pico_aon_timer/aon_timer.c`, the `aon_timer_start` function always uses the XOSC as reference clock to generate ticks. In some experiments I did, I needed to turn off the XOSC and change the reference clock. The datasheet (12.10.5.1.) mentions the possibility to use the LPOSC instead of the XOSC to generate ticks when the latter isn't in use. The sdk provides a method to this end: `powman_timer_set_1khz_tick_source_lposc()`. The datasheet (12.10.5.3.) also mentions the `CLK_REF_SELECTED` register which specifies which clock source is used as reference clock. In my experiments, I ended up modying the `aon_timer_start` function like the following: ```c bool aon_timer_start(const struct timespec *ts) { #if HAS_RP2040_RTC rtc_init(); return aon_timer_set_time(ts); #elif HAS_POWMAN_TIMER // Check if the XOSC is set as reference clock. If not the case, use the LPOSC to generate ticks uint16_t *clk_ref_selected = (uint16_t *)(CLOCKS_BASE+CLOCKS_CLK_REF_SELECTED_OFFSET); if(*clk_ref_selected == 0x4) { powman_timer_set_1khz_tick_source_xosc(); } else { powman_timer_set_1khz_tick_source_lposc(); } bool ok = aon_timer_set_time(ts); if (ok) { powman_timer_set_ms(timespec_to_ms(ts)); powman_timer_start(); } return ok; #else panic_unsupported(); #endif } ``` The code above was working for me when using the ROSC or LPOSC as reference clocks. The only issue was that when using the LPOSC to generate ticks, the default frequency specified by the sdk (32768) was slightly too fast compared to the one I measured on my board (between 29 and 30kHz). So maybe it would be relevant to let the user specify its own frequency when using the LPOSC as 1khz tick instead of relying on the default. Note that I haven't found the `CLK_REF_SELECTED` register in `clock_hw` so I couldn't use this struct to access the value. This is why I was reading the value directly from the memory without using the `clock_hw` struct.
lurch commented 2026-08-01 17:25:23 -05:00 (Migrated from github.com)

The only issue was that when using the LPOSC to generate ticks, the default frequency specified by the sdk (32768) was slightly too fast compared to the one I measured on my board (between 29 and 30kHz).

I believe this is what the TRIM register in the LPOSC is for - see 8.4.1. in the datasheet. (I think the intention is that you'd measure the frequency of the LPOSC whilst still running from the XOSC, and then trim the LPOSC so that it's running as close as possible to 32768 Hz, before then switching over to the LPOSC? I only know this because it previously came up in #2856 )

Note that I haven't found the CLK_REF_SELECTED register in clock_hw so I couldn't use this struct to access the value. This is why I was reading the value directly from the memory without using the clock_hw struct.

I can't say for certain, but looking at https://github.com/raspberrypi/pico-sdk/blob/develop/src/rp2350/hardware_regs/include/hardware/regs/clocks.h and https://github.com/raspberrypi/pico-sdk/blob/develop/src/rp2350/hardware_structs/include/hardware/structs/clocks.h it looks like you might be able to get the value you need from clocks_hw->clk[clk_ref].selected ? (see also https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/pico_runtime_init/runtime_init_clocks.c#L56 )
P.S. It's probably not worth it, but I guess you could replace your 0x4 with 1 << CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC 😉

> The only issue was that when using the LPOSC to generate ticks, the default frequency specified by the sdk (32768) was slightly too fast compared to the one I measured on my board (between 29 and 30kHz). I believe this is what the `TRIM` register in the LPOSC is for - see 8.4.1. in the datasheet. (I _think_ the intention is that you'd measure the frequency of the LPOSC whilst still running from the XOSC, and then trim the LPOSC so that it's running as close as possible to 32768 Hz, before then switching over to the LPOSC? I only know this because it previously came up in #2856 ) > Note that I haven't found the `CLK_REF_SELECTED` register in `clock_hw` so I couldn't use this struct to access the value. This is why I was reading the value directly from the memory without using the `clock_hw` struct. I can't say for certain, but looking at https://github.com/raspberrypi/pico-sdk/blob/develop/src/rp2350/hardware_regs/include/hardware/regs/clocks.h and https://github.com/raspberrypi/pico-sdk/blob/develop/src/rp2350/hardware_structs/include/hardware/structs/clocks.h it looks like you _might_ be able to get the value you need from `clocks_hw->clk[clk_ref].selected` ? (see also https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/pico_runtime_init/runtime_init_clocks.c#L56 ) P.S. It's probably not worth it, but I guess you could replace your `0x4` with `1 << CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC` 😉
will-v-pi commented 2026-08-02 00:53:37 -05:00 (Migrated from github.com)

The only issue was that when using the LPOSC to generate ticks, the default frequency specified by the sdk (32768) was slightly too fast compared to the one I measured on my board (between 29 and 30kHz).

I believe this is what the TRIM register in the LPOSC is for - see 8.4.1. in the datasheet. (I think the intention is that you'd measure the frequency of the LPOSC whilst still running from the XOSC, and then trim the LPOSC so that it's running as close as possible to 32768 Hz, before then switching over to the LPOSC? I only know this because it previously came up in #2856 )

I don’t think you need to use TRIM in this scenario - as of SDK 2.3.0 there is a new weak powman_timer_get_lposc_calib_freq which reads the frequency from the OTP by default, but you can override that function to return the measured frequency instead (see hardware_powman in the release notes).

> > The only issue was that when using the LPOSC to generate ticks, the default frequency specified by the sdk (32768) was slightly too fast compared to the one I measured on my board (between 29 and 30kHz). > > I believe this is what the `TRIM` register in the LPOSC is for - see 8.4.1. in the datasheet. (I _think_ the intention is that you'd measure the frequency of the LPOSC whilst still running from the XOSC, and then trim the LPOSC so that it's running as close as possible to 32768 Hz, before then switching over to the LPOSC? I only know this because it previously came up in [#2856](https://github.com/raspberrypi/pico-sdk/issues/2856) ) I don’t think you need to use `TRIM` in this scenario - as of SDK 2.3.0 there is a new weak `powman_timer_get_lposc_calib_freq` which reads the frequency from the OTP by default, but you can override that function to return the measured frequency instead (see `hardware_powman` in the [release notes](https://github.com/raspberrypi/pico-sdk/releases/tag/2.3.0)).
antoineomond commented 2026-08-03 07:50:54 -05:00 (Migrated from github.com)

I can't say for certain, but looking at https://github.com/raspberrypi/pico-sdk/blob/develop/src/rp2350/hardware_regs/include/hardware/regs/clocks.h and https://github.com/raspberrypi/pico-sdk/blob/develop/src/rp2350/hardware_structs/include/hardware/structs/clocks.h it looks like you might be able to get the value you need from clocks_hw->clk[clk_ref].selected ? (see also https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/pico_runtime_init/runtime_init_clocks.c#L56 )
P.S. It's probably not worth it, but I guess you could replace your 0x4 with 1 << CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC 😉

I updated my code, thanks!

I don’t think you need to use TRIM in this scenario - as of SDK 2.3.0 there is a new weak powman_timer_get_lposc_calib_freq which reads the frequency from the OTP by default, but you can override that function to return the measured frequency instead (see hardware_powman in the release notes).

It turns out that on my board, the OTP value for the LPOSC frequency returns 61.364 kHz, which is above the upper boundary specified by the sdk (40 kHz). Due to that, powman_timer_get_lposc_calib_freq() returns 0. Because of that, powman_timer_set_1khz_tick_source_lposc_with_hz ends up not modifying the LPOSC frequency at all (which stays at 32.786 kHz).
I am not sure why I have such a high value from the OTP. So I ended up overriding the powman_timer_get_lposc_calib_freq() function and returning the value from the frequency counter. This way I get much better timings. Thanks!

Also, is the code in my first post worth a pull request? It may be too ad-hoc and need more thought to it but if it is relevant I can open one?

> I can't say for certain, but looking at https://github.com/raspberrypi/pico-sdk/blob/develop/src/rp2350/hardware_regs/include/hardware/regs/clocks.h and https://github.com/raspberrypi/pico-sdk/blob/develop/src/rp2350/hardware_structs/include/hardware/structs/clocks.h it looks like you might be able to get the value you need from clocks_hw->clk[clk_ref].selected ? (see also https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/pico_runtime_init/runtime_init_clocks.c#L56 ) P.S. It's probably not worth it, but I guess you could replace your 0x4 with 1 << CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC 😉 I updated my code, thanks! > I don’t think you need to use TRIM in this scenario - as of SDK 2.3.0 there is a new weak powman_timer_get_lposc_calib_freq which reads the frequency from the OTP by default, but you can override that function to return the measured frequency instead (see hardware_powman in the [release notes](https://github.com/raspberrypi/pico-sdk/releases/tag/2.3.0)). It turns out that on my board, the OTP value for the LPOSC frequency returns 61.364 kHz, which is above the upper boundary specified by the sdk (40 kHz). Due to that, `powman_timer_get_lposc_calib_freq()` returns 0. Because of that, `powman_timer_set_1khz_tick_source_lposc_with_hz` ends up not modifying the LPOSC frequency at all (which stays at 32.786 kHz). I am not sure why I have such a high value from the OTP. So I ended up overriding the `powman_timer_get_lposc_calib_freq()` function and returning the value from the frequency counter. This way I get much better timings. Thanks! Also, is the code in my first post worth a pull request? It may be too ad-hoc and need more thought to it but if it is relevant I can open one?
P33M commented 2026-08-03 08:58:17 -05:00 (Migrated from github.com)

Can you post a picture of the topside of the Pico 2 with the chip markings visible? You may need to provide angled illumination.

Can you post a picture of the topside of the Pico 2 with the chip markings visible? You may need to provide angled illumination.
antoineomond commented 2026-08-04 04:01:40 -05:00 (Migrated from github.com)
Image Please tell me if you need better quality.
<img width="2048" height="1536" alt="Image" src="https://github.com/user-attachments/assets/c92e9d6d-7241-4de6-a1c8-48cec3d82ac9" /> Please tell me if you need better quality.
will-v-pi commented 2026-08-04 05:35:00 -05:00 (Migrated from github.com)

Also, is the code in my first post worth a pull request? It may be too ad-hoc and need more thought to it but if it is relevant I can open one?

I think a better way would be a new inline powman_timer_set_1khz_tick_source_default function, which can be toggled using a #define between XOSC and LPOSC, because powman_timer_set_1khz_tick_source_xosc is used in multiple places in the SDK - I'll submit a PR to do that

> Also, is the code in my first post worth a pull request? It may be too ad-hoc and need more thought to it but if it is relevant I can open one? I think a better way would be a new inline `powman_timer_set_1khz_tick_source_default` function, which can be toggled using a `#define` between XOSC and LPOSC, because `powman_timer_set_1khz_tick_source_xosc` is used in multiple places in the SDK - I'll submit a PR to do that
will-v-pi commented 2026-08-04 06:50:31 -05:00 (Migrated from github.com)

I have opened a PR to do that https://github.com/raspberrypi/pico-sdk/pull/3110 - should allow setting PICO_POWMAN_DEFAULT_TICK_SOURCE_XOSC=0 to use the LPOSC instead in aon_timer_start and pico_low_power

I have opened a PR to do that https://github.com/raspberrypi/pico-sdk/pull/3110 - should allow setting `PICO_POWMAN_DEFAULT_TICK_SOURCE_XOSC=0` to use the LPOSC instead in `aon_timer_start` and `pico_low_power`
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
gh-raspberrypi/pico-sdk#3107
No description provided.