@@ -71,6 +71,11 @@ static uint32_t get_comparator(int32_t chan)
71
71
return nrf_rtc_cc_get (RTC , chan );
72
72
}
73
73
74
+ static bool event_check (int32_t chan )
75
+ {
76
+ return nrf_rtc_event_check (RTC , RTC_CHANNEL_EVENT_ADDR (chan ));
77
+ }
78
+
74
79
static void event_clear (int32_t chan )
75
80
{
76
81
nrf_rtc_event_clear (RTC , RTC_CHANNEL_EVENT_ADDR (chan ));
@@ -225,63 +230,56 @@ uint64_t z_nrf_rtc_timer_get_ticks(k_timeout_t t)
225
230
* @param[in] chan A channel for which a new CC value is to be set.
226
231
*
227
232
* @param[in] abs_val An absolute value of CC register to be set.
228
- *
229
- * @returns CC value that was actually set. It is equal to @p abs_val or
230
- * shifted ahead if @p abs_val was too near in the future (+1 case).
231
233
*/
232
- static uint32_t set_absolute_alarm (int32_t chan , uint32_t abs_val )
234
+ static void set_absolute_alarm (int32_t chan , uint32_t abs_val )
233
235
{
234
- uint32_t now ;
235
- uint32_t now2 ;
236
236
uint32_t cc_val = abs_val & COUNTER_MAX ;
237
- uint32_t prev_cc = get_comparator (chan );
238
- uint32_t tick_inc = 2 ;
237
+ uint32_t cc_inc = 2 ;
238
+
239
+ /* Disable event routing for the channel to avoid getting a COMPARE
240
+ * event for the previous CC value before the new one takes effect
241
+ * (however, even if such spurious event was generated, it would be
242
+ * properly filtered out in process_channel(), where the target time
243
+ * is checked).
244
+ * Clear also the event as it may already be generated at this point.
245
+ */
246
+ event_disable (chan );
247
+ event_clear (chan );
239
248
240
- do {
241
- now = counter () ;
249
+ for (;;) {
250
+ uint32_t now ;
242
251
243
- /* Handle case when previous event may generate an event.
244
- * It is handled by setting CC to now (far in the future),
245
- * in case previous event was set for next tick wait for half
246
- * LF tick and clear event that may have been generated.
252
+ set_comparator (chan , cc_val );
253
+ /* Enable event routing after the required CC value was set.
254
+ * Even though the above operation may get repeated (see below),
255
+ * there is no need to disable event routing in every iteration
256
+ * of the loop, as the COMPARE event resulting from any attempt
257
+ * of setting the CC register is acceptable (as mentioned above,
258
+ * process_channel() does the proper filtering).
247
259
*/
248
- set_comparator (chan , now );
249
- if (counter_sub (prev_cc , now ) == 1 ) {
250
- /* It should wait for half of RTC tick 15.26us. As
251
- * busy wait runs from different clock source thus
252
- * wait longer to cover for discrepancy.
253
- */
254
- k_busy_wait (19 );
255
- }
260
+ event_enable (chan );
256
261
257
- /* RTC may not generate event if CC is set for 1 tick from now.
258
- * Because of that if requested cc_val is in the past or next tick,
259
- * set CC to further in future. Start with 2 ticks from now but
260
- * if that fails go even futher. It may fail if operation got
261
- * interrupted and RTC counter progressed or if optimization is
262
- * turned off.
263
- */
264
- if (counter_sub (cc_val , now + 2 ) > COUNTER_HALF_SPAN ) {
265
- cc_val = now + tick_inc ;
266
- tick_inc ++ ;
267
- }
262
+ now = counter ();
268
263
269
- event_clear (chan );
270
- event_enable (chan );
271
- set_comparator (chan , cc_val );
272
- now2 = counter ();
273
- prev_cc = cc_val ;
274
- /* Rerun the algorithm if counter progressed during execution
275
- * and cc_val is in the past or one tick from now. In such
276
- * scenario, it is possible that event will not be generated.
277
- * Rerunning the algorithm will delay the alarm but ensure that
278
- * event will be generated at the moment indicated by value in
279
- * CC register.
264
+ /* RTC may not generate a COMPARE event if its COUNTER value
265
+ * is N and a given CC register is set to N or N+1. If it turns
266
+ * out that the above configuration of the comparator resulted
267
+ * in such CC value or even in a value that is considered to be
268
+ * from the past, repeat the operation using a CC value that is
269
+ * guaranteed to generate the event. Start with 2 RTC ticks from
270
+ * now and if that fails (because the operation gets delayed),
271
+ * go even futher in the next attempt.
272
+ * But if the COMPARE event turns out to be already generated,
273
+ * there is obviously no need to continue the loop.
280
274
*/
281
- } while ((now2 != now ) &&
282
- (counter_sub (cc_val , now2 + 2 ) > COUNTER_HALF_SPAN ));
283
-
284
- return cc_val ;
275
+ if ((counter_sub (cc_val , now + 2 ) > COUNTER_HALF_SPAN ) &&
276
+ !event_check (chan )) {
277
+ cc_val = now + cc_inc ;
278
+ cc_inc ++ ;
279
+ } else {
280
+ break ;
281
+ }
282
+ }
285
283
}
286
284
287
285
static int compare_set_nolocks (int32_t chan , uint64_t target_time ,
@@ -302,9 +300,7 @@ static int compare_set_nolocks(int32_t chan, uint64_t target_time,
302
300
/* Target time is valid and is different than currently set.
303
301
* Set CC value.
304
302
*/
305
- uint32_t cc_set = set_absolute_alarm (chan , cc_value );
306
-
307
- target_time += counter_sub (cc_set , cc_value );
303
+ set_absolute_alarm (chan , cc_value );
308
304
}
309
305
} else {
310
306
/* Force ISR handling when exiting from critical section. */
@@ -454,7 +450,7 @@ static bool channel_processing_check_and_clear(int32_t chan)
454
450
* or be forced.
455
451
*/
456
452
result = atomic_and (& force_isr_mask , ~BIT (chan )) ||
457
- nrf_rtc_event_check ( RTC , RTC_CHANNEL_EVENT_ADDR ( chan ) );
453
+ event_check ( chan );
458
454
459
455
if (result ) {
460
456
event_clear (chan );
@@ -493,6 +489,13 @@ static void process_channel(int32_t chan)
493
489
cc_data [chan ].callback = NULL ;
494
490
cc_data [chan ].target_time = TARGET_TIME_INVALID ;
495
491
event_disable (chan );
492
+ /* Because of the way set_absolute_alarm() sets the CC
493
+ * register, it may turn out that another COMPARE event
494
+ * has been generated for the same alarm. Make sure the
495
+ * event is cleared, so that the ISR is not executed
496
+ * again unnecessarily.
497
+ */
498
+ event_clear (chan );
496
499
}
497
500
498
501
full_int_unlock (mcu_critical_state );
0 commit comments