Skip to content

Commit 86e194d

Browse files
committed
[Nuvoton] Reduce blocking code in lp_ticker
1. Introduce S/W interrupt enable/disable to reduce calls to TIMER_EnableInt/TIMER_DisableInt. 2. Allow dummy interrupt because clear interrupt flag is not synchronized. 3. Enable LPTICKER_DELAY_TICKS to make lp_ticker_set_interrupt non-blocking.
1 parent b18f690 commit 86e194d

File tree

5 files changed

+188
-83
lines changed

5 files changed

+188
-83
lines changed

targets/TARGET_NUVOTON/TARGET_M451/lp_ticker.c

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,23 @@ 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+
/* S/W interrupt enable/disable
46+
*
47+
* Because H/W interrupt enable/disable (TIMER_EnableInt/TIMER_DisableInt) needs delay for lp_ticker,
48+
* we introduce S/W interrupt enable/disable to avoid blocking code. With S/W interrupt enable/disable,
49+
* H/W interrupt is always enabled after ticker_init. A S/W flag is used to tell whether or not
50+
* ticker_irq_handler is ready to call.
51+
*/
52+
53+
/* Ticker uninitialized */
54+
#define NU_TICKER_UNINIT 0
55+
/* Ticker initialized with interrupt disabled */
56+
#define NU_TICKER_INIT_INTR_DIS 1
57+
/* Ticker initialized with interrupt enabled */
58+
#define NU_TICKER_INIT_INTR_EN 2
59+
60+
/* Track ticker status */
61+
static volatile uint16_t ticker_stat = NU_TICKER_UNINIT;
4662

4763
#define TMR_CMP_MIN 2
4864
#define TMR_CMP_MAX 0xFFFFFFu
@@ -52,14 +68,14 @@ static int ticker_inited = 0;
5268

5369
void lp_ticker_init(void)
5470
{
55-
if (ticker_inited) {
71+
if (ticker_stat) {
5672
/* By HAL spec, ticker_init allows the ticker to keep counting and disables the
5773
* ticker interrupt. */
5874
lp_ticker_disable_interrupt();
5975
lp_ticker_clear_interrupt();
6076
return;
6177
}
62-
ticker_inited = 1;
78+
ticker_stat = NU_TICKER_INIT_INTR_DIS;
6379

6480
// Reset module
6581
SYS_ResetModule(TIMER_MODINIT.rsetidx);
@@ -92,7 +108,7 @@ void lp_ticker_init(void)
92108

93109
NVIC_EnableIRQ(TIMER_MODINIT.irq_n);
94110

95-
TIMER_DisableInt(timer_base);
111+
TIMER_EnableInt(timer_base);
96112
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
97113

98114
TIMER_EnableWakeup(timer_base);
@@ -129,12 +145,12 @@ void lp_ticker_free(void)
129145
/* Disable IP clock */
130146
CLK_DisableModuleClock(TIMER_MODINIT.clkidx);
131147

132-
ticker_inited = 0;
148+
ticker_stat = NU_TICKER_UNINIT;
133149
}
134150

135151
timestamp_t lp_ticker_read()
136152
{
137-
if (! ticker_inited) {
153+
if (ticker_stat == NU_TICKER_UNINIT) {
138154
lp_ticker_init();
139155
}
140156

@@ -145,6 +161,9 @@ timestamp_t lp_ticker_read()
145161

146162
void lp_ticker_set_interrupt(timestamp_t timestamp)
147163
{
164+
/* We can call ticker_irq_handler now. */
165+
ticker_stat = NU_TICKER_INIT_INTR_EN;
166+
148167
/* In continuous mode, counter will be reset to zero with the following sequence:
149168
* 1. Stop counting
150169
* 2. Configure new CMP value
@@ -160,27 +179,33 @@ void lp_ticker_set_interrupt(timestamp_t timestamp)
160179
uint32_t cmp_timer = timestamp * NU_TMRCLK_PER_TICK;
161180
cmp_timer = NU_CLAMP(cmp_timer, TMR_CMP_MIN, TMR_CMP_MAX);
162181

182+
/* NOTE: Rely on LPTICKER_DELAY_TICKS to be non-blocking. */
163183
timer_base->CMP = cmp_timer;
164-
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
165-
166-
TIMER_EnableInt(timer_base);
167-
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
168184
}
169185

170186
void lp_ticker_disable_interrupt(void)
171187
{
172-
TIMER_DisableInt((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
173-
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
188+
/* We cannot call ticker_irq_handler now. */
189+
ticker_stat = NU_TICKER_INIT_INTR_DIS;
174190
}
175191

176192
void lp_ticker_clear_interrupt(void)
177193
{
178-
TIMER_ClearIntFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
179-
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
194+
/* To avoid sync issue, we clear TIF/TWKF simultaneously rather than call separate
195+
* driver API:
196+
*
197+
* TIMER_ClearIntFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
198+
* TIMER_ClearWakeupFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
199+
*/
200+
TIMER_T *timer_base = (TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname);
201+
timer_base->INTSTS = TIMER_INTSTS_TIF_Msk | TIMER_INTSTS_TWKF_Msk;
180202
}
181203

182204
void lp_ticker_fire_interrupt(void)
183205
{
206+
/* We can call ticker_irq_handler now. */
207+
ticker_stat = NU_TICKER_INIT_INTR_EN;
208+
184209
// NOTE: This event was in the past. Set the interrupt as pending, but don't process it here.
185210
// This prevents a recursive loop under heavy load which can lead to a stack overflow.
186211
NVIC_SetPendingIRQ(TIMER_MODINIT.irq_n);
@@ -197,14 +222,15 @@ const ticker_info_t* lp_ticker_get_info()
197222

198223
static void tmr1_vec(void)
199224
{
200-
TIMER_ClearIntFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
201-
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
202-
203-
TIMER_ClearWakeupFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
204-
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
225+
/* NOTE: We need to clear interrupt flag earlier to reduce possibility of dummy interrupt.
226+
* This is because "clear interrupt flag" needs delay which isn't added here to avoid
227+
* blocking in ISR code. */
228+
lp_ticker_clear_interrupt();
205229

206230
// NOTE: lp_ticker_set_interrupt() may get called in lp_ticker_irq_handler();
207-
lp_ticker_irq_handler();
231+
if (ticker_stat == NU_TICKER_INIT_INTR_EN) {
232+
lp_ticker_irq_handler();
233+
}
208234
}
209235

210236
#endif

targets/TARGET_NUVOTON/TARGET_M480/lp_ticker.c

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,23 @@ 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+
/* S/W interrupt enable/disable
46+
*
47+
* Because H/W interrupt enable/disable (TIMER_EnableInt/TIMER_DisableInt) needs delay for lp_ticker,
48+
* we introduce S/W interrupt enable/disable to avoid blocking code. With S/W interrupt enable/disable,
49+
* H/W interrupt is always enabled after ticker_init. A S/W flag is used to tell whether or not
50+
* ticker_irq_handler is ready to call.
51+
*/
52+
53+
/* Ticker uninitialized */
54+
#define NU_TICKER_UNINIT 0
55+
/* Ticker initialized with interrupt disabled */
56+
#define NU_TICKER_INIT_INTR_DIS 1
57+
/* Ticker initialized with interrupt enabled */
58+
#define NU_TICKER_INIT_INTR_EN 2
59+
60+
/* Track ticker status */
61+
static volatile uint16_t ticker_stat = NU_TICKER_UNINIT;
4662

4763
#define TMR_CMP_MIN 2
4864
#define TMR_CMP_MAX 0xFFFFFFu
@@ -52,14 +68,14 @@ static int ticker_inited = 0;
5268

5369
void lp_ticker_init(void)
5470
{
55-
if (ticker_inited) {
71+
if (ticker_stat) {
5672
/* By HAL spec, ticker_init allows the ticker to keep counting and disables the
5773
* ticker interrupt. */
5874
lp_ticker_disable_interrupt();
5975
lp_ticker_clear_interrupt();
6076
return;
6177
}
62-
ticker_inited = 1;
78+
ticker_stat = NU_TICKER_INIT_INTR_DIS;
6379

6480
// Reset module
6581
SYS_ResetModule(TIMER_MODINIT.rsetidx);
@@ -92,7 +108,7 @@ void lp_ticker_init(void)
92108

93109
NVIC_EnableIRQ(TIMER_MODINIT.irq_n);
94110

95-
TIMER_DisableInt(timer_base);
111+
TIMER_EnableInt(timer_base);
96112
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
97113

98114
TIMER_EnableWakeup(timer_base);
@@ -129,12 +145,12 @@ void lp_ticker_free(void)
129145
/* Disable IP clock */
130146
CLK_DisableModuleClock(TIMER_MODINIT.clkidx);
131147

132-
ticker_inited = 0;
148+
ticker_stat = NU_TICKER_UNINIT;
133149
}
134150

135151
timestamp_t lp_ticker_read()
136152
{
137-
if (! ticker_inited) {
153+
if (ticker_stat == NU_TICKER_UNINIT) {
138154
lp_ticker_init();
139155
}
140156

@@ -145,6 +161,9 @@ timestamp_t lp_ticker_read()
145161

146162
void lp_ticker_set_interrupt(timestamp_t timestamp)
147163
{
164+
/* We can call ticker_irq_handler now. */
165+
ticker_stat = NU_TICKER_INIT_INTR_EN;
166+
148167
/* In continuous mode, counter will be reset to zero with the following sequence:
149168
* 1. Stop counting
150169
* 2. Configure new CMP value
@@ -160,27 +179,33 @@ void lp_ticker_set_interrupt(timestamp_t timestamp)
160179
uint32_t cmp_timer = timestamp * NU_TMRCLK_PER_TICK;
161180
cmp_timer = NU_CLAMP(cmp_timer, TMR_CMP_MIN, TMR_CMP_MAX);
162181

182+
/* NOTE: Rely on LPTICKER_DELAY_TICKS to be non-blocking. */
163183
timer_base->CMP = cmp_timer;
164-
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
165-
166-
TIMER_EnableInt(timer_base);
167-
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
168184
}
169185

170186
void lp_ticker_disable_interrupt(void)
171187
{
172-
TIMER_DisableInt((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
173-
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
188+
/* We cannot call ticker_irq_handler now. */
189+
ticker_stat = NU_TICKER_INIT_INTR_DIS;
174190
}
175191

176192
void lp_ticker_clear_interrupt(void)
177193
{
178-
TIMER_ClearIntFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
179-
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
194+
/* To avoid sync issue, we clear TIF/TWKF simultaneously rather than call separate
195+
* driver API:
196+
*
197+
* TIMER_ClearIntFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
198+
* TIMER_ClearWakeupFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
199+
*/
200+
TIMER_T *timer_base = (TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname);
201+
timer_base->INTSTS = TIMER_INTSTS_TIF_Msk | TIMER_INTSTS_TWKF_Msk;
180202
}
181203

182204
void lp_ticker_fire_interrupt(void)
183205
{
206+
/* We can call ticker_irq_handler now. */
207+
ticker_stat = NU_TICKER_INIT_INTR_EN;
208+
184209
// NOTE: This event was in the past. Set the interrupt as pending, but don't process it here.
185210
// This prevents a recursive loop under heavy load which can lead to a stack overflow.
186211
NVIC_SetPendingIRQ(TIMER_MODINIT.irq_n);
@@ -197,14 +222,15 @@ const ticker_info_t* lp_ticker_get_info()
197222

198223
static void tmr1_vec(void)
199224
{
200-
TIMER_ClearIntFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
201-
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
202-
203-
TIMER_ClearWakeupFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
204-
wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3);
225+
/* NOTE: We need to clear interrupt flag earlier to reduce possibility of dummy interrupt.
226+
* This is because "clear interrupt flag" needs delay which isn't added here to avoid
227+
* blocking in ISR code. */
228+
lp_ticker_clear_interrupt();
205229

206230
// NOTE: lp_ticker_set_interrupt() may get called in lp_ticker_irq_handler();
207-
lp_ticker_irq_handler();
231+
if (ticker_stat == NU_TICKER_INIT_INTR_EN) {
232+
lp_ticker_irq_handler();
233+
}
208234
}
209235

210236
#endif

0 commit comments

Comments
 (0)