-
Notifications
You must be signed in to change notification settings - Fork 12
/
NHW_RTC.c
896 lines (744 loc) · 28.1 KB
/
NHW_RTC.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
/*
* Copyright (c) 2017 Oticon A/S
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* RTC - Real-time counter
*
* https://infocenter.nordicsemi.com/topic/ps_nrf52833/rtc.html?cp=5_1_0_5_19
* https://infocenter.nordicsemi.com/topic/ps_nrf5340/rtc.html?cp=4_0_0_6_27
*/
/*
* This file provides the implementation of the RTC peripherals,
* and instantiates N of them, as described in the configuration (NHW_config.h)
*
* Notes:
*
* * TICK events are NOT modeled
*
* * The COUNTER register is only updated when read with the proper HAL function
*
* * Unlike the real HW, there is no jitter or (variable) delay in the tasks/events,
* operations, or synchronization of configuration:
* * Triggering a task (Thru the PPI or register write) starts the operation
* immediately (in HW it takes between 1/2 and 1+1/2 LFCLKs:
* "CLEAR and STOP and TRIGOVRFLW [..] will be delayed as long as it
* takes for the peripheral to clock a falling edge and a rising edge of the LFCLK.")
* * Events and interrupts are raised immediately once produced (in real HW
* they can be raised relatively at +-1/2 LFCLK or +-1/2 PCLK16M of each other)
* (In real HW this is due to the 32K-16M clock domain crossing synchronization)
* * A STOP task stops the counter immediately.
*
* * As the CLEAR task does not have delay, a SHORT of COMPARE<n>_CLEAR will cause the
* CLEAR to be instantaneous.
* While in the real HW, as per the spec:
* "If the COMPARE[i]_CLEAR short is enabled, the COUNTER will be cleared
* *one LFClk after* the COMPARE event"
*
* * Unlike in real HW reading the COUNTER register is instantaneous (in real HW
* it takes up to 6 PCLK16M cycles, during which the CPU is stalled.
*
* * Writing to the PRESCALER register when the RTC is running is not prevented
* (unlike in real HW), but doing so can have unintended consequences.
* Though the PRESCALER is shadowed into an internal register on the tasks
* START, CLEAR, and TRIGOVRFLW as per the spec.
*
* * Note, in nrf52 devices, the spec seems to confusingly state that the LFCLK
* clock must be ready before the RTC can be used, yet that a TRIGOVRFLW task
* will get the RTC to request the LFClock.
* But in real life (and the model) it seems no task will automatically
* request the clock.
* For nrf5340 devices, TRIGOVRFLW does not seem to request the CLOCK either,
* but START seems to as per the spec.
*
* * Note this model does not yet automatically request the LFCLK (for a nRF5340)
* with task START
*
* * This models assumes that once the LFCLK is started it is never stopped.
*
* * Note that, just like for the real RTC, the event generation logic is slightly
* different than for other peripherals
*
* Implementation notes:
* In a naive (but very simple) implementation the RTC model could be called
* every LFCLK tick. But this would be really slow (as we'd need to call it 32768
* times per second).
* Instead this model checks ahead, when the next compare or overflow event would
* trigger and sets a timer for its callback.
* There is one common timer exposed to the HW scheduler, and a set of internal timers
* per RTC instance (one per CC register, and one for the counter overflow)
*
* The RTC keeps track internally of the time with submicrosecond resolution
* (9 decimal bits) to have a exact representation of the LF clock ticks,
* and avoid accumulating rounding errors.
* With this current implementation, the maximum runtime of the RTC is limited to
* 2**64/2**9 microseconds from boot (~1142 years)
*
*
* Pending to implement:
* * TICK events
*
* * Delay of TASKs CLEAR, STOP and TRIGOVRFLW
*
* * For nrf5340: With task START, the RTC should automatically request the LFCLK source with RC oscillator if the LFCLK is not already running.
*/
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include "bs_tracing.h"
#include "bs_oswrap.h"
#include "nsi_hw_scheduler.h"
#include "nsi_tasks.h"
#include "nsi_hws_models_if.h"
#include "NHW_common_types.h"
#include "NHW_config.h"
#include "NHW_peri_types.h"
#include "NHW_xPPI.h"
#include "NHW_CLOCK.h"
#include "irq_ctrl.h"
#include "NHW_RTC.h"
#define RTC_COUNTER_MASK 0xFFFFFF /*24 bits*/
#define RTC_TRIGGER_OVERFLOW_COUNTER_VALUE 0xFFFFF0
#define SUB_US_BITS 9 // Bits representing sub-microsecond units
#define LF_CLOCK_PERIOD_subus 15625 /*in a fixed point format with 9 bits per us, the LF clock period*/
#define LF_CLOCK_PERIOD_us 31 /* the LF clock period in us ceil(1e6/32768) = ceil(30.517578125us) */
struct rtc_status {
NRF_RTC_Type *NRF_RTC_regs;
int n_CCs; //Number of compare/capture registers in this rtc instance
bs_time_t *cc_timers; //[n_CCs] When each CC will match (in microseconds)
bs_time_t overflow_timer; // When the timer will overflow (in microseconds)
uint64_t overflow_timer_sub_us; // When the timer will overflow (in sub-microsecond units)
uint64_t counter_startT_sub_us; //Time when the counter was "started" (really the time that would correspond to COUNTER = 0)
uint64_t counter_startT_negative_sub_us;
uint32_t counter_at_stop; //Internal counter value when the counter was *stopped*
uint32_t INTEN;
uint32_t PRESC;
bool running; // Is this RTC running/started
#if (NHW_HAS_DPPI)
uint dppi_map; //To which DPPI instance are this RTC subscribe&publish ports connected to
struct nhw_subsc_mem* subscribed_CAPTURE; //[n_CCs]
struct nhw_subsc_mem subscribed_START;
struct nhw_subsc_mem subscribed_STOP;
struct nhw_subsc_mem subscribed_CLEAR;
struct nhw_subsc_mem subscribed_TRIGOVRFLW;
#endif
};
static uint64_t first_lf_tick_time_sub_us;
static bs_time_t Timer_RTC = TIME_NEVER;
static struct rtc_status nhw_rtc_st[NHW_RTC_TOTAL_INST];
NRF_RTC_Type NRF_RTC_regs[NHW_RTC_TOTAL_INST];
static bs_time_t sub_us_time_to_us_time(uint64_t sub_us_time);
static uint64_t us_time_to_sub_us_time(bs_time_t us_time);
static void nhw_rtc_TASKS_CLEAR(uint rtc);
static void nhw_rtc_signal_OVERFLOW(uint rtc);
static void nhw_rtc_signal_COMPARE(uint rtc, uint cc);
static void nhw_rtc_init(void) {
#if (NHW_HAS_DPPI)
/* Mapping of peripheral instance to DPPI instance */
uint nhw_rtc_dppi_map[NHW_RTC_TOTAL_INST] = NHW_RTC_DPPI_MAP;
#endif
int RTC_n_CCs[NHW_RTC_TOTAL_INST] = NHW_RTC_N_CC;
memset(NRF_RTC_regs, 0, sizeof(NRF_RTC_regs));
for (int i = 0; i < NHW_RTC_TOTAL_INST ; i++) {
struct rtc_status *rtc_st = &nhw_rtc_st[i];
rtc_st->NRF_RTC_regs = &NRF_RTC_regs[i];
rtc_st->n_CCs = RTC_n_CCs[i];
rtc_st->counter_startT_sub_us = TIME_NEVER;
rtc_st->cc_timers = (bs_time_t *)bs_malloc(sizeof(bs_time_t)*RTC_n_CCs[i]);
for (int j = 0 ; j < rtc_st->n_CCs ; j++) {
rtc_st->cc_timers[j] = TIME_NEVER;
}
rtc_st->overflow_timer = TIME_NEVER;
rtc_st->overflow_timer_sub_us = TIME_NEVER;
#if (NHW_HAS_DPPI)
rtc_st->dppi_map = nhw_rtc_dppi_map[i];
rtc_st->subscribed_CAPTURE = (struct nhw_subsc_mem*)bs_calloc(RTC_n_CCs[i], sizeof(struct nhw_subsc_mem));
#endif
}
Timer_RTC = TIME_NEVER;
}
NSI_TASK(nhw_rtc_init, HW_INIT, 100);
/*
* Free all RTC instances resources before program exit
*/
static void nhw_rtc_free(void)
{
for (int t = 0; t < NHW_RTC_TOTAL_INST; t++) {
struct rtc_status *rtc_st = &nhw_rtc_st[t];
free(rtc_st->cc_timers);
rtc_st->cc_timers = NULL;
#if (NHW_HAS_DPPI)
free(rtc_st->subscribed_CAPTURE);
rtc_st->subscribed_CAPTURE = NULL;
#endif /* (NHW_HAS_DPPI) */
}
}
NSI_TASK(nhw_rtc_free, ON_EXIT_PRE, 100);
/**
* Convert a time delta in sub-microseconds units to the equivalent time in microseconds.
* The value is always rounded UP. This is because otherwise events would be registered
* in a time in microseconds before the event actually occurred. This would lead to many imprecise
* event timings for example if the timing of an event would be calculated base on the last LF
* clock tick (which happens for example when triggering the CLEAR or TRIGGER_OVERFLOW tasks)
*/
static bs_time_t sub_us_time_to_us_time(uint64_t sub_us_time)
{
bs_time_t us_time = sub_us_time >> SUB_US_BITS;
if(sub_us_time % (1U << SUB_US_BITS) != 0) //rounding up
{
us_time += 1;
}
return us_time;
}
/**
* Convert a time delta in microseconds to the equivalent time in sub-microseconds units
*/
static uint64_t us_time_to_sub_us_time(bs_time_t us_time)
{
return us_time << SUB_US_BITS;
}
static uint64_t get_time_in_sub_us(void)
{
bs_time_t now = nsi_hws_get_time();
if (now >= sub_us_time_to_us_time(TIME_NEVER)) {
bs_trace_error_time_line("Bummer, the RTC model only supports running for 1142 years\n");
/*If you really need this, generalize the calculation to more than 64 bits*/
}
return us_time_to_sub_us_time(now);
}
static uint64_t get_last_lf_tick_time_sub_us(void) {
uint64_t now_sub_us = get_time_in_sub_us();
uint64_t n_lf_ticks = (now_sub_us - first_lf_tick_time_sub_us) / LF_CLOCK_PERIOD_subus; //floor()
uint64_t last_tick_time_sub_us = n_lf_ticks * LF_CLOCK_PERIOD_subus;
last_tick_time_sub_us += first_lf_tick_time_sub_us;
return last_tick_time_sub_us;
}
/**
* Convert a time delta in sub-microsecond units to the equivalent count accounting for the PRESCALER
* Note that the number is rounded down [floor()]
*/
static uint64_t time_sub_us_to_counter(uint rtc, uint64_t delta_sub_us) {
uint64_t ticks;
ticks = delta_sub_us / ((uint64_t)LF_CLOCK_PERIOD_subus * (nhw_rtc_st[rtc].PRESC + 1));
return ticks;
}
/**
* Convert a counter delta to sub-microsecond units accounting for the PRESCALER
*/
static uint64_t counter_to_time_sub_us(uint rtc, uint64_t counter) {
uint64_t Elapsed;
Elapsed = counter * (uint64_t)LF_CLOCK_PERIOD_subus * (nhw_rtc_st[rtc].PRESC + 1);
return Elapsed;
}
/**
* Return the time in sub-microsecond units it takes for the COUNTER to do 1 wrap
*/
static uint64_t time_of_1_counter_wrap_sub_us(uint rtc) {
return counter_to_time_sub_us(rtc, (uint64_t)RTC_COUNTER_MASK + 1);
}
/*
* Return the *next* time (in us) when the RTC counter will reach <counter_match>
*/
static bs_time_t get_counter_match_time(uint rtc, uint64_t counter_match, uint64_t* next_match_sub_us)
{
struct rtc_status *this = &nhw_rtc_st[rtc];
bs_time_t next_match_us = TIME_NEVER;
*next_match_sub_us = TIME_NEVER;
if (this->running == true) {
uint64_t now_sub_us = get_time_in_sub_us();
uint64_t counter_match_sub_us = counter_to_time_sub_us(rtc, counter_match);
if(this->counter_startT_sub_us > 0)
{
*next_match_sub_us = this->counter_startT_sub_us
+ counter_match_sub_us;
}
else if (counter_match_sub_us > this->counter_startT_negative_sub_us)
{
*next_match_sub_us = counter_match_sub_us - this->counter_startT_negative_sub_us;
}
else
{
*next_match_sub_us = time_of_1_counter_wrap_sub_us(rtc)
+ counter_match_sub_us - this->counter_startT_negative_sub_us;
}
while(*next_match_sub_us <= now_sub_us)
{
*next_match_sub_us += time_of_1_counter_wrap_sub_us(rtc);
}
next_match_us = sub_us_time_to_us_time(*next_match_sub_us);
}
return next_match_us;
}
static void nhw_rtc_update_master_timer(void) {
Timer_RTC = TIME_NEVER;
for (int rtc = 0; rtc < NHW_RTC_TOTAL_INST ; rtc++) {
struct rtc_status *this = &nhw_rtc_st[rtc];
if (this->running == false) {
continue;
}
for (int cc = 0 ; cc < this->n_CCs ; cc++) {
if (this->cc_timers[cc] < Timer_RTC) {
Timer_RTC = this->cc_timers[cc];
}
}
if (this->overflow_timer < Timer_RTC) {
Timer_RTC = this->overflow_timer;
}
}
nsi_hws_find_next_event();
}
/**
* Save in cc_timers[cc] the *next* time when this RTC will match the
* CC[cc] register
*/
static void update_cc_timer(uint rtc, uint cc) {
uint64_t match_sub_us; // Only to comply to the interface
nhw_rtc_st[rtc].cc_timers[cc] = get_counter_match_time(rtc, NRF_RTC_regs[rtc].CC[cc], &match_sub_us);
}
/*
* Update all cc_timers[*] for a RTC instance
* to the *next* time when they will match
*/
static void update_all_cc_timers(uint rtc) {
for (int cc = 0 ; cc < nhw_rtc_st[rtc].n_CCs; cc++) {
update_cc_timer(rtc, cc);
}
}
static void update_overflow_timer(uint rtc) {
struct rtc_status *this = &nhw_rtc_st[rtc];
this->overflow_timer = get_counter_match_time(rtc, RTC_COUNTER_MASK + 1, &this->overflow_timer_sub_us);
}
static void update_timers(int rtc)
{
update_all_cc_timers(rtc);
update_overflow_timer(rtc);
nhw_rtc_update_master_timer();
}
/**
* Sets the internal state of the counter like if the counter was just set to the specified value.
* This is done by setting the "counter start time"
* (counter_startT*) to an appropriate value, so that the time elapsed from the counter start
* corresponds to the given counter value. Such virtual "counter start time" can be negative.
*/
static void nhw_rtc_set_counter(uint rtc, uint64_t counter_val)
{
struct rtc_status *this = &nhw_rtc_st[rtc];
counter_val &= RTC_COUNTER_MASK;
uint64_t counter_val_sub_us = counter_to_time_sub_us(rtc, counter_val);
// All the functions which use this reset the <PRESC>, so it is like the counter was set
// on the last LF clock tick
uint64_t last_lf_tick_sub_us = get_last_lf_tick_time_sub_us();
if(last_lf_tick_sub_us >= counter_val_sub_us)
{
this->counter_startT_sub_us = last_lf_tick_sub_us - counter_val_sub_us;
this->counter_startT_negative_sub_us = 0;
}
else
{
this->counter_startT_sub_us = 0;
this->counter_startT_negative_sub_us = counter_val_sub_us - last_lf_tick_sub_us;
}
NRF_RTC_regs[rtc].COUNTER = counter_val;
update_timers(rtc);
}
static void handle_overflow_event(uint rtc)
{
struct rtc_status *this = &nhw_rtc_st[rtc];
// The real time (in sub-microsecond units, not in microseconds)
// in which the current overflow event occurs.
// update_overflow_timer will overwrite overflow_timer_sub_us[rtc]
uint64_t current_overflow_event_sub_us = this->overflow_timer_sub_us;
update_overflow_timer(rtc); //Next time it will overflow
bs_trace_raw_time(8, "RTC%i: Timer overflow\n", rtc);
this->counter_startT_sub_us = current_overflow_event_sub_us;
this->counter_startT_negative_sub_us = 0;
nhw_rtc_signal_OVERFLOW(rtc);
}
static void nhw_rtc_timer_triggered(void) {
for (int rtc = 0; rtc < NHW_RTC_TOTAL_INST ; rtc++) {
struct rtc_status *rtc_el = &nhw_rtc_st[rtc];
if (rtc_el->running == false) {
continue;
}
for (int cc = 0 ; cc < rtc_el->n_CCs ; cc++) {
if (rtc_el->cc_timers[cc] == Timer_RTC ){ //This CC is matching now
update_cc_timer(rtc, cc); //Next time it will match
nhw_rtc_signal_COMPARE(rtc, cc);
}
}
if (rtc_el->overflow_timer == Timer_RTC) { //Overflow occurred now
handle_overflow_event(rtc); // this must always be the last event, as it might update counter_startT_sub_us
}
}
nhw_rtc_update_master_timer();
}
NSI_HW_EVENT(Timer_RTC, nhw_rtc_timer_triggered, 50);
/**
* Check if an EVTEN or INTEN has the tick event set
*/
static void check_not_supported_TICK(uint32_t i) {
if (i & RTC_EVTEN_TICK_Msk) {
bs_trace_warning_line_time("RTC: The TICK functionality is not modelled\n");
}
}
void nhw_rtc_notify_first_lf_tick(void) {
first_lf_tick_time_sub_us = get_time_in_sub_us();
bs_trace_raw_time(9, "RTC: First lf tick\n");
}
/*
* Update the counter register so it can be read by SW
*/
void nhw_rtc_update_COUNTER(uint rtc) {
struct rtc_status *this = &nhw_rtc_st[rtc];
NRF_RTC_Type *RTC_regs = &NRF_RTC_regs[rtc];
if (this->running == true) {
uint64_t count;
count = time_sub_us_to_counter(rtc,
get_time_in_sub_us() - this->counter_startT_sub_us
+ this->counter_startT_negative_sub_us);
RTC_regs->COUNTER = count & RTC_COUNTER_MASK;
} else {
RTC_regs->COUNTER = this->counter_at_stop & RTC_COUNTER_MASK;
}
}
/**
* TASK_START triggered handler
*/
static void nhw_rtc_TASKS_START(uint rtc) {
struct rtc_status *this = &nhw_rtc_st[rtc];
if (this->running == true) {
return;
}
bs_trace_raw_time(5, "RTC%i: TASK_START\n", rtc);
this->running = true;
/* Pre-scaler value is latched to an internal register on tasks START, CLEAR, and TRIGOVRFLW */
this->PRESC = NRF_RTC_regs[rtc].PRESCALER;
//If the counter is not zero at start, is like if the counter was started earlier
nhw_rtc_set_counter(rtc, this->counter_at_stop);
}
/**
* TASK_STOP triggered handler
*/
static void nhw_rtc_TASKS_STOP(uint rtc) {
struct rtc_status *this = &nhw_rtc_st[rtc];
if (this->running == false) {
return;
}
bs_trace_raw_time(5, "RTC%i: TASK_STOP\n", rtc);
this->running = false;
this->counter_at_stop = time_sub_us_to_counter(rtc,
get_time_in_sub_us() - this->counter_startT_sub_us
+ this->counter_startT_negative_sub_us); //we save the value when the counter was stoped in case it is started again without clearing it
this->counter_at_stop &= RTC_COUNTER_MASK;
NRF_RTC_regs[rtc].COUNTER = this->counter_at_stop;
for (int cc = 0 ; cc < this->n_CCs ; cc++) {
this->cc_timers[cc] = TIME_NEVER;
}
this->overflow_timer = TIME_NEVER;
nhw_rtc_update_master_timer();
}
/**
* TASK_CLEAR triggered handler
*/
static void nhw_rtc_TASKS_CLEAR(uint rtc) {
bs_trace_raw_time(5, "RTC%i: TASK_CLEAR\n", rtc);
/* Pre-scaler value is latched to an internal register on tasks START, CLEAR, and TRIGOVRFLW */
nhw_rtc_st[rtc].PRESC = NRF_RTC_regs[rtc].PRESCALER;
nhw_rtc_st[rtc].counter_at_stop = 0;
nhw_rtc_set_counter(rtc, 0);
}
/**
* TASK_TRIGGER_OVERFLOW triggered handler
*/
static void nhw_rtc_TASKS_TRIGOVRFLW(uint rtc) {
bs_trace_raw_time(5, "RTC%i: TASK_TRIGGER_OVERFLOW\n", rtc);
/* Pre-scaler value is latched to an internal register on tasks START, CLEAR, and TRIGOVRFLW */
nhw_rtc_st[rtc].PRESC = NRF_RTC_regs[rtc].PRESCALER;
nhw_rtc_st[rtc].counter_at_stop = RTC_TRIGGER_OVERFLOW_COUNTER_VALUE;
nhw_rtc_set_counter(rtc, RTC_TRIGGER_OVERFLOW_COUNTER_VALUE);
}
#if (NHW_RTC_HAS_CAPTURE)
static void nhw_rtc_TASKS_CAPTURE(uint rtc, uint cc_n) {
NRF_RTC_Type *RTC_regs = &NRF_RTC_regs[rtc];
nhw_rtc_update_COUNTER(rtc);
RTC_regs->CC[cc_n] = RTC_regs->COUNTER;
nhw_rtc_regw_sideeffects_CC(rtc, cc_n);
}
#endif /* NHW_RTC_HAS_CAPTURE */
static void nhw_rtc_eval_interrupts(uint rtc) {
/* Mapping of peripheral instance to {int controller instance, int number} */
static struct nhw_irq_mapping nhw_rtc_irq_map[NHW_RTC_TOTAL_INST] = NHW_RTC_INT_MAP;
static bool RTC_int_line[NHW_RTC_TOTAL_INST]; /* Is the RTC currently driving its interrupt line high */
struct rtc_status *this = &nhw_rtc_st[rtc];
bool new_int_line = false;
for (int cc = 0; cc < this->n_CCs; cc++) {
int mask = this->INTEN & (RTC_INTENSET_COMPARE0_Msk << cc);
if (NRF_RTC_regs[rtc].EVENTS_COMPARE[cc] && mask) {
new_int_line = true;
break; /* No need to check more */
}
}
if (NRF_RTC_regs[rtc].EVENTS_TICK && (this->INTEN & RTC_INTENSET_TICK_Msk)) {
new_int_line = true;
}
if (NRF_RTC_regs[rtc].EVENTS_OVRFLW && (this->INTEN & RTC_INTENSET_OVRFLW_Msk)) {
new_int_line = true;
}
if (RTC_int_line[rtc] == false && new_int_line == true) {
RTC_int_line[rtc] = true;
hw_irq_ctrl_raise_level_irq_line(nhw_rtc_irq_map[rtc].cntl_inst,
nhw_rtc_irq_map[rtc].int_nbr);
} else if (RTC_int_line[rtc] == true && new_int_line == false) {
RTC_int_line[rtc] = false;
hw_irq_ctrl_lower_level_irq_line(nhw_rtc_irq_map[rtc].cntl_inst,
nhw_rtc_irq_map[rtc].int_nbr);
}
}
static void nhw_rtc_signal_COMPARE(uint rtc, uint cc)
{
struct rtc_status *this = &nhw_rtc_st[rtc];
NRF_RTC_Type *RTC_regs = &NRF_RTC_regs[rtc];
#if (NHW_RTC_HAS_SHORT_COMP_CLEAR)
if (RTC_regs->SHORTS & (RTC_SHORTS_COMPARE0_CLEAR_Msk << cc)) {
nhw_rtc_TASKS_CLEAR(rtc);
bs_trace_warning_line_time("RTC: COMPARE->CLEAR short used, but CLEAR is instantaneous."
"If you are using this to generate a periodic interrupt, the period"
"will be 1 count too short\n");
}
#endif /* NHW_RTC_HAS_SHORT_COMP_CLEAR */
uint32_t mask = RTC_EVTEN_COMPARE0_Msk << cc;
if (!((RTC_regs->EVTEN | this->INTEN) & mask)) {
return;
}
RTC_regs->EVENTS_COMPARE[cc] = 1;
if (RTC_regs->EVTEN & mask) {
#if (NHW_HAS_PPI)
ppi_event_types_t event = RTC0_EVENTS_COMPARE_0;
switch (rtc){
case 0:
event = RTC0_EVENTS_COMPARE_0;
break;
case 1:
event = RTC1_EVENTS_COMPARE_0;
break;
case 2:
event = RTC2_EVENTS_COMPARE_0;
break;
}
event += cc;
nrf_ppi_event(event);
#elif (NHW_HAS_DPPI)
nhw_dppi_event_signal_if(this->dppi_map,
RTC_regs->PUBLISH_COMPARE[cc]);
#endif
}
nhw_rtc_eval_interrupts(rtc);
}
static void nhw_rtc_signal_OVERFLOW(uint rtc)
{
struct rtc_status *this = &nhw_rtc_st[rtc];
NRF_RTC_Type *RTC_regs = &NRF_RTC_regs[rtc];
if (!((RTC_regs->EVTEN | this->INTEN) & RTC_EVTEN_OVRFLW_Msk)) {
return;
}
RTC_regs->EVENTS_OVRFLW = 1;
if (RTC_regs->EVTEN & RTC_EVTEN_OVRFLW_Msk) {
#if (NHW_HAS_PPI)
ppi_event_types_t event = RTC0_EVENTS_OVRFLW;
switch (rtc){
case 0:
event = RTC0_EVENTS_OVRFLW;
break;
case 1:
event = RTC1_EVENTS_OVRFLW;
break;
case 2:
event = RTC2_EVENTS_OVRFLW;
break;
}
nrf_ppi_event(event);
#elif (NHW_HAS_DPPI)
nhw_dppi_event_signal_if(this->dppi_map,
RTC_regs->PUBLISH_OVRFLW);
#endif
}
nhw_rtc_eval_interrupts(rtc);
}
/*static*/ void nhw_rtc_signal_TICK(uint rtc) /*Not yet used, as all TICK functionality is not yet implemented */
{
struct rtc_status *this = &nhw_rtc_st[rtc];
NRF_RTC_Type *RTC_regs = &NRF_RTC_regs[rtc];
if (!((RTC_regs->EVTEN | this->INTEN) & RTC_EVTEN_TICK_Msk)) {
return;
}
RTC_regs->EVENTS_TICK = 1;
if (RTC_regs->EVTEN & RTC_EVTEN_TICK_Msk) {
#if (NHW_HAS_PPI)
ppi_event_types_t event = RTC0_EVENTS_TICK;
switch (rtc){
case 0:
event = RTC0_EVENTS_TICK;
break;
case 1:
event = RTC1_EVENTS_TICK;
break;
case 2:
event = RTC2_EVENTS_TICK;
break;
}
nrf_ppi_event(event);
#elif (NHW_HAS_DPPI)
nhw_dppi_event_signal_if(this->dppi_map,
RTC_regs->PUBLISH_TICK);
#endif
}
nhw_rtc_eval_interrupts(rtc);
}
void nhw_rtc_regw_sideeffect_TASKS_START(uint i) {
NRF_RTC_Type *RTC_regs = &NRF_RTC_regs[i];
if (RTC_regs->TASKS_START) {
RTC_regs->TASKS_START = 0;
nhw_rtc_TASKS_START(i);
}
}
void nhw_rtc_regw_sideeffect_TASKS_STOP(uint i) {
NRF_RTC_Type *RTC_regs = &NRF_RTC_regs[i];
if (RTC_regs->TASKS_STOP) {
RTC_regs->TASKS_STOP = 0;
nhw_rtc_TASKS_STOP(i);
}
}
void nhw_rtc_regw_sideeffect_TASKS_CLEAR(uint i) {
NRF_RTC_Type *RTC_regs = &NRF_RTC_regs[i];
if (RTC_regs->TASKS_CLEAR) {
RTC_regs->TASKS_CLEAR = 0;
nhw_rtc_TASKS_CLEAR(i);
}
}
void nhw_rtc_regw_sideeffect_TASKS_TRIGOVRFLW(uint i) {
NRF_RTC_Type *RTC_regs = &NRF_RTC_regs[i];
if (RTC_regs->TASKS_TRIGOVRFLW) {
RTC_regs->TASKS_TRIGOVRFLW = 0;
nhw_rtc_TASKS_TRIGOVRFLW(i);
}
}
#if (NHW_RTC_HAS_CAPTURE)
void nhw_rtc_regw_sideeffect_TASKS_CAPTURE(uint i, uint cc) {
NRF_RTC_Type *RTC_regs = &NRF_RTC_regs[i];
if (RTC_regs->TASKS_CAPTURE[cc]) {
RTC_regs->TASKS_CAPTURE[cc] = 0;
nhw_rtc_TASKS_CAPTURE(i, cc);
}
}
#endif /* NHW_RTC_HAS_CAPTURE */
#if (NHW_HAS_DPPI)
static void nhw_rtc_taskcapture_wrap(void* param) {
unsigned int inst = (uintptr_t)param >> 16;
uint cc_n = (uintptr_t)param & 0xFFFF;
nhw_rtc_TASKS_CAPTURE(inst, cc_n);
}
void nhw_rtc_regw_sideeffects_SUBSCRIBE_CAPTURE(uint inst, uint cc_n) {
struct rtc_status *this = &nhw_rtc_st[inst];
nhw_dppi_common_subscribe_sideeffect(this->dppi_map,
this->NRF_RTC_regs->SUBSCRIBE_CAPTURE[cc_n],
&this->subscribed_CAPTURE[cc_n],
nhw_rtc_taskcapture_wrap,
(void*)((inst << 16) + cc_n));
}
#define NHW_RTC_REGW_SIDEFFECTS_SUBSCRIBE(TASK_N) \
static void nhw_rtc_task##TASK_N##_wrap(void* param) \
{ \
nhw_rtc_TASKS_##TASK_N((int) param); \
} \
\
void nhw_rtc_regw_sideeffects_SUBSCRIBE_##TASK_N(uint inst) \
{ \
struct rtc_status *this = &nhw_rtc_st[inst]; \
\
nhw_dppi_common_subscribe_sideeffect(this->dppi_map, \
this->NRF_RTC_regs->SUBSCRIBE_##TASK_N, \
&this->subscribed_##TASK_N, \
nhw_rtc_task##TASK_N##_wrap, \
(void*) inst); \
}
NHW_RTC_REGW_SIDEFFECTS_SUBSCRIBE(START)
NHW_RTC_REGW_SIDEFFECTS_SUBSCRIBE(STOP)
NHW_RTC_REGW_SIDEFFECTS_SUBSCRIBE(CLEAR)
NHW_RTC_REGW_SIDEFFECTS_SUBSCRIBE(TRIGOVRFLW)
#endif /* NHW_HAS_DPPI */
void nhw_rtc_regw_sideeffect_INTENSET(uint rtc)
{
struct rtc_status *this = &nhw_rtc_st[rtc];
NRF_RTC_Type *RTC_regs = &NRF_RTC_regs[rtc];
if ( RTC_regs->INTENSET ){
this->INTEN |= RTC_regs->INTENSET;
RTC_regs->INTENSET = this->INTEN;
check_not_supported_TICK(this->INTEN);
nhw_rtc_eval_interrupts(rtc);
}
}
void nhw_rtc_regw_sideeffect_INTENCLR(uint rtc)
{
struct rtc_status *this = &nhw_rtc_st[rtc];
NRF_RTC_Type *RTC_regs = &NRF_RTC_regs[rtc];
if ( RTC_regs->INTENCLR ){
this->INTEN &= ~RTC_regs->INTENCLR;
RTC_regs->INTENSET = this->INTEN;
RTC_regs->INTENCLR = 0;
nhw_rtc_eval_interrupts(rtc);
}
}
void nhw_rtc_regw_sideeffect_EVTENSET(uint i) {
NRF_RTC_Type *RTC_regs = &NRF_RTC_regs[i];
if ( RTC_regs->EVTENSET ){
RTC_regs->EVTEN |= RTC_regs->EVTENSET;
RTC_regs->EVTENSET = RTC_regs->EVTEN;
check_not_supported_TICK(RTC_regs->EVTEN);
}
}
void nhw_rtc_regw_sideeffect_EVTENCLR(uint i) {
NRF_RTC_Type *RTC_regs = &NRF_RTC_regs[i];
if ( RTC_regs->EVTENCLR ){
RTC_regs->EVTEN &= ~RTC_regs->EVTENCLR;
RTC_regs->EVTENSET = RTC_regs->EVTEN;
RTC_regs->EVTENCLR = 0;
}
}
void nhw_rtc_regw_sideeffects_EVENTS_all(uint rtc) {
nhw_rtc_eval_interrupts(rtc);
}
void nhw_rtc_regw_sideeffects_CC(uint rtc, uint cc_n) {
struct rtc_status *this = &nhw_rtc_st[rtc];
if (this->running == true) {
update_cc_timer(rtc, cc_n);
nhw_rtc_update_master_timer();
}
}
#if (NHW_HAS_PPI)
void nhw_rtc0_TASKS_START(void) { nhw_rtc_TASKS_START(0); }
void nhw_rtc0_TASKS_STOP(void) { nhw_rtc_TASKS_STOP(0); }
void nhw_rtc0_TASKS_CLEAR(void) { nhw_rtc_TASKS_CLEAR(0); }
void nhw_rtc0_TASKS_TRIGOVRFLW(void) { nhw_rtc_TASKS_TRIGOVRFLW(0); }
void nhw_rtc1_TASKS_START(void) { nhw_rtc_TASKS_START(1); }
void nhw_rtc1_TASKS_STOP(void) { nhw_rtc_TASKS_STOP(1); }
void nhw_rtc1_TASKS_CLEAR(void) { nhw_rtc_TASKS_CLEAR(1); }
void nhw_rtc1_TASKS_TRIGOVRFLW(void) { nhw_rtc_TASKS_TRIGOVRFLW(1); }
void nhw_rtc2_TASKS_START(void) { nhw_rtc_TASKS_START(2); }
void nhw_rtc2_TASKS_STOP(void) { nhw_rtc_TASKS_STOP(2); }
void nhw_rtc2_TASKS_CLEAR(void) { nhw_rtc_TASKS_CLEAR(2); }
void nhw_rtc2_TASKS_TRIGOVRFLW(void) { nhw_rtc_TASKS_TRIGOVRFLW(2); }
#endif /* NHW_HAS_PPI */
int64_t nhw_rtc_start_time_get(uint rtc) {
struct rtc_status *this = &nhw_rtc_st[rtc];
if (!this->running) {
bs_trace_error_time_line("RTC is not running. Start time unknown\n");
return 0;
}
if(this->counter_startT_sub_us > 0) {
return (int64_t)sub_us_time_to_us_time(this->counter_startT_sub_us);
} else {
return -(int64_t)sub_us_time_to_us_time(this->counter_startT_negative_sub_us);
}
}