mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-09-02 22:32:24 +00:00
Explicitly ignore the nanosleep() return value. nanosleep() is called to slow down native_posix in real time mode. There is no real reason to handle this call returning too early: The call may return earlier if a signal was sent to the process (e.g. the user pressed Ctrl+C), or in case of errors. In case of SIGKILL, SIGTERM, what we do is what we should do (not wait, but terminate the program as requested by the user ASAP) In case of another signal, we will wait the appropriate amount of time in the next systick (so we won't accumulate any error over time) So there is no real need to add extra code here to handle those cases. Solves coverity CID 211046 Signed-off-by: Alberto Escolar Piedras <alpi@oticon.com>
546 lines
13 KiB
C
546 lines
13 KiB
C
/*
|
|
* Copyright (c) 2017 Oticon A/S
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* This provides a model of:
|
|
* - A system tick
|
|
* - A real time clock
|
|
* - A one shot HW timer which can be used to awake the CPU at a given time
|
|
* - The clock source for all of this, and therefore for native_posix
|
|
*
|
|
* Please see doc/board.rst for more information, specially sections:
|
|
* About time in native_posix
|
|
* Peripherals:
|
|
* Clock source, system tick and timer
|
|
* Real time clock
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <time.h>
|
|
#include <stdbool.h>
|
|
#include <math.h>
|
|
#include "hw_models_top.h"
|
|
#include "irq_ctrl.h"
|
|
#include "board_soc.h"
|
|
#include "zephyr/types.h"
|
|
#include <arch/posix/posix_trace.h>
|
|
#include <sys/util.h>
|
|
#include "cmdline.h"
|
|
#include "soc.h"
|
|
|
|
#define DEBUG_NP_TIMER 0
|
|
|
|
#if DEBUG_NP_TIMER
|
|
|
|
/**
|
|
* Helper function to convert a 64 bit time in microseconds into a string.
|
|
* The format will always be: hh:mm:ss.ssssss\0
|
|
*
|
|
* Note: the caller has to allocate the destination buffer (at least 17 chars)
|
|
*/
|
|
#include <stdio.h>
|
|
static char *us_time_to_str(char *dest, uint64_t time)
|
|
{
|
|
if (time != NEVER) {
|
|
unsigned int hour;
|
|
unsigned int minute;
|
|
unsigned int second;
|
|
unsigned int us;
|
|
|
|
hour = (time / 3600U / 1000000U) % 24;
|
|
minute = (time / 60U / 1000000U) % 60;
|
|
second = (time / 1000000U) % 60;
|
|
us = time % 1000000;
|
|
|
|
sprintf(dest, "%02u:%02u:%02u.%06u", hour, minute, second, us);
|
|
} else {
|
|
sprintf(dest, " NEVER/UNKNOWN ");
|
|
|
|
}
|
|
return dest;
|
|
}
|
|
#endif
|
|
|
|
uint64_t hw_timer_timer;
|
|
|
|
uint64_t hw_timer_tick_timer;
|
|
uint64_t hw_timer_awake_timer;
|
|
|
|
static uint64_t tick_p; /* Period of the ticker */
|
|
static int64_t silent_ticks;
|
|
|
|
static bool real_time_mode =
|
|
#if defined(CONFIG_NATIVE_POSIX_SLOWDOWN_TO_REAL_TIME)
|
|
true;
|
|
#else
|
|
false;
|
|
#endif
|
|
|
|
static bool reset_rtc; /*"Reset" the RTC on boot*/
|
|
|
|
/*
|
|
* When this executable started running, this value shall not be changed after
|
|
* boot
|
|
*/
|
|
static uint64_t boot_time;
|
|
|
|
/*
|
|
* Ratio of the simulated clock to the real host time
|
|
* For ex. a clock_ratio = 1+100e-6 means the simulated time is 100ppm faster
|
|
* than real time
|
|
*/
|
|
static double clock_ratio = 1.0;
|
|
|
|
#if DEBUG_NP_TIMER
|
|
/*
|
|
* Offset of the simulated time vs the real host time due to drift/clock ratio
|
|
* until "last_radj_*time"
|
|
*
|
|
* A positive value means simulated time is ahead of the host time
|
|
*
|
|
* This variable is only kept for debugging purposes
|
|
*/
|
|
static int64_t last_drift_offset;
|
|
#endif
|
|
|
|
/*
|
|
* Offsets of the RTC relative to the hardware models simu_time
|
|
* "simu_time" == simulated time which starts at 0 on boot
|
|
*/
|
|
static int64_t rtc_offset;
|
|
|
|
/* Last host/real time when the ratio was adjusted */
|
|
static uint64_t last_radj_rtime;
|
|
/* Last simulated time when the ratio was adjusted */
|
|
static uint64_t last_radj_stime;
|
|
|
|
extern uint64_t posix_get_hw_cycle(void);
|
|
|
|
void hwtimer_set_real_time_mode(bool new_rt)
|
|
{
|
|
real_time_mode = new_rt;
|
|
}
|
|
|
|
static void hwtimer_update_timer(void)
|
|
{
|
|
hw_timer_timer = MIN(hw_timer_tick_timer, hw_timer_awake_timer);
|
|
}
|
|
|
|
static inline void host_clock_gettime(struct timespec *tv)
|
|
{
|
|
#if defined(CLOCK_MONOTONIC_RAW)
|
|
clock_gettime(CLOCK_MONOTONIC_RAW, tv);
|
|
#else
|
|
clock_gettime(CLOCK_MONOTONIC, tv);
|
|
#endif
|
|
}
|
|
|
|
uint64_t get_host_us_time(void)
|
|
{
|
|
struct timespec tv;
|
|
|
|
host_clock_gettime(&tv);
|
|
return (uint64_t)tv.tv_sec * 1e6 + tv.tv_nsec / 1000;
|
|
}
|
|
|
|
void hwtimer_init(void)
|
|
{
|
|
silent_ticks = 0;
|
|
hw_timer_tick_timer = NEVER;
|
|
hw_timer_awake_timer = NEVER;
|
|
hwtimer_update_timer();
|
|
if (real_time_mode) {
|
|
boot_time = get_host_us_time();
|
|
last_radj_rtime = boot_time;
|
|
last_radj_stime = 0U;
|
|
}
|
|
if (!reset_rtc) {
|
|
struct timespec tv;
|
|
uint64_t realhosttime;
|
|
|
|
clock_gettime(CLOCK_REALTIME, &tv);
|
|
realhosttime = (uint64_t)tv.tv_sec * 1e6 + tv.tv_nsec / 1000;
|
|
|
|
rtc_offset += realhosttime;
|
|
}
|
|
}
|
|
|
|
void hwtimer_cleanup(void)
|
|
{
|
|
|
|
}
|
|
|
|
/**
|
|
* Enable the HW timer tick interrupts with a period <period> in micoseconds
|
|
*/
|
|
void hwtimer_enable(uint64_t period)
|
|
{
|
|
tick_p = period;
|
|
hw_timer_tick_timer = hwm_get_time() + tick_p;
|
|
hwtimer_update_timer();
|
|
hwm_find_next_timer();
|
|
}
|
|
|
|
static void hwtimer_tick_timer_reached(void)
|
|
{
|
|
if (real_time_mode) {
|
|
uint64_t expected_rt = (hw_timer_tick_timer - last_radj_stime)
|
|
/ clock_ratio
|
|
+ last_radj_rtime;
|
|
uint64_t real_time = get_host_us_time();
|
|
|
|
int64_t diff = expected_rt - real_time;
|
|
|
|
#if DEBUG_NP_TIMER
|
|
char es[30];
|
|
char rs[30];
|
|
|
|
us_time_to_str(es, expected_rt - boot_time);
|
|
us_time_to_str(rs, real_time - boot_time);
|
|
printf("tick @%5llims: diff = expected_rt - real_time = "
|
|
"%5lli = %s - %s\n",
|
|
hw_timer_tick_timer/1000U, diff, es, rs);
|
|
#endif
|
|
|
|
if (diff > 0) { /* we need to slow down */
|
|
struct timespec requested_time;
|
|
struct timespec remaining;
|
|
|
|
requested_time.tv_sec = diff / 1e6;
|
|
requested_time.tv_nsec = (diff -
|
|
requested_time.tv_sec*1e6)*1e3;
|
|
|
|
(void) nanosleep(&requested_time, &remaining);
|
|
}
|
|
}
|
|
|
|
hw_timer_tick_timer += tick_p;
|
|
hwtimer_update_timer();
|
|
|
|
if (silent_ticks > 0) {
|
|
silent_ticks -= 1;
|
|
} else {
|
|
hw_irq_ctrl_set_irq(TIMER_TICK_IRQ);
|
|
}
|
|
}
|
|
|
|
static void hwtimer_awake_timer_reached(void)
|
|
{
|
|
hw_timer_awake_timer = NEVER;
|
|
hwtimer_update_timer();
|
|
hw_irq_ctrl_set_irq(PHONY_HARD_IRQ);
|
|
}
|
|
|
|
void hwtimer_timer_reached(void)
|
|
{
|
|
uint64_t Now = hw_timer_timer;
|
|
|
|
if (hw_timer_awake_timer == Now) {
|
|
hwtimer_awake_timer_reached();
|
|
}
|
|
|
|
if (hw_timer_tick_timer == Now) {
|
|
hwtimer_tick_timer_reached();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The timer HW will awake the CPU (without an interrupt) at least when <time>
|
|
* comes (it may awake it earlier)
|
|
*
|
|
* If there was a previous request for an earlier time, the old one will prevail
|
|
*
|
|
* This is meant for k_busy_wait() like functionality
|
|
*/
|
|
void hwtimer_wake_in_time(uint64_t time)
|
|
{
|
|
if (hw_timer_awake_timer > time) {
|
|
hw_timer_awake_timer = time;
|
|
hwtimer_update_timer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The kernel wants to skip the next sys_ticks tick interrupts
|
|
* If sys_ticks == 0, the next interrupt will be raised.
|
|
*/
|
|
void hwtimer_set_silent_ticks(int64_t sys_ticks)
|
|
{
|
|
silent_ticks = sys_ticks;
|
|
}
|
|
|
|
int64_t hwtimer_get_pending_silent_ticks(void)
|
|
{
|
|
return silent_ticks;
|
|
}
|
|
|
|
|
|
/**
|
|
* During boot set the real time clock simulated time not start
|
|
* from the real host time
|
|
*/
|
|
void hwtimer_reset_rtc(void)
|
|
{
|
|
reset_rtc = true;
|
|
}
|
|
|
|
/**
|
|
* Set a time offset (microseconds) of the RTC simulated time
|
|
* Note: This should not be used after starting
|
|
*/
|
|
void hwtimer_set_rtc_offset(int64_t offset)
|
|
{
|
|
rtc_offset = offset;
|
|
}
|
|
|
|
/**
|
|
* Set the ratio of the simulated time to host (real) time.
|
|
* Note: This should not be used after starting
|
|
*/
|
|
void hwtimer_set_rt_ratio(double ratio)
|
|
{
|
|
clock_ratio = ratio;
|
|
}
|
|
|
|
/**
|
|
* Increase or decrease the RTC simulated time by offset_delta
|
|
*/
|
|
void hwtimer_adjust_rtc_offset(int64_t offset_delta)
|
|
{
|
|
rtc_offset += offset_delta;
|
|
}
|
|
|
|
/**
|
|
* Adjust the ratio of the simulated time by a factor
|
|
*/
|
|
void hwtimer_adjust_rt_ratio(double ratio_correction)
|
|
{
|
|
uint64_t current_stime = hwm_get_time();
|
|
int64_t s_diff = current_stime - last_radj_stime;
|
|
/* Accumulated real time drift time since last adjustment: */
|
|
|
|
last_radj_rtime += s_diff / clock_ratio;
|
|
last_radj_stime = current_stime;
|
|
|
|
#if DEBUG_NP_TIMER
|
|
char ct[30];
|
|
int64_t r_drift = (long double)(clock_ratio-1.0)/(clock_ratio)*s_diff;
|
|
|
|
last_drift_offset += r_drift;
|
|
us_time_to_str(ct, current_stime);
|
|
|
|
printf("%s(): @%s, s_diff= %llius after last adjust\n"
|
|
" during which we drifted %.3fms\n"
|
|
" total acc drift (last_drift_offset) = %.3fms\n"
|
|
" last_radj_rtime = %.3fms (+%.3fms )\n"
|
|
" Ratio adjusted to %f\n",
|
|
__func__, ct, s_diff,
|
|
r_drift/1000.0,
|
|
last_drift_offset/1000.0,
|
|
last_radj_rtime/1000.0,
|
|
s_diff/clock_ratio/1000.0,
|
|
clock_ratio*ratio_correction);
|
|
#endif
|
|
|
|
clock_ratio *= ratio_correction;
|
|
}
|
|
|
|
/**
|
|
* Return the current simulated RTC time in microseconds
|
|
*/
|
|
int64_t hwtimer_get_simu_rtc_time(void)
|
|
{
|
|
return hwm_get_time() + rtc_offset;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return a version of the host time which would have drifted as if the host
|
|
* real time clock had been running from the native_posix clock, and adjusted
|
|
* both in rate and in offsets as the native_posix has been.
|
|
*
|
|
* Note that this time may be significantly ahead of the simulated time
|
|
* (the time the Zephyr kernel thinks it is).
|
|
* This will be the case in general if native_posix is not able to run at or
|
|
* faster than real time.
|
|
*/
|
|
void hwtimer_get_pseudohost_rtc_time(uint32_t *nsec, uint64_t *sec)
|
|
{
|
|
/*
|
|
* Note: long double has a 64bits mantissa in x86.
|
|
* Therefore to avoid loss of precision after 500 odd years into
|
|
* the epoc, we first calculate the offset from the last adjustment
|
|
* time split in us and ns. So we keep the full precision for 500 odd
|
|
* years after the last clock ratio adjustment (or native_posix boot,
|
|
* whichever is latest).
|
|
* Meaning, we will still start to loose precision after 500 off
|
|
* years of runtime without a clock ratio adjustment, but that really
|
|
* should not be much of a problem, given that the ns lower digits are
|
|
* pretty much noise anyhow.
|
|
* (So, all this is a huge overkill)
|
|
*
|
|
* The operation below in plain is just:
|
|
* st = (rt - last_rt_adj_time)*ratio + last_dt_adj_time
|
|
* where st = simulated time
|
|
* rt = real time
|
|
* last_rt_adj_time = time (real) when the last ratio
|
|
* adjustment took place
|
|
* last_st_adj_time = time (simulated) when the last ratio
|
|
* adjustment took place
|
|
* ratio = ratio between simulated time and real time
|
|
*/
|
|
struct timespec tv;
|
|
|
|
host_clock_gettime(&tv);
|
|
|
|
uint64_t rt_us = (uint64_t)tv.tv_sec * 1000000ULL + tv.tv_nsec / 1000;
|
|
uint32_t rt_ns = tv.tv_nsec % 1000;
|
|
|
|
long double drt_us = (long double)rt_us - last_radj_rtime;
|
|
long double drt_ns = drt_us * 1000.0 + (long double)rt_ns;
|
|
long double st = drt_ns * clock_ratio +
|
|
(long double)(last_radj_stime + rtc_offset) * 1000.0;
|
|
|
|
*nsec = fmodl(st, 1e9);
|
|
*sec = st / 1e9;
|
|
}
|
|
|
|
static struct {
|
|
double stop_at;
|
|
double rtc_offset;
|
|
double rt_drift;
|
|
double rt_ratio;
|
|
} args;
|
|
|
|
static void cmd_stop_at_found(char *argv, int offset)
|
|
{
|
|
ARG_UNUSED(offset);
|
|
if (args.stop_at < 0) {
|
|
posix_print_error_and_exit("Error: stop-at must be positive "
|
|
"(%s)\n", argv);
|
|
}
|
|
hwm_set_end_of_time(args.stop_at*1e6);
|
|
}
|
|
|
|
static void cmd_realtime_found(char *argv, int offset)
|
|
{
|
|
ARG_UNUSED(argv);
|
|
ARG_UNUSED(offset);
|
|
hwtimer_set_real_time_mode(true);
|
|
}
|
|
|
|
static void cmd_no_realtime_found(char *argv, int offset)
|
|
{
|
|
ARG_UNUSED(argv);
|
|
ARG_UNUSED(offset);
|
|
hwtimer_set_real_time_mode(false);
|
|
}
|
|
|
|
static void cmd_rtcoffset_found(char *argv, int offset)
|
|
{
|
|
ARG_UNUSED(argv);
|
|
ARG_UNUSED(offset);
|
|
hwtimer_set_rtc_offset(args.rtc_offset*1e6);
|
|
}
|
|
|
|
static void cmd_rt_drift_found(char *argv, int offset)
|
|
{
|
|
ARG_UNUSED(argv);
|
|
ARG_UNUSED(offset);
|
|
if (!(args.rt_drift > -1)) {
|
|
posix_print_error_and_exit("The drift needs to be > -1. "
|
|
"Please use --help for more info\n");
|
|
}
|
|
args.rt_ratio = args.rt_drift + 1;
|
|
hwtimer_set_rt_ratio(args.rt_ratio);
|
|
}
|
|
|
|
static void cmd_rt_ratio_found(char *argv, int offset)
|
|
{
|
|
ARG_UNUSED(argv);
|
|
ARG_UNUSED(offset);
|
|
if ((args.rt_ratio <= 0)) {
|
|
posix_print_error_and_exit("The ratio needs to be > 0. "
|
|
"Please use --help for more info\n");
|
|
}
|
|
hwtimer_set_rt_ratio(args.rt_ratio);
|
|
}
|
|
|
|
static void cmd_rtcreset_found(char *argv, int offset)
|
|
{
|
|
ARG_UNUSED(argv);
|
|
ARG_UNUSED(offset);
|
|
hwtimer_reset_rtc();
|
|
}
|
|
|
|
static void native_add_time_options(void)
|
|
{
|
|
static struct args_struct_t timer_options[] = {
|
|
/*
|
|
* Fields:
|
|
* manual, mandatory, switch,
|
|
* option_name, var_name ,type,
|
|
* destination, callback,
|
|
* description
|
|
*/
|
|
{false, false, true,
|
|
"rt", "", 'b',
|
|
NULL, cmd_realtime_found,
|
|
"Slow down the execution to the host real time, "
|
|
"or a ratio of it (see --rt-ratio below)"},
|
|
|
|
{false, false, true,
|
|
"no-rt", "", 'b',
|
|
NULL, cmd_no_realtime_found,
|
|
"Do NOT slow down the execution to real time, but advance "
|
|
"Zephyr's time as fast as possible and decoupled from the host "
|
|
"time"},
|
|
|
|
{false, false, false,
|
|
"rt-drift", "dratio", 'd',
|
|
(void *)&args.rt_drift, cmd_rt_drift_found,
|
|
"Drift of the simulated clock relative to the host real time. "
|
|
"Normally this would be set to a value of a few ppm (e.g. 50e-6"
|
|
") "
|
|
"This option has no effect in non real time mode"
|
|
},
|
|
|
|
{false, false, false,
|
|
"rt-ratio", "ratio", 'd',
|
|
(void *)&args.rt_ratio, cmd_rt_ratio_found,
|
|
"Relative speed of the simulated time vs real time. "
|
|
"For ex. set to 2 to have simulated time pass at double the "
|
|
"speed of real time. "
|
|
"Note that both rt-drift & rt-ratio adjust the same clock "
|
|
"speed, and therefore it does not make sense to use them "
|
|
"simultaneously. "
|
|
"This option has no effect in non real time mode"
|
|
},
|
|
|
|
{false, false, false,
|
|
"rtc-offset", "time_offset", 'd',
|
|
(void *)&args.rtc_offset, cmd_rtcoffset_found,
|
|
"At boot offset the RTC clock by this amount of seconds"
|
|
},
|
|
|
|
{false, false, true,
|
|
"rtc-reset", "", 'b',
|
|
NULL, cmd_rtcreset_found,
|
|
"Start the simulated real time clock at 0. Otherwise it starts "
|
|
"matching the value provided by the host real time clock"},
|
|
|
|
{false, false, false,
|
|
"stop_at", "time", 'd',
|
|
(void *)&args.stop_at, cmd_stop_at_found,
|
|
"In simulated seconds, when to stop automatically"},
|
|
|
|
ARG_TABLE_ENDMARKER};
|
|
|
|
native_add_command_line_opts(timer_options);
|
|
}
|
|
|
|
NATIVE_TASK(native_add_time_options, PRE_BOOT_1, 1);
|