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