-
Notifications
You must be signed in to change notification settings - Fork 6.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'zephyrproject-rtos:main' into main
- Loading branch information
Showing
395 changed files
with
8,088 additions
and
2,384 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* | ||
* 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 */ | ||
|
||
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 */ |
Oops, something went wrong.