mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2026-02-07 01:44:41 +08:00
[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>
This commit is contained in:
206
components/drivers/clock_time/README.md
Normal file
206
components/drivers/clock_time/README.md
Normal file
@@ -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
|
||||
218
components/drivers/clock_time/adapters/README.md
Normal file
218
components/drivers/clock_time/adapters/README.md
Normal file
@@ -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
|
||||
189
components/drivers/clock_time/adapters/clock_time_arm_gtimer.c
Normal file
189
components/drivers/clock_time/adapters/clock_time_arm_gtimer.c
Normal file
@@ -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 <rtthread.h>
|
||||
#include <rtdevice.h>
|
||||
#include <rthw.h>
|
||||
|
||||
#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 */
|
||||
158
components/drivers/clock_time/adapters/clock_time_systick.c
Normal file
158
components/drivers/clock_time/adapters/clock_time_systick.c
Normal file
@@ -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 <rtthread.h>
|
||||
#include <rtdevice.h>
|
||||
|
||||
#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 */
|
||||
Reference in New Issue
Block a user