board: posix: add native_posix board definition

Signed-off-by: Alberto Escolar Piedras <alpi@oticon.com>
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
This commit is contained in:
Anas Nashif 2017-12-21 08:14:19 -05:00 committed by Anas Nashif
parent 76f7644118
commit 6b55598ad4
30 changed files with 1645 additions and 3 deletions

View file

@ -18,3 +18,4 @@ under :file:`doc/templates/board.tmpl`
arc/index.rst
nios2/index.rst
xtensa/index.rst
posix/index.rst

10
boards/posix/index.rst Normal file
View file

@ -0,0 +1,10 @@
.. _boards_posix:
Native POSIX Boards
###################
.. toctree::
:maxdepth: 1
:glob:
**/*

View file

@ -0,0 +1,10 @@
zephyr_library()
zephyr_library_compile_definitions(_POSIX_CHEATS_H)
zephyr_library_sources(
hw_models_top.c
timer_model.c
irq_handler.c
irq_ctrl.c
main.c
tracing.c
)

View file

@ -0,0 +1,18 @@
if BOARD_NATIVE_POSIX
comment "Simple process (POSIX) Options"
config NATIVE_POSIX_SLOWDOWN_TO_REAL_TIME
bool "Slow down exectution to real time"
depends on BOARD_NATIVE_POSIX
default y
help
When selected the execution of the process will be slowed down to real time.
(if there is a lot of load it may be slower than real time)
If deselected, the process will run as fast as possible.
Note that this only decouples simulated time from real/wall time. In either
case the zephyr kernel and application cannot tell the diffence unless they
interact with some other driver/device which runs at real time.
endif

View file

@ -0,0 +1,10 @@
config BOARD_NATIVE_POSIX
bool "Posix simple process"
depends on SOC_POSIX
select NATIVE_POSIX_TIMER
help
Will produce a console Linux process which can be executed natively.
It provides some minimal needed models:
An interrupt controller, timer (system tick), and redirects kernel prints to
stdout.

View file

@ -0,0 +1,12 @@
/*
* Copyright (c) 2015 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __INC_BOARD_H
#define __INC_BOARD_H
#include <soc.h>
#endif /* __INC_BOARD_H */

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2013-2014 Wind River Systems, Inc.
* Copyright (c) 2017 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _BOARD_IRQ_H
#define _BOARD_IRQ_H
#include "sw_isr_table.h"
#include "zephyr/types.h"
#ifdef __cplusplus
extern "C" {
#endif
void _isr_declare(unsigned int irq_p, int flags, void isr_p(void *),
void *isr_param_p);
void _irq_priority_set(unsigned int irq, unsigned int prio, u32_t flags);
/**
* Configure a static interrupt.
*
* @param irq_p IRQ line number
* @param priority_p Interrupt priority
* @param isr_p Interrupt service routine
* @param isr_param_p ISR parameter
* @param flags_p IRQ options
*
* @return The vector assigned to this interrupt
*/
#define _ARCH_IRQ_CONNECT(irq_p, priority_p, isr_p, isr_param_p, flags_p) \
({ \
_isr_declare(irq_p, 0, isr_p, isr_param_p); \
_irq_priority_set(irq_p, priority_p, flags_p); \
irq_p; \
})
/**
* Configure a 'direct' static interrupt.
*
* See include/irq.h for details.
*/
#define _ARCH_IRQ_DIRECT_CONNECT(irq_p, priority_p, isr_p, flags_p) \
({ \
_isr_declare(irq_p, ISR_FLAG_DIRECT, (void (*)(void *))isr_p, NULL); \
_irq_priority_set(irq_p, priority_p, flags_p); \
irq_p; \
})
/**
* The return of "name(void)" is the indicaton of the interrupt
* (maybe) having caused a kernel decision to context switch
*
* Note that this convention is changed relative to the ARM and x86 archs
*/
#define _ARCH_ISR_DIRECT_DECLARE(name) \
static inline int name##_body(void); \
int name(void) \
{ \
int check_reschedule; \
check_reschedule = name##_body(); \
return check_reschedule; \
} \
static inline int name##_body(void)
#define _ARCH_ISR_DIRECT_PM()
#ifdef __cplusplus
}
#endif
#endif /* _BOARD_IRQ_H */

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2017 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file Extra definitions provided by the board to soc.h
*
* Background:
* The POSIC ARCH/SOC/board layering is different than in normal archs
* The "SOC" does not provide almost any of the typical SOC functionality
* but that is left for the "board" to define it
* Device code may rely on the soc.h defining some things (like the interrupts
* numbers)
* Therefore this file is included from the inf_clock soc.h to allow a board
* to define that kind of SOC related snippets
*/
#ifndef _POSIX_SP_BOARD_SOC_H
#define _POSIX_SP_BOARD_SOC_H
#ifdef __cplusplus
extern "C" {
#endif
#define TIMER_TICK_IRQ 0
/*
* This interrupt will awake the CPU if IRQs are not locked,
* This interrupt does not have an associated status bit or handler
*/
#define PHONY_WEAK_IRQ 0xFFFE
/*
* This interrupt will awake the CPU even if IRQs are locked,
* This interrupt does not have an associated status bit or handler
* (the lock is only ignored when the interrupt is raised from the HW models,
* SW threads should not try to use this)
*/
#define PHONY_HARD_IRQ 0xFFFF
#ifdef __cplusplus
}
#endif
#endif /* _POSIX_SP_BOARD_SOC_H */

View file

@ -0,0 +1,179 @@
.. _native_posix:
Native POSIX execution (native_posix)
#######################################
Overview
********
Using this board, a Zephyr application can be compiled together with
the Zephyr kernel, creating a normal console executable that runs as
a native application on the host OS, without emulation. Instead,
you use native host tools for compiling, debugging, and analyzing your
Zephyr application, eliminating the need for architecture-specific
target hardware in the early phases of development.
Host system dependencies
========================
This port is designed to run in POSIX compatible operating systems.
It has only been tested on Linux, but should also be compatible with macOS.
.. note::
You must have the 32-bit C library installed in your system
(in Ubuntu 16.04 install the gcc-multilib package)
Architecture
************
This board is based on the POSIX architecture port of Zephyr.
In this architecture each Zephyr thread is mapped to one POSIX pthread,
but only one of these pthreads executes at a time.
This architecture provides the same interface to the Kernel as other
architectures and is therefore transparent for the application.
This board does not try to emulate any particular embedded CPU or SOC.
The code is compiled natively for the host x86 system, as a 32-bit
binary assuming pointer and integer types are 32-bits wide.
To ensure determinism when the Zephyr code is running,
and to ease application debugging,
the issue of code execution speed is ignored.
The board uses a different time than real time: simulated time.
This simulated time is, in principle, not linked to the host time.
The Zephyr application sees the code executing as if the CPU were running at
an infinitely high clock, and fully decoupled from the underlying host CPU
speed.
No simulated time passes while the application or kernel code execute.
The CPU boot is emulated by creating the Zephyr initialization thread and
letting it run. This in turn may spawn more Zephyr threads.
Eventually the SW will run to completion, that is, it will set the CPU
back to sleep.
At this point, control is transferred back to the HW models and the simulation
time can be advanced.
When the HW models raise an interrupt, the CPU wakes back up: the interrupt
is handled, the SW runs until completion again, and control is
transferred back to the HW models, all in zero simulated time.
If the SW unmasks a pending interrupt while running, or triggers a SW
interrupt, the interrupt controller may raise the interrupt immediately
depending on interrupt priorities, masking, and locking state.
Normally the resulting executable runs fully decoupled from the real host time.
That is, simulated time will advance as fast as it can. This is desirable when
running in a debuger or testing in batch, but not if one wants to interact
with external interfaces which are based on the real host time.
Peripherals
***********
The following peripherals are currently provided with this board:
**Console/printk driver**:
A driver is provided that redirects any printk write to the native
host application's stdout.
**Simple timer**:
A simple timer provides the kernel with a 10ms tick.
This peripheral driver also provides the needed functionality for this
architecture-specific k_busy_wait().
This timer, may also be configured with NATIVE_POSIX_SLOWDOWN_TO_REAL_TIME
to slow down the execution to real host time.
This will provide the illusion that the simulated time is running at the same
speed as the real host time.
In reality, the timer will monitor how much real host time
has actually passed since boot, and when needed, the timer will pause
the execution before raising each timer interrupt.
Normally the Zephyr application and HW models run in very little time
on the host CPU, so this is a good enough approach.
**Interrupt controller**
A simple yet generic interrupt controller is provided. It can nest interrupts
and provides interrupt priorities. Interrupts can be individually masked or
unmasked. SW interrupts are also supported.
Important limitations
=====================
The assumption that simulated time can only pass while the CPU is sleeping
means that there can not be busy wait loops in the application code that
wait for something to happen without letting the CPU sleep.
If busy wait loops do exist, they will behave as infinite loops and
will stall the execution.
As simulated time does not pass while the CPU is running, it also means no HW
interrupts will interrupt the threads' execution unless the SW enables or
unmasks them.
This is intentional, as it provides a deterministic environment to develop and
debug.
But note that this may hide issues in the SW that may only be triggered in the
real platform.
This native port of Zephyr is not meant to, and could not possibly
help debug races between HW and SW, or similar timing related issues.
How to use it
*************
Compiling
=========
Specify the native_posix board target to build a native POSIX application:
.. zephyr-app-commands::
:zephyr-app: samples/hello_world
:board: native_posix
:goals: build
:compact:
Running
=======
The result of the compilation is an executable (zephyr.exe) placed in the
zephyr/ subdirectory of the build folder.
Run the zephyr.exe executable as you would any other Linux console application.
.. code-block:: console
$ zephyr/zephyr.exe
# Press Ctrl+C to exit
Note that the Zephyr kernel does not actually exit once the application is
finished. It simply goes into the idle loop forever.
Therefore you must stop the application manually (Ctrl+C in Linux).
Application tests using the ``ztest`` framework will exit after all
tests have completed.
If you want your application to gracefully finish when it reaches some point,
you may add a conditionally compiled (CONFIG_BOARD_NATIVE_POSIX) call to
``main_clean_up(exit_code)`` at that point.
Debugging
=========
Since the Zephyr executable is a native application, it can be debuged and
instrumented as any other native program. The program is compiled with debug
information, so it can be run directly in, for example, ``gdb`` or instrumented
with ``valgrind``.
Because the execution of your Zephyr application is fully deterministic
(there are no asynchronous or random components), you can execute the
code multiple times and get the exact same result and instrumenting the
code does not affect the execution of the code.

View file

@ -0,0 +1,140 @@
/*
* Copyright (c) 2017 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* Barebones HW model sufficient to run some of the sample apps
*/
#include <stdint.h>
#include "hw_models_top.h"
#include "timer_model.h"
#include "irq_ctrl.h"
#include "posix_board_if.h"
#include "posix_soc_if.h"
static u64_t device_time; /* The actual time as known by the device */
static u64_t end_of_time = NEVER; /* When will this device stop */
/* List of HW model timers: */
extern u64_t hw_timer_timer; /* When should this timer_model be called */
extern u64_t irq_ctrl_timer;
static enum { HWTIMER = 0, IRQCNT, NUMBER_OF_TIMERS, NONE }
next_timer_index = NONE;
static u64_t *Timer_list[NUMBER_OF_TIMERS] = {
&hw_timer_timer,
&irq_ctrl_timer
};
static u64_t next_timer_time;
static void hwm_sleep_until_next_timer(void)
{
if (next_timer_time >= device_time) {
device_time = next_timer_time;
} else {
posix_print_warning("next_timer_time corrupted (%"PRIu64"<= %"
PRIu64", timer idx=%i)\n",
next_timer_time,
device_time,
next_timer_index);
}
if (device_time >= end_of_time) {
posix_print_trace("\n\n\n\n\n\nAutostopped after %.3Lfs\n",
((long double)end_of_time)/1.0e6);
main_clean_up(0);
}
}
/**
* Find in between all timers which is the next one
* and update next_timer_* accordingly
*/
void hwm_find_next_timer(void)
{
next_timer_index = 0;
next_timer_time = *Timer_list[0];
for (unsigned int i = 1; i < NUMBER_OF_TIMERS ; i++) {
if (next_timer_time > *Timer_list[i]) {
next_timer_index = i;
next_timer_time = *Timer_list[i];
}
}
}
/**
* Entry point for the HW models
* The HW models execute in an infinite loop until terminated
*/
void hwm_main_loop(void)
{
while (1) {
hwm_sleep_until_next_timer();
switch (next_timer_index) {
case HWTIMER:
hwtimer_timer_reached();
break;
case IRQCNT:
hw_irq_ctrl_timer_triggered();
break;
default:
posix_print_error_and_exit(
"next_timer_index corrupted\n");
break;
}
hwm_find_next_timer();
}
}
/**
* Set the simulated time when the process will stop
*/
void hwm_set_end_of_time(u64_t new_end_of_time)
{
end_of_time = new_end_of_time;
}
/**
* Return the current time as known by the device
*/
u64_t hwm_get_time(void)
{
return device_time;
}
/**
* Function to initialize the HW models
*/
void hwm_init(void)
{
hwtimer_init();
hw_irq_ctrl_init();
hwm_find_next_timer();
}
/**
* Function to free any resources allocated by the HW models
* Note that this function needs to be designed so it is possible
* to call it more than once during cleanup
*/
void hwm_cleanup(void)
{
hwtimer_cleanup();
hw_irq_ctrl_cleanup();
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2017 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _NATIVE_POSIX_HW_MODELS_H
#define _NATIVE_POSIX_HW_MODELS_H
#include "zephyr/types.h"
#include <inttypes.h>
#ifdef __cplusplus
extern "C" {
#endif
#define NEVER UINT64_MAX
void hwm_main_loop(void);
void hwm_init(void);
void hwm_cleanup(void);
void hwm_set_end_of_time(u64_t new_end_of_time);
u64_t hwm_get_time(void);
void hwm_find_next_timer(void);
#ifdef __cplusplus
}
#endif
#endif /* _NATIVE_POSIX_HW_MODELS_H */

View file

@ -0,0 +1,272 @@
/*
* Copyright (c) 2017 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*
* HW IRQ controller model
*/
#include <stdbool.h>
#include "hw_models_top.h"
#include "irq_ctrl.h"
#include "irq_handler.h"
#include "arch/posix/arch.h" /* for find_lsb_set() */
#include "board_soc.h"
#include "posix_soc.h"
#include "zephyr/types.h"
u64_t irq_ctrl_timer = NEVER;
static u64_t irq_status; /* pending interrupts */
static u64_t irq_premask; /* interrupts before the mask */
/*
* Mask of which interrupts will actually cause the cpu to vector into its
* irq handler
* If an interrupt is masked in this way, it will be pending in the premask in
* case it is enabled later before clearing it.
* If the irq_mask enables and interrupt pending in irq_premask, it will cause
* the controller to raise the interrupt immediately
*/
static u64_t irq_mask;
/*
* Interrupts lock/disable. When set, interrupts are registered
* (in the irq_status) but do not awake the cpu. if when unlocked,
* irq_status != 0 an interrupt will be raised immediately
*/
static bool irqs_locked;
static bool lock_ignore; /* For the hard fake IRQ, temporarily ignore lock */
static u8_t irq_prio[N_IRQS]; /* Priority of each interrupt */
/* note that prio = 0 == highest, prio=255 == lowest */
static int currently_running_prio = 256; /* 255 is the lowest prio interrupt */
void hw_irq_ctrl_init(void)
{
irq_mask = 0; /* Let's assume all interrupts are disable at boot */
irq_premask = 0;
irqs_locked = false;
lock_ignore = false;
for (int i = 0 ; i < N_IRQS; i++) {
irq_prio[i] = 255;
}
}
void hw_irq_ctrl_cleanup(void)
{
/* Nothing to be done */
}
void hw_irq_ctrl_set_cur_prio(int new)
{
currently_running_prio = new;
}
int hw_irq_ctrl_get_cur_prio(void)
{
return currently_running_prio;
}
void hw_irq_ctrl_prio_set(unsigned int irq, unsigned int prio)
{
irq_prio[irq] = prio;
}
u8_t hw_irq_ctrl_get_prio(unsigned int irq)
{
return irq_prio[irq];
}
/**
* Get the currently pending highest priority interrupt which has a priority
* higher than a possibly currently running interrupt
*
* If none, return -1
*/
int hw_irq_ctrl_get_highest_prio_irq(void)
{
if (irqs_locked) {
return -1;
}
u64_t irq_status = hw_irq_ctrl_get_irq_status();
int winner = -1;
int winner_prio = 256;
while (irq_status != 0) {
int irq_nbr = find_lsb_set(irq_status) - 1;
irq_status &= ~((u64_t) 1 << irq_nbr);
if ((winner_prio > (int)irq_prio[irq_nbr])
&& (currently_running_prio > (int)irq_prio[irq_nbr])) {
winner = irq_nbr;
winner_prio = irq_prio[irq_nbr];
}
}
return winner;
}
u32_t hw_irq_ctrl_get_current_lock(void)
{
return irqs_locked;
}
u32_t hw_irq_ctrl_change_lock(u32_t new_lock)
{
u32_t previous_lock = irqs_locked;
irqs_locked = new_lock;
if ((previous_lock == true) && (new_lock == false)) {
if (irq_status != 0) {
posix_irq_handler_im_from_sw();
}
}
return previous_lock;
}
u64_t hw_irq_ctrl_get_irq_status(void)
{
return irq_status;
}
void hw_irq_ctrl_clear_all_enabled_irqs(void)
{
irq_status = 0;
irq_premask &= ~irq_mask;
}
void hw_irq_ctrl_clear_all_irqs(void)
{
irq_status = 0;
irq_premask = 0;
}
void hw_irq_ctrl_disable_irq(unsigned int irq)
{
irq_mask &= ~((u64_t)1<<irq);
}
int hw_irq_ctrl_is_irq_enabled(unsigned int irq)
{
return (irq_mask & ((u64_t)1 << irq))?1:0;
}
u64_t hw_irq_ctrl_get_irq_mask(void)
{
return irq_mask;
}
void hw_irq_ctrl_clear_irq(unsigned int irq)
{
irq_status &= ~((u64_t)1<<irq);
irq_premask &= ~((u64_t)1<<irq);
}
/**
* Enable an interrupt
*
* This function may only be called from SW threads
*
* If the enabled interrupt is pending, it will immediately vector to its
* interrupt handler and continue (maybe with some swap() before)
*/
void hw_irq_ctrl_enable_irq(unsigned int irq)
{
irq_mask |= ((u64_t)1<<irq);
if (irq_premask & ((u64_t)1<<irq)) { /* if IRQ is pending */
hw_irq_ctrl_raise_im_from_sw(irq);
}
}
static inline void hw_irq_ctrl_irq_raise_prefix(unsigned int irq)
{
if (irq < N_IRQS) {
irq_premask |= ((u64_t)1<<irq);
if (irq_mask & (1 << irq)) {
irq_status |= ((u64_t)1<<irq);
}
} else if (irq == PHONY_HARD_IRQ) {
lock_ignore = true;
}
}
/**
* Set/Raise an interrupt
*
* This function is meant to be used by either the SW manual IRQ raising
* or by HW which wants the IRQ to be raised in one delta cycle from now
*/
void hw_irq_ctrl_set_irq(unsigned int irq)
{
hw_irq_ctrl_irq_raise_prefix(irq);
if ((irqs_locked == false) || (lock_ignore)) {
/*
* Awake CPU in 1 delta
* Note that we awake the CPU even if the IRQ is disabled
* => we assume the CPU is always idling in a WFE() like
* instruction and the CPU is allowed to awake just with the irq
* being marked as pending
*/
irq_ctrl_timer = hwm_get_time();
hwm_find_next_timer();
}
}
static void irq_raising_from_hw_now(void)
{
/*
* We always awake the CPU even if the IRQ was masked,
* but not if irqs are locked unless this is due to a
* PHONY_HARD_IRQ
*/
if ((irqs_locked == false) || (lock_ignore)) {
lock_ignore = false;
posix_interrupt_raised();
}
}
/**
* Set/Raise an interrupt inmediately.
* Like hw_irq_ctrl_set_irq() but awake immediately the CPU instead of in
* 1 delta cycle
*
* Call only from HW threads
*/
void hw_irq_ctrl_raise_im(unsigned int irq)
{
hw_irq_ctrl_irq_raise_prefix(irq);
irq_raising_from_hw_now();
}
/**
* Like hw_irq_ctrl_raise_im() but for SW threads
*
* Call only from SW threads
*/
void hw_irq_ctrl_raise_im_from_sw(unsigned int irq)
{
hw_irq_ctrl_irq_raise_prefix(irq);
if (irqs_locked == false) {
posix_irq_handler_im_from_sw();
}
}
void hw_irq_ctrl_timer_triggered(void)
{
irq_ctrl_timer = NEVER;
irq_raising_from_hw_now();
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2017 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _NATIVE_POSIX_IRQ_CTRL_H
#define _NATIVE_POSIX_IRQ_CTRL_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
void hw_irq_ctrl_init(void);
void hw_irq_ctrl_cleanup(void);
void hw_irq_ctrl_timer_triggered(void);
void hw_irq_ctrl_set_cur_prio(int new);
int hw_irq_ctrl_get_cur_prio(void);
void hw_irq_ctrl_prio_set(unsigned int irq, unsigned int prio);
uint8_t hw_irq_ctrl_get_prio(unsigned int irq);
int hw_irq_ctrl_get_highest_prio_irq(void);
uint32_t hw_irq_ctrl_get_current_lock(void);
uint32_t hw_irq_ctrl_change_lock(uint32_t new_lock);
uint64_t hw_irq_ctrl_get_irq_status(void);
void hw_irq_ctrl_disable_irq(unsigned int irq);
int hw_irq_ctrl_is_irq_enabled(unsigned int irq);
void hw_irq_ctrl_clear_irq(unsigned int irq);
void hw_irq_ctrl_enable_irq(unsigned int irq);
void hw_irq_ctrl_set_irq(unsigned int irq);
void hw_irq_ctrl_raise_im(unsigned int irq);
void hw_irq_ctrl_raise_im_from_sw(unsigned int irq);
#define N_IRQS 32
#ifdef __cplusplus
}
#endif
#endif /* _NATIVE_POSIX_IRQ_CTRL_H */

View file

@ -0,0 +1,301 @@
/*
* Copyright (c) 2014 Wind River Systems, Inc.
* Copyright (c) 2017 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*
* SW side of the IRQ handling
*/
#include <stdint.h>
#include "irq_handler.h"
#include "irq_offload.h"
#include "kernel_structs.h"
#include "irq_ctrl.h"
#include "posix_core.h"
#include "board_soc.h"
#include "sw_isr_table.h"
#include "soc.h"
typedef void (*normal_irq_f_ptr)(void *);
typedef int (*direct_irq_f_ptr)(void);
typedef struct _isr_list isr_table_entry_t;
static isr_table_entry_t irq_vector_table[N_IRQS] = { { 0 } };
static int currently_running_irq = -1;
static inline void vector_to_irq(int irq_nbr, int *may_swap)
{
if (irq_vector_table[irq_nbr].func == NULL) {
posix_print_error_and_exit("Received irq %i without a "
"registered handler\n",
irq_nbr);
} else {
if (irq_vector_table[irq_nbr].flags & ISR_FLAG_DIRECT) {
*may_swap |= ((direct_irq_f_ptr)
irq_vector_table[irq_nbr].func)();
} else {
((normal_irq_f_ptr)irq_vector_table[irq_nbr].func)
(irq_vector_table[irq_nbr].param);
*may_swap = 1;
/*
* TODO: look into how this kind of ISRs are meant
* to be wrapped
*/
}
}
}
/**
* When an interrupt is raised, this function is called to handle it and, if
* needed, swap to a re-enabled thread
*
* Note that even that this function is executing in a Zephyr thread, it is
* effectively the model of the interrupt controller passing context to the IRQ
* handler and therefore its priority handling
*/
void posix_irq_handler(void)
{
uint64_t irq_lock;
int irq_nbr;
int may_swap = 0;
irq_lock = hw_irq_ctrl_get_current_lock();
if (irq_lock) {
/* "spurious" wakes can happen with interrupts locked */
return;
}
_kernel.nested++;
while ((irq_nbr = hw_irq_ctrl_get_highest_prio_irq()) != -1) {
int last_current_running_prio = hw_irq_ctrl_get_cur_prio();
int last_running_irq = currently_running_irq;
hw_irq_ctrl_set_cur_prio(hw_irq_ctrl_get_prio(irq_nbr));
hw_irq_ctrl_clear_irq(irq_nbr);
currently_running_irq = irq_nbr;
vector_to_irq(irq_nbr, &may_swap);
currently_running_irq = last_running_irq;
hw_irq_ctrl_set_cur_prio(last_current_running_prio);
}
_kernel.nested--;
/* Call swap if all the following is true:
* 1) may_swap was enabled
* 2) We are not nesting irq_handler calls (interrupts)
* 3) Current thread is preemptible
* 4) Next thread to run in the ready queue is not this thread
*/
if (may_swap &&
(hw_irq_ctrl_get_cur_prio() == 256) &&
(_current->base.preempt < _NON_PREEMPT_THRESHOLD) &&
(_kernel.ready_q.cache != _current)) {
__swap(irq_lock);
}
}
/**
* Thru this function the IRQ controller can raise an immediate interrupt which
* will interrupt the SW itself
* (this function should only be called from the HW model code, from SW threads)
*/
void posix_irq_handler_im_from_sw(void)
{
/*
* if a higher priority interrupt than the possibly currently running is
* pending we go immediately into irq_handler() to vector into its
* handler
*/
if (hw_irq_ctrl_get_highest_prio_irq() != -1) {
if (!posix_is_cpu_running()) {
posix_print_error_and_exit("programming error: "
"nrf_irq_cont_handler_irq_im_from_sw() "
"called from a HW model thread\n");
}
posix_irq_handler();
}
}
/**
* @brief Disable all interrupts on the CPU
*
* This routine disables interrupts. It can be called from either interrupt,
* task or fiber level. This routine returns an architecture-dependent
* lock-out key representing the "interrupt disable state" prior to the call;
* this key can be passed to irq_unlock() to re-enable interrupts.
*
* The lock-out key should only be used as the argument to the irq_unlock()
* API. It should never be used to manually re-enable interrupts or to inspect
* or manipulate the contents of the source register.
*
* This function can be called recursively: it will return a key to return the
* state of interrupt locking to the previous level.
*
* WARNINGS
* Invoking a kernel routine with interrupts locked may result in
* interrupts being re-enabled for an unspecified period of time. If the
* called routine blocks, interrupts will be re-enabled while another
* thread executes, or while the system is idle.
*
* The "interrupt disable state" is an attribute of a thread. Thus, if a
* fiber or task disables interrupts and subsequently invokes a kernel
* routine that causes the calling thread to block, the interrupt
* disable state will be restored when the thread is later rescheduled
* for execution.
*
* @return An architecture-dependent lock-out key representing the
* "interrupt disable state" prior to the call.
*
*/
unsigned int posix_irq_lock(void)
{
return hw_irq_ctrl_change_lock(true);
}
unsigned int _arch_irq_lock(void)
{
return posix_irq_lock();
}
/**
*
* @brief Enable all interrupts on the CPU
*
* This routine re-enables interrupts on the CPU. The @a key parameter is a
* board-dependent lock-out key that is returned by a previous invocation of
* board_irq_lock().
*
* This routine can be called from either interrupt, task or fiber level.
*
* @return N/A
*
*/
void posix_irq_unlock(unsigned int key)
{
hw_irq_ctrl_change_lock(key);
}
void _arch_irq_unlock(unsigned int key)
{
posix_irq_unlock(key);
}
void posix_irq_full_unlock(void)
{
hw_irq_ctrl_change_lock(false);
}
void _arch_irq_enable(unsigned int irq)
{
hw_irq_ctrl_enable_irq(irq);
}
void _arch_irq_disable(unsigned int irq)
{
hw_irq_ctrl_disable_irq(irq);
}
int _arch_irq_is_enabled(unsigned int irq)
{
return hw_irq_ctrl_is_irq_enabled(irq);
}
void _arch_isr_direct_header(void)
{
/* Nothing to be done */
}
int posix_get_current_irq(void)
{
return currently_running_irq;
}
/**
* Configure a static interrupt.
*
* _isr_declare will populate the interrupt table table with the interrupt's
* parameters, the vector table and the software ISR table.
*
* We additionally set the priority in the interrupt controller at
* runtime.
*
* @param irq_p IRQ line number
* @param flags [plug it directly (1), or as a SW managed interrupt (0)]
* @param isr_p Interrupt service routine
* @param isr_param_p ISR parameter
* @param flags_p IRQ options
*/
void _isr_declare(unsigned int irq_p, int flags, void isr_p(void *),
void *isr_param_p)
{
irq_vector_table[irq_p].irq = irq_p;
irq_vector_table[irq_p].func = isr_p;
irq_vector_table[irq_p].param = isr_param_p;
irq_vector_table[irq_p].flags = flags;
}
/*
* @internal
*
* @brief Set an interrupt's priority
*
* Lower values take priority over higher values.
*
* @return N/A
*/
void _irq_priority_set(unsigned int irq, unsigned int prio, uint32_t flags)
{
hw_irq_ctrl_prio_set(irq, prio);
}
/**
* Similar to ARM's NVIC_SetPendingIRQ
* set a pending IRQ from SW
*
* Note that this will interrupt immediately if the interrupt is not masked and
* IRQs are not locked, and this interrupt has higher priority than a possibly
* currently running interrupt
*/
void posix_sw_set_pending_IRQ(unsigned int IRQn)
{
hw_irq_ctrl_raise_im_from_sw(IRQn);
}
/**
* Similar to ARM's NVIC_ClearPendingIRQ
* clear a pending irq from SW
*/
void posix_sw_clear_pending_IRQ(unsigned int IRQn)
{
hw_irq_ctrl_clear_irq(IRQn);
}
/**
* @brief Run a function in interrupt context
*
* In this simple board, the function can just be run directly
*/
void irq_offload(irq_offload_routine_t routine, void *parameter)
{
_kernel.nested++;
routine(parameter);
_kernel.nested--;
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2017 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _NATIVE_POSIX_IRQ_HANDLER_H
#define _NATIVE_POSIX_IRQ_HANDLER_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
void posix_irq_handler_im_from_sw(void);
void posix_sw_set_pending_IRQ(unsigned int IRQn);
void posix_sw_clear_pending_IRQ(unsigned int IRQn);
#ifdef __cplusplus
}
#endif
#endif /* _NATIVE_POSIX_IRQ_HANDLER_H */

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2017 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* The basic principle of operation is:
* No asynchronous behavior, no undeterminism.
* If you run the same thing 20 times, you get exactly the same result 20
* times.
* It does not matter if you are running from console, or in a debugger
* and you go for lunch in the middle of the debug session.
*
* This is achieved as follows:
* The HW models run in their own simulated time. We do really not attempt
* to link ourselves to the actual real time / wall time of the machine as this
* would make execution undeterministic and debugging or instrumentation not
* really possible. Although we may slow the run to real time.
*/
#include <soc.h>
#include "hw_models_top.h"
#include <stdlib.h>
#include "misc/util.h"
#define STOP_AFTER_5_SECONDS 0
void main_clean_up(int exit_code)
{
static int max_exit_code;
max_exit_code = max(exit_code, max_exit_code);
/*
* posix_soc_clean_up may not return if this is called from a SW thread,
* but instead it would get main_clean_up() recalled again
* ASAP from the HW thread
*/
posix_soc_clean_up();
hwm_cleanup();
exit(exit_code);
}
/**
* This is the actual main for the Linux process,
* the Zephyr application main is renamed something else thru a define.
*
* Note that normally one wants to use this POSIX arch to be part of a
* simulation engine, with some proper HW models and whatnot
*
* This is just a very simple demo which is able to run some of the sample
* apps (hello world, synchronization, philosophers) and run the sanity-check
* regression
*/
int main(void)
{
hwm_init();
#if (STOP_AFTER_5_SECONDS)
hwm_set_end_of_time(5e6);
#endif
posix_boot_cpu();
hwm_main_loop();
return 0;
}

View file

@ -0,0 +1,8 @@
identifier: native_posix
name: Native POSIX port
type: native
arch: posix
toolchain:
- zephyr
testing:
default: true

View file

@ -0,0 +1,6 @@
CONFIG_ARCH_POSIX=y
CONFIG_SOC_INF_CLOCK=y
CONFIG_BOARD_NATIVE_POSIX=y
CONFIG_CONSOLE=y
CONFIG_SERIAL=y
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=1000000

View file

@ -0,0 +1,141 @@
/*
* Copyright (c) 2017 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* This file provides both a model of a simple HW timer and its driver
*
* If you want this timer model to slow down the execution to real time
* set (CONFIG_)NATIVE_POSIX_SLOWDOWN_TO_REAL_TIME
*/
#include <stdint.h>
#include "hw_models_top.h"
#include "irq_ctrl.h"
#include "board_soc.h"
#include "zephyr/types.h"
#include "posix_soc_if.h"
#include "misc/util.h"
u64_t hw_timer_timer;
u64_t hw_timer_tick_timer;
u64_t hw_timer_awake_timer;
static u64_t tick_p = 10000; /* period of the ticker */
static unsigned int silent_ticks;
#if (CONFIG_NATIVE_POSIX_SLOWDOWN_TO_REAL_TIME)
#include <time.h>
static u64_t Boot_time;
static struct timespec tv;
#endif
static void hwtimer_update_timer(void)
{
hw_timer_timer = min(hw_timer_tick_timer, hw_timer_awake_timer);
}
void hwtimer_init(void)
{
silent_ticks = 0;
hw_timer_tick_timer = tick_p;
hw_timer_awake_timer = NEVER;
hwtimer_update_timer();
#if (CONFIG_NATIVE_POSIX_SLOWDOWN_TO_REAL_TIME)
clock_gettime(CLOCK_MONOTONIC, &tv);
Boot_time = tv.tv_sec*1e6 + tv.tv_nsec/1000;
#endif
}
void hwtimer_cleanup(void)
{
}
static void hwtimer_tick_timer_reached(void)
{
#if (CONFIG_NATIVE_POSIX_SLOWDOWN_TO_REAL_TIME)
u64_t expected_realtime = Boot_time + hw_timer_tick_timer;
clock_gettime(CLOCK_MONOTONIC, &tv);
u64_t actual_real_time = tv.tv_sec*1e6 + tv.tv_nsec/1000;
int64_t diff = expected_realtime - actual_real_time;
if (diff > 0) { /* we need to slow down */
struct timespec requested_time;
struct timespec remaining;
requested_time.tv_sec = diff / 1e6;
requested_time.tv_nsec = (diff - requested_time.tv_sec*1e6)*1e3;
int s = nanosleep(&requested_time, &remaining);
if (s == -1) {
posix_print_trace("Interrupted or error\n");
}
}
#endif
hw_timer_tick_timer += tick_p;
hwtimer_update_timer();
if (silent_ticks > 0) {
silent_ticks -= 1;
} else {
hw_irq_ctrl_set_irq(TIMER_TICK_IRQ);
}
}
static void hwtimer_awake_timer_reached(void)
{
hw_timer_awake_timer = NEVER;
hwtimer_update_timer();
hw_irq_ctrl_set_irq(PHONY_HARD_IRQ);
}
void hwtimer_timer_reached(void)
{
u64_t Now = hw_timer_timer;
if (hw_timer_awake_timer == Now) {
hwtimer_awake_timer_reached();
}
if (hw_timer_tick_timer == Now) {
hwtimer_tick_timer_reached();
}
}
/**
* The timer HW will awake the CPU (without an interrupt) at least when <time>
* comes (it may awake it earlier)
*
* If there was a previous request for an earlier time, the old one will prevail
*
* This is meant for k_busy_wait() like functionality
*/
void hwtimer_wake_in_time(u64_t time)
{
if (hw_timer_awake_timer > time) {
hw_timer_awake_timer = time;
hwtimer_update_timer();
}
}
void hwtimer_set_silent_ticks(int sys_ticks)
{
silent_ticks = sys_ticks;
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2017 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _NATIVE_POSIX_TIMER_MODEL_H
#define _NATIVE_POSIX_TIMER_MODEL_H
#include "hw_models_top.h"
#ifdef __cplusplus
extern "C" {
#endif
void hwtimer_init(void);
void hwtimer_cleanup(void);
void hwtimer_timer_reached(void);
void hwtimer_wake_in_time(u64_t time);
void hwtimer_set_silent_ticks(int sys_ticks);
#ifdef __cplusplus
}
#endif
#endif /* _NATIVE_POSIX_TIMER_MODEL_H */

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2017 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* Functions to print errors and traces
*/
#include <stdlib.h> /* for exit */
#include <stdio.h> /* for printfs */
#include <stdarg.h> /* for va args */
#include "posix_board_if.h"
void posix_print_error_and_exit(const char *format, ...)
{
va_list variable_args;
va_start(variable_args, format);
vfprintf(stderr, format, variable_args);
va_end(variable_args);
main_clean_up(1);
}
void posix_print_warning(const char *format, ...)
{
va_list variable_args;
va_start(variable_args, format);
vfprintf(stderr, format, variable_args);
va_end(variable_args);
}
void posix_print_trace(const char *format, ...)
{
va_list variable_args;
va_start(variable_args, format);
vfprintf(stdout, format, variable_args);
va_end(variable_args);
}

View file

@ -225,3 +225,33 @@ rely on testing in the QEMU emulation environment only.
.. _GCC ARM Embedded: https://launchpad.net/gcc-arm-embedded
Running a Sample Application natively (POSIX OS)
================================================
It is also possible to compile some of the sample and test applications to run
as native process on a POSIX OS (e.g. Linux).
To be able to do this, remember to have installed the 32 bit libC if your OS is
natively 64bit.
To compile and run an application in this way, type:
.. code-block:: console
$ cd $ZEPHYR_BASE/samples/hello_world
$ mkdir build && cd build
$ cmake -DBOARD=native_posix ..
$ make
and then:
.. code-block:: console
$ make run
# or just:
$ zephyr/zephyr.exe
# Press Ctrl+C to exit
This executable can be instrumented like any other Linux process. For ex. with gdb
or valgrind.
Note that the native port is currently only tested in Linux.

View file

@ -54,7 +54,7 @@ Install the required packages in a Ubuntu host system with:
$ sudo apt-get install --no-install-recommends git cmake ninja-build gperf \
ccache doxygen dfu-util device-tree-compiler \
python3-ply python3-pip python3-setuptools xz-utils file make
python3-ply python3-pip python3-setuptools xz-utils file make gcc-multilib
Install the required packages in a Fedora host system with:

View file

@ -6,3 +6,4 @@ zephyr_sources_if_kconfig(ipm_console_sender.c)
zephyr_sources_if_kconfig(uart_pipe.c)
zephyr_sources_if_kconfig(telnet_console.c)
zephyr_sources_if_kconfig(xtensa_sim_console.c)
zephyr_sources_if_kconfig(native_posix_console.c)

View file

@ -198,6 +198,15 @@ config XTENSA_SIM_CONSOLE
help
Use simulator console to print messages.
config NATIVE_POSIX_CONSOLE
bool
prompt "Print to console"
depends on BOARD_NATIVE_POSIX
select CONSOLE_HAS_DRIVER
default y
help
Use native console to print messages.
config XTENSA_CONSOLE_INIT_PRIORITY
int
prompt "Init priority"
@ -206,5 +215,13 @@ config XTENSA_CONSOLE_INIT_PRIORITY
help
Device driver initialization priority.
config NATIVE_POSIX_CONSOLE_INIT_PRIORITY
int
prompt "Init priority"
default 60
depends on NATIVE_POSIX_CONSOLE
help
Device driver initialization priority.
source "drivers/console/Kconfig.telnet"
endif

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2017 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include "init.h"
/**
*
* @brief Initialize the driver that provides the printk output
*
* @return 0 if successful, otherwise failed.
*/
static int printk_init(struct device *arg)
{
ARG_UNUSED(arg);
/* 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);
extern void __printk_hook_install(int (*fn)(int));
__printk_hook_install(putchar);
return 0;
}
SYS_INIT(printk_init, PRE_KERNEL_1, CONFIG_NATIVE_POSIX_CONSOLE_INIT_PRIORITY);

View file

@ -8,3 +8,4 @@ zephyr_sources_if_kconfig( pulpino_timer.c)
zephyr_sources_if_kconfig( riscv_machine_timer.c)
zephyr_sources_if_kconfig( cortex_m_systick.c)
zephyr_sources_ifdef(CONFIG_XTENSA_TIMER xtensa_sys_timer.c)
zephyr_sources_if_kconfig( native_posix_timer.c)

View file

@ -176,6 +176,14 @@ config RISCV_MACHINE_TIMER
This module implements a kernel device driver for the generic RISCV machine
timer driver. It provides the standard "system clock driver" interfaces.
config NATIVE_POSIX_TIMER
bool "(POSIX) native_posix timer driver"
default y
depends on BOARD_NATIVE_POSIX
help
This module implements a kernel device driver for the native_posix HW timer
model
config XTENSA_TIMER
bool "Xtensa timer support"
depends on XTENSA
@ -241,4 +249,5 @@ config SYSTEM_CLOCK_INIT_PRIORITY
value for the system clock driver. As driver initialization might need
the clock to be running already, you should let the default value as it
is (0).
endmenu

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2017 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* Driver for the timer model of the POSIX native_posix board
* It provides the interfaces required by the kernel and the sanity testcases
* It also provides a custom k_busy_wait() which can be used with the
* POSIX arch and InfClock SOC
*/
#include "zephyr/types.h"
#include "irq.h"
#include "device.h"
#include "drivers/system_timer.h"
#include "timer_model.h"
#include "soc.h"
/**
* Return the current HW cycle counter
* (number of microseconds since boot in 32bits)
*/
u32_t _timer_cycle_get_32(void)
{
return hwm_get_time();
}
#ifdef CONFIG_TICKLESS_IDLE
void _timer_idle_enter(int32_t sys_ticks)
{
hwtimer_set_silent_ticks(sys_ticks);
}
void _timer_idle_exit(void)
{
hwtimer_set_silent_ticks(0);
}
#endif
static void sp_timer_isr(void *arg)
{
ARG_UNUSED(arg);
_sys_clock_tick_announce();
}
int _sys_clock_driver_init(struct device *device)
{
ARG_UNUSED(device);
IRQ_CONNECT(TIMER_TICK_IRQ, 1, sp_timer_isr, 0, 0);
irq_enable(TIMER_TICK_IRQ);
return 0;
}
#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
*/
void k_busy_wait(u32_t usec_to_wait)
{
u64_t time_end = hwm_get_time() + usec_to_wait;
while (hwm_get_time() < time_end) {
/*There may be wakes due to other interrupts*/
hwtimer_wake_in_time(time_end);
posix_halt_cpu();
}
}
#endif

View file

@ -17,7 +17,7 @@ mapping:
type: str
"type":
type: str
enum: [ 'mcu', 'qemu', 'sim', 'unit' ]
enum: [ 'mcu', 'qemu', 'sim', 'unit', 'native']
"arch":
type: str
"toolchain":
@ -25,7 +25,7 @@ mapping:
seq:
-
type: str
enum: [ 'gccarmemb', 'issm', 'xcc', 'zephyr', 'espressif']
enum: [ 'gccarmemb', 'issm', 'xcc', 'zephyr', 'espressif', 'host']
"ram":
type: int
"flash":