From 0f3a297f4db6467705b6205c753e8dc06eeaa27a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 15:35:12 +0000 Subject: [PATCH] [clock_time] Add README and example adapters - Added comprehensive README for clock_time subsystem - Created ARM Generic Timer adapter example - Created SysTick/DWT adapter example for Cortex-M - Added adapter development guide with best practices Co-authored-by: BernardXiong <1241087+BernardXiong@users.noreply.github.com> --- components/drivers/clock_time/README.md | 206 +++++++++++++++++ .../drivers/clock_time/adapters/README.md | 218 ++++++++++++++++++ .../adapters/clock_time_arm_gtimer.c | 189 +++++++++++++++ .../clock_time/adapters/clock_time_systick.c | 158 +++++++++++++ 4 files changed, 771 insertions(+) create mode 100644 components/drivers/clock_time/README.md create mode 100644 components/drivers/clock_time/adapters/README.md create mode 100644 components/drivers/clock_time/adapters/clock_time_arm_gtimer.c create mode 100644 components/drivers/clock_time/adapters/clock_time_systick.c diff --git a/components/drivers/clock_time/README.md b/components/drivers/clock_time/README.md new file mode 100644 index 0000000000..bc47142342 --- /dev/null +++ b/components/drivers/clock_time/README.md @@ -0,0 +1,206 @@ +# Clock Time Subsystem + +## Overview + +The `clock_time` subsystem is a unified framework for time management in RT-Thread, replacing the legacy `hwtimer`, `ktime`, and `cputime` subsystems with a single, coherent API. + +## Background + +Previously, RT-Thread had three separate time-related subsystems: + +1. **hwtimer**: Device abstraction for hardware timers with read/write interfaces +2. **cputime**: CPU time tracking with ops structure for high-resolution counters +3. **ktime**: Kernel time with boottime tracking and high-resolution timers (hrtimer) + +These subsystems had overlapping functionality, inconsistent APIs, and no clear separation of concerns, leading to: +- Confusion about which subsystem to use +- Duplicated code in BSP drivers +- Difficulty maintaining compatibility +- Scattered documentation + +## Design Goals + +The `clock_time` subsystem addresses these issues by: + +1. **Unification**: Single device abstraction (`rt_clock_time_device`) for all timer hardware +2. **Clarity**: Clear separation between clocksource (counter) and clockevent (timeout) capabilities +3. **Compatibility**: Backward compatibility layers for all legacy APIs +4. **Simplicity**: Fewer concepts to learn, easier BSP integration +5. **Flexibility**: Works with both MCU (tick-based fallback) and MPU (high-res timers) platforms + +## Architecture + +``` +┌─────────────────────────────────────────────┐ +│ Application / POSIX APIs │ +└───────────────────┬─────────────────────────┘ + │ +┌───────────────────▼─────────────────────────┐ +│ Unified clock_time API │ +│ - Clocksource (counter, boottime) │ +│ - Clockevent (timeout, hrtimer) │ +│ - Time conversion utilities │ +└───────────────────┬─────────────────────────┘ + │ +┌───────────────────▼─────────────────────────┐ +│ rt_clock_time_device + ops │ +│ Capability flags: CLOCKSOURCE | CLOCKEVENT │ +└───────────────────┬─────────────────────────┘ + │ +┌───────────────────▼─────────────────────────┐ +│ BSP Hardware Timer Driver │ +│ (SysTick, ARM Timer, RISC-V Timer, etc.) │ +└─────────────────────────────────────────────┘ +``` + +## Key Components + +### 1. Clock Time Device + +The `rt_clock_time_device` structure encapsulates a hardware timer with: +- **ops**: Operations structure (`rt_clock_time_ops`) +- **caps**: Capability flags indicating supported features +- **res_scale**: Resolution scaling factor + +### 2. Operations Structure + +```c +struct rt_clock_time_ops +{ + rt_uint64_t (*get_freq)(void); /* Get frequency in Hz */ + rt_uint64_t (*get_counter)(void); /* Get current counter */ + rt_err_t (*set_timeout)(rt_uint64_t delta); /* Set timeout, 0 to cancel */ +}; +``` + +### 3. Capabilities + +- `RT_CLOCK_TIME_CAP_CLOCKSOURCE`: Provides free-running counter for timekeeping +- `RT_CLOCK_TIME_CAP_CLOCKEVENT`: Supports programmable timeout events + +### 4. High-Resolution Timers + +The `rt_clock_hrtimer` provides: +- One-shot and periodic timers +- Sorted linked-list scheduling +- SMP-safe spinlock protection +- Fallback to software timers when hardware timeout is unavailable + +## Migration Guide + +### For BSP Developers + +**Old hwtimer approach:** +```c +static const struct rt_hwtimer_ops my_ops = { ... }; +static struct rt_hwtimer_device my_device; +rt_device_hwtimer_register(&my_device, "timer0", RT_NULL); +``` + +**New clock_time approach:** +```c +static const struct rt_clock_time_ops my_ops = { + .get_freq = my_get_freq, + .get_counter = my_get_counter, + .set_timeout = my_set_timeout, +}; +static struct rt_clock_time_device my_device; +my_device.ops = &my_ops; +rt_clock_time_device_register(&my_device, "timer0", + RT_CLOCK_TIME_CAP_CLOCKSOURCE | RT_CLOCK_TIME_CAP_CLOCKEVENT); +``` + +### For Application Developers + +**Old cputime/ktime APIs** still work when compatibility layers are enabled: +- `clock_cpu_gettime()` → `rt_clock_time_getcnt()` +- `rt_ktime_boottime_get_ns()` → `rt_clock_time_boottime_ns()` +- `rt_ktime_hrtimer_start()` → `rt_clock_hrtimer_start()` + +**Recommended new APIs:** +```c +/* Get time information */ +rt_uint64_t freq = rt_clock_time_getfreq(); +rt_uint64_t cnt = rt_clock_time_getcnt(); + +/* High-precision delays */ +rt_clock_udelay(100); /* 100 microseconds */ +rt_clock_mdelay(10); /* 10 milliseconds */ + +/* High-resolution timers */ +struct rt_clock_hrtimer timer; +rt_clock_hrtimer_init(&timer, "my_timer", RT_TIMER_FLAG_ONE_SHOT, + callback, param); +rt_clock_hrtimer_start(&timer, delay_cnt); +``` + +## Compatibility Layers + +Three compatibility layers are provided via Kconfig: + +1. **RT_CLOCK_TIME_COMPAT_KTIME**: Enables ktime API compatibility +2. **RT_CLOCK_TIME_COMPAT_CPUTIME**: Enables cputime API compatibility +3. **RT_CLOCK_TIME_COMPAT_HWTIMER**: Enables hwtimer device API compatibility + +These allow gradual migration without breaking existing code. + +## File Structure + +``` +components/drivers/clock_time/ +├── Kconfig # Configuration options +├── SConscript # Build script +├── src/ +│ ├── clock_time.c # Core device management +│ ├── hrtimer.c # High-resolution timer implementation +│ ├── clock_time_tick.c # Tick-based fallback adapter +│ ├── ktime_compat.c # ktime compatibility layer +│ └── cputime_compat.c # cputime compatibility layer +└── include/drivers/ + └── clock_time.h # Public API header +``` + +## Testing Status + +- [x] Basic compilation verified +- [ ] BSP integration examples created +- [ ] QEMU runtime testing +- [ ] CI build verification +- [ ] Performance benchmarking + +## Known Limitations + +1. **Type width**: `unsigned long` is used for counter values to maintain ktime compatibility. On 32-bit systems, this limits maximum counter values. For very high-frequency timers (>4GHz), consider using 64-bit platforms or prescaling. + +2. **Fallback mode**: When no hardware clockevent is available, the system falls back to software timers (tick-based). This provides lower precision but ensures functionality. + +3. **Migration timeline**: The old subsystems are marked as deprecated but not removed to allow time for migration. + +## Future Work + +1. **Architecture-specific optimizations**: Add optimized adapters for common architectures (ARM Generic Timer, RISC-V timer, etc.) +2. **Power management integration**: Better support for low-power modes with clock gating +3. **Advanced scheduling**: Consider red-black tree for timer scheduling in scenarios with many concurrent timers +4. **Complete migration**: Remove deprecated subsystems after all BSPs migrate + +## Documentation + +- [English Documentation](../../documentation/6.components/device-driver/clock_time.md) +- [Chinese Documentation](../../documentation/6.components/device-driver/clock_time_zh.md) + +## Contributing + +When contributing to clock_time: + +1. Maintain backward compatibility with existing APIs +2. Add tests for new functionality +3. Update documentation +4. Follow RT-Thread coding style +5. Consider both MCU and MPU use cases + +## References + +- Issue: [Feature] 对 hwtimer/ktime/cputime 进行整体重构 +- Design discussion in issue comments +- POSIX clock APIs: clock_gettime(2), clock_settime(2) +- Linux clocksource/clockevent framework diff --git a/components/drivers/clock_time/adapters/README.md b/components/drivers/clock_time/adapters/README.md new file mode 100644 index 0000000000..04298c40bd --- /dev/null +++ b/components/drivers/clock_time/adapters/README.md @@ -0,0 +1,218 @@ +# Clock Time Adapters + +This directory contains reference adapter implementations for various hardware timers. +These adapters demonstrate how to integrate hardware timers with the unified `clock_time` subsystem. + +## Available Adapters + +### 1. ARM Generic Timer (`clock_time_arm_gtimer.c`) + +**Target Platforms:** +- ARMv7-A (ARM Cortex-A with Generic Timer) +- ARMv8-A/ARMv8-R (AArch64 and AArch32) + +**Features:** +- Clocksource: Uses CNTPCT (physical counter) +- Clockevent: Uses CNTP_TVAL/CVAL for programmable interrupts +- Full capabilities: Both CLOCKSOURCE and CLOCKEVENT + +**Usage:** +```c +// In your BSP board.c or timer initialization +rt_clock_time_arm_gtimer_init(); + +// In your timer ISR +void ARM_GTIMER_IRQHandler(void) +{ + rt_interrupt_enter(); + rt_clock_time_arm_gtimer_isr(); + rt_interrupt_leave(); +} +``` + +**Registers Used:** +- CNTFRQ_EL0: Counter frequency +- CNTPCT_EL0: Physical counter value +- CNTP_TVAL_EL0: Timer value (countdown) +- CNTP_CTL_EL0: Timer control + +### 2. SysTick/DWT (`clock_time_systick.c`) + +**Target Platforms:** +- ARM Cortex-M0/M0+/M3/M4/M7/M33/M55 + +**Features:** +- Clocksource: Uses DWT CYCCNT (cycle counter) if available +- Falls back to tick counter if DWT is not available +- Clockevent: Not supported (SysTick is used for system tick) + +**Usage:** +```c +// DWT is automatically enabled during initialization +// No ISR needed - this is clocksource only + +// Optional: Set CPU frequency if SystemCoreClock is not accurate +rt_clock_time_systick_set_freq(168000000); // 168 MHz +``` + +**Notes:** +- DWT may not be available on all Cortex-M cores or may be disabled by debugger +- Provides microsecond-level precision on typical MCUs (>= 1 MHz clock) +- For clockevent capability, pair with a hardware timer (TIM, LPTIM, etc.) + +## Creating Your Own Adapter + +To create an adapter for your hardware timer: + +### Step 1: Implement the ops structure + +```c +static rt_uint64_t my_timer_get_freq(void) +{ + return TIMER_FREQUENCY_HZ; +} + +static rt_uint64_t my_timer_get_counter(void) +{ + return MY_TIMER->COUNT; /* Read hardware counter */ +} + +static rt_err_t my_timer_set_timeout(rt_uint64_t delta) +{ + if (delta == 0) + { + /* Cancel timeout */ + MY_TIMER->CTRL &= ~TIMER_ENABLE; + return RT_EOK; + } + + /* Set compare value for interrupt */ + MY_TIMER->COMPARE = MY_TIMER->COUNT + delta; + MY_TIMER->CTRL |= TIMER_ENABLE | TIMER_INT_ENABLE; + + return RT_EOK; +} + +static const struct rt_clock_time_ops my_timer_ops = +{ + .get_freq = my_timer_get_freq, + .get_counter = my_timer_get_counter, + .set_timeout = my_timer_set_timeout, +}; +``` + +### Step 2: Register the device + +```c +int my_timer_init(void) +{ + static struct rt_clock_time_device my_device; + + /* Initialize hardware */ + // ... hardware setup code ... + + my_device.ops = &my_timer_ops; + + /* Register with appropriate capabilities */ + return rt_clock_time_device_register(&my_device, "my_timer", + RT_CLOCK_TIME_CAP_CLOCKSOURCE | RT_CLOCK_TIME_CAP_CLOCKEVENT); +} +INIT_DEVICE_EXPORT(my_timer_init); +``` + +### Step 3: Implement ISR (if using clockevent) + +```c +void MY_TIMER_IRQHandler(void) +{ + rt_interrupt_enter(); + + /* Clear hardware interrupt flag */ + MY_TIMER->STATUS = TIMER_INT_FLAG; + + /* Process high-resolution timer timeouts */ + rt_clock_hrtimer_process(); + + rt_interrupt_leave(); +} +``` + +## Capability Selection Guidelines + +### Clocksource Only +Use when: +- Timer can only provide a counter, no interrupt capability +- Timer is already used for system tick +- Examples: SysTick (used for OS tick), Read-only counters + +### Clockevent Only +Use when: +- Timer can generate interrupts but has no readable counter +- Rare case, most timers have both + +### Both Clocksource and Clockevent +Use when: +- Timer has a readable counter AND can generate interrupts +- This is the most common and preferred configuration +- Examples: ARM Generic Timer, most hardware timers + +## Performance Considerations + +### Counter Frequency +- **Low frequency (1-100 kHz)**: Good for power-sensitive applications, limited precision +- **Medium frequency (1-10 MHz)**: Good balance for most MCU applications +- **High frequency (>10 MHz)**: Best precision, higher CPU overhead + +### Counter Width +- **16-bit**: May overflow quickly, need careful handling +- **32-bit**: Good for most applications, overflows after ~4 seconds at 1 GHz +- **64-bit**: Effectively never overflows, preferred for ARMv8 + +### Interrupt Latency +- Keep ISR short - only call `rt_clock_hrtimer_process()` +- Disable timer interrupt while processing to avoid re-entry +- Consider interrupt priority relative to other system interrupts + +## Debugging Tips + +1. **Verify frequency**: Print actual frequency during initialization + ```c + rt_kprintf("Timer freq: %d Hz\n", (rt_uint32_t)rt_clock_time_getfreq()); + ``` + +2. **Test counter increment**: Verify counter is actually counting + ```c + rt_uint64_t cnt1 = rt_clock_time_getcnt(); + rt_thread_mdelay(1); + rt_uint64_t cnt2 = rt_clock_time_getcnt(); + rt_kprintf("Counter delta: %d (expected ~%d)\n", + (rt_uint32_t)(cnt2 - cnt1), + (rt_uint32_t)(rt_clock_time_getfreq() / 1000)); + ``` + +3. **Check interrupt firing**: Add debug output in ISR + ```c + static int isr_count = 0; + void timer_isr(void) { + isr_count++; + if (isr_count % 1000 == 0) { + rt_kprintf("Timer ISR count: %d\n", isr_count); + } + } + ``` + +## Examples in BSPs + +Look for these files in various BSPs for real-world examples: +- `bsp/qemu-virt64-aarch64/drivers/drv_timer.c` - ARM Generic Timer +- `bsp/stm32/libraries/HAL_Drivers/drv_hwtimer.c` - STM32 hardware timers +- `bsp/rockchip/*/driver/hwtimer/` - Rockchip timer drivers + +## Contributing + +When contributing a new adapter: +1. Add comprehensive comments explaining hardware specifics +2. Include initialization and ISR examples +3. Document any platform-specific requirements +4. Test on actual hardware if possible +5. Update this README with your adapter details diff --git a/components/drivers/clock_time/adapters/clock_time_arm_gtimer.c b/components/drivers/clock_time/adapters/clock_time_arm_gtimer.c new file mode 100644 index 0000000000..ba651a9d11 --- /dev/null +++ b/components/drivers/clock_time/adapters/clock_time_arm_gtimer.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-12-04 RT-Thread ARM Generic Timer adapter for clock_time + */ + +/** + * @file clock_time_arm_gtimer.c + * @brief ARM Generic Timer adapter for the unified clock_time subsystem + * + * This file demonstrates how to adapt ARM Generic Timer (ARMv7-A/ARMv8) + * to the clock_time framework. It can be used as a reference for BSP developers. + * + * Features: + * - Uses CNTPCT (physical counter) for clocksource + * - Uses CNTP_TVAL/CVAL for clockevent (timer interrupts) + * - Provides both clocksource and clockevent capabilities + * + * Usage: + * 1. Copy this file to your BSP driver directory + * 2. Adjust register access macros for your platform + * 3. Call rt_clock_time_arm_gtimer_init() during board initialization + * 4. Implement timer ISR to call rt_clock_hrtimer_process() + */ + +#include +#include +#include + +#ifdef RT_USING_CLOCK_TIME + +/* ARM Generic Timer system registers (AArch64) */ +#if defined(ARCH_ARMV8) + +static inline rt_uint64_t arm_gtimer_get_cntfrq(void) +{ + rt_uint64_t val; + __asm__ volatile("mrs %0, cntfrq_el0" : "=r"(val)); + return val; +} + +static inline rt_uint64_t arm_gtimer_get_cntpct(void) +{ + rt_uint64_t val; + __asm__ volatile("mrs %0, cntpct_el0" : "=r"(val)); + return val; +} + +static inline void arm_gtimer_set_cntp_tval(rt_uint32_t val) +{ + __asm__ volatile("msr cntp_tval_el0, %0" : : "r"(val)); +} + +static inline void arm_gtimer_set_cntp_ctl(rt_uint32_t val) +{ + __asm__ volatile("msr cntp_ctl_el0, %0" : : "r"(val)); +} + +#elif defined(ARCH_ARM_CORTEX_A) + +/* ARMv7-A Generic Timer */ +static inline rt_uint32_t arm_gtimer_get_cntfrq(void) +{ + rt_uint32_t val; + __asm__ volatile("mrc p15, 0, %0, c14, c0, 0" : "=r"(val)); + return val; +} + +static inline rt_uint64_t arm_gtimer_get_cntpct(void) +{ + rt_uint32_t low, high; + __asm__ volatile("mrrc p15, 0, %0, %1, c14" : "=r"(low), "=r"(high)); + return ((rt_uint64_t)high << 32) | low; +} + +static inline void arm_gtimer_set_cntp_tval(rt_uint32_t val) +{ + __asm__ volatile("mcr p15, 0, %0, c14, c2, 0" : : "r"(val)); +} + +static inline void arm_gtimer_set_cntp_ctl(rt_uint32_t val) +{ + __asm__ volatile("mcr p15, 0, %0, c14, c2, 1" : : "r"(val)); +} + +#else +#error "ARM Generic Timer adapter requires ARCH_ARMV8 or ARCH_ARM_CORTEX_A" +#endif + +/* Control register bits */ +#define CNTP_CTL_ENABLE (1 << 0) +#define CNTP_CTL_IMASK (1 << 1) +#define CNTP_CTL_ISTATUS (1 << 2) + +static rt_uint64_t _arm_gtimer_get_freq(void) +{ + return arm_gtimer_get_cntfrq(); +} + +static rt_uint64_t _arm_gtimer_get_counter(void) +{ + return arm_gtimer_get_cntpct(); +} + +static rt_err_t _arm_gtimer_set_timeout(rt_uint64_t delta) +{ + if (delta == 0) + { + /* Cancel timeout - disable timer */ + arm_gtimer_set_cntp_ctl(0); + return RT_EOK; + } + + /* Clamp to 32-bit for TVAL register */ + if (delta > 0xFFFFFFFF) + { + delta = 0xFFFFFFFF; + } + + /* Set timer value and enable */ + arm_gtimer_set_cntp_tval((rt_uint32_t)delta); + arm_gtimer_set_cntp_ctl(CNTP_CTL_ENABLE); + + return RT_EOK; +} + +static const struct rt_clock_time_ops _arm_gtimer_ops = +{ + .get_freq = _arm_gtimer_get_freq, + .get_counter = _arm_gtimer_get_counter, + .set_timeout = _arm_gtimer_set_timeout, +}; + +static struct rt_clock_time_device _arm_gtimer_device; + +/** + * @brief Initialize ARM Generic Timer as clock_time device + * + * This should be called during board initialization, typically in + * rt_hw_timer_init() or similar BSP initialization function. + * + * @return RT_EOK on success, error code otherwise + */ +int rt_clock_time_arm_gtimer_init(void) +{ + _arm_gtimer_device.ops = &_arm_gtimer_ops; + + /* Register with both clocksource and clockevent capabilities */ + rt_err_t result = rt_clock_time_device_register(&_arm_gtimer_device, + "arm_gtimer", + RT_CLOCK_TIME_CAP_CLOCKSOURCE | + RT_CLOCK_TIME_CAP_CLOCKEVENT); + + if (result == RT_EOK) + { + rt_kprintf("ARM Generic Timer: freq=%d Hz\n", (rt_uint32_t)_arm_gtimer_get_freq()); + } + + return result; +} +INIT_DEVICE_EXPORT(rt_clock_time_arm_gtimer_init); + +/** + * @brief ARM Generic Timer interrupt handler + * + * This should be called from the timer IRQ handler in your BSP. + * Typically registered in the interrupt controller during initialization. + * + * Example: + * void ARM_GTIMER_IRQHandler(void) + * { + * rt_clock_time_arm_gtimer_isr(); + * rt_interrupt_leave(); + * } + */ +void rt_clock_time_arm_gtimer_isr(void) +{ + /* Disable timer to clear interrupt */ + arm_gtimer_set_cntp_ctl(0); + + /* Process hrtimer timeouts */ + rt_clock_hrtimer_process(); +} + +#endif /* RT_USING_CLOCK_TIME */ diff --git a/components/drivers/clock_time/adapters/clock_time_systick.c b/components/drivers/clock_time/adapters/clock_time_systick.c new file mode 100644 index 0000000000..26fb167aef --- /dev/null +++ b/components/drivers/clock_time/adapters/clock_time_systick.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-12-04 RT-Thread SysTick adapter for clock_time (Cortex-M example) + */ + +/** + * @file clock_time_systick.c + * @brief ARM Cortex-M SysTick adapter for the unified clock_time subsystem + * + * This file demonstrates how to adapt SysTick timer to the clock_time framework. + * SysTick is commonly used on ARM Cortex-M processors. + * + * Features: + * - Uses DWT CYCCNT for high-resolution counter (clocksource) + * - Falls back to tick counter if DWT is not available + * - SysTick itself provides system tick, not high-res timeout + * + * Note: This example provides only clocksource capability. For clockevent + * capability, you would need to use a different hardware timer. + * + * Usage: + * 1. Enable DWT cycle counter in your board initialization + * 2. This adapter will automatically register during initialization + */ + +#include +#include + +#if defined(RT_USING_CLOCK_TIME) && defined(ARCH_ARM_CORTEX_M) + +/* DWT (Data Watchpoint and Trace) registers */ +#define DWT_BASE 0xE0001000UL +#define DWT_CYCCNT (*(volatile rt_uint32_t *)(DWT_BASE + 0x004)) +#define DWT_CTRL (*(volatile rt_uint32_t *)(DWT_BASE + 0x000)) +#define DWT_CTRL_CYCCNTENA (1 << 0) + +/* DEM (Debug Exception and Monitor) registers */ +#define DEM_BASE 0xE000EDFC +#define DEM_CR (*(volatile rt_uint32_t *)(DEM_BASE + 0x000)) +#define DEM_CR_TRCENA (1 << 24) + +static rt_bool_t _dwt_available = RT_FALSE; +static rt_uint32_t _cpu_freq_hz = 0; + +/** + * @brief Enable DWT cycle counter + * + * @return RT_TRUE if DWT is available and enabled, RT_FALSE otherwise + */ +static rt_bool_t _dwt_enable(void) +{ + /* Enable DWT */ + DEM_CR |= DEM_CR_TRCENA; + + /* Reset counter */ + DWT_CYCCNT = 0; + + /* Enable counter */ + DWT_CTRL |= DWT_CTRL_CYCCNTENA; + + /* Verify counter is counting */ + rt_uint32_t start = DWT_CYCCNT; + for (volatile int i = 0; i < 100; i++); + rt_uint32_t end = DWT_CYCCNT; + + return (end > start); +} + +static rt_uint64_t _systick_get_freq(void) +{ + if (_dwt_available) + { + /* Return CPU frequency (DWT CYCCNT increments at CPU clock rate) */ + return _cpu_freq_hz ? _cpu_freq_hz : SystemCoreClock; + } + else + { + /* Fall back to tick frequency */ + return RT_TICK_PER_SECOND; + } +} + +static rt_uint64_t _systick_get_counter(void) +{ + if (_dwt_available) + { + /* Return DWT cycle counter */ + return DWT_CYCCNT; + } + else + { + /* Fall back to tick counter */ + return rt_tick_get(); + } +} + +static const struct rt_clock_time_ops _systick_ops = +{ + .get_freq = _systick_get_freq, + .get_counter = _systick_get_counter, + .set_timeout = RT_NULL, /* SysTick doesn't support programmable timeout */ +}; + +static struct rt_clock_time_device _systick_device; + +/** + * @brief Initialize SysTick/DWT as clock_time device + * + * @return RT_EOK on success, error code otherwise + */ +int rt_clock_time_systick_init(void) +{ + /* Try to enable DWT cycle counter */ + _dwt_available = _dwt_enable(); + + _systick_device.ops = &_systick_ops; + + /* Register with clocksource capability only */ + rt_err_t result = rt_clock_time_device_register(&_systick_device, + "systick", + RT_CLOCK_TIME_CAP_CLOCKSOURCE); + + if (result == RT_EOK) + { + if (_dwt_available) + { + rt_kprintf("SysTick/DWT: freq=%d Hz (DWT cycle counter enabled)\n", + (rt_uint32_t)_systick_get_freq()); + } + else + { + rt_kprintf("SysTick: freq=%d Hz (DWT not available, using tick counter)\n", + RT_TICK_PER_SECOND); + } + } + + return result; +} +INIT_DEVICE_EXPORT(rt_clock_time_systick_init); + +/** + * @brief Set CPU frequency for DWT counter + * + * This should be called if SystemCoreClock is not accurate or not available. + * + * @param freq_hz CPU frequency in Hz + */ +void rt_clock_time_systick_set_freq(rt_uint32_t freq_hz) +{ + _cpu_freq_hz = freq_hz; +} + +#endif /* RT_USING_CLOCK_TIME && ARCH_ARM_CORTEX_M */