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 #include <stddef.h>
9 #include <stdbool.h>
10 #include "bs_cmd_line.h"
11 #include "bs_dynargs.h"
12 #include "bs_utils.h"
13 #include "bs_types.h"
14 #include "bs_tracing.h"
15 #include "nsi_tasks.h"
16 #include "nsi_hws_models_if.h"
17 #include "NRF_HWLowL.h"
18 #include "xo_if.h"
19 #include "bsim_args_runner.h"
20 
21 /* By default every second we will inform the Phy simulator about our timing */
22 #define BSIM_DEFAULT_PHY_MAX_RESYNC_OFFSET 1000000
23 
24 static struct {
25 	double start_offset;
26 	bs_time_t max_resync_offset;
27 	bool delay_init;
28 	bool sync_preinit;
29 	bool sync_preboot;
30 } sync_args;
31 
32 static bs_time_t phy_sync_ctrl_event_timer = TIME_NEVER;
33 static bs_time_t last_resync_time;
34 
psc_program_next_event(void)35 static void psc_program_next_event(void)
36 {
37 	if (sync_args.max_resync_offset != TIME_NEVER) {
38 		phy_sync_ctrl_event_timer = last_resync_time + sync_args.max_resync_offset;
39 
40 		bs_time_t now = nsi_hws_get_time();
41 
42 		if (phy_sync_ctrl_event_timer < now) {
43 			phy_sync_ctrl_event_timer = now;
44 		}
45 	} else {
46 		phy_sync_ctrl_event_timer = TIME_NEVER;
47 	}
48 	nsi_hws_find_next_event();
49 }
50 
phy_sync_ctrl_event_reached(void)51 static void phy_sync_ctrl_event_reached(void)
52 {
53 
54 	last_resync_time = nsi_hws_get_time();
55 
56 	hwll_sync_time_with_phy(last_resync_time);
57 	psc_program_next_event();
58 }
59 
60 NSI_HW_EVENT(phy_sync_ctrl_event_timer, phy_sync_ctrl_event_reached, 900);
61 
phy_sync_ctrl_init(void)62 static void phy_sync_ctrl_init(void)
63 {
64 	last_resync_time = nsi_hws_get_time();
65 
66 	psc_program_next_event();
67 }
68 
69 NSI_TASK(phy_sync_ctrl_init, HW_INIT, 100);
70 
71 /**
72  * Keep track of the last time we synchronized the time with the Phy
73  */
phy_sync_ctrl_set_last_phy_sync_time(bs_time_t time)74 void phy_sync_ctrl_set_last_phy_sync_time(bs_time_t time)
75 {
76 	last_resync_time = time;
77 	psc_program_next_event();
78 }
79 
80 /**
81  * Configure the maximum resynchronization offset
82  * (How long, in simulation time, can we spend without talking with the Phy)
83  * Note that this value may be configured as a command line argument too
84  */
phy_sync_ctrl_set_max_resync_offset(bs_time_t max_resync_offset)85 void phy_sync_ctrl_set_max_resync_offset(bs_time_t max_resync_offset)
86 {
87 	sync_args.max_resync_offset = max_resync_offset;
88 
89 	psc_program_next_event();
90 }
91 
92 /* For backwards compatibility with the old board code */
tm_set_phy_max_resync_offset(bs_time_t offset_in_us)93 void tm_set_phy_max_resync_offset(bs_time_t offset_in_us)
94 {
95 	phy_sync_ctrl_set_max_resync_offset(offset_in_us);
96 }
97 
98 static double tmp_start_of;
99 
cmd_start_of_found(char * argv,int offset)100 static void cmd_start_of_found(char *argv, int offset)
101 {
102 	if (tmp_start_of < 0) {
103 		bs_trace_error_line("start offset (%lf) cannot be smaller than 0\n", tmp_start_of);
104 	}
105 	sync_args.start_offset = tmp_start_of;
106 	xo_model_set_toffset(sync_args.start_offset);
107 }
108 
cmd_no_delay_init_found(char * argv,int offset)109 static void cmd_no_delay_init_found(char *argv, int offset)
110 {
111 	sync_args.delay_init = false;
112 }
113 
cmd_no_sync_preinit_found(char * argv,int offset)114 static void cmd_no_sync_preinit_found(char *argv, int offset)
115 {
116 	sync_args.sync_preinit = false;
117 }
118 
cmd_no_sync_preboot_found(char * argv,int offset)119 static void cmd_no_sync_preboot_found(char *argv, int offset)
120 {
121 	sync_args.sync_preboot = false;
122 }
123 
124 static double tmp_max_resync_offset;
125 
cmd_max_resync_offset_found(char * argv,int offset)126 static void cmd_max_resync_offset_found(char *argv, int offset)
127 {
128 	if (tmp_max_resync_offset < 500) {
129 		bs_trace_warning("You are attempting to set a very low phy resynchronization of %d."
130 				"Note this will have a performance impact\n",
131 				tmp_max_resync_offset);
132 	}
133 	sync_args.max_resync_offset = tmp_max_resync_offset;
134 }
135 
phy_sync_ctrl_register_args(void)136 static void phy_sync_ctrl_register_args(void)
137 {
138 	static bs_args_struct_t args_struct_toadd[] = {
139 		{
140 		.option = "start_offset",
141 		.name = "start_of",
142 		.type = 'f',
143 		.dest = (void *)&tmp_start_of,
144 		.call_when_found = cmd_start_of_found,
145 		.descript = "Offset in time (at the start of the simulation) of this device. "
146 			    "At time 0 of the device, the phy will be at <start_of>"
147 		},
148 		{
149 		.is_switch = true,
150 		.option = "sync_preinit",
151 		.type = 'b',
152 		.dest = (void *)&sync_args.sync_preinit,
153 		.descript = "Postpone HW initialization and CPU boot until the Phy has "
154 			    "reached time 0 (or start_offset) (by default not set)"
155 		},
156 		{
157 		.is_switch = true,
158 		.option = "no_sync_preinit",
159 		.type = 'b',
160 		.call_when_found = cmd_no_sync_preinit_found,
161 		.descript = "Clear sync_preinit. Note that by default sync_preinit is not set"
162 		},
163 		{
164 		.is_switch = true,
165 		.option = "sync_preboot",
166 		.type = 'b',
167 		.dest = (void *)&sync_args.sync_preboot,
168 		.descript = "Postpone CPU boot (but not HW initialization) until the "
169 			    "Phy has reached time 0 (or start_offset) (by default not set)"
170 			    "If sync_preinit is set, this option has no effect."
171 		},
172 		{
173 		.is_switch = true,
174 		.option = "no_sync_preboot",
175 		.type = 'b',
176 		.call_when_found = cmd_no_sync_preboot_found,
177 		.descript = "Clear sync_preboot. Note that by default sync_preboot is not set"
178 		},
179 		{
180 		.is_switch = true,
181 		.option = "delay_init",
182 		.type = 'b',
183 		.dest = (void *)&sync_args.delay_init,
184 		.descript = "If start_offset is used, postpone HW initialization and CPU boot "
185 			    "until start_offset is reached (by default not set)"
186 		},
187 		{
188 		.is_switch = true,
189 		.option = "no_delay_init",
190 		.type = 'b',
191 		.call_when_found = cmd_no_delay_init_found,
192 		.descript = "Clear delay_init. Note that by default delay_init is not set"
193 		},
194 		{
195 		.option = "mro",
196 		.name = "max_resync_offset",
197 		.type = 'd',
198 		.dest = (void *)&tmp_max_resync_offset,
199 		.call_when_found = cmd_max_resync_offset_found,
200 		.descript = "Set the max Phy synchronization offset, that is, how far the device "
201 			    "time can be from the Phy time before it resynchronizes with the Phy "
202 			    "again (by default 1e6, 1s). Note that this value may be changed "
203 			    "programmatically by tests"
204 		},
205 		ARG_TABLE_ENDMARKER
206 	};
207 
208 	bs_add_extra_dynargs(args_struct_toadd);
209 
210 	sync_args.max_resync_offset = BSIM_DEFAULT_PHY_MAX_RESYNC_OFFSET;
211 }
212 
213 NSI_TASK(phy_sync_ctrl_register_args, PRE_BOOT_1, 10);
214 
phy_sync_ctrl_connect_to_2G4_phy(void)215 void phy_sync_ctrl_connect_to_2G4_phy(void)
216 {
217 	static bool ever_run;
218 
219 	if (ever_run) {
220 		return;
221 	}
222 	ever_run = true;
223 
224 	bs_trace_raw(9, "%s: Connecting to phy...\n", __func__);
225 	hwll_connect_to_phy(bsim_args_get_2G4_device_nbr(),
226 			    bsim_args_get_simid(),
227 			    bsim_args_get_2G4_phy_id());
228 	bs_trace_raw(9, "%s: Connected\n", __func__);
229 }
230 
phy_sync_ctrl_pre_boot2(void)231 void phy_sync_ctrl_pre_boot2(void)
232 {
233 	static bool ever_run;
234 
235 	if (ever_run) {
236 		return;
237 	}
238 	ever_run = true;
239 
240 	if (((sync_args.start_offset > 0) && (sync_args.delay_init))
241 		|| sync_args.sync_preinit) {
242 		/* Delay the next steps until the simulation time has
243 		 * reached either time 0 or start_offset.
244 		 */
245 		hwll_wait_for_phy_simu_time(BS_MAX(sync_args.start_offset, 0));
246 		sync_args.sync_preboot = false; /* Already sync'ed */
247 	}
248 }
249 
phy_sync_ctrl_pre_boot3(void)250 void phy_sync_ctrl_pre_boot3(void)
251 {
252 	static bool ever_run;
253 
254 	if (ever_run) {
255 		return;
256 	}
257 	ever_run = true;
258 
259 	/*
260 	 * If sync_preboot was set, we sync with the phy
261 	 * right before booting the CPU
262 	 */
263 	if (sync_args.sync_preboot) {
264 		hwll_wait_for_phy_simu_time(BS_MAX(sync_args.start_offset, 0));
265 	}
266 }
267