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 * Overall HW models scheduler for the native simulator
10 *
11 * Models events are registered with NSI_HW_EVENT().
12 */
13
14 #include <stdint.h>
15 #include <signal.h>
16 #include <stddef.h>
17 #include <inttypes.h>
18 #include "nsi_tracing.h"
19 #include "nsi_main.h"
20 #include "nsi_safe_call.h"
21 #include "nsi_hw_scheduler.h"
22 #include "nsi_hws_models_if.h"
23
24 static uint64_t simu_time; /* The actual time as known by the HW models */
25 static uint64_t end_of_time = NSI_NEVER; /* When will this device stop */
26
27 extern struct nsi_hw_event_st __nsi_hw_events_start[];
28 extern struct nsi_hw_event_st __nsi_hw_events_end[];
29
30 static unsigned int number_of_events;
31
32 static unsigned int next_timer_index;
33 static uint64_t next_timer_time;
34
35 /* Have we received a SIGTERM or SIGINT */
36 static volatile sig_atomic_t signaled_end;
37
38 /**
39 * Handler for SIGTERM and SIGINT
40 */
nsi_hws_signal_end_handler(int sig)41 static void nsi_hws_signal_end_handler(int sig)
42 {
43 signaled_end = 1;
44 }
45
46 /**
47 * Set the handler for SIGTERM and SIGINT which will cause the
48 * program to exit gracefully when they are received the 1st time
49 *
50 * Note that our handler only sets a variable indicating the signal was
51 * received, and in each iteration of the hw main loop this variable is
52 * evaluated.
53 * If for some reason (the program is stuck) we never evaluate it, the program
54 * would never exit.
55 * Therefore we set SA_RESETHAND: This way, the 2nd time the signal is received
56 * the default handler would be called to terminate the program no matter what.
57 *
58 * Note that SA_RESETHAND requires either _POSIX_C_SOURCE>=200809 or
59 * _XOPEN_SOURCE>=500
60 */
nsi_hws_set_sig_handler(void)61 static void nsi_hws_set_sig_handler(void)
62 {
63 struct sigaction act;
64
65 act.sa_handler = nsi_hws_signal_end_handler;
66 NSI_SAFE_CALL(sigemptyset(&act.sa_mask));
67
68 act.sa_flags = SA_RESETHAND;
69
70 NSI_SAFE_CALL(sigaction(SIGTERM, &act, NULL));
71 NSI_SAFE_CALL(sigaction(SIGINT, &act, NULL));
72 }
73
74
nsi_hws_sleep_until_next_event(void)75 static void nsi_hws_sleep_until_next_event(void)
76 {
77 if (next_timer_time >= simu_time) { /* LCOV_EXCL_BR_LINE */
78 simu_time = next_timer_time;
79 } else {
80 /* LCOV_EXCL_START */
81 nsi_print_warning("next_timer_time corrupted (%"PRIu64"<= %"
82 PRIu64", timer idx=%i)\n",
83 (uint64_t)next_timer_time,
84 (uint64_t)simu_time,
85 next_timer_index);
86 /* LCOV_EXCL_STOP */
87 }
88
89 if (signaled_end || (simu_time > end_of_time)) {
90 nsi_print_trace("\nStopped at %.3Lfs\n",
91 ((long double)simu_time)/1.0e6L);
92 nsi_exit(0);
93 }
94 }
95
96
97 /**
98 * Find in between all events timers which is the next one.
99 * (and update the internal next_timer_* accordingly)
100 */
nsi_hws_find_next_event(void)101 void nsi_hws_find_next_event(void)
102 {
103 next_timer_index = 0;
104 next_timer_time = *__nsi_hw_events_start[0].timer;
105
106 for (unsigned int i = 1; i < number_of_events ; i++) {
107 if (next_timer_time > *__nsi_hw_events_start[i].timer) {
108 next_timer_index = i;
109 next_timer_time = *__nsi_hw_events_start[i].timer;
110 }
111 }
112 }
113
nsi_hws_get_next_event_time(void)114 uint64_t nsi_hws_get_next_event_time(void)
115 {
116 return next_timer_time;
117 }
118
119 /**
120 * Execute the next scheduled HW event
121 * (advancing time until that event would trigger)
122 */
nsi_hws_one_event(void)123 void nsi_hws_one_event(void)
124 {
125 nsi_hws_sleep_until_next_event();
126
127 if (next_timer_index < number_of_events) { /* LCOV_EXCL_BR_LINE */
128 __nsi_hw_events_start[next_timer_index].callback();
129 } else {
130 nsi_print_error_and_exit("next_timer_index corrupted\n"); /* LCOV_EXCL_LINE */
131 }
132
133 nsi_hws_find_next_event();
134 }
135
136 /**
137 * Set the simulated time when the process will stop
138 */
nsi_hws_set_end_of_time(uint64_t new_end_of_time)139 void nsi_hws_set_end_of_time(uint64_t new_end_of_time)
140 {
141 end_of_time = new_end_of_time;
142 }
143
144 /**
145 * Return the current simulated time as known by the device
146 */
nsi_hws_get_time(void)147 uint64_t nsi_hws_get_time(void)
148 {
149 return simu_time;
150 }
151
152 /**
153 * Function to initialize the HW scheduler
154 *
155 * Note that the HW models should register their initialization functions
156 * as NSI_TASKS of HW_INIT level.
157 */
nsi_hws_init(void)158 void nsi_hws_init(void)
159 {
160 number_of_events = __nsi_hw_events_end - __nsi_hw_events_start;
161
162 nsi_hws_set_sig_handler();
163 nsi_hws_find_next_event();
164 }
165
166 /**
167 * Function to free any resources allocated by the HW scheduler
168 *
169 * Note that the HW models should register their initialization functions
170 * as NSI_TASKS of ON_EXIT_PRE/POST levels.
171 */
nsi_hws_cleanup(void)172 void nsi_hws_cleanup(void)
173 {
174 }
175