Skip to content

Commit 9145264

Browse files
ioannisgnashif
authored andcommitted
boards: arm: qemu_cortex_m0: implement custom system clock driver
For the qemu_cortex_m0 we implement a custom system clock driver based on the nRF51 TIMER peripheral. The system clock is configured to run at 1 MHz frequency. Signed-off-by: Ioannis Glaropoulos <[email protected]>
1 parent b0ef6d0 commit 9145264

File tree

2 files changed

+192
-0
lines changed

2 files changed

+192
-0
lines changed

boards/arm/qemu_cortex_m0/Kconfig.defconfig

+4
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,8 @@ config SYS_CLOCK_TICKS_PER_SEC
3535
int
3636
default 100
3737

38+
config ENTROPY_NRF_FORCE_ALT
39+
bool
40+
default y
41+
3842
endif # BOARD_QEMU_CORTEX_M0
+188
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/*
2+
* Copyright (c) 2016-2019 Nordic Semiconductor ASA
3+
* Copyright (c) 2018 Intel Corporation
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
#include <soc.h>
9+
#include <drivers/clock_control.h>
10+
#include <drivers/clock_control/nrf_clock_control.h>
11+
#include <drivers/timer/system_timer.h>
12+
#include <sys_clock.h>
13+
#include <hal/nrf_timer.h>
14+
#include <spinlock.h>
15+
16+
#define TIMER NRF_TIMER0
17+
18+
#define COUNTER_MAX 0xffffffff
19+
#define CYC_PER_TICK (sys_clock_hw_cycles_per_sec() \
20+
/ CONFIG_SYS_CLOCK_TICKS_PER_SEC)
21+
#define MAX_TICKS ((COUNTER_MAX - CYC_PER_TICK) / CYC_PER_TICK)
22+
23+
static struct k_spinlock lock;
24+
25+
static u32_t last_count;
26+
27+
static u32_t counter_sub(u32_t a, u32_t b)
28+
{
29+
return (a - b) & COUNTER_MAX;
30+
}
31+
32+
static void set_comparator(u32_t cyc)
33+
{
34+
nrf_timer_cc_write(TIMER, 0, cyc & COUNTER_MAX);
35+
}
36+
37+
static u32_t counter(void)
38+
{
39+
nrf_timer_task_trigger(TIMER, nrf_timer_capture_task_get(1));
40+
41+
return nrf_timer_cc_read(TIMER, 1);
42+
}
43+
44+
void timer0_nrf_isr(void *arg)
45+
{
46+
ARG_UNUSED(arg);
47+
TIMER->EVENTS_COMPARE[0] = 0;
48+
49+
k_spinlock_key_t key = k_spin_lock(&lock);
50+
u32_t t = counter();
51+
u32_t dticks = counter_sub(t, last_count) / CYC_PER_TICK;
52+
53+
last_count += dticks * CYC_PER_TICK;
54+
55+
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
56+
u32_t next = last_count + CYC_PER_TICK;
57+
58+
/* As below: we're guaranteed to get an interrupt as
59+
* long as it's set two or more cycles in the future
60+
*/
61+
if (counter_sub(next, t) < 3) {
62+
next += CYC_PER_TICK;
63+
}
64+
set_comparator(next);
65+
}
66+
67+
k_spin_unlock(&lock, key);
68+
z_clock_announce(IS_ENABLED(CONFIG_TICKLESS_KERNEL) ? dticks : 1);
69+
}
70+
71+
int z_clock_driver_init(struct device *device)
72+
{
73+
struct device *clock;
74+
75+
ARG_UNUSED(device);
76+
77+
clock = device_get_binding(DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_16M");
78+
if (!clock) {
79+
return -1;
80+
}
81+
82+
/* turn on clock in blocking mode. */
83+
clock_control_on(clock, (void *)1);
84+
85+
nrf_timer_frequency_set(TIMER, NRF_TIMER_FREQ_1MHz);
86+
nrf_timer_bit_width_set(TIMER, NRF_TIMER_BIT_WIDTH_32);
87+
nrf_timer_cc_write(TIMER, 0, CYC_PER_TICK);
88+
nrf_timer_int_enable(TIMER, TIMER_INTENSET_COMPARE0_Msk);
89+
90+
/* Clear the event flag and possible pending interrupt */
91+
nrf_timer_event_clear(TIMER, NRF_TIMER_EVENT_COMPARE0);
92+
NVIC_ClearPendingIRQ(TIMER0_IRQn);
93+
94+
IRQ_CONNECT(TIMER0_IRQn, 1, timer0_nrf_isr, 0, 0);
95+
irq_enable(TIMER0_IRQn);
96+
97+
nrf_timer_task_trigger(TIMER, NRF_TIMER_TASK_CLEAR);
98+
nrf_timer_task_trigger(TIMER, NRF_TIMER_TASK_START);
99+
100+
if (!IS_ENABLED(TICKLESS_KERNEL)) {
101+
set_comparator(counter() + CYC_PER_TICK);
102+
}
103+
104+
return 0;
105+
}
106+
107+
void z_clock_set_timeout(s32_t ticks, bool idle)
108+
{
109+
ARG_UNUSED(idle);
110+
111+
#ifdef CONFIG_TICKLESS_KERNEL
112+
ticks = (ticks == K_FOREVER) ? MAX_TICKS : ticks;
113+
ticks = MAX(MIN(ticks - 1, (s32_t)MAX_TICKS), 0);
114+
115+
k_spinlock_key_t key = k_spin_lock(&lock);
116+
u32_t cyc, dt, t = counter();
117+
bool zli_fixup = IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS);
118+
119+
/* Round up to next tick boundary */
120+
cyc = ticks * CYC_PER_TICK + 1 + counter_sub(t, last_count);
121+
cyc += (CYC_PER_TICK - 1);
122+
cyc = (cyc / CYC_PER_TICK) * CYC_PER_TICK;
123+
cyc += last_count;
124+
125+
if (counter_sub(cyc, t) > 2) {
126+
set_comparator(cyc);
127+
} else {
128+
set_comparator(cyc);
129+
dt = counter_sub(cyc, counter());
130+
if (dt == 0 || dt > 0x7fffff) {
131+
/* Missed it! */
132+
NVIC_SetPendingIRQ(TIMER0_IRQn);
133+
if (IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS)) {
134+
zli_fixup = false;
135+
}
136+
} else if (dt == 1) {
137+
/* Too soon, interrupt won't arrive. */
138+
set_comparator(cyc + 2);
139+
}
140+
/* Otherwise it was two cycles out, we're fine */
141+
}
142+
143+
#ifdef CONFIG_ZERO_LATENCY_IRQS
144+
/* Failsafe. ZLIs can preempt us even though interrupts are
145+
* masked, blowing up the sensitive timing above. If the
146+
* feature is enabled and we haven't recorded the presence of
147+
* a pending interrupt then we need a final check (in a loop!
148+
* because this too can be interrupted) to confirm that the
149+
* comparator is still in the future. Don't bother being
150+
* fancy with cycle counting here, just set an interrupt
151+
* "soon" that we know will get the timer back to a known
152+
* state. This handles (via some hairy modular expressions)
153+
* the wraparound cases where we are preempted for as much as
154+
* half the counter space.
155+
*/
156+
if (zli_fixup && counter_sub(cyc, counter()) <= 0x7fffff) {
157+
while (counter_sub(cyc, counter() + 2) > 0x7fffff) {
158+
cyc = counter() + 3;
159+
set_comparator(cyc);
160+
}
161+
}
162+
#endif
163+
164+
k_spin_unlock(&lock, key);
165+
#endif /* CONFIG_TICKLESS_KERNEL */
166+
}
167+
168+
u32_t z_clock_elapsed(void)
169+
{
170+
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
171+
return 0;
172+
}
173+
174+
k_spinlock_key_t key = k_spin_lock(&lock);
175+
u32_t ret = counter_sub(counter(), last_count) / CYC_PER_TICK;
176+
177+
k_spin_unlock(&lock, key);
178+
return ret;
179+
}
180+
181+
u32_t z_timer_cycle_get_32(void)
182+
{
183+
k_spinlock_key_t key = k_spin_lock(&lock);
184+
u32_t ret = counter_sub(counter(), last_count) + last_count;
185+
186+
k_spin_unlock(&lock, key);
187+
return ret;
188+
}

0 commit comments

Comments
 (0)