1 /*
2 * Copyright (c) 2017 Oticon A/S
3 * Copyright (c) 2023 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /**
9 * This is a fake HW device timer (one instance per embedded CPU)
10 * which aids in the execution of a test application which
11 * runs in parallel to the normal embedded application
12 */
13
14 #include "bs_types.h"
15 #include "irq_ctrl.h"
16 #include "nsi_tasks.h"
17 #include "nsi_cpun_if.h"
18 #include "nsi_hw_scheduler.h"
19 #include "nsi_hws_models_if.h"
20 #include "NHW_common_types.h"
21 #include "NHW_config.h"
22
23 struct bs_ticker_status {
24 bs_time_t ticker_timer; /* Next time to awake either by the timer or a awake_cpu_asap() call */
25 bs_time_t ticker_timer_lt; /* Next wake due to the plain bsticker timer */
26 bs_time_t tick_period;
27 bool awake_cpu_asap;
28 };
29
30 static bs_time_t bst_ticker_timer = TIME_NEVER;
31 /* Mapping of peripheral instance to {int controller instance, int number} */
32 static struct nhw_irq_mapping nhw_bsticker_irq_map[NHW_BSTICKER_TOTAL_INST] = NHW_BSTICKER_TIMER_INT_MAP;
33 static struct bs_ticker_status bs_ticket_st[NHW_BSTICKER_TOTAL_INST];
34
35 /*
36 * Initialize all fake timer instances for this SOC
37 */
nhw_bst_ticker_init(void)38 static void nhw_bst_ticker_init(void)
39 {
40 for (int i = 0; i < NHW_BSTICKER_TOTAL_INST ; i++) {
41 bs_ticket_st[i].ticker_timer = TIME_NEVER;
42 bs_ticket_st[i].ticker_timer_lt = TIME_NEVER;
43 bs_ticket_st[i].tick_period = TIME_NEVER;
44 }
45 }
46
47 NSI_TASK(nhw_bst_ticker_init, HW_INIT, 10);
48
nhw_bst_ticker_update_main_timer(void)49 static void nhw_bst_ticker_update_main_timer(void)
50 {
51 bst_ticker_timer = bs_ticket_st[0].ticker_timer;
52
53 for (int i = 1; i < NHW_FAKE_TIMER_TOTAL_INST ; i++) {
54 if (bs_ticket_st[i].ticker_timer < bst_ticker_timer) {
55 bst_ticker_timer = bs_ticket_st[i].ticker_timer;
56 }
57 }
58
59 nsi_hws_find_next_event();
60 }
61
nhw_bst_ticker_find_next_time_inner(struct bs_ticker_status * this)62 static void nhw_bst_ticker_find_next_time_inner(struct bs_ticker_status *this)
63 {
64 if (this->awake_cpu_asap == true) {
65 this->ticker_timer = nsi_hws_get_time(); //We will awake it in this same microsecond
66 } else {
67 this->ticker_timer = this->ticker_timer_lt;
68 }
69 }
70
nhw_bst_ticker_find_next_time(uint inst)71 static void nhw_bst_ticker_find_next_time(uint inst) {
72 struct bs_ticker_status *this = &bs_ticket_st[inst];
73
74 nhw_bst_ticker_find_next_time_inner(this);
75
76 nhw_bst_ticker_update_main_timer();
77 }
78
79 /**
80 * Set the FW_test ticker to trigger periodically, with a period of <tick_period>
81 * Next trigger will occur tick_period (us= + current time
82 */
bst_ticker_amp_set_period(uint inst,bs_time_t tick_period)83 void bst_ticker_amp_set_period(uint inst, bs_time_t tick_period)
84 {
85 struct bs_ticker_status *this = &bs_ticket_st[inst];
86
87 this->tick_period = tick_period;
88 this->ticker_timer_lt = tick_period + nsi_hws_get_time();
89 nhw_bst_ticker_find_next_time(inst);
90 }
91
92 /**
93 * Set the next time the FW test ticker will trigger at <absolute_time> us
94 */
bst_ticker_amp_set_next_tick_absolutelute(uint inst,bs_time_t absolute_time)95 void bst_ticker_amp_set_next_tick_absolutelute(uint inst, bs_time_t absolute_time)
96 {
97 bs_ticket_st[inst].ticker_timer_lt = absolute_time;
98 nhw_bst_ticker_find_next_time(inst);
99 }
100
101 /**
102 * Set the next time the FW test ticker will trigger
103 * at <delta_time> (us) + current Hw time
104 */
bst_ticker_amp_set_next_tick_delta(uint inst,bs_time_t delta_time)105 void bst_ticker_amp_set_next_tick_delta(uint inst, bs_time_t delta_time)
106 {
107 bs_ticket_st[inst].ticker_timer_lt = delta_time + nsi_hws_get_time();
108 nhw_bst_ticker_find_next_time(inst);
109 }
110
nhw_bst_ticker_triggered(void)111 static void nhw_bst_ticker_triggered(void)
112 {
113 bs_time_t now = nsi_hws_get_time();
114
115 for (int i = 0; i < NHW_BSTICKER_TOTAL_INST ; i++ ) {
116 struct bs_ticker_status *this = &bs_ticket_st[i];
117
118 if (this->awake_cpu_asap == true) {
119 this->awake_cpu_asap = false;
120 hw_irq_ctrl_raise_im(nhw_bsticker_irq_map[i].cntl_inst,
121 PHONY_HARD_IRQ);
122 } else {
123 if (this->tick_period != TIME_NEVER) {
124 this->ticker_timer_lt = this->tick_period + now;
125 } else {
126 this->ticker_timer_lt = TIME_NEVER;
127 }
128 (void) nsif_cpun_test_hook(i, NULL);
129 }
130
131 nhw_bst_ticker_find_next_time_inner(this);
132 }
133
134 nhw_bst_ticker_update_main_timer();
135 }
136
137 NSI_HW_EVENT(bst_ticker_timer, nhw_bst_ticker_triggered, 1 /* Purposely the second */);
138
139 /**
140 * Awake the MCU as soon as possible (in this same microsecond, in a following delta)
141 */
bst_ticker_amp_awake_cpu_asap(uint inst)142 void bst_ticker_amp_awake_cpu_asap(uint inst)
143 {
144 bs_ticket_st[inst].awake_cpu_asap = true;
145 nhw_bst_ticker_find_next_time(inst);
146 }
147