microkernel: deprecate task IRQs

This mechanism does not add enough value to the kernel to be worth
maintaining it. Drivers that need deferred processing of interrupts
can simply define their own task and have the interrupt handler
release an event that the task waits on.

The API is marked as deprecated and it is removed from unit test
coverage as well as the documentation.

Change-Id: Ib87b91cb41e9b6d7fdf0dc62b240a531b6a8889f
Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
Andrew Boie 2016-03-28 14:11:44 -07:00 committed by Benjamin Walsh
commit a83f895dd5
18 changed files with 7 additions and 1965 deletions

View file

@ -16,4 +16,3 @@ nanokernel applications.
microkernel_memory
microkernel_synchronization
microkernel_data
microkernel_task_irqs.rst

View file

@ -1,115 +0,0 @@
.. _microkernel_task_irqs:
Interrupt Services
##################
Concepts
********
The microkernel's :dfn:`task IRQ` objects allow interrupts to be serviced
by tasks, rather than only by interrupt service routines (ISRs). These allow
a microkernel project to have both task-level device drivers and interrupt-level
device drivers.
Any number of task IRQs can be defined in a microkernel system. Each task IRQ
has a numeric identifier that uniquely identifies it. These identifiers range
from 0 to N-1, where N is the total number of task IRQs in the system.
A task that wants to service interrupts from a device must first allocate a
task IRQ and bind it to the device's interrupt source by specifying the IRQ
and interrupt priority level assigned to that device by the system designer.
Once a task IRQ has been allocated by a task, it cannot be used by other
tasks; this prevents other tasks from interfering with the proper processing
of interrupts from that device.
When an interrupt is generated by the device, the kernel runs an ISR that
masks the interrupt and signals the occurrence of the interrupt to the
associated task IRQ. The task can then use the task IRQ to recognize that
an interrupt has occurred and take action to service the interrupt. At some
point during interrupt servicing, the task must instruct the task IRQ to
acknowledge the interrupt; this causes the kernel to unmask the interrupt so
that future interrupts can be detected.
Purpose
*******
Use a task IRQ when the work required to process an interrupt cannot be done
in an ISR -- either because it takes a long time, or because it requires the
processing routine to block.
Usage
*****
Configuring Task IRQs
=====================
Set the :option:`MAX_NUM_TASK_IRQS` configuration option to specify the number
of task IRQs allowed in the project.
The default value of zero for this option disables task IRQs.
.. note::
Unlike most other microkernel object types, task-level IRQs are defined
as a group using a configuration option, rather than as individual
public objects in an MDEF or private objects in a source file.
Example: Allocating a Task IRQ
==============================
This code associates a task IRQ with interrupts generated by a device.
Interrupts from that device are then enabled so they can be processed
using the task IRQ.
.. code-block:: c
#define FOO_DEVICE 2 /* device "foo" uses task IRQ object 2 */
#define FOO_IRQ 37 /* device "foo" uses IRQ 37 */
#define FOO_PRIO 3 /* device "foo" uses interrupt priority 3 */
#define FOO_IRQ_FLAGS 0 /* device "foo" IRQ flags. Unused on non-x86 */
if (task_irq_alloc(FOO_DEVICE, FOO_IRQ, FOO_PRIO, FOO_IRQ_FLAGS) ==
INVALID_VECTOR) {
/* The task IRQ or the interrupt source is not available */
printf("Task IRQ allocation failed!");
}
Example: Servicing Interrupts using a Task IRQ
==============================================
This code allows a task to wait for an interrupt from a device,
acknowledge the interrupt, and take the necessary steps to service it.
.. code-block:: c
task_irq_wait(FOO_DEVICE, TICKS_UNLIMITED);
/* Device interrupt is now masked */
/* Do pre-acknowledgement device processing (if any) */
task_irq_ack(FOO_DEVICE);
/* Device interrupt is now unmasked */
/* Do post-acknowledgement device processing (if any) */
The steps required to service a device are device-specific. In some cases
all processing may need to be completed before the interrupt is acknowledged,
while in other cases no processing at all should be done until the interrupt
is acknowledged. Some devices may require processing both before and after
acknowledgement.
APIs
****
Task IRQ APIs provided by :file:`microkernel.h`
===============================================
:cpp:func:`task_irq_alloc()`
Bind a task IRQ to a device and enable interrupts.
:cpp:func:`task_irq_ack()`
Acknowledge an interrupt and re-enable the interrupt.
:cpp:func:`task_irq_wait()`
Wait for an interrupt to occur within a specified time period.

View file

@ -53,7 +53,7 @@ extern "C" {
* @return assigned interrupt vector if successful, INVALID_VECTOR if not
*/
extern uint32_t task_irq_alloc(kirq_t irq_obj, uint32_t irq, uint32_t priority,
uint32_t flags);
uint32_t flags) __attribute__((deprecated));
/**
*
@ -65,7 +65,7 @@ extern uint32_t task_irq_alloc(kirq_t irq_obj, uint32_t irq, uint32_t priority,
*
* @return N/A
*/
extern void task_irq_ack(kirq_t irq_obj);
extern void task_irq_ack(kirq_t irq_obj) __attribute__((deprecated));
/**
* @brief Wait for task IRQ to signal an interrupt.
@ -85,7 +85,8 @@ extern void task_irq_ack(kirq_t irq_obj);
* @a timeout = TICKS_NONE.
* @sa TICKS_NONE, TICKS_UNLIMITED
*/
extern int task_irq_wait(kirq_t irq_obj, int32_t timeout);
extern int task_irq_wait(kirq_t irq_obj, int32_t timeout)
__attribute__((deprecated));
/**
* @}
@ -95,4 +96,4 @@ extern int task_irq_wait(kirq_t irq_obj, int32_t timeout);
}
#endif
#endif /* TASK_IRQ_H */
#endif /* TASK_IRQ_H */

View file

@ -10,7 +10,5 @@ CONFIG_MICROKERNEL_SERVER_STACK_SIZE=4096
CONFIG_SYS_POWER_MANAGEMENT=y
CONFIG_MAX_NUM_TASK_IRQS=2
CONFIG_NUM_IRQS=43
CONFIG_SW_ISR_TABLE_DYNAMIC=y

View file

@ -10,9 +10,7 @@ CONFIG_NUM_TIMER_PACKETS=16
CONFIG_MICROKERNEL_SERVER_STACK_SIZE=4096
CONFIG_SYS_POWER_MANAGEMENT=y
CONFIG_MAX_NUM_TASK_IRQS=2
CONFIG_NUM_DYNAMIC_STUBS=2
CONFIG_FLOAT=y
CONFIG_SSE=y
CONFIG_FP_SHARING=y

View file

@ -8,6 +8,5 @@ CONFIG_NUM_COMMAND_PACKETS=64
CONFIG_NUM_TIMER_PACKETS=16
CONFIG_MICROKERNEL_SERVER_STACK_SIZE=4096
CONFIG_SYS_POWER_MANAGEMENT=y
CONFIG_MAX_NUM_TASK_IRQS=2
CONFIG_NUM_IRQS=43
CONFIG_SW_ISR_TABLE_DYNAMIC=y

View file

@ -9,5 +9,4 @@ CONFIG_NUM_COMMAND_PACKETS=64
CONFIG_NUM_TIMER_PACKETS=16
CONFIG_MICROKERNEL_SERVER_STACK_SIZE=4096
CONFIG_SYS_POWER_MANAGEMENT=y
CONFIG_MAX_NUM_TASK_IRQS=2
CONFIG_NUM_DYNAMIC_STUBS=2

View file

@ -61,10 +61,6 @@ static pfunc func_array[] = {
(pfunc)task_mem_map_alloc,
(pfunc)_task_mem_map_free,
#ifdef TEST_max
/* task device interrupt functions */
(pfunc)task_irq_alloc,
(pfunc)task_irq_wait,
(pfunc)task_irq_ack,
/* semaphore functions */
(pfunc)isr_sem_give,
(pfunc)task_sem_give,

View file

@ -1,6 +0,0 @@
MDEF_FILE = prj.mdef
KERNEL_TYPE = micro
BOARD ?= qemu_x86
CONF_FILE = prj_$(ARCH).conf
include ${ZEPHYR_BASE}/Makefile.inc

View file

@ -1,58 +0,0 @@
Title: Task Level Interrupt Handling
Description:
This test exercises the APIs of the task level interrupt handling feature.
--------------------------------------------------------------------------------
Building and Running Project:
This microkernel project outputs to the console. It can be built and executed
on QEMU as follows:
make qemu
--------------------------------------------------------------------------------
Troubleshooting:
Problems caused by out-dated project information can be addressed by
issuing one of the following commands then rebuilding the project:
make clean # discard results of previous builds
# but keep existing configuration info
or
make pristine # discard results of previous builds
# and restore pre-defined configuration info
--------------------------------------------------------------------------------
Sample Output:
Starting task level interrupt handling tests
===================================================================
IRQ object 0 using IRQ8 allocated
IRQ object 1 using IRQ14 allocated
IRQ object 2 using IRQ32 allocated
IRQ object 3 using IRQ34 allocated
Generating interrupts for all allocated IRQ objects...
Received event for IRQ object 0
Received event for IRQ object 1
Received event for IRQ object 2
Received event for IRQ object 3
Attempt to allocate an IRQ object that
is already allocated by another task...
Re-allocation of IRQ object 3 prevented
Attempt to allocate an IRQ that
is already allocated by another task...
Re-allocation of IRQ34 prevented
Attempt to free an IRQ object...
IRQ object 2 freed
===================================================================
PROJECT EXECUTION SUCCESSFUL

View file

@ -1,14 +0,0 @@
% Application : kernel task level device interrupt test
% TASK NAME PRIO ENTRY STACK GROUPS
% ===================================================
TASK MONITORTASK 10 MonitorTaskEntry 1024 [EXE]
TASK tTaskAMain 11 taskAMain 1024 [EXE]
TASK tTaskBMain 12 taskBMain 1024 [EXE]
TASK tRegisterWait 13 registerWait 1024 [EXE]
% SEMA NAME
% =================
SEMA SEM_TASKDONE
SEMA SEM_TASKFAIL
SEMA SEM_RDY

View file

@ -1,6 +0,0 @@
# Let stack canaries use non-random number generator.
# This option is NOT to be used in production code.
CONFIG_TEST_RANDOM_GENERATOR=y
CONFIG_SW_ISR_TABLE_DYNAMIC=y
CONFIG_MAX_NUM_TASK_IRQS=5
CONFIG_NUM_IRQS=4

View file

@ -1,5 +0,0 @@
# Let stack canaries use non-random number generator.
# This option is NOT to be used in production code.
CONFIG_TEST_RANDOM_GENERATOR=y
CONFIG_MAX_NUM_TASK_IRQS=5

View file

@ -1,3 +0,0 @@
ccflags-y += -I${srctree}/tests/include
obj-y = main.o raise_int.o test_device.o

View file

@ -1,147 +0,0 @@
/* main.c - main testing module */
/*
* Copyright (c) 2013-2014 Wind River Systems, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* DESCRIPTION
* This file contains the main testing module that invokes all the tests.
*/
#include <zephyr.h>
#include <tc_util.h>
/* One of the task IRQ objects will not be allocated */
#define NUM_TASK_IRQS CONFIG_MAX_NUM_TASK_IRQS - 1
#define NUM_TEST_TASKS 3 /* # of test tasks to monitor */
/* # ticks to wait for test completion */
#define TIMEOUT (60 * sys_clock_ticks_per_sec)
static ksem_t resultSems[] = { SEM_TASKDONE, SEM_TASKFAIL, ENDLIST };
static ksem_t rdySem = SEM_RDY;
#define NUM_OBJECTS 4
extern uint32_t irq_vectors[NUM_OBJECTS];
/**
*
* @brief Entry point for taskA
*
* This routine signals "task done" or "task fail", based on the return code of
* taskA.
*
* @return N/A
*/
void taskAMain(void)
{
extern int taskA(ksem_t semRdy);
task_sem_give(resultSems[taskA(rdySem)]);
}
/**
*
* @brief Entry point for taskB
*
* This routine signals "task done" or "task fail", based on the return code of
* taskB.
*
* @return N/A
*/
void taskBMain(void)
{
extern int taskB(ksem_t semRdy);
task_sem_give(resultSems[taskB(rdySem)]);
}
/**
*
* @brief Wait for devices to be registered and generate SW ints
*
* This routine waits for the tasks to indicate the IRQ objects are allocated and
* then generates SW interrupts for all IRQs. Signals "task done" if all task
* indicated the IRQs are allocated or signals "task fail"if not.
*
* @return N/A
*/
void registerWait(void)
{
extern void raiseInt(uint8_t id);
int tasksDone;
int irq_obj;
/* Wait for the 2 tasks to finish registering their IRQ objects*/
for (tasksDone = 0; tasksDone < NUM_TEST_TASKS - 1; tasksDone++) {
if (task_sem_take(SEM_RDY, TIMEOUT) != RC_OK) {
TC_ERROR("Monitor task timed out\n");
task_sem_give(resultSems[TC_FAIL]);
return;
}
}
TC_PRINT("Generating interrupts for all allocated IRQ objects...\n");
for (irq_obj = 0; irq_obj < NUM_OBJECTS; irq_obj++) {
if (irq_vectors[irq_obj] != INVALID_VECTOR) {
raiseInt((uint8_t)irq_vectors[irq_obj]);
}
}
task_sem_give(resultSems[TC_PASS]);
}
/**
*
* @brief Entry point for MonitorTask
*
* This routine keeps tabs on the progress of the tasks doing the actual testing
* and generates the final test case summary message.
*
* @return N/A
*/
void MonitorTaskEntry(void)
{
ksem_t result;
int tasksDone;
PRINT_DATA("Starting task level interrupt handling tests\n");
PRINT_LINE;
/*
* the various test tasks start executing automatically;
* wait for all tasks to complete or a failure to occur,
* then issue the appropriate test case summary message
*/
for (tasksDone = 0; tasksDone < NUM_TEST_TASKS; tasksDone++) {
result = task_sem_group_take(resultSems, TIMEOUT);
if (result != resultSems[TC_PASS]) {
if (result != resultSems[TC_FAIL]) {
TC_ERROR("Monitor task timed out\n");
}
TC_END_RESULT(TC_FAIL);
TC_END_REPORT(TC_FAIL);
return;
}
}
TC_END_RESULT(TC_PASS);
TC_END_REPORT(TC_PASS);
}

File diff suppressed because it is too large Load diff

View file

@ -1,185 +0,0 @@
/* test_device.c - APIs for testing task level interrupts */
/*
* Copyright (c) 2013-2014 Wind River Systems, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
DESCRIPTION
This module exercises the task level interrupt handling feature.
Each function allocates 2 IRQ objects and then tests for an event
associated with the IRQ. The taskA() function also attempts to allocate an IRQ
that has already been allocated by another task.
*/
#include <zephyr.h>
#include <misc/printk.h>
#include <tc_util.h>
#define DEV1_ID 0
#define DEV2_ID 1
#define DEV3_ID 2
#define DEV4_ID 3
#define DEV5_ID 4
#if defined(CONFIG_X86)
#define DEV1_IRQ 8
#define DEV2_IRQ 14
#define DEV3_IRQ 32
#define DEV4_IRQ 34
#elif defined(CONFIG_CPU_CORTEX_M3_M4)
#define DEV1_IRQ 0
#define DEV2_IRQ 1
#define DEV3_IRQ 2
#define DEV4_IRQ 3
#else
#error "Unknown target"
#endif
#define NUM_OBJECTS 4
uint32_t irq_vectors[NUM_OBJECTS] = {[0 ... (NUM_OBJECTS - 1)] = INVALID_VECTOR};
/**
*
* @brief First of 2 tasks to allocate IRQ objects and check for events
*
* This task allocates 2 IRQ objects with unique IRQs and then tests for an
* interrupt associated with those IRQs. The function then attempts to allocate
* a device that has already been allocate from taskB.
*
* @return TC_PASS, TC_FAIL
*/
int taskA(ksem_t semRdy)
{
irq_vectors[DEV1_ID] = task_irq_alloc(DEV1_ID, DEV1_IRQ, 1, 0);
if (irq_vectors[DEV1_ID] == INVALID_VECTOR) {
TC_ERROR("Not able to allocate IRQ object\n");
return TC_FAIL;
}
TC_PRINT("IRQ object %d using IRQ%d allocated\n", DEV1_ID, DEV1_IRQ);
irq_vectors[DEV2_ID] = task_irq_alloc(DEV2_ID, DEV2_IRQ, 2, 0);
if (irq_vectors[DEV2_ID] == INVALID_VECTOR) {
TC_ERROR("Not able to allocate IRQ object\n");
return TC_FAIL;
}
TC_PRINT("IRQ object %d using IRQ%d allocated\n\n", DEV2_ID, DEV2_IRQ);
/* Send semaphore to let loader know IRQ objects have been allocated */
task_sem_give(semRdy);
if (task_irq_wait(DEV1_ID, TICKS_UNLIMITED) != RC_OK) {
TC_ERROR("Not able to test IRQ object event\n");
return TC_FAIL;
}
TC_PRINT("Received event for IRQ object %d\n", DEV1_ID);
task_irq_ack(DEV1_ID);
if (task_irq_wait(DEV2_ID, TICKS_UNLIMITED) != RC_OK) {
TC_ERROR("Not able to test IRQ object event\n");
return TC_FAIL;
}
TC_PRINT("Received event for IRQ object %d\n", DEV2_ID);
task_irq_ack(DEV2_ID);
/* Wait for other task to receive its events */
(void)task_sem_take(semRdy, TICKS_UNLIMITED);
TC_PRINT("\nAttempt to allocate an IRQ object that\n");
TC_PRINT("is already allocated by another task...\n");
if (task_irq_alloc(DEV4_ID, DEV4_IRQ, 1, 0) != INVALID_VECTOR) {
TC_ERROR("Error: Was able to allocate\n\n");
return TC_FAIL;
}
TC_PRINT("Re-allocation of IRQ object %d prevented\n", DEV4_ID);
TC_PRINT("\nAttempt to allocate an IRQ that\n"
"is already allocated by another task...\n");
if (task_irq_alloc(DEV5_ID, DEV4_IRQ, 1, 0) != INVALID_VECTOR) {
TC_ERROR("Error: Was able to allocate\n\n");
return TC_FAIL;
}
TC_PRINT("Re-allocation of IRQ%d prevented\n\n", DEV4_IRQ);
/* Signal other task that we are done processing */
task_sem_give(semRdy);
return TC_PASS;
}
/**
*
* @brief Second of 2 tasks to allocate IRQ objects and check for events
*
* This task allocates 2 IRQ objects with unique IRQs and then tests for an
* interrupt associated with those IRQs. The function then frees an IRQ object
* using task_irq_free().
*
* @return TC_PASS, TC_FAIL
*/
int taskB(ksem_t semRdy)
{
irq_vectors[DEV3_ID] = task_irq_alloc(DEV3_ID, DEV3_IRQ, 1, 0);
if (irq_vectors[DEV3_ID] == INVALID_VECTOR) {
TC_ERROR("Not able to allocate IRQ object\n");
return TC_FAIL;
}
TC_PRINT("IRQ object %d using IRQ%d allocated\n", DEV3_ID, DEV3_IRQ);
irq_vectors[DEV4_ID] = task_irq_alloc(DEV4_ID, DEV4_IRQ, 1, 0);
if (irq_vectors[DEV4_ID] == INVALID_VECTOR) {
TC_ERROR("Not able to allocate IRQ object\n");
return TC_FAIL;
}
TC_PRINT("IRQ object %d using IRQ%d allocated\n\n", DEV4_ID, DEV4_IRQ);
/* Send semaphore to let loader/main know objects have been allocated */
task_sem_give(semRdy);
if (task_irq_wait(DEV3_ID, TICKS_UNLIMITED) != RC_OK) {
TC_ERROR("Not able to test IRQ object event\n");
return TC_FAIL;
}
TC_PRINT("Received event for IRQ object %d\n", DEV3_ID);
task_irq_ack(DEV3_ID);
if (task_irq_wait(DEV4_ID, TICKS_UNLIMITED) != RC_OK) {
TC_ERROR("Not able to test IRQ object event\n");
return TC_FAIL;
}
TC_PRINT("Received event for IRQ object %d\n", DEV4_ID);
task_irq_ack(DEV4_ID);
/* Signal other task that we are done receiving events */
task_sem_give(semRdy);
/*
* Wait for other task to finish processing. The signal just previously
* sent will not be seen here as the other task is a higher priority and
* will thus consume the signal first.
*/
(void)task_sem_take(semRdy, TICKS_UNLIMITED);
return TC_PASS;
}

View file

@ -1,3 +0,0 @@
[test]
tags = core