/* * Copyright (c) 2017 Oticon A/S * Copyright (c) 2023 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include "bs_cmd_line.h" #include "bs_dynargs.h" #include "bs_utils.h" #include "bs_types.h" #include "bs_tracing.h" #include "nsi_tasks.h" #include "nsi_hws_models_if.h" #include "NRF_HWLowL.h" #include "xo_if.h" #include "bsim_args_runner.h" /* By default every second we will inform the Phy simulator about our timing */ #define BSIM_DEFAULT_PHY_MAX_RESYNC_OFFSET 1000000 static struct { double start_offset; bs_time_t max_resync_offset; bool delay_init; bool sync_preinit; bool sync_preboot; } sync_args; static bs_time_t phy_sync_ctrl_event_timer = TIME_NEVER; static bs_time_t last_resync_time; static void psc_program_next_event(void) { if (sync_args.max_resync_offset != TIME_NEVER) { phy_sync_ctrl_event_timer = last_resync_time + sync_args.max_resync_offset; bs_time_t now = nsi_hws_get_time(); if (phy_sync_ctrl_event_timer < now) { phy_sync_ctrl_event_timer = now; } } else { phy_sync_ctrl_event_timer = TIME_NEVER; } nsi_hws_find_next_event(); } static void phy_sync_ctrl_event_reached(void) { last_resync_time = nsi_hws_get_time(); hwll_sync_time_with_phy(last_resync_time); psc_program_next_event(); } NSI_HW_EVENT(phy_sync_ctrl_event_timer, phy_sync_ctrl_event_reached, 900); static void phy_sync_ctrl_init(void) { last_resync_time = nsi_hws_get_time(); psc_program_next_event(); } NSI_TASK(phy_sync_ctrl_init, HW_INIT, 100); /** * Keep track of the last time we synchronized the time with the Phy */ void phy_sync_ctrl_set_last_phy_sync_time(bs_time_t time) { last_resync_time = time; psc_program_next_event(); } /** * Configure the maximum resynchronization offset * (How long, in simulation time, can we spend without talking with the Phy) * Note that this value may be configured as a command line argument too */ void phy_sync_ctrl_set_max_resync_offset(bs_time_t max_resync_offset) { sync_args.max_resync_offset = max_resync_offset; psc_program_next_event(); } /* For backwards compatibility with the old board code */ void tm_set_phy_max_resync_offset(bs_time_t offset_in_us) { phy_sync_ctrl_set_max_resync_offset(offset_in_us); } static double tmp_start_of; static void cmd_start_of_found(char *argv, int offset) { if (tmp_start_of < 0) { bs_trace_error_line("start offset (%lf) cannot be smaller than 0\n", tmp_start_of); } sync_args.start_offset = tmp_start_of; xo_model_set_toffset(sync_args.start_offset); } static void cmd_no_delay_init_found(char *argv, int offset) { sync_args.delay_init = false; } static void cmd_no_sync_preinit_found(char *argv, int offset) { sync_args.sync_preinit = false; } static void cmd_no_sync_preboot_found(char *argv, int offset) { sync_args.sync_preboot = false; } static double tmp_max_resync_offset; static void cmd_max_resync_offset_found(char *argv, int offset) { if (tmp_max_resync_offset < 500) { bs_trace_warning("You are attempting to set a very low phy resynchronization of %d." "Note this will have a performance impact\n", tmp_max_resync_offset); } sync_args.max_resync_offset = tmp_max_resync_offset; } static void phy_sync_ctrl_register_args(void) { static bs_args_struct_t args_struct_toadd[] = { { .option = "start_offset", .name = "start_of", .type = 'f', .dest = (void *)&tmp_start_of, .call_when_found = cmd_start_of_found, .descript = "Offset in time (at the start of the simulation) of this device. " "At time 0 of the device, the phy will be at " }, { .is_switch = true, .option = "sync_preinit", .type = 'b', .dest = (void *)&sync_args.sync_preinit, .descript = "Postpone HW initialization and CPU boot until the Phy has " "reached time 0 (or start_offset) (by default not set)" }, { .is_switch = true, .option = "no_sync_preinit", .type = 'b', .call_when_found = cmd_no_sync_preinit_found, .descript = "Clear sync_preinit. Note that by default sync_preinit is not set" }, { .is_switch = true, .option = "sync_preboot", .type = 'b', .dest = (void *)&sync_args.sync_preboot, .descript = "Postpone CPU boot (but not HW initialization) until the " "Phy has reached time 0 (or start_offset) (by default not set)" "If sync_preinit is set, this option has no effect." }, { .is_switch = true, .option = "no_sync_preboot", .type = 'b', .call_when_found = cmd_no_sync_preboot_found, .descript = "Clear sync_preboot. Note that by default sync_preboot is not set" }, { .is_switch = true, .option = "delay_init", .type = 'b', .dest = (void *)&sync_args.delay_init, .descript = "If start_offset is used, postpone HW initialization and CPU boot " "until start_offset is reached (by default not set)" }, { .is_switch = true, .option = "no_delay_init", .type = 'b', .call_when_found = cmd_no_delay_init_found, .descript = "Clear delay_init. Note that by default delay_init is not set" }, { .option = "mro", .name = "max_resync_offset", .type = 'd', .dest = (void *)&tmp_max_resync_offset, .call_when_found = cmd_max_resync_offset_found, .descript = "Set the max Phy synchronization offset, that is, how far the device " "time can be from the Phy time before it resynchronizes with the Phy " "again (by default 1e6, 1s). Note that this value may be changed " "programmatically by tests" }, ARG_TABLE_ENDMARKER }; bs_add_extra_dynargs(args_struct_toadd); sync_args.max_resync_offset = BSIM_DEFAULT_PHY_MAX_RESYNC_OFFSET; } NSI_TASK(phy_sync_ctrl_register_args, PRE_BOOT_1, 10); void phy_sync_ctrl_connect_to_2G4_phy(void) { static bool ever_run; if (ever_run) { return; } ever_run = true; bs_trace_raw(9, "%s: Connecting to phy...\n", __func__); hwll_connect_to_phy(bsim_args_get_2G4_device_nbr(), bsim_args_get_simid(), bsim_args_get_2G4_phy_id()); bs_trace_raw(9, "%s: Connected\n", __func__); } void phy_sync_ctrl_pre_boot2(void) { static bool ever_run; if (ever_run) { return; } ever_run = true; if (((sync_args.start_offset > 0) && (sync_args.delay_init)) || sync_args.sync_preinit) { /* Delay the next steps until the simulation time has * reached either time 0 or start_offset. */ hwll_wait_for_phy_simu_time(BS_MAX(sync_args.start_offset, 0)); sync_args.sync_preboot = false; /* Already sync'ed */ } } void phy_sync_ctrl_pre_boot3(void) { static bool ever_run; if (ever_run) { return; } ever_run = true; /* * If sync_preboot was set, we sync with the phy * right before booting the CPU */ if (sync_args.sync_preboot) { hwll_wait_for_phy_simu_time(BS_MAX(sync_args.start_offset, 0)); } }