Skip to content

Commit 2353e7b

Browse files
authored
Merge pull request #7029 from OpenNuvoton/nuvoton_5.9_ticker
Nuvoton: Adhere to reworked ticker spec to release with Mbed OS 5.9
2 parents 20adbf0 + 4f04ae4 commit 2353e7b

File tree

12 files changed

+484
-85
lines changed

12 files changed

+484
-85
lines changed

TESTS/mbed_hal/common_tickers/main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ unsigned int ticker_overflow_delta;
5252

5353
/* Auxiliary function to count ticker ticks elapsed during execution of N cycles of empty while loop.
5454
* Parameter <step> is used to disable compiler optimisation. */
55+
MBED_NOINLINE
5556
uint32_t count_ticks(uint32_t cycles, uint32_t step)
5657
{
5758
register uint32_t reg_cycles = cycles;

targets/TARGET_NUVOTON/TARGET_M451/lp_ticker.c

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,42 @@ static const struct nu_modinit_s timer1_modinit = {TIMER_1, TMR1_MODULE, CLK_CLK
4242

4343
#define TIMER_MODINIT timer1_modinit
4444

45-
static int ticker_inited = 0;
45+
/* Timer interrupt enable/disable
46+
*
47+
* Because Timer interrupt enable/disable (TIMER_EnableInt/TIMER_DisableInt) needs wait for lp_ticker,
48+
* we call NVIC_DisableIRQ/NVIC_EnableIRQ instead.
49+
*/
50+
51+
/* Track ticker status */
52+
static volatile uint16_t ticker_inited = 0;
4653

4754
#define TMR_CMP_MIN 2
4855
#define TMR_CMP_MAX 0xFFFFFFu
4956

50-
/* NOTE: When system clock is higher than timer clock, we need to add 3 engine clock
51-
* (recommended by designer) delay to wait for above timer control to take effect. */
57+
/* Synchronization issue with LXT/LIRC-clocked Timer
58+
*
59+
* PCLK : typical HCLK/2
60+
* ECLK (engine clock) : LXT/LIRC for Timer used to implement lp_ticker
61+
*
62+
* When system clock is higher than Timer clock (LXT/LIRC), we need to add delay for ECLK
63+
* domain to take effect:
64+
* 1. Write : typical 1PCLK + 2ECLK
65+
* Read-check doesn't work because it just checks PCLK domain and doesn't check into
66+
* ECLK domain.
67+
* 2. Clear interrupt flag : typical 2PCLK
68+
* It is very rare that we would meet dummy interrupt and get stuck in ISR until
69+
* 'clear interrupt flag' takes effect. The issue is ignorable because the pending
70+
* time is very short (at most 1 dummy interrupt). We won't take special handling for it.
71+
*/
5272

5373
void lp_ticker_init(void)
5474
{
5575
if (ticker_inited) {
76+
/* By HAL spec, ticker_init allows the ticker to keep counting and disables the
77+
* ticker interrupt. */
78+
lp_ticker_disable_interrupt();
79+
lp_ticker_clear_interrupt();
80+
NVIC_ClearPendingIRQ(TIMER_MODINIT.irq_n);
5681
return;
5782
}
5883
ticker_inited = 1;
@@ -86,7 +111,7 @@ void lp_ticker_init(void)
86111
// Set vector
87112
NVIC_SetVector(TIMER_MODINIT.irq_n, (uint32_t) TIMER_MODINIT.var);
88113

89-
NVIC_EnableIRQ(TIMER_MODINIT.irq_n);
114+
NVIC_DisableIRQ(TIMER_MODINIT.irq_n);
90115

91116
TIMER_EnableInt(timer_base);
92117
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
@@ -101,6 +126,33 @@ void lp_ticker_init(void)
101126
while(! (timer_base->CTL & TIMER_CTL_ACTSTS_Msk));
102127
}
103128

129+
void lp_ticker_free(void)
130+
{
131+
TIMER_T *timer_base = (TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname);
132+
133+
/* Stop counting */
134+
TIMER_Stop(timer_base);
135+
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
136+
137+
/* Wait for timer to stop counting and unset active flag */
138+
while((timer_base->CTL & TIMER_CTL_ACTSTS_Msk));
139+
140+
/* Disable wakeup */
141+
TIMER_DisableWakeup(timer_base);
142+
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
143+
144+
/* Disable interrupt */
145+
TIMER_DisableInt(timer_base);
146+
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
147+
148+
NVIC_DisableIRQ(TIMER_MODINIT.irq_n);
149+
150+
/* Disable IP clock */
151+
CLK_DisableModuleClock(TIMER_MODINIT.clkidx);
152+
153+
ticker_inited = 0;
154+
}
155+
104156
timestamp_t lp_ticker_read()
105157
{
106158
if (! ticker_inited) {
@@ -129,27 +181,39 @@ void lp_ticker_set_interrupt(timestamp_t timestamp)
129181
uint32_t cmp_timer = timestamp * NU_TMRCLK_PER_TICK;
130182
cmp_timer = NU_CLAMP(cmp_timer, TMR_CMP_MIN, TMR_CMP_MAX);
131183

184+
/* NOTE: Rely on LPTICKER_DELAY_TICKS to be non-blocking. */
132185
timer_base->CMP = cmp_timer;
133-
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
186+
187+
/* We can call ticker_irq_handler now. */
188+
NVIC_EnableIRQ(TIMER_MODINIT.irq_n);
134189
}
135190

136191
void lp_ticker_disable_interrupt(void)
137192
{
138-
TIMER_DisableInt((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
139-
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
193+
/* We cannot call ticker_irq_handler now. */
194+
NVIC_DisableIRQ(TIMER_MODINIT.irq_n);
140195
}
141196

142197
void lp_ticker_clear_interrupt(void)
143198
{
144-
TIMER_ClearIntFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
145-
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
199+
/* To avoid sync issue, we clear TIF/TWKF simultaneously rather than call separate
200+
* driver API:
201+
*
202+
* TIMER_ClearIntFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
203+
* TIMER_ClearWakeupFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
204+
*/
205+
TIMER_T *timer_base = (TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname);
206+
timer_base->INTSTS = TIMER_INTSTS_TIF_Msk | TIMER_INTSTS_TWKF_Msk;
146207
}
147208

148209
void lp_ticker_fire_interrupt(void)
149210
{
150211
// NOTE: This event was in the past. Set the interrupt as pending, but don't process it here.
151212
// This prevents a recursive loop under heavy load which can lead to a stack overflow.
152213
NVIC_SetPendingIRQ(TIMER_MODINIT.irq_n);
214+
215+
/* We can call ticker_irq_handler now. */
216+
NVIC_EnableIRQ(TIMER_MODINIT.irq_n);
153217
}
154218

155219
const ticker_info_t* lp_ticker_get_info()
@@ -163,11 +227,7 @@ const ticker_info_t* lp_ticker_get_info()
163227

164228
static void tmr1_vec(void)
165229
{
166-
TIMER_ClearIntFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
167-
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
168-
169-
TIMER_ClearWakeupFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
170-
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
230+
lp_ticker_clear_interrupt();
171231

172232
// NOTE: lp_ticker_set_interrupt() may get called in lp_ticker_irq_handler();
173233
lp_ticker_irq_handler();

targets/TARGET_NUVOTON/TARGET_M451/us_ticker.c

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
*/
1616

1717
#include "us_ticker_api.h"
18+
19+
#if DEVICE_USTICKER
20+
1821
#include "sleep_api.h"
1922
#include "mbed_assert.h"
2023
#include "nu_modutil.h"
@@ -37,14 +40,20 @@ static const struct nu_modinit_s timer0_modinit = {TIMER_0, TMR0_MODULE, CLK_CLK
3740

3841
#define TIMER_MODINIT timer0_modinit
3942

40-
static int ticker_inited = 0;
43+
/* Track ticker status */
44+
static volatile uint16_t ticker_inited = 0;
4145

4246
#define TMR_CMP_MIN 2
4347
#define TMR_CMP_MAX 0xFFFFFFu
4448

4549
void us_ticker_init(void)
4650
{
4751
if (ticker_inited) {
52+
/* By HAL spec, ticker_init allows the ticker to keep counting and disables the
53+
* ticker interrupt. */
54+
us_ticker_disable_interrupt();
55+
us_ticker_clear_interrupt();
56+
NVIC_ClearPendingIRQ(TIMER_MODINIT.irq_n);
4857
return;
4958
}
5059
ticker_inited = 1;
@@ -73,7 +82,7 @@ void us_ticker_init(void)
7382

7483
NVIC_SetVector(TIMER_MODINIT.irq_n, (uint32_t) TIMER_MODINIT.var);
7584

76-
NVIC_EnableIRQ(TIMER_MODINIT.irq_n);
85+
NVIC_DisableIRQ(TIMER_MODINIT.irq_n);
7786

7887
TIMER_EnableInt(timer_base);
7988

@@ -82,6 +91,26 @@ void us_ticker_init(void)
8291
while(! (timer_base->CTL & TIMER_CTL_ACTSTS_Msk));
8392
}
8493

94+
void us_ticker_free(void)
95+
{
96+
TIMER_T *timer_base = (TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname);
97+
98+
/* Stop counting */
99+
TIMER_Stop(timer_base);
100+
101+
/* Wait for timer to stop counting and unset active flag */
102+
while((timer_base->CTL & TIMER_CTL_ACTSTS_Msk));
103+
104+
/* Disable interrupt */
105+
TIMER_DisableInt(timer_base);
106+
NVIC_DisableIRQ(TIMER_MODINIT.irq_n);
107+
108+
/* Disable IP clock */
109+
CLK_DisableModuleClock(TIMER_MODINIT.clkidx);
110+
111+
ticker_inited = 0;
112+
}
113+
85114
uint32_t us_ticker_read()
86115
{
87116
if (! ticker_inited) {
@@ -110,11 +139,15 @@ void us_ticker_set_interrupt(timestamp_t timestamp)
110139
uint32_t cmp_timer = timestamp * NU_TMRCLK_PER_TICK;
111140
cmp_timer = NU_CLAMP(cmp_timer, TMR_CMP_MIN, TMR_CMP_MAX);
112141
timer_base->CMP = cmp_timer;
142+
143+
/* We can call ticker_irq_handler now. */
144+
NVIC_EnableIRQ(TIMER_MODINIT.irq_n);
113145
}
114146

115147
void us_ticker_disable_interrupt(void)
116148
{
117-
TIMER_DisableInt((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
149+
/* We cannot call ticker_irq_handler now. */
150+
NVIC_DisableIRQ(TIMER_MODINIT.irq_n);
118151
}
119152

120153
void us_ticker_clear_interrupt(void)
@@ -127,6 +160,9 @@ void us_ticker_fire_interrupt(void)
127160
// NOTE: This event was in the past. Set the interrupt as pending, but don't process it here.
128161
// This prevents a recursive loop under heavy load which can lead to a stack overflow.
129162
NVIC_SetPendingIRQ(TIMER_MODINIT.irq_n);
163+
164+
/* We can call ticker_irq_handler now. */
165+
NVIC_EnableIRQ(TIMER_MODINIT.irq_n);
130166
}
131167

132168
const ticker_info_t* us_ticker_get_info()
@@ -140,8 +176,10 @@ const ticker_info_t* us_ticker_get_info()
140176

141177
static void tmr0_vec(void)
142178
{
143-
TIMER_ClearIntFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
144-
179+
us_ticker_clear_interrupt();
180+
145181
// NOTE: us_ticker_set_interrupt() may get called in us_ticker_irq_handler();
146182
us_ticker_irq_handler();
147183
}
184+
185+
#endif

0 commit comments

Comments
 (0)