Skip to content

Conversation

@billwatersiii
Copy link
Contributor

Add PDL-based low-power timer for the E84 board

@billwatersiii
Copy link
Contributor Author

@andyross, @talih0, @teburd, please review this pull request.

Copy link
Contributor

@teburd teburd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there was some fundamental misunderstanding of how timers work in Zephyr when this was written, and I see it was mostly copied from the existing cat1-lp-timer driver and updated to the new HAL API if I understand this right. So likely both need fixing to correctly work.

This is maybe one of those few occasions where only the base address of the register block for the IP is needed and the few registers (typically 2-3..) are kept in C and Zephyr #define's rather than using the HAL.

.clock_frequency = DT_INST_PROP(n, clock_frequency), \
}; \
\
DEVICE_DT_INST_DEFINE(n, lptimer_init, NULL, &ifx_cat1_lptimer_data##n, \
Copy link
Contributor

@teburd teburd Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to make the timer a device, its a singleton in Zephyr's view. Just a couple of static variables tied to the compilation unit for the hardware timer being used as the kernel timer.

Take a look at how cortex_m_systick.c is written for example. It sets up an initialization function with SYS_INIT and a few static variables tied to the compilation unit to stash some state (mostly the last announced tick or cycle count), along with the appropriate sys_clock functions implemented.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@teburd , Don't I need this for a multi-core device? Where each core has its own lptimer.

Copy link
Contributor

@teburd teburd Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can still use DT nodes and attributes, but you don't need to deal with struct device.

There's other multicore devices in the tree to peruse on how they might go about this. See NXP's RT1170 for example of an AMP design. But yes, each AMP core running an instance of Zephyr needs its own timer. In SMP this isn't the case, a single timer is expected.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@teburd , I am not finding "NXP's RT1170". Can you point me to it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

@mcatee-infineon mcatee-infineon Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@teburd, the driver should now be updated to this flow.


lptimer_value = Cy_MCWDT_GetCount(config->reg_addr, CY_MCWDT_COUNTER2);

/* Gives the value of LPTIM counter (ms) since the previous 'announce' */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the number of elapsed kernel ticks (kernel ticks are a synthesized clock running at SYS_CLOCK_TICKS_PER_SEC) since the last announce call was made. So I think there's a misunderstanding below in the math.

What you want is something that converts delta_cycles to delta_ticks where each tick is equal to some number of cycles.

E.g.

uint32_t delta_cycles = current_cycle_count - announced_cycle_count;
uint32_t delta_ticks = delta_cycles/cycles_per_tick;

return delta_ticks;

However, some special care typically needs to happen for rollovers and things like this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

}

/* Set the delay value for the next wakeup interrupt */
lptimer_set_delay(lptimer_dev, ((uint32_t)(ticks)*config->clock_frequency) /
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be setting the counter compare register to some cycle count into the future based on ticks*cycles_per_tick, like...

uint32_t timer_compare = current_cycle_count + ticks*cycles_per_tick;

sys_write32(SOME_TIMER_REG, timer_compare);

With some protection around short timeouts (usually some minimum cycle delay), and error checking for rollovers

Copy link
Contributor

@teburd teburd Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot to mention that the timer compare should also be tick aligned or the timer may trigger early which is an error. Meaning timer_compare % cycles_per_tick == 0 && timer_compare >= (cycle_count + ticks*cycles_per_tick) should be true or a programming error has occurred. The assertion above assumes no rollover which is possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

if ((data->isr_instruction & LPTIMER_ISR_CALL_USER_CB_MASK) != 0) {
/* announce the elapsed time in ms */
uint32_t lptimer_value = Cy_MCWDT_GetCount(config->reg_addr, CY_MCWDT_COUNTER2);
int32_t delta_ticks = ((uint64_t)(lptimer_value - last_lptimer_value) *
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This math is also not quite right. It should be something like...

uint32_t current_cycle_count = sys_read32(SOME_TIMER_COUNT_REG);
uint32_t delta_ticks = (current_cycle_count - announced_cycle_count)/cycles_per_tick;

announced_cycle_count = delta_ticks*cycles_per_tick;

sys_clock_announce(....);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

@billwatersiii
Copy link
Contributor Author

I think there was some fundamental misunderstanding of how timers work in Zephyr when this was written, and I see it was mostly copied from the existing cat1-lp-timer driver and updated to the new HAL API if I understand this right. So likely both need fixing to correctly work.

This is maybe one of those few occasions where only the base address of the register block for the IP is needed and the few registers (typically 2-3..) are kept in C and Zephyr #define's rather than using the HAL.

@teburd , you are correct, this is just a port of the existing driver from using the HAL to the PDL. Thanks for your feedback. I will work on that now.

@billwatersiii billwatersiii requested a review from teburd November 21, 2025 18:30
@zephyrbot zephyrbot requested a review from jsbatch November 21, 2025 18:30
@billwatersiii billwatersiii force-pushed the ifx_pdl_lp_timer branch 3 times, most recently from 25be703 to b938b87 Compare November 21, 2025 19:48
@mcatee-infineon
Copy link
Contributor

Rebasing PR onto main

Add PDL-based low-power timer for the E84 board

Signed-off-by: Bill Waters <bill.waters@infineon.com>
@mcatee-infineon
Copy link
Contributor

@teburd. This PR has been updated with refactoring akin to that done in #99689

@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants