blob: 2e6ff74e336762b56ba3ee90cbac2b689282dd46 [file] [log] [blame]
/*
* Copyright (c) 2017 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* This file provides both a model of a simple HW timer and its driver
*
* If you want this timer model to slow down the execution to real time
* set (CONFIG_)NATIVE_POSIX_SLOWDOWN_TO_REAL_TIME
*/
#include <stdint.h>
#include "hw_models_top.h"
#include "irq_ctrl.h"
#include "board_soc.h"
#include "zephyr/types.h"
#include "posix_soc_if.h"
#include "misc/util.h"
u64_t hw_timer_timer;
u64_t hw_timer_tick_timer;
u64_t hw_timer_awake_timer;
static u64_t tick_p; /* Period of the ticker */
static s64_t silent_ticks;
#if (CONFIG_NATIVE_POSIX_SLOWDOWN_TO_REAL_TIME)
#include <time.h>
static u64_t Boot_time;
static struct timespec tv;
#endif
extern u64_t posix_get_hw_cycle(void);
static void hwtimer_update_timer(void)
{
hw_timer_timer = min(hw_timer_tick_timer, hw_timer_awake_timer);
}
void hwtimer_init(void)
{
silent_ticks = 0;
hw_timer_tick_timer = NEVER;
hw_timer_awake_timer = NEVER;
hwtimer_update_timer();
#if (CONFIG_NATIVE_POSIX_SLOWDOWN_TO_REAL_TIME)
clock_gettime(CLOCK_MONOTONIC, &tv);
Boot_time = tv.tv_sec*1e6 + tv.tv_nsec/1000;
#endif
}
void hwtimer_cleanup(void)
{
}
/**
* Enable the HW timer tick interrupts with a period <period> in micoseconds
*/
void hwtimer_enable(u64_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 (CONFIG_NATIVE_POSIX_SLOWDOWN_TO_REAL_TIME)
u64_t expected_realtime = Boot_time + hw_timer_tick_timer;
clock_gettime(CLOCK_MONOTONIC, &tv);
u64_t actual_real_time = tv.tv_sec*1e6 + tv.tv_nsec/1000;
int64_t diff = expected_realtime - actual_real_time;
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;
nanosleep(&requested_time, &remaining);
}
#endif
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)
{
u64_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(u64_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(s64_t sys_ticks)
{
silent_ticks = sys_ticks;
}
s64_t hwtimer_get_pending_silent_ticks(void)
{
return silent_ticks;
}