1 /*
2  * Copyright (c) 2017 Oticon A/S
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * Reduced set of HW models sufficient to run some of the sample apps
9  * and regression tests
10  */
11 
12 #include <stdint.h>
13 #include <signal.h>
14 #include <stddef.h>
15 #include <stdlib.h>
16 #include <pthread.h>
17 #include "hw_models_top.h"
18 #include "timer_model.h"
19 #include "irq_ctrl.h"
20 #include "posix_board_if.h"
21 #include "hw_counter.h"
22 #include <zephyr/arch/posix/posix_soc_if.h>
23 #include "posix_arch_internal.h"
24 #include <zephyr/sys/util.h>
25 
26 
27 static uint64_t simu_time; /* The actual time as known by the HW models */
28 static uint64_t end_of_time = NEVER; /* When will this device stop */
29 
30 /* List of HW model timers: */
31 extern uint64_t hw_timer_timer; /* When should this timer_model be called */
32 extern uint64_t irq_ctrl_timer;
33 extern uint64_t hw_counter_timer;
34 
35 static enum {
36 	HWTIMER = 0,
37 	IRQCNT,
38 	HW_COUNTER,
39 	NUMBER_OF_TIMERS,
40 	NONE
41 } next_timer_index = NONE;
42 
43 static uint64_t *Timer_list[NUMBER_OF_TIMERS] = {
44 	&hw_timer_timer,
45 	&irq_ctrl_timer,
46 	&hw_counter_timer,
47 };
48 
49 static uint64_t next_timer_time;
50 
51 /* Have we received a SIGTERM or SIGINT */
52 static volatile sig_atomic_t signaled_end;
53 
54 /**
55  * Handler for SIGTERM and SIGINT
56  */
hwm_signal_end_handler(int sig)57 void hwm_signal_end_handler(int sig)
58 {
59 	signaled_end = 1;
60 }
61 
62 /**
63  * Set the handler for SIGTERM and SIGINT which will cause the
64  * program to exit gracefully when they are received the 1st time
65  *
66  * Note that our handler only sets a variable indicating the signal was
67  * received, and in each iteration of the hw main loop this variable is
68  * evaluated.
69  * If for some reason (the program is stuck) we never evaluate it, the program
70  * would never exit.
71  * Therefore we set SA_RESETHAND: This way, the 2nd time the signal is received
72  * the default handler would be called to terminate the program no matter what.
73  *
74  * Note that SA_RESETHAND requires either _POSIX_C_SOURCE>=200809 or
75  * _XOPEN_SOURCE>=500
76  */
hwm_set_sig_handler(void)77 void hwm_set_sig_handler(void)
78 {
79 	struct sigaction act;
80 
81 	act.sa_handler = hwm_signal_end_handler;
82 	PC_SAFE_CALL(sigemptyset(&act.sa_mask));
83 
84 	act.sa_flags = SA_RESETHAND;
85 
86 	PC_SAFE_CALL(sigaction(SIGTERM, &act, NULL));
87 	PC_SAFE_CALL(sigaction(SIGINT, &act, NULL));
88 }
89 
90 
hwm_sleep_until_next_timer(void)91 static void hwm_sleep_until_next_timer(void)
92 {
93 	if (next_timer_time >= simu_time) { /* LCOV_EXCL_BR_LINE */
94 		simu_time = next_timer_time;
95 	} else {
96 		/* LCOV_EXCL_START */
97 		posix_print_warning("next_timer_time corrupted (%"PRIu64"<= %"
98 				PRIu64", timer idx=%i)\n",
99 				(uint64_t)next_timer_time,
100 				(uint64_t)simu_time,
101 				next_timer_index);
102 		/* LCOV_EXCL_STOP */
103 	}
104 
105 	if (signaled_end || (simu_time > end_of_time)) {
106 		posix_print_trace("\nStopped at %.3Lfs\n",
107 				((long double)simu_time)/1.0e6L);
108 		posix_exit(0);
109 	}
110 }
111 
112 
113 /**
114  * Find in between all timers which is the next one
115  * and update  next_timer_* accordingly
116  */
hwm_find_next_timer(void)117 void hwm_find_next_timer(void)
118 {
119 	next_timer_index = 0;
120 	next_timer_time  = *Timer_list[0];
121 
122 	for (unsigned int i = 1; i < NUMBER_OF_TIMERS ; i++) {
123 		if (next_timer_time > *Timer_list[i]) {
124 			next_timer_index = i;
125 			next_timer_time = *Timer_list[i];
126 		}
127 	}
128 }
129 
130 /**
131  * Execute the next scheduled HW event/timer
132  */
hwm_one_event(void)133 void hwm_one_event(void)
134 {
135 	hwm_sleep_until_next_timer();
136 
137 	switch (next_timer_index) { /* LCOV_EXCL_BR_LINE */
138 	case HWTIMER:
139 		hwtimer_timer_reached();
140 		break;
141 	case IRQCNT:
142 		hw_irq_ctrl_timer_triggered();
143 		break;
144 	case HW_COUNTER:
145 		hw_counter_triggered();
146 		break;
147 	default:
148 		/* LCOV_EXCL_START */
149 		posix_print_error_and_exit(
150 					   "next_timer_index corrupted\n");
151 		break;
152 		/* LCOV_EXCL_STOP */
153 	}
154 
155 	hwm_find_next_timer();
156 }
157 
158 /**
159  * Set the simulated time when the process will stop
160  */
hwm_set_end_of_time(uint64_t new_end_of_time)161 void hwm_set_end_of_time(uint64_t new_end_of_time)
162 {
163 	end_of_time = new_end_of_time;
164 }
165 
166 /**
167  * Return the current time as known by the device
168  */
hwm_get_time(void)169 uint64_t hwm_get_time(void)
170 {
171 	return simu_time;
172 }
173 
posix_get_hw_cycle(void)174 uint64_t posix_get_hw_cycle(void)
175 {
176 	return hwm_get_time();
177 }
178 
179 /**
180  * Function to initialize the HW models
181  */
hwm_init(void)182 void hwm_init(void)
183 {
184 	hwm_set_sig_handler();
185 	hwtimer_init();
186 	hw_counter_init();
187 	hw_irq_ctrl_init();
188 
189 	hwm_find_next_timer();
190 }
191 
192 /**
193  * Function to free any resources allocated by the HW models
194  * Note that this function needs to be designed so it is possible
195  * to call it more than once during cleanup
196  */
hwm_cleanup(void)197 void hwm_cleanup(void)
198 {
199 	hwtimer_cleanup();
200 	hw_irq_ctrl_cleanup();
201 }
202