blob: b7b6c507d4f93b9b14241eb2c89495f94ec1a809 [file] [log] [blame]
/*
* Copyright (c) 2017 Oticon A/S
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stddef.h>
#include <stdbool.h>
#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 <start_of>"
},
{
.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));
}
}