blob: 53d7d90c50ccdfcd818eb4b3a1ce0e7e135ac087 [file] [log] [blame]
/*
* Copyright (c) 2017 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* For all purposes, Zephyr threads see a CPU running at an infinitely high
* clock.
*
* Therefore, the code will always run until completion after each interrupt,
* after which arch_cpu_idle() will be called releasing the execution back to
* the HW models.
*
* The HW models raising an interrupt will "awake the cpu" by calling
* posix_interrupt_raised() which will transfer control to the irq handler,
* which will run inside SW/Zephyr context. After which a arch_swap() to
* whatever Zephyr thread may follow. Again, once Zephyr is done, control is
* given back to the HW models.
*
* The Zephyr OS+APP code and the HW models are gated by a mutex +
* condition as there is no reason to let the zephyr threads run while the
* HW models run or vice versa
*
*/
#include <zephyr/arch/posix/posix_soc_if.h>
#include "posix_soc.h"
#include "posix_board_if.h"
#include "posix_core.h"
#include "posix_arch_internal.h"
#include "kernel_internal.h"
#include "soc.h"
#include "nce_if.h"
static void *nce_st;
int posix_is_cpu_running(void)
{
return nce_is_cpu_running(nce_st);
}
/**
* 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.
*/
void posix_change_cpu_state_and_wait(bool halted)
{
if (halted) {
nce_halt_cpu(nce_st);
} else {
nce_wake_cpu(nce_st);
}
}
/**
* HW models shall call this function to "awake the CPU"
* when they are raising an interrupt
*/
void posix_interrupt_raised(void)
{
/* We change the CPU to running state (we awake it), and block this
* thread until the CPU is halted again
*/
nce_wake_cpu(nce_st);
}
/**
* Normally called from arch_cpu_idle():
* the idle loop will call this function to set the CPU to "sleep".
* Others may also call this function with care. The CPU will be set to sleep
* until some interrupt awakes it.
* Interrupts should be enabled before calling.
*/
void posix_halt_cpu(void)
{
/*
* We set the CPU in the halted state (this blocks this pthread
* until the CPU is awoken again by the HW models)
*/
nce_halt_cpu(nce_st);
/* We are awoken, normally that means some interrupt has just come
* => let the "irq handler" check if/what interrupt was raised
* and call the appropriate irq handler.
*
* Note that, the interrupt handling may trigger a arch_swap() to
* another Zephyr thread. When posix_irq_handler() returns, the Zephyr
* kernel has swapped back to this thread again
*/
posix_irq_handler();
/*
* And we go back to whatever Zephyr thread called us.
*/
}
/**
* Implementation of arch_cpu_atomic_idle() for this SOC
*/
void posix_atomic_halt_cpu(unsigned int imask)
{
posix_irq_full_unlock();
posix_halt_cpu();
posix_irq_unlock(imask);
}
/**
* The HW models will call this function to "boot" the CPU
* == spawn the Zephyr init thread, which will then spawn
* anything it wants, and run until the CPU is set back to idle again
*/
void posix_boot_cpu(void)
{
nce_st = nce_init();
posix_arch_init();
nce_boot_cpu(nce_st, z_cstart);
}
/**
* Clean up all memory allocated by the SOC and POSIX core
*
* This function can be called from both HW and SW threads
*/
void posix_soc_clean_up(void)
{
nce_terminate(nce_st);
posix_arch_clean_up();
run_native_tasks(_NATIVE_ON_EXIT_LEVEL);
}