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