/* * 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 #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); }