blob: 5632ff3c96180f7ac5c1f7aa20d6c6fc00a5fb78 [file] [log] [blame]
/*
* Copyright (c) 2017 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* Reduced set of HW models sufficient to run some of the sample apps
* and regression tests
*/
#include <stdint.h>
#include <signal.h>
#include <stddef.h>
#include <stdlib.h>
#include <pthread.h>
#include "hw_models_top.h"
#include "timer_model.h"
#include "irq_ctrl.h"
#include "posix_board_if.h"
#include "hw_counter.h"
#include <zephyr/arch/posix/posix_soc_if.h>
#include "posix_arch_internal.h"
#include "sdl_events.h"
#include <zephyr/sys/util.h>
static uint64_t simu_time; /* The actual time as known by the HW models */
static uint64_t end_of_time = NEVER; /* When will this device stop */
/* List of HW model timers: */
extern uint64_t hw_timer_timer; /* When should this timer_model be called */
extern uint64_t irq_ctrl_timer;
extern uint64_t hw_counter_timer;
#ifdef CONFIG_HAS_SDL
extern uint64_t sdl_event_timer;
#endif
static enum {
HWTIMER = 0,
IRQCNT,
HW_COUNTER,
#ifdef CONFIG_HAS_SDL
SDLEVENTTIMER,
#endif
NUMBER_OF_TIMERS,
NONE
} next_timer_index = NONE;
static uint64_t *Timer_list[NUMBER_OF_TIMERS] = {
&hw_timer_timer,
&irq_ctrl_timer,
&hw_counter_timer,
#ifdef CONFIG_HAS_SDL
&sdl_event_timer,
#endif
};
static uint64_t next_timer_time;
/* Have we received a SIGTERM or SIGINT */
static volatile sig_atomic_t signaled_end;
/**
* Handler for SIGTERM and SIGINT
*/
void hwm_signal_end_handler(int sig)
{
signaled_end = 1;
}
/**
* Set the handler for SIGTERM and SIGINT which will cause the
* program to exit gracefully when they are received the 1st time
*
* Note that our handler only sets a variable indicating the signal was
* received, and in each iteration of the hw main loop this variable is
* evaluated.
* If for some reason (the program is stuck) we never evaluate it, the program
* would never exit.
* Therefore we set SA_RESETHAND: This way, the 2nd time the signal is received
* the default handler would be called to terminate the program no matter what.
*
* Note that SA_RESETHAND requires either _POSIX_C_SOURCE>=200809 or
* _XOPEN_SOURCE>=500
*/
void hwm_set_sig_handler(void)
{
struct sigaction act;
act.sa_handler = hwm_signal_end_handler;
PC_SAFE_CALL(sigemptyset(&act.sa_mask));
act.sa_flags = SA_RESETHAND;
PC_SAFE_CALL(sigaction(SIGTERM, &act, NULL));
PC_SAFE_CALL(sigaction(SIGINT, &act, NULL));
}
static void hwm_sleep_until_next_timer(void)
{
if (next_timer_time >= simu_time) { /* LCOV_EXCL_BR_LINE */
simu_time = next_timer_time;
} else {
/* LCOV_EXCL_START */
posix_print_warning("next_timer_time corrupted (%"PRIu64"<= %"
PRIu64", timer idx=%i)\n",
(uint64_t)next_timer_time,
(uint64_t)simu_time,
next_timer_index);
/* LCOV_EXCL_STOP */
}
if (signaled_end || (simu_time > end_of_time)) {
posix_print_trace("\nStopped at %.3Lfs\n",
((long double)simu_time)/1.0e6L);
posix_exit(0);
}
}
/**
* Find in between all timers which is the next one
* and update next_timer_* accordingly
*/
void hwm_find_next_timer(void)
{
next_timer_index = 0;
next_timer_time = *Timer_list[0];
for (unsigned int i = 1; i < NUMBER_OF_TIMERS ; i++) {
if (next_timer_time > *Timer_list[i]) {
next_timer_index = i;
next_timer_time = *Timer_list[i];
}
}
}
/**
* Execute the next scheduled HW event/timer
*/
void hwm_one_event(void)
{
hwm_sleep_until_next_timer();
switch (next_timer_index) { /* LCOV_EXCL_BR_LINE */
case HWTIMER:
hwtimer_timer_reached();
break;
case IRQCNT:
hw_irq_ctrl_timer_triggered();
break;
case HW_COUNTER:
hw_counter_triggered();
break;
#ifdef CONFIG_HAS_SDL
case SDLEVENTTIMER:
sdl_handle_events();
break;
#endif
default:
/* LCOV_EXCL_START */
posix_print_error_and_exit(
"next_timer_index corrupted\n");
break;
/* LCOV_EXCL_STOP */
}
hwm_find_next_timer();
}
/**
* Set the simulated time when the process will stop
*/
void hwm_set_end_of_time(uint64_t new_end_of_time)
{
end_of_time = new_end_of_time;
}
/**
* Return the current time as known by the device
*/
uint64_t hwm_get_time(void)
{
return simu_time;
}
uint64_t posix_get_hw_cycle(void)
{
return hwm_get_time();
}
/**
* Function to initialize the HW models
*/
void hwm_init(void)
{
hwm_set_sig_handler();
hwtimer_init();
hw_counter_init();
hw_irq_ctrl_init();
hwm_find_next_timer();
}
/**
* Function to free any resources allocated by the HW models
* Note that this function needs to be designed so it is possible
* to call it more than once during cleanup
*/
void hwm_cleanup(void)
{
hwtimer_cleanup();
hw_irq_ctrl_cleanup();
}