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:
parent
76f7644118
commit
6b55598ad4
|
@ -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
10
boards/posix/index.rst
Normal file
|
@ -0,0 +1,10 @@
|
|||
.. _boards_posix:
|
||||
|
||||
Native POSIX Boards
|
||||
###################
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:glob:
|
||||
|
||||
**/*
|
10
boards/posix/native_posix/CMakeLists.txt
Normal file
10
boards/posix/native_posix/CMakeLists.txt
Normal 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
|
||||
)
|
18
boards/posix/native_posix/Kconfig
Normal file
18
boards/posix/native_posix/Kconfig
Normal 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
|
10
boards/posix/native_posix/Kconfig.board
Normal file
10
boards/posix/native_posix/Kconfig.board
Normal 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.
|
12
boards/posix/native_posix/board.h
Normal file
12
boards/posix/native_posix/board.h
Normal 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 */
|
76
boards/posix/native_posix/board_irq.h
Normal file
76
boards/posix/native_posix/board_irq.h
Normal 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 */
|
46
boards/posix/native_posix/board_soc.h
Normal file
46
boards/posix/native_posix/board_soc.h
Normal 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 */
|
179
boards/posix/native_posix/doc/board.rst
Normal file
179
boards/posix/native_posix/doc/board.rst
Normal 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.
|
140
boards/posix/native_posix/hw_models_top.c
Normal file
140
boards/posix/native_posix/hw_models_top.c
Normal 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();
|
||||
}
|
||||
|
||||
|
30
boards/posix/native_posix/hw_models_top.h
Normal file
30
boards/posix/native_posix/hw_models_top.h
Normal 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 */
|
272
boards/posix/native_posix/irq_ctrl.c
Normal file
272
boards/posix/native_posix/irq_ctrl.c
Normal 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();
|
||||
}
|
||||
|
||||
|
45
boards/posix/native_posix/irq_ctrl.h
Normal file
45
boards/posix/native_posix/irq_ctrl.h
Normal 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 */
|
301
boards/posix/native_posix/irq_handler.c
Normal file
301
boards/posix/native_posix/irq_handler.c
Normal 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--;
|
||||
}
|
26
boards/posix/native_posix/irq_handler.h
Normal file
26
boards/posix/native_posix/irq_handler.h
Normal 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 */
|
71
boards/posix/native_posix/main.c
Normal file
71
boards/posix/native_posix/main.c
Normal 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;
|
||||
|
||||
}
|
8
boards/posix/native_posix/native_posix.yaml
Normal file
8
boards/posix/native_posix/native_posix.yaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
identifier: native_posix
|
||||
name: Native POSIX port
|
||||
type: native
|
||||
arch: posix
|
||||
toolchain:
|
||||
- zephyr
|
||||
testing:
|
||||
default: true
|
6
boards/posix/native_posix/native_posix_defconfig
Normal file
6
boards/posix/native_posix/native_posix_defconfig
Normal 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
|
141
boards/posix/native_posix/timer_model.c
Normal file
141
boards/posix/native_posix/timer_model.c
Normal 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;
|
||||
}
|
||||
|
||||
|
26
boards/posix/native_posix/timer_model.h
Normal file
26
boards/posix/native_posix/timer_model.h
Normal 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 */
|
44
boards/posix/native_posix/tracing.c
Normal file
44
boards/posix/native_posix/tracing.c
Normal 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);
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
35
drivers/console/native_posix_console.c
Normal file
35
drivers/console/native_posix_console.c
Normal 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);
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
77
drivers/timer/native_posix_timer.c
Normal file
77
drivers/timer/native_posix_timer.c
Normal 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
|
|
@ -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":
|
||||
|
|
Loading…
Reference in a new issue