mirror of
https://github.com/Dasharo/zephyr.git
synced 2026-03-06 14:57:20 -08:00
Native simulator: Add first version in-tree
Add the first version of the native simulator. The simultaor is taken as is from https://github.com/BabbleSim/native_simulator/ sha: 74986abfe088a1780e604dae65f87470b4c2a0eb Signed-off-by: Alberto Escolar Piedras <alberto.escolar.piedras@nordicsemi.no>
This commit is contained in:
committed by
Chris Friedt
parent
feaf0ff576
commit
850fc2f22f
11
scripts/native_simulator/.gitignore
vendored
Normal file
11
scripts/native_simulator/.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
_build/*
|
||||
*.o
|
||||
*.a
|
||||
*.d
|
||||
*.elf
|
||||
*.gcda
|
||||
*.gcno
|
||||
*~
|
||||
~*.~*
|
||||
/.*project
|
||||
.settings/*
|
||||
127
scripts/native_simulator/Makefile
Normal file
127
scripts/native_simulator/Makefile
Normal file
@@ -0,0 +1,127 @@
|
||||
# Copyright 2023 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Native Simulator (NSI) Makefile.
|
||||
# It builds the simulator runner itself, and produces the final
|
||||
# Linux executable by linking it to the the embedded cpu library
|
||||
|
||||
# By default all the build output is placed under the _build folder, but the user can override it
|
||||
# setting the NSI_BUILD_PATH
|
||||
|
||||
NSI_CONFIG_FILE?=nsi_config
|
||||
-include ${NSI_CONFIG_FILE}
|
||||
|
||||
NSI_PATH?=./
|
||||
NSI_BUILD_PATH?=$(abspath _build/)
|
||||
EXE_NAME?=native_simulator.exe
|
||||
NSI_EXE?=${NSI_BUILD_PATH}/${EXE_NAME}
|
||||
NSI_EMBEDDED_CPU_SW?=
|
||||
NSI_ARCH?=-m32
|
||||
NSI_COVERAGE?=--coverage
|
||||
NSI_BUILD_OPTIONS?=${NSI_ARCH} ${NSI_COVERAGE}
|
||||
NSI_LINK_OPTIONS?=${NSI_ARCH} ${NSI_COVERAGE}
|
||||
NSI_EXTRA_SRCS?=
|
||||
NSI_EXTRA_LIBS?=
|
||||
|
||||
SHELL?=bash
|
||||
NSI_CC?=gcc
|
||||
NSI_AR?=ar
|
||||
NSI_OBJCOPY?=objcopy
|
||||
|
||||
no_default:
|
||||
@echo "There is no default rule, please specify what you want to build,\
|
||||
or run make help for more info"
|
||||
|
||||
NSI_DEBUG?=-g
|
||||
NSI_OPT?=-O0
|
||||
NSI_WARNINGS?=-Wall -Wpedantic
|
||||
NSI_CPPFLAGS?=-D_POSIX_C_SOURCE=200809 -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED
|
||||
NO_PIE_CO:=-fno-pie -fno-pic
|
||||
DEPENDFLAGS:=-MMD -MP
|
||||
CFLAGS:=${NSI_DEBUG} ${NSI_WARNINGS} ${NSI_OPT} ${NO_PIE_CO} \
|
||||
-ffunction-sections -fdata-sections ${DEPENDFLAGS} -std=c11 ${NSI_BUILD_OPTIONS}
|
||||
FINALLINK_FLAGS:=${NO_PIE_CO} -no-pie ${NSI_WARNINGS} \
|
||||
-Wl,--gc-sections -lm -ldl -pthread \
|
||||
${NSI_LINK_OPTIONS}
|
||||
|
||||
RUNNER_LIB:=runner.a
|
||||
|
||||
SRCS:=$(shell ls ${NSI_PATH}common/src/*.c ${NSI_PATH}native/src/*.c )
|
||||
|
||||
INCLUDES:=-I${NSI_PATH}common/src/include/ \
|
||||
-I${NSI_PATH}native/src/include/ \
|
||||
-I${NSI_PATH}common/src
|
||||
|
||||
EXTRA_OBJS:=$(abspath $(addprefix $(NSI_BUILD_PATH)/,$(sort ${NSI_EXTRA_SRCS:%.c=%.o})))
|
||||
OBJS:=$(abspath $(addprefix $(NSI_BUILD_PATH)/,${SRCS:${NSI_PATH}%.c=%.o})) ${EXTRA_OBJS}
|
||||
|
||||
DEPENDFILES:=$(addsuffix .d,$(basename ${OBJS}))
|
||||
|
||||
-include ${DEPENDFILES}
|
||||
|
||||
${NSI_BUILD_PATH}:
|
||||
@if [ ! -d ${NSI_BUILD_PATH} ]; then mkdir -p ${NSI_BUILD_PATH}; fi
|
||||
|
||||
#Extra sources build:
|
||||
${NSI_BUILD_PATH}/%.o: /%.c ${NSI_PATH}Makefile ${NSI_CONFIG_FILE}
|
||||
@if [ ! -d $(dir $@) ]; then mkdir -p $(dir $@); fi
|
||||
${NSI_CC} ${NSI_CPPFLAGS} ${INCLUDES} ${CFLAGS} -c $< -o $@
|
||||
|
||||
${NSI_BUILD_PATH}/%.o: ${NSI_PATH}/%.c ${NSI_PATH}Makefile ${NSI_CONFIG_FILE}
|
||||
@if [ ! -d $(dir $@) ]; then mkdir -p $(dir $@); fi
|
||||
${NSI_CC} ${NSI_CPPFLAGS} ${INCLUDES} ${CFLAGS} -c $< -o $@
|
||||
|
||||
${NSI_BUILD_PATH}/linker_script.ld : ${NSI_PATH}/common/other/linker_script.pre.ld | ${NSI_BUILD_PATH}
|
||||
${NSI_CC} -x c -E -P $< -o $@ ${DEPENDFLAGS}
|
||||
|
||||
${NSI_BUILD_PATH}/${RUNNER_LIB}: ${OBJS}
|
||||
if [ -f $@ ]; then rm $@ ; fi
|
||||
${NSI_AR} -cr $@ ${OBJS}
|
||||
|
||||
${NSI_EXE}: ${NSI_BUILD_PATH}/${RUNNER_LIB} ${NSI_EMBEDDED_CPU_SW} ${NSI_EXTRA_LIBS} \
|
||||
${NSI_BUILD_PATH}/linker_script.ld
|
||||
@if [ -z ${NSI_EMBEDDED_CPU_SW} ] || [ ! -f ${NSI_EMBEDDED_CPU_SW} ]; then \
|
||||
echo "Error: Input embedded CPU SW not found (NSI_EMBEDDED_CPU_SW=${NSI_EMBEDDED_CPU_SW} )"; \
|
||||
false; \
|
||||
fi
|
||||
${NSI_OBJCOPY} --localize-hidden ${NSI_EMBEDDED_CPU_SW} ${NSI_BUILD_PATH}/cpu_0.sw.o \
|
||||
-w --localize-symbol=_*
|
||||
${NSI_CC} -Wl,--whole-archive ${NSI_BUILD_PATH}/cpu_0.sw.o ${NSI_BUILD_PATH}/${RUNNER_LIB} \
|
||||
${NSI_EXTRA_LIBS} -Wl,--no-whole-archive \
|
||||
-o $@ ${FINALLINK_FLAGS} -T ${NSI_BUILD_PATH}/linker_script.ld
|
||||
|
||||
Makefile: ;
|
||||
|
||||
link_with_esw: ${NSI_EXE};
|
||||
|
||||
runner_lib: ${NSI_BUILD_PATH}/${RUNNER_LIB}
|
||||
|
||||
all: link_with_esw
|
||||
|
||||
clean:
|
||||
@echo "Deleting intermediate compilation results + libraries + executables (*.d .o .a .exe)"
|
||||
find $(NSI_BUILD_PATH) -name "*.o" -or -name "*.exe" -or -name "*.a" -or -name "*.d" | xargs rm -f
|
||||
|
||||
clean_coverage:
|
||||
find $(NSI_BUILD_PATH) -name "*.gcda" -or -name "*.gcno" | xargs rm -f ; true
|
||||
|
||||
clean_all: clean clean_coverage ;
|
||||
|
||||
.PHONY: clean clean_coverage clean_all link_with_esw runner_lib no_default all ${DEPENDFILES}
|
||||
|
||||
ifndef NSI_BUILD_VERBOSE
|
||||
.SILENT:
|
||||
endif
|
||||
|
||||
help:
|
||||
@echo "*******************************"
|
||||
@echo "* Native Simulator makefile *"
|
||||
@echo "*******************************"
|
||||
@echo "Provided rules:"
|
||||
@echo " clean : clean all build output"
|
||||
@echo " clean_coverage : clean all coverage files"
|
||||
@echo " clean_all : clean + clean_coverage"
|
||||
@echo " link_with_esw : Link the runner with the CPU embedded sw"
|
||||
@echo " runner_lib : Build the runner itself (pending the embedded SW)"
|
||||
@echo " all : link_with_esw"
|
||||
@echo "Note that you can use TAB to autocomplete rules in the command line in modern OSs"
|
||||
50
scripts/native_simulator/common/other/linker_script.pre.ld
Normal file
50
scripts/native_simulator/common/other/linker_script.pre.ld
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Linker command/script file for the native simulator runner
|
||||
*/
|
||||
|
||||
#define NSI_INIT_LEVEL(level) \
|
||||
__nsi_##level##_tasks_start = .; \
|
||||
KEEP(*(SORT(.nsi_##level[0-9]_task))); \
|
||||
KEEP(*(SORT(.nsi_##level[1-9][0-9]_task))); \
|
||||
KEEP(*(SORT(.nsi_##level[1-9][0-9][0-9]_task))); \
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
nsi_tasks :
|
||||
{
|
||||
__nsi_tasks_start = .;
|
||||
NSI_INIT_LEVEL(PRE_BOOT_1)
|
||||
NSI_INIT_LEVEL(PRE_BOOT_2)
|
||||
NSI_INIT_LEVEL(HW_INIT)
|
||||
NSI_INIT_LEVEL(PRE_BOOT_3)
|
||||
NSI_INIT_LEVEL(FIRST_SLEEP)
|
||||
NSI_INIT_LEVEL(ON_EXIT_PRE)
|
||||
NSI_INIT_LEVEL(ON_EXIT_POST)
|
||||
__nsi_tasks_end = .;
|
||||
}
|
||||
|
||||
nsi_hw_events :
|
||||
{
|
||||
__nsi_hw_events_callbacks_start = .;
|
||||
KEEP(*(SORT(.nsi_hw_event[0-9]_callback))); \
|
||||
KEEP(*(SORT(.nsi_hw_event[1-9][0-9]_callback))); \
|
||||
KEEP(*(SORT(.nsi_hw_event[1-9][0-9][0-9]_callback)));
|
||||
__nsi_hw_events_callbacks_end = .;
|
||||
__nsi_hw_events_timers_start = .;
|
||||
KEEP(*(SORT(.nsi_hw_event[0-9]_timer))); \
|
||||
KEEP(*(SORT(.nsi_hw_event[1-9][0-9]_timer))); \
|
||||
KEEP(*(SORT(.nsi_hw_event[1-9][0-9][0-9]_timer)));
|
||||
__nsi_hw_events_timers_end = .;
|
||||
}
|
||||
} INSERT AFTER .data;
|
||||
|
||||
/*
|
||||
* Note this script augments the default host linker script
|
||||
*/
|
||||
32
scripts/native_simulator/common/src/include/nce_if.h
Normal file
32
scripts/native_simulator/common/src/include/nce_if.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef NSI_COMMON_SRC_INCL_NCE_IF_H
|
||||
#define NSI_COMMON_SRC_INCL_NCE_IF_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Native simulator CPU start/stop emulation module interface
|
||||
*
|
||||
* Check docs/NCE.md for an overview.
|
||||
*
|
||||
* A descriptions of each function can be found in the .c file
|
||||
*/
|
||||
|
||||
void *nce_init(void);
|
||||
void nce_terminate(void *this);
|
||||
void nce_boot_cpu(void *this, void (*start_routine)(void));
|
||||
void nce_halt_cpu(void *this);
|
||||
void nce_wake_cpu(void *this);
|
||||
int nce_is_cpu_running(void *this);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NSI_COMMON_SRC_INCL_NCE_IF_H */
|
||||
33
scripts/native_simulator/common/src/include/nct_if.h
Normal file
33
scripts/native_simulator/common/src/include/nct_if.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef NSI_COMMON_SRC_INCL_NCT_IF_H
|
||||
#define NSI_COMMON_SRC_INCL_NCT_IF_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Interface provided by the Native simulator CPU threading emulation
|
||||
*
|
||||
* A description of each function can be found in the C file
|
||||
*
|
||||
* In docs/NCT.md you can find more information
|
||||
*/
|
||||
|
||||
void *nct_init(void (*fptr)(void *));
|
||||
void nct_clean_up(void *this);
|
||||
void nct_swap_threads(void *this, int next_allowed_thread_nbr);
|
||||
void nct_first_thread_start(void *this, int next_allowed_thread_nbr);
|
||||
int nct_new_thread(void *this, void *payload);
|
||||
void nct_abort_thread(void *this, int thread_idx);
|
||||
int nct_get_unique_thread_id(void *this, int thread_idx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NSI_COMMON_SRC_INCL_NCT_IF_H */
|
||||
19
scripts/native_simulator/common/src/include/nsi_cpu_es_if.h
Normal file
19
scripts/native_simulator/common/src/include/nsi_cpu_es_if.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*
|
||||
* Interfaces the Native Simulator provides to
|
||||
* the embedded CPU SW
|
||||
*/
|
||||
|
||||
#ifndef NSI_COMMON_SRC_INCL_NSI_CPU_ES_IF_H
|
||||
#define NSI_COMMON_SRC_INCL_NSI_CPU_ES_IF_H
|
||||
|
||||
#include "nsi_tracing.h"
|
||||
#include "nsi_main.h"
|
||||
#include "nsi_hw_scheduler.h"
|
||||
|
||||
#endif /* NSI_COMMON_SRC_INCL_NSI_CPU_ES_IF_H */
|
||||
100
scripts/native_simulator/common/src/include/nsi_cpu_if.h
Normal file
100
scripts/native_simulator/common/src/include/nsi_cpu_if.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef NSI_COMMON_SRC_INCL_NSI_CPU_IF_H
|
||||
#define NSI_COMMON_SRC_INCL_NSI_CPU_IF_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Any symbol annotated by this macro will be visible outside of the
|
||||
* embedded SW library, both by the native simulator runner,
|
||||
* and other possible embedded CPU's SW.
|
||||
*/
|
||||
#define NATIVE_SIMULATOR_IF __attribute__((visibility("default"))) \
|
||||
__attribute__((__section__(".native_sim_if")))
|
||||
/*
|
||||
* Implementation note:
|
||||
* The interface between the embedded SW and the native simulator is allocated in its
|
||||
* own section to allow the embedded software developers to, using a linker script,
|
||||
* direct the linker to keep those symbols even when doing its linking with garbage collection.
|
||||
* It is also be possible for the embedded SW to require the linker to keep those
|
||||
* symbols by requiring each of them to be kept explicitly by name (either by defining them
|
||||
* as entry points, or as required in the output).
|
||||
* It is also possible for the embedded SW developers to not use garbage collection
|
||||
* during their SW linking.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Interfaces the Native Simulator _expects_ from the embedded CPUs:
|
||||
*/
|
||||
|
||||
/*
|
||||
* Called during the earliest initialization (before command line parsing)
|
||||
*
|
||||
* The embedded SW library may provide this function to perform any
|
||||
* early initialization, including registering its own command line arguments
|
||||
* in the runner.
|
||||
*/
|
||||
NATIVE_SIMULATOR_IF void nsif_cpu0_pre_cmdline_hooks(void);
|
||||
|
||||
/*
|
||||
* Called during initialization (before the HW models are initialized)
|
||||
*
|
||||
* The embedded SW library may provide this function to perform any
|
||||
* early initialization, after the command line arguments have been parsed.
|
||||
*/
|
||||
NATIVE_SIMULATOR_IF void nsif_cpu0_pre_hw_init_hooks(void);
|
||||
|
||||
/*
|
||||
* Called by the runner to boot the CPU.
|
||||
*
|
||||
* The embedded SW library must provide this function.
|
||||
* This function is expected to return after the embedded CPU
|
||||
* has gone to sleep for the first time.
|
||||
*
|
||||
* The expectation is that the embedded CPU SW will spawn a
|
||||
* new pthread while in this call, and run the embedded SW
|
||||
* initialization in that pthread.
|
||||
*
|
||||
* It is recommended for the embedded SW to use the NCE (CPU start/stop emulation)
|
||||
* component to achieve this.
|
||||
*/
|
||||
NATIVE_SIMULATOR_IF void nsif_cpu0_boot(void);
|
||||
|
||||
/*
|
||||
* Called by the runner when the simulation is ending/exiting
|
||||
*
|
||||
* The embedded SW library may provide this function.
|
||||
* to do any cleanup it needs.
|
||||
*/
|
||||
NATIVE_SIMULATOR_IF void nsif_cpu0_cleanup(void);
|
||||
|
||||
/*
|
||||
* Called by the runner each time an interrupt is raised by the HW
|
||||
*
|
||||
* The embedded SW library must provide this function.
|
||||
* This function is expected to return after the embedded CPU
|
||||
* has gone back to sleep.
|
||||
*/
|
||||
NATIVE_SIMULATOR_IF void nsif_cpu0_irq_raised(void);
|
||||
|
||||
/*
|
||||
* Called by the runner each time an interrupt is raised in SW context itself.
|
||||
* That is, when a embedded SW action in the HW models, causes an immediate
|
||||
* interrupt to be raised (while the execution is still in the
|
||||
* context of the calling SW thread).
|
||||
*/
|
||||
NATIVE_SIMULATOR_IF void nsif_cpu0_irq_raised_from_sw(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NSI_COMMON_SRC_INCL_NSI_CPU_IF_H */
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* The native simulator provides a set of trampolines to some of the simplest
|
||||
* host C library symbols.
|
||||
* These are intended to facilitate test embedded code interacting with the host.
|
||||
*
|
||||
* We should never include here symbols which require host headers be exposed
|
||||
* to the embedded side, for example due to non-basic types being used in
|
||||
* function calls, as that would break the include path isolation
|
||||
*
|
||||
* Naming convention: nsi_hots_<fun>() where <func> is the name of the equivalent
|
||||
* C library call we call thru.
|
||||
*/
|
||||
|
||||
#ifndef NSI_COMMON_SRC_INCL_NSI_HOST_TRAMPOLINES_H
|
||||
#define NSI_COMMON_SRC_INCL_NSI_HOST_TRAMPOLINES_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* void nsi_host_exit (int status); Use nsi_exit() instead */
|
||||
/* int nsi_host_printf (const char *fmt, ...); Use the nsi_tracing.h equivalents */
|
||||
long nsi_host_random(void);
|
||||
void nsi_host_srandom(unsigned int seed);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NSI_COMMON_SRC_INCL_NSI_HOST_TRAMPOLINES_H */
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Oticon A/S
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef NSI_COMMON_SRC_INCL_HW_SCHEDULER_H
|
||||
#define NSI_COMMON_SRC_INCL_HW_SCHEDULER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define NSI_NEVER UINT64_MAX
|
||||
|
||||
/* API intended for the native simulator specific embedded drivers: */
|
||||
uint64_t nsi_hws_get_time(void);
|
||||
|
||||
/* Internal APIs to the native_simulator and its HW models: */
|
||||
void nsi_hws_init(void);
|
||||
void nsi_hws_cleanup(void);
|
||||
void nsi_hws_one_event(void);
|
||||
void nsi_hws_set_end_of_time(uint64_t new_end_of_time);
|
||||
void nsi_hws_find_next_event(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NSI_COMMON_SRC_INCL_HW_SCHEDULER_H */
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef NSI_COMMON_SRC_INCL_HWS_MODELS_IF_H
|
||||
#define NSI_COMMON_SRC_INCL_HWS_MODELS_IF_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "nsi_utils.h"
|
||||
#include "nsi_hw_scheduler.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Register an event timer and event callback
|
||||
*
|
||||
* The HW scheduler will keep track of this event, and call its callback whenever its
|
||||
* timer is reached.
|
||||
* The ordering of events in the same microsecond is given by prio (lowest first),
|
||||
* and if also in the same priority by alphabetical order of the callback.
|
||||
* (Normally HW models will not care about the event ordering, and will simply set a prio like 100)
|
||||
*
|
||||
* Only very particular models will need to execute before or after others.
|
||||
*/
|
||||
#define NSI_HW_EVENT(timer, fn, prio) \
|
||||
static void (* const NSI_CONCAT(__nsi_hw_event_cb_, fn))(void) \
|
||||
__attribute__((__used__)) \
|
||||
__attribute__((__section__(".nsi_hw_event" NSI_STRINGIFY(prio) "_callback")))\
|
||||
= fn; \
|
||||
static uint64_t * const NSI_CONCAT(__nsi_hw_event_ti_, fn) \
|
||||
__attribute__((__used__)) \
|
||||
__attribute__((__section__(".nsi_hw_event" NSI_STRINGIFY(prio) "_timer")))\
|
||||
= &timer
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NSI_COMMON_SRC_INCL_HWS_MODELS_IF_H */
|
||||
27
scripts/native_simulator/common/src/include/nsi_main.h
Normal file
27
scripts/native_simulator/common/src/include/nsi_main.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef NSI_COMMON_SRC_INCL_NSI_MAIN_H
|
||||
#define NSI_COMMON_SRC_INCL_NSI_MAIN_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Terminate the execution of the native simulator
|
||||
*
|
||||
* exit_code: Requested exit code to the shell
|
||||
* Note that other components may have requested a different
|
||||
* exit code which may have precedence if it was !=0
|
||||
*/
|
||||
void nsi_exit(int exit_code);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NSI_COMMON_SRC_INCL_NSI_MAIN_H */
|
||||
47
scripts/native_simulator/common/src/include/nsi_tracing.h
Normal file
47
scripts/native_simulator/common/src/include/nsi_tracing.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef NSI_COMMON_SRC_INCL_NSI_TRACING_H
|
||||
#define NSI_COMMON_SRC_INCL_NSI_TRACING_H
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Native simulator tracing API:
|
||||
* Print a message/warning/error to the tracing backend
|
||||
* and in case of nsi_print_error_and_exit() also call nsi_exit()
|
||||
*
|
||||
* All print()/vprint() APIs take the same arguments as printf()/vprintf().
|
||||
*/
|
||||
|
||||
void nsi_print_error_and_exit(const char *format, ...);
|
||||
void nsi_print_warning(const char *format, ...);
|
||||
void nsi_print_trace(const char *format, ...);
|
||||
void nsi_vprint_error_and_exit(const char *format, va_list vargs);
|
||||
void nsi_vprint_warning(const char *format, va_list vargs);
|
||||
void nsi_vprint_trace(const char *format, va_list vargs);
|
||||
|
||||
/*
|
||||
* @brief Is the tracing backend connected to a ptty/terminal or not
|
||||
*
|
||||
* @param nbr: Which output. Options are: 0 trace output, 1: warning and error output
|
||||
*
|
||||
* @return
|
||||
* 0 : Not a ptty (i.e. probably a pipe to another program)
|
||||
* 1 : Connected to a ptty (for ex. stdout/err to the invoking terminal)
|
||||
* -1: Unknown at this point
|
||||
*/
|
||||
int nsi_trace_over_tty(int nbr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NSI_COMMON_SRC_INCL_NSI_TRACING_H */
|
||||
144
scripts/native_simulator/common/src/main.c
Normal file
144
scripts/native_simulator/common/src/main.c
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Oticon A/S
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*
|
||||
* Native simulator entry point (main)
|
||||
*
|
||||
* Documentation can be found starting in docs/README.md
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "nsi_cpu_if.h"
|
||||
#include "nsi_tasks.h"
|
||||
#include "nsi_cmdline.h"
|
||||
#include "nsi_utils.h"
|
||||
#include "nsi_hw_scheduler.h"
|
||||
|
||||
void nsi_exit(int exit_code)
|
||||
{
|
||||
static int max_exit_code;
|
||||
|
||||
max_exit_code = NSI_MAX(exit_code, max_exit_code);
|
||||
/*
|
||||
* nsif_cpu0_cleanup may not return if this is called from a SW thread,
|
||||
* but instead it would get nsi_exit() recalled again
|
||||
* ASAP from the HW thread
|
||||
*/
|
||||
nsif_cpu0_cleanup();
|
||||
nsi_run_tasks(NSITASK_ON_EXIT_PRE_LEVEL);
|
||||
nsi_hws_cleanup();
|
||||
nsi_cleanup_cmd_line();
|
||||
nsi_run_tasks(NSITASK_ON_EXIT_POST_LEVEL);
|
||||
exit(max_exit_code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all early native_posix initialization steps, including command
|
||||
* line parsing and CPU start, until we are ready to let the HW models
|
||||
* run via hwm_one_event()
|
||||
*/
|
||||
static void nsi_init(int argc, char *argv[])
|
||||
{
|
||||
/*
|
||||
* Let's ensure that even if we are redirecting to a file, we get stdout
|
||||
* and stderr line buffered (default for console)
|
||||
* Note that glibc ignores size. But just in case we set a reasonable
|
||||
* number in case somebody tries to compile against a different library
|
||||
*/
|
||||
setvbuf(stdout, NULL, _IOLBF, 512);
|
||||
setvbuf(stderr, NULL, _IOLBF, 512);
|
||||
|
||||
nsi_run_tasks(NSITASK_PRE_BOOT_1_LEVEL);
|
||||
nsif_cpu0_pre_cmdline_hooks();
|
||||
|
||||
nsi_handle_cmd_line(argc, argv);
|
||||
|
||||
nsi_run_tasks(NSITASK_PRE_BOOT_2_LEVEL);
|
||||
nsif_cpu0_pre_hw_init_hooks();
|
||||
|
||||
nsi_run_tasks(NSITASK_HW_INIT_LEVEL);
|
||||
nsi_hws_init();
|
||||
|
||||
nsi_run_tasks(NSITASK_PRE_BOOT_3_LEVEL);
|
||||
|
||||
nsif_cpu0_boot();
|
||||
|
||||
nsi_run_tasks(NSITASK_FIRST_SLEEP_LEVEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the simulator for at least the specified timeout, then
|
||||
* return. Note that this does not affect event timing, so the "next
|
||||
* event" may be significantly after the request if the hardware has
|
||||
* not been configured to e.g. send an interrupt when expected.
|
||||
*/
|
||||
void nsi_exec_for(uint64_t us)
|
||||
{
|
||||
uint64_t start = nsi_hws_get_time();
|
||||
|
||||
do {
|
||||
nsi_hws_one_event();
|
||||
} while (nsi_hws_get_time() < (start + us));
|
||||
}
|
||||
|
||||
#ifndef NSI_LIBFUZZER
|
||||
|
||||
/**
|
||||
*
|
||||
* Note that this main() is not used when building fuzz cases,
|
||||
* as libfuzzer has its own main(),
|
||||
* and calls the "OS" through a per-case fuzz test entry point.
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
nsi_init(argc, argv);
|
||||
while (true) {
|
||||
nsi_hws_one_event();
|
||||
}
|
||||
|
||||
/* This line should be unreachable */
|
||||
return 1; /* LCOV_EXCL_LINE */
|
||||
}
|
||||
|
||||
#else /* NSI_LIBFUZZER */
|
||||
|
||||
/**
|
||||
* Entry point for fuzzing (when enabled). Works by placing the data
|
||||
* into two known symbols, triggering an app-visible interrupt, and
|
||||
* then letting the simulator run for a fixed amount of time (intended to be
|
||||
* "long enough" to handle the event and reach a quiescent state
|
||||
* again)
|
||||
*/
|
||||
uint8_t *nsi_fuzz_buf, nsi_fuzz_sz;
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t sz)
|
||||
{
|
||||
static bool nsi_initialized;
|
||||
|
||||
if (!nsi_initialized) {
|
||||
nsi_init(0, NULL);
|
||||
nsi_initialized = true;
|
||||
}
|
||||
|
||||
/* Provide the fuzz data to the embedded OS as an interrupt, with
|
||||
* "DMA-like" data placed into nsi_fuzz_buf/sz
|
||||
*/
|
||||
nsi_fuzz_buf = (void *)data;
|
||||
nsi_fuzz_sz = sz;
|
||||
hw_irq_ctrl_set_irq(NSI_FUZZ_IRQ);
|
||||
|
||||
/* Give the OS time to process whatever happened in that
|
||||
* interrupt and reach an idle state.
|
||||
*/
|
||||
nsi_exec_for(NSI_FUZZ_TIME);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* NSI_LIBFUZZER */
|
||||
292
scripts/native_simulator/common/src/nce.c
Normal file
292
scripts/native_simulator/common/src/nce.c
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Oticon A/S
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*
|
||||
* Native simulator CPU emulator,
|
||||
* an *optional* module provided by the native simulator
|
||||
* the hosted embedded OS / SW can use to emulate the CPU
|
||||
* being started and stopped.
|
||||
*
|
||||
* Its mode of operation is that it step-locks the HW
|
||||
* and SW operation, so that only one of them executes at
|
||||
* a time. Check the docs for more info.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include "nce_if.h"
|
||||
#include "nsi_safe_call.h"
|
||||
|
||||
struct nce_status_t {
|
||||
/* Conditional variable to know if the CPU is running or halted/idling */
|
||||
pthread_cond_t cond_cpu;
|
||||
/* Mutex for the conditional variable cond_cpu */
|
||||
pthread_mutex_t mtx_cpu;
|
||||
/* Variable which tells if the CPU is halted (1) or not (0) */
|
||||
bool cpu_halted;
|
||||
bool terminate; /* Are we terminating the program == cleaning up */
|
||||
void (*start_routine)(void);
|
||||
};
|
||||
|
||||
#define NCE_DEBUG_PRINTS 0
|
||||
|
||||
#define PREFIX "NCE: "
|
||||
#define ERPREFIX PREFIX"error on "
|
||||
#define NO_MEM_ERR PREFIX"Can't allocate memory\n"
|
||||
|
||||
#if NCE_DEBUG_PRINTS
|
||||
#define NCE_DEBUG(fmt, ...) nsi_print_trace(PREFIX fmt, __VA_ARGS__)
|
||||
#else
|
||||
#define NCE_DEBUG(...)
|
||||
#endif
|
||||
|
||||
extern void nsi_exit(int exit_code);
|
||||
|
||||
/*
|
||||
* Initialize an instance of the native simulator CPU emulator
|
||||
* and return a pointer to it.
|
||||
* That pointer should be passed to all subsequent calls to this module.
|
||||
*/
|
||||
void *nce_init(void)
|
||||
{
|
||||
struct nce_status_t *this;
|
||||
|
||||
this = calloc(1, sizeof(struct nce_status_t));
|
||||
|
||||
if (this == NULL) { /* LCOV_EXCL_BR_LINE */
|
||||
nsi_print_error_and_exit(NO_MEM_ERR); /* LCOV_EXCL_LINE */
|
||||
}
|
||||
this->cpu_halted = true;
|
||||
this->terminate = false;
|
||||
|
||||
NSI_SAFE_CALL(pthread_cond_init(&this->cond_cpu, NULL));
|
||||
NSI_SAFE_CALL(pthread_mutex_init(&this->mtx_cpu, NULL));
|
||||
|
||||
return (void *)this;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function will:
|
||||
*
|
||||
* If called from a SW thread, release the HW thread which is blocked in
|
||||
* a nce_wake_cpu() and never return.
|
||||
*
|
||||
* If called from a HW thread, do the necessary clean up of this nce instance
|
||||
* and return right away.
|
||||
*/
|
||||
void nce_terminate(void *this_arg)
|
||||
{
|
||||
struct nce_status_t *this = (struct nce_status_t *)this_arg;
|
||||
|
||||
/* LCOV_EXCL_START */ /* See Note1 */
|
||||
/*
|
||||
* If we are being called from a HW thread we can cleanup
|
||||
*
|
||||
* Otherwise (!cpu_halted) we give back control to the HW thread and
|
||||
* tell it to terminate ASAP
|
||||
*/
|
||||
if (this == NULL || this->cpu_halted) {
|
||||
/*
|
||||
* Note: The nce_status structure cannot be safely free'd up
|
||||
* as the user is allowed to call nce_clean_up()
|
||||
* repeatedly on the same structure.
|
||||
* Instead we rely of on the host OS process cleanup.
|
||||
* If you got here due to valgrind's leak report, please use the
|
||||
* provided valgrind suppression file valgrind.supp
|
||||
*/
|
||||
return;
|
||||
} else if (this->terminate == false) {
|
||||
|
||||
this->terminate = true;
|
||||
|
||||
NSI_SAFE_CALL(pthread_mutex_lock(&this->mtx_cpu));
|
||||
|
||||
this->cpu_halted = true;
|
||||
|
||||
NSI_SAFE_CALL(pthread_cond_broadcast(&this->cond_cpu));
|
||||
NSI_SAFE_CALL(pthread_mutex_unlock(&this->mtx_cpu));
|
||||
|
||||
while (1) {
|
||||
sleep(1);
|
||||
/* This SW thread will wait until being cancelled from
|
||||
* the HW thread. sleep() is a cancellation point, so it
|
||||
* won't really wait 1 second
|
||||
*/
|
||||
}
|
||||
}
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function which changes the status of the CPU (halted or running)
|
||||
* and waits until somebody else changes it to the opposite
|
||||
*
|
||||
* Both HW and SW threads will use this function to transfer control to the
|
||||
* other side.
|
||||
*
|
||||
* This is how the idle thread halts the CPU and gets halted until the HW models
|
||||
* raise a new interrupt; and how the HW models awake the CPU, and wait for it
|
||||
* to complete and go to idle.
|
||||
*/
|
||||
static void change_cpu_state_and_wait(struct nce_status_t *this, bool halted)
|
||||
{
|
||||
NSI_SAFE_CALL(pthread_mutex_lock(&this->mtx_cpu));
|
||||
|
||||
NCE_DEBUG("Going to halted = %d\n", halted);
|
||||
|
||||
this->cpu_halted = halted;
|
||||
|
||||
/* We let the other side know the CPU has changed state */
|
||||
NSI_SAFE_CALL(pthread_cond_broadcast(&this->cond_cpu));
|
||||
|
||||
/* We wait until the CPU state has been changed. Either:
|
||||
* we just awoke it, and therefore wait until the CPU has run until
|
||||
* completion before continuing (before letting the HW models do
|
||||
* anything else)
|
||||
* or
|
||||
* we are just hanging it, and therefore wait until the HW models awake
|
||||
* it again
|
||||
*/
|
||||
while (this->cpu_halted == halted) {
|
||||
/* Here we unlock the mutex while waiting */
|
||||
pthread_cond_wait(&this->cond_cpu, &this->mtx_cpu);
|
||||
}
|
||||
|
||||
NCE_DEBUG("Awaken after halted = %d\n", halted);
|
||||
|
||||
NSI_SAFE_CALL(pthread_mutex_unlock(&this->mtx_cpu));
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function that wraps the SW start_routine
|
||||
*/
|
||||
static void *sw_wrapper(void *this_arg)
|
||||
{
|
||||
struct nce_status_t *this = (struct nce_status_t *)this_arg;
|
||||
|
||||
/* Ensure nce_boot_cpu has reached the cond loop */
|
||||
NSI_SAFE_CALL(pthread_mutex_lock(&this->mtx_cpu));
|
||||
NSI_SAFE_CALL(pthread_mutex_unlock(&this->mtx_cpu));
|
||||
|
||||
#if (NCE_DEBUG_PRINTS)
|
||||
pthread_t sw_thread = pthread_self();
|
||||
|
||||
NCE_DEBUG("SW init started (%lu)\n",
|
||||
sw_thread);
|
||||
#endif
|
||||
|
||||
this->start_routine();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Boot the emulated CPU, that is:
|
||||
* * Spawn a new pthread which will run the first embedded SW thread <start_routine>
|
||||
* * Hold the caller until that embedded SW thread (or a child it spawns)
|
||||
* calls nce_halt_cpu()
|
||||
*
|
||||
* Note that during this, an embedded SW thread may call nsi_exit(), which would result
|
||||
* in this function never returning.
|
||||
*/
|
||||
void nce_boot_cpu(void *this_arg, void (*start_routine)(void))
|
||||
{
|
||||
struct nce_status_t *this = (struct nce_status_t *)this_arg;
|
||||
|
||||
NSI_SAFE_CALL(pthread_mutex_lock(&this->mtx_cpu));
|
||||
|
||||
this->cpu_halted = false;
|
||||
this->start_routine = start_routine;
|
||||
|
||||
/* Create a thread for the embedded SW init: */
|
||||
pthread_t sw_thread;
|
||||
|
||||
NSI_SAFE_CALL(pthread_create(&sw_thread, NULL, sw_wrapper, this_arg));
|
||||
|
||||
/* And we wait until the embedded OS has send the CPU to sleep for the first time */
|
||||
while (this->cpu_halted == false) {
|
||||
pthread_cond_wait(&this->cond_cpu, &this->mtx_cpu);
|
||||
}
|
||||
NSI_SAFE_CALL(pthread_mutex_unlock(&this->mtx_cpu));
|
||||
|
||||
if (this->terminate) {
|
||||
nsi_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Halt the CPU, that is:
|
||||
* * Hold this embedded SW thread until the CPU is awaken again,
|
||||
* and release the HW thread which had been held on
|
||||
* nce_boot_cpu() or nce_wake_cpu().
|
||||
*
|
||||
* Note: Can only be called from embedded SW threads
|
||||
* Calling it from a HW thread is a programming error.
|
||||
*/
|
||||
void nce_halt_cpu(void *this_arg)
|
||||
{
|
||||
struct nce_status_t *this = (struct nce_status_t *)this_arg;
|
||||
|
||||
if (this->cpu_halted == true) {
|
||||
nsi_print_error_and_exit("Programming error on: %s ",
|
||||
"This CPU was already halted\n");
|
||||
}
|
||||
change_cpu_state_and_wait(this, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Awake the CPU, that is:
|
||||
* * Hold this HW thread until the CPU is set to idle again
|
||||
* * Release the SW thread which had been held on nce_halt_cpu()
|
||||
*
|
||||
* Note: Can only be called from HW threads
|
||||
* Calling it from a SW thread is a programming error.
|
||||
*/
|
||||
void nce_wake_cpu(void *this_arg)
|
||||
{
|
||||
struct nce_status_t *this = (struct nce_status_t *)this_arg;
|
||||
|
||||
if (this->cpu_halted == false) {
|
||||
nsi_print_error_and_exit("Programming error on: %s ",
|
||||
"This CPU was already awake\n");
|
||||
}
|
||||
change_cpu_state_and_wait(this, false);
|
||||
|
||||
/*
|
||||
* If while the SW was running it was decided to terminate the execution
|
||||
* we stop immediately.
|
||||
*/
|
||||
if (this->terminate) {
|
||||
nsi_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return 0 if the CPU is sleeping (or terminated)
|
||||
* and !=0 if the CPU is running
|
||||
*/
|
||||
int nce_is_cpu_running(void *this_arg)
|
||||
{
|
||||
struct nce_status_t *this = (struct nce_status_t *)this_arg;
|
||||
|
||||
if (this != NULL) {
|
||||
return !this->cpu_halted;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Notes about coverage:
|
||||
*
|
||||
* Note1: When the application is closed due to a SIGTERM, the path in this
|
||||
* function will depend on when that signal was received. Typically during a
|
||||
* regression run, both paths will be covered. But in some cases they won't.
|
||||
* Therefore and to avoid confusing developers with spurious coverage changes
|
||||
* we exclude this function from the coverage check
|
||||
*/
|
||||
690
scripts/native_simulator/common/src/nct.c
Normal file
690
scripts/native_simulator/common/src/nct.c
Normal file
File diff suppressed because it is too large
Load Diff
19
scripts/native_simulator/common/src/nsi_host_trampolines.c
Normal file
19
scripts/native_simulator/common/src/nsi_host_trampolines.c
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* See description in header
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
long nsi_host_random(void)
|
||||
{
|
||||
return random();
|
||||
}
|
||||
|
||||
void nsi_host_srandom(unsigned int seed)
|
||||
{
|
||||
srandom(seed);
|
||||
}
|
||||
181
scripts/native_simulator/common/src/nsi_hw_scheduler.c
Normal file
181
scripts/native_simulator/common/src/nsi_hw_scheduler.c
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Oticon A/S
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Overall HW models scheduler for the native simulator
|
||||
*
|
||||
* Models events are registered with NSI_HW_EVENT().
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
#include "nsi_tracing.h"
|
||||
#include "nsi_main.h"
|
||||
#include "nsi_safe_call.h"
|
||||
#include "nsi_hw_scheduler.h"
|
||||
|
||||
static uint64_t simu_time; /* The actual time as known by the HW models */
|
||||
static uint64_t end_of_time = NSI_NEVER; /* When will this device stop */
|
||||
|
||||
extern uint64_t *__nsi_hw_events_timers_start[];
|
||||
extern uint64_t *__nsi_hw_events_timers_end[];
|
||||
|
||||
extern void (*__nsi_hw_events_callbacks_start[])(void);
|
||||
extern void (*__nsi_hw_events_callbacks_end[])(void);
|
||||
|
||||
static unsigned int number_of_timers;
|
||||
static unsigned int number_of_callbacks;
|
||||
|
||||
static unsigned int next_timer_index;
|
||||
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
|
||||
*/
|
||||
static void nsi_hws_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
|
||||
*/
|
||||
static void nsi_hws_set_sig_handler(void)
|
||||
{
|
||||
struct sigaction act;
|
||||
|
||||
act.sa_handler = nsi_hws_signal_end_handler;
|
||||
NSI_SAFE_CALL(sigemptyset(&act.sa_mask));
|
||||
|
||||
act.sa_flags = SA_RESETHAND;
|
||||
|
||||
NSI_SAFE_CALL(sigaction(SIGTERM, &act, NULL));
|
||||
NSI_SAFE_CALL(sigaction(SIGINT, &act, NULL));
|
||||
}
|
||||
|
||||
|
||||
static void nsi_hws_sleep_until_next_event(void)
|
||||
{
|
||||
if (next_timer_time >= simu_time) { /* LCOV_EXCL_BR_LINE */
|
||||
simu_time = next_timer_time;
|
||||
} else {
|
||||
/* LCOV_EXCL_START */
|
||||
nsi_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)) {
|
||||
nsi_print_trace("\nStopped at %.3Lfs\n",
|
||||
((long double)simu_time)/1.0e6L);
|
||||
nsi_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find in between all events timers which is the next one.
|
||||
* (and update the internal next_timer_* accordingly)
|
||||
*/
|
||||
void nsi_hws_find_next_event(void)
|
||||
{
|
||||
next_timer_index = 0;
|
||||
next_timer_time = *__nsi_hw_events_timers_start[0];
|
||||
|
||||
for (unsigned int i = 1; i < number_of_timers ; i++) {
|
||||
if (next_timer_time > *__nsi_hw_events_timers_start[i]) {
|
||||
next_timer_index = i;
|
||||
next_timer_time = *__nsi_hw_events_timers_start[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the next scheduled HW event
|
||||
* (advancing time until that event would trigger)
|
||||
*/
|
||||
void nsi_hws_one_event(void)
|
||||
{
|
||||
nsi_hws_sleep_until_next_event();
|
||||
|
||||
if (next_timer_index < number_of_timers) { /* LCOV_EXCL_BR_LINE */
|
||||
__nsi_hw_events_callbacks_start[next_timer_index]();
|
||||
} else {
|
||||
nsi_print_error_and_exit("next_timer_index corrupted\n"); /* LCOV_EXCL_LINE */
|
||||
}
|
||||
|
||||
nsi_hws_find_next_event();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the simulated time when the process will stop
|
||||
*/
|
||||
void nsi_hws_set_end_of_time(uint64_t new_end_of_time)
|
||||
{
|
||||
end_of_time = new_end_of_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current simulated time as known by the device
|
||||
*/
|
||||
uint64_t nsi_hws_get_time(void)
|
||||
{
|
||||
return simu_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to initialize the HW scheduler
|
||||
*
|
||||
* Note that the HW models should register their initialization functions
|
||||
* as NSI_TASKS of HW_INIT level.
|
||||
*/
|
||||
void nsi_hws_init(void)
|
||||
{
|
||||
number_of_timers =
|
||||
(__nsi_hw_events_timers_end - __nsi_hw_events_timers_start);
|
||||
number_of_callbacks =
|
||||
(__nsi_hw_events_callbacks_end - __nsi_hw_events_callbacks_start);
|
||||
|
||||
/* LCOV_EXCL_START */
|
||||
if (number_of_timers != number_of_callbacks || number_of_timers == 0) {
|
||||
nsi_print_error_and_exit("number_of_timers corrupted\n");
|
||||
}
|
||||
/* LCOV_EXCL_STOP */
|
||||
|
||||
nsi_hws_set_sig_handler();
|
||||
nsi_hws_find_next_event();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to free any resources allocated by the HW scheduler
|
||||
*
|
||||
* Note that the HW models should register their initialization functions
|
||||
* as NSI_TASKS of ON_EXIT_PRE/POST levels.
|
||||
*/
|
||||
void nsi_hws_cleanup(void)
|
||||
{
|
||||
}
|
||||
37
scripts/native_simulator/common/src/nsi_internal.h
Normal file
37
scripts/native_simulator/common/src/nsi_internal.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef NSI_COMMON_SRC_NSI_INTERNAL_H
|
||||
#define NSI_COMMON_SRC_NSI_INTERNAL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief find least significant bit set in a 32-bit word
|
||||
*
|
||||
* This routine finds the first bit set starting from the least significant bit
|
||||
* in the argument passed in and returns the index of that bit. Bits are
|
||||
* numbered starting at 1 from the least significant bit. A return value of
|
||||
* zero indicates that the value passed is zero.
|
||||
*
|
||||
* @return least significant bit set, 0 if @a op is 0
|
||||
*/
|
||||
|
||||
static inline unsigned int nsi_find_lsb_set(uint32_t op)
|
||||
{
|
||||
return __builtin_ffs(op);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NSI_COMMON_SRC_NSI_INTERNAL_H */
|
||||
37
scripts/native_simulator/common/src/nsi_safe_call.h
Normal file
37
scripts/native_simulator/common/src/nsi_safe_call.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef NSI_COMMON_SRC_NSI_SAFE_CALLL_H
|
||||
#define NSI_COMMON_SRC_NSI_SAFE_CALLL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "nsi_tracing.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef nsi_unlikely
|
||||
#define nsi_unlikely(x) (__builtin_expect((bool)!!(x), false) != 0L)
|
||||
#endif
|
||||
|
||||
#define NSI_SAFE_CALL(a) nsi_safe_call(a, #a)
|
||||
|
||||
static inline void nsi_safe_call(int test, const char *test_str)
|
||||
{
|
||||
/* LCOV_EXCL_START */ /* See Note1 */
|
||||
if (nsi_unlikely(test)) {
|
||||
nsi_print_error_and_exit("Error on: %s\n",
|
||||
test_str);
|
||||
}
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NSI_COMMON_SRC_NSI_SAFE_CALLL_H */
|
||||
42
scripts/native_simulator/common/src/nsi_tasks.c
Normal file
42
scripts/native_simulator/common/src/nsi_tasks.c
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Run the set of special NSI tasks corresponding to the given level
|
||||
*
|
||||
* @param level One of NSITASK_*_LEVEL as defined in nsi_tasks.h
|
||||
*/
|
||||
void nsi_run_tasks(int level)
|
||||
{
|
||||
extern void (*__nsi_PRE_BOOT_1_tasks_start[])(void);
|
||||
extern void (*__nsi_PRE_BOOT_2_tasks_start[])(void);
|
||||
extern void (*__nsi_HW_INIT_tasks_start[])(void);
|
||||
extern void (*__nsi_PRE_BOOT_3_tasks_start[])(void);
|
||||
extern void (*__nsi_FIRST_SLEEP_tasks_start[])(void);
|
||||
extern void (*__nsi_ON_EXIT_PRE_tasks_start[])(void);
|
||||
extern void (*__nsi_ON_EXIT_POST_tasks_start[])(void);
|
||||
extern void (*__nsi_tasks_end[])(void);
|
||||
|
||||
static void (**nsi_pre_tasks[])(void) = {
|
||||
__nsi_PRE_BOOT_1_tasks_start,
|
||||
__nsi_PRE_BOOT_2_tasks_start,
|
||||
__nsi_HW_INIT_tasks_start,
|
||||
__nsi_PRE_BOOT_3_tasks_start,
|
||||
__nsi_FIRST_SLEEP_tasks_start,
|
||||
__nsi_ON_EXIT_PRE_tasks_start,
|
||||
__nsi_ON_EXIT_POST_tasks_start,
|
||||
__nsi_tasks_end
|
||||
};
|
||||
|
||||
void (**fptr)(void);
|
||||
|
||||
for (fptr = nsi_pre_tasks[level]; fptr < nsi_pre_tasks[level+1];
|
||||
fptr++) {
|
||||
if (*fptr) { /* LCOV_EXCL_BR_LINE */
|
||||
(*fptr)();
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user