posix: Add cpu_hold() function to better emulate code delay
In native_posix and nrf52_bsim add the cpu_hold() function, which can be used to emulate the time it takes for code to execute. It is very similar to arch_busy_wait(), but while arch_busy_wait() returns when the requested time has passed, cpu_hold() ensures that the time passes in the callers context independently of how much time may pass in some other context. Signed-off-by: Alberto Escolar Piedras <alpi@oticon.com>
This commit is contained in:
parent
570a84c3cd
commit
6d3476117b
10 changed files with 154 additions and 65 deletions
|
@ -25,6 +25,14 @@
|
|||
#include <arch/posix/posix_soc_if.h>
|
||||
#include <tracing/tracing.h>
|
||||
|
||||
#if !defined(CONFIG_ARCH_HAS_CUSTOM_BUSY_WAIT)
|
||||
#error "The POSIX architecture needs a custom busy_wait implementation. \
|
||||
CONFIG_ARCH_HAS_CUSTOM_BUSY_WAIT must be selected"
|
||||
/* Each POSIX arch board (or SOC) must provide an implementation of
|
||||
* arch_busy_wait()
|
||||
*/
|
||||
#endif
|
||||
|
||||
void arch_cpu_idle(void)
|
||||
{
|
||||
sys_trace_idle();
|
||||
|
|
|
@ -14,6 +14,7 @@ zephyr_library_sources(
|
|||
tracing.c
|
||||
cmdline_common.c
|
||||
cmdline.c
|
||||
cpu_wait.c
|
||||
hw_counter.c
|
||||
)
|
||||
|
||||
|
|
75
boards/posix/native_posix/cpu_wait.c
Normal file
75
boards/posix/native_posix/cpu_wait.c
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Oticon A/S
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "timer_model.h"
|
||||
#include <arch/posix/posix_soc_if.h>
|
||||
#include <posix_board_if.h>
|
||||
#include <posix_soc.h>
|
||||
|
||||
/**
|
||||
* Replacement to the kernel k_busy_wait()
|
||||
* Will block this thread (and therefore the whole Zephyr) during usec_to_wait
|
||||
*
|
||||
* Note that interrupts may be received in the meanwhile and that therefore this
|
||||
* thread may lose context.
|
||||
* Therefore the wait time may be considerably longer.
|
||||
*
|
||||
* All this function ensures is that it will return after usec_to_wait or later.
|
||||
*
|
||||
* This special arch_busy_wait() is necessary due to how the POSIX arch/SOC INF
|
||||
* models a CPU. Conceptually it could be thought as if the MCU was running
|
||||
* at an infinitely high clock, and therefore no simulated time passes while
|
||||
* executing instructions(*1).
|
||||
* Therefore to be able to busy wait this function does the equivalent of
|
||||
* programming a dedicated timer which will raise a non-maskable interrupt,
|
||||
* and halting the CPU.
|
||||
*
|
||||
* (*1) In reality simulated time is simply not advanced just due to the "MCU"
|
||||
* running. Meaning, the SW running on the MCU is assumed to take 0 time.
|
||||
*/
|
||||
void arch_busy_wait(uint32_t usec_to_wait)
|
||||
{
|
||||
uint64_t time_end = hwm_get_time() + usec_to_wait;
|
||||
|
||||
while (hwm_get_time() < time_end) {
|
||||
/*
|
||||
* There may be wakes due to other interrupts including
|
||||
* other threads calling arch_busy_wait
|
||||
*/
|
||||
hwtimer_wake_in_time(time_end);
|
||||
posix_halt_cpu();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will block this thread (and therefore the whole Zephyr) during usec_to_waste
|
||||
*
|
||||
* Very similar to arch_busy_wait(), but if an interrupt or context switch
|
||||
* occurs this function will continue waiting after, ensuring that
|
||||
* usec_to_waste are spent in this context, irrespectively of how much more
|
||||
* time would be spent on interrupt handling or possible switched-in tasks.
|
||||
*
|
||||
* Can be used to emulate code execution time.
|
||||
*/
|
||||
void posix_cpu_hold(uint32_t usec_to_waste)
|
||||
{
|
||||
uint64_t time_start;
|
||||
int64_t to_wait = usec_to_waste;
|
||||
|
||||
while (to_wait > 0) {
|
||||
/*
|
||||
* There may be wakes due to other interrupts or nested calls to
|
||||
* cpu_hold in interrupt handlers
|
||||
*/
|
||||
time_start = hwm_get_time();
|
||||
hwtimer_wake_in_time(time_start + to_wait);
|
||||
posix_change_cpu_state_and_wait(true);
|
||||
to_wait -= hwm_get_time() - time_start;
|
||||
|
||||
posix_irq_handler();
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@ zephyr_compile_options(
|
|||
|
||||
zephyr_library_sources(
|
||||
irq_handler.c
|
||||
k_busy_wait.c
|
||||
cpu_wait.c
|
||||
bstests_entry.c
|
||||
argparse.c
|
||||
main.c
|
||||
|
|
66
boards/posix/nrf52_bsim/cpu_wait.c
Normal file
66
boards/posix/nrf52_bsim/cpu_wait.c
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (c) 2017 Oticon A/S
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "zephyr/types.h"
|
||||
#include "fake_timer.h"
|
||||
#include "time_machine.h"
|
||||
#include <arch/posix/posix_soc_if.h>
|
||||
#include <posix_board_if.h>
|
||||
#include <posix_soc.h>
|
||||
|
||||
/**
|
||||
* Replacement to the kernel k_busy_wait()
|
||||
* Will block this thread (and therefore the whole Zephyr) during usec_to_wait
|
||||
*
|
||||
* Note that interrupts may be received in the meanwhile and that therefore this
|
||||
* thread may lose context.
|
||||
* Therefore the wait time may be considerably longer.
|
||||
*
|
||||
* All this function ensures is that it will return after usec_to_wait or later.
|
||||
*/
|
||||
void arch_busy_wait(uint32_t usec_to_wait)
|
||||
{
|
||||
bs_time_t time_end = tm_get_hw_time() + usec_to_wait;
|
||||
|
||||
while (tm_get_hw_time() < time_end) {
|
||||
/*
|
||||
* There may be wakes due to other interrupts or nested calls to
|
||||
* k_busy_wait in interrupt handlers
|
||||
*/
|
||||
fake_timer_wake_in_time(time_end);
|
||||
posix_halt_cpu();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will block this thread (and therefore the whole Zephyr) during usec_to_waste
|
||||
*
|
||||
* Very similar to arch_busy_wait(), but if an interrupt or context switch
|
||||
* occurs this function will continue waiting after, ensuring that
|
||||
* usec_to_waste are spent in this context, irrespectively of how much more
|
||||
* time would be spent on interrupt handling or possible switched-in tasks.
|
||||
*
|
||||
* Can be used to emulate code execution time.
|
||||
*/
|
||||
void posix_cpu_hold(uint32_t usec_to_waste)
|
||||
{
|
||||
bs_time_t time_start;
|
||||
int64_t to_wait = usec_to_waste;
|
||||
|
||||
while (to_wait > 0) {
|
||||
/*
|
||||
* There may be wakes due to other interrupts or nested calls to
|
||||
* cpu_hold in interrupt handlers
|
||||
*/
|
||||
time_start = tm_get_hw_time();
|
||||
fake_timer_wake_in_time(time_start + to_wait);
|
||||
posix_change_cpu_state_and_wait(true);
|
||||
to_wait -= tm_get_hw_time() - time_start;
|
||||
|
||||
posix_irq_handler();
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2017 Oticon A/S
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "zephyr/types.h"
|
||||
#include "fake_timer.h"
|
||||
#include "time_machine.h"
|
||||
#include <arch/posix/posix_soc_if.h>
|
||||
|
||||
#if defined(CONFIG_ARCH_HAS_CUSTOM_BUSY_WAIT)
|
||||
/**
|
||||
* Replacement to the kernel k_busy_wait()
|
||||
* Will block this thread (and therefore the whole zephyr) during usec_to_wait
|
||||
*
|
||||
* Note that interrupts may be received in the meanwhile and that therefore this
|
||||
* thread may lose context
|
||||
*/
|
||||
void arch_busy_wait(uint32_t usec_to_wait)
|
||||
{
|
||||
bs_time_t time_end = tm_get_hw_time() + usec_to_wait;
|
||||
|
||||
while (tm_get_hw_time() < time_end) {
|
||||
/*There may be wakes due to other interrupts*/
|
||||
fake_timer_wake_in_time(time_end);
|
||||
posix_halt_cpu();
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -115,40 +115,6 @@ uint32_t z_clock_elapsed(void)
|
|||
}
|
||||
|
||||
|
||||
#if defined(CONFIG_ARCH_HAS_CUSTOM_BUSY_WAIT)
|
||||
/**
|
||||
* Replacement to the kernel k_busy_wait()
|
||||
* Will block this thread (and therefore the whole zephyr) during usec_to_wait
|
||||
*
|
||||
* Note that interrupts may be received in the meanwhile and that therefore this
|
||||
* thread may loose context
|
||||
*
|
||||
* This special arch_busy_wait() is necessary due to how the POSIX arch/SOC INF
|
||||
* models a CPU. Conceptually it could be thought as if the MCU was running
|
||||
* at an infinitely high clock, and therefore no simulated time passes while
|
||||
* executing instructions(*1).
|
||||
* Therefore to be able to busy wait this function does the equivalent of
|
||||
* programming a dedicated timer which will raise a non-maskable interrupt,
|
||||
* and halting the CPU.
|
||||
*
|
||||
* (*1) In reality simulated time is simply not advanced just due to the "MCU"
|
||||
* running. Meaning, the SW running on the MCU is assumed to take 0 time.
|
||||
*/
|
||||
void arch_busy_wait(uint32_t usec_to_wait)
|
||||
{
|
||||
uint64_t time_end = hwm_get_time() + usec_to_wait;
|
||||
|
||||
while (hwm_get_time() < time_end) {
|
||||
/*
|
||||
* There may be wakes due to other interrupts including
|
||||
* other threads calling arch_busy_wait
|
||||
*/
|
||||
hwtimer_wake_in_time(time_end);
|
||||
posix_halt_cpu();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SYSTEM_CLOCK_DISABLE)
|
||||
/**
|
||||
*
|
||||
|
|
|
@ -24,6 +24,7 @@ extern "C" {
|
|||
void posix_irq_handler(void);
|
||||
void posix_exit(int exit_code);
|
||||
uint64_t posix_get_hw_cycle(void);
|
||||
void posix_cpu_hold(uint32_t usec_to_waste);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ extern "C" {
|
|||
void posix_interrupt_raised(void);
|
||||
void posix_boot_cpu(void);
|
||||
int posix_is_cpu_running(void);
|
||||
void posix_change_cpu_state_and_wait(bool halted);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ int posix_is_cpu_running(void)
|
|||
* raise a new interrupt; and how the HW models awake the CPU, and wait for it
|
||||
* to complete and go to idle.
|
||||
*/
|
||||
static void posix_change_cpu_state_and_wait(bool halted)
|
||||
void posix_change_cpu_state_and_wait(bool halted)
|
||||
{
|
||||
PC_SAFE_CALL(pthread_mutex_lock(&mtx_cpu));
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue