[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:
copilot-swe-agent[bot]
2025-12-04 15:35:12 +00:00
parent f3dd8afc7d
commit 0f3a297f4d
4 changed files with 771 additions and 0 deletions

View 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

View 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

View 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 */

View 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 */