📄 synth_intr.c
字号:
//=============================================================================
//
// synth_intr.c
//
// Interrupt and clock code for the Linux synthetic target.
//
//=============================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 2005 eCosCentric Ltd
// Copyright (C) 2002 Bart Veer
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//=============================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): bartv
// Contributors: bartv, asl
// Date: 2001-03-30
// Purpose: Implement the interrupt subsystem for the synthetic target
//####DESCRIPTIONEND####
//=============================================================================
// sigprocmask handling.
//
// In the synthetic target interrupts and exceptions are based around
// POSIX sighandlers. When the clock ticks a SIGALRM signal is raised.
// When the I/O auxiliary wants to raise some other interrupt, it
// sends a SIGIO signal. When an exception occurs this results in
// signals like SIGILL and SIGSEGV. This implies an implementation
// where the VSR is the signal handler. Disabling interrupts would
// then mean using sigprocmask() to block certain signals, and
// enabling interrupts means unblocking those signals.
//
// However there are a few problems. One of these is performance: some
// bits of the system such as buffered tracing make very extensive use
// of enabling and disabling interrupts, so making a sigprocmask
// system call each time adds a lot of overhead. More seriously, there
// is a subtle discrepancy between POSIX signal handling and hardware
// interrupts. Signal handlers are expected to return, and then the
// system automatically passes control back to the foreground thread.
// In the process, the sigprocmask is manipulated before invoking the
// signal handler and restored afterwards. Interrupt handlers are
// different: it is quite likely that an interrupt results in another
// eCos thread being activated, so the signal handler does not
// actually return until the interrupted thread gets another chance to
// run.
//
// The second problem can be addressed by making the sigprocmask part
// of the thread state, saving and restoring it as part of a context
// switch (c.f. siglongjmp()). This matches quite nicely onto typical
// real hardware, where there might be a flag inside some control
// register that controls whether or not interrupts are enabled.
// However this adds more system calls to context switch overhead.
//
// The alternative approach is to implement interrupt enabling and
// disabling in software. The sigprocmask is manipulated only once,
// during initialization, such that certain signals are allowed
// through and others are blocked. When a signal is raised the signal
// handler will always be invoked, but it will decide in software
// whether or not the signal should be processed immediately. This
// alternative approach does not correspond particularly well with
// real hardware: effectively the VSR is always allowed to run.
// However for typical applications this will not really matter, and
// the performance gains outweigh the discrepancy.
//
// Nested interrupts and interrupt priorities can be implemented in
// software, specifically by manipulating the current mask of blocked
// interrupts. This is not currently implemented.
//
// At first glance it might seem that an interrupt stack could be
// implemented trivially using sigaltstack. This does not quite work:
// signal handlers do not always return immediately, so the system
// does not get a chance to clean up the signal handling stack. A
// separate interrupt stack is possible but would have to be
// implemented here, in software, e.g. by having the signal handler
// invoke the VSR on that stack. Unfortunately the system may have
// pushed quite a lot of state on to the current stack already when
// raising the signal, so things could get messy.
// ----------------------------------------------------------------------------
#include <pkgconf/hal.h>
#include <pkgconf/hal_synth.h>
// There are various dependencies on the kernel, e.g. how exceptions
// should be handled.
#include <pkgconf/system.h>
#ifdef CYGPKG_KERNEL
# include <pkgconf/kernel.h>
#endif
#include <cyg/infra/cyg_type.h> // base types
#include <cyg/infra/diag.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/hal/hal_io.h>
#include <cyg/infra/cyg_ass.h> // Assertions are safe in the synthetic target
#include "synth_protocol.h"
// ----------------------------------------------------------------------------
// Statics.
// The bogomips rating, used by HAL_DELAY_US()
int hal_bogomips;
// Are interrupts currently enabled?
volatile cyg_bool_t hal_interrupts_enabled = false;
// These flags are updated by the signal handler when a signal comes in
// and interrupts are disabled.
static volatile cyg_bool_t synth_sigio_pending = false;
static volatile cyg_bool_t synth_sigalrm_pending = false;
// The current VSR, to be invoked by the signal handler. This allows
// application code to install an alternative VSR, without that VSR
// having to check for interrupts being disabled and updating the
// pending flags. Effectively, the VSR is only invoked when interrupts
// are enabled.
static void (*synth_VSR)(void) = (void (*)(void)) 0;
// The current ISR status and mask registers, or rather software
// emulations thereof. These are not static since application-specific
// VSRs may want to examine/manipulate these. They are also not
// exported in any header file, forcing people writing such VSRs to
// know what they are doing.
volatile cyg_uint32 synth_pending_isrs = 0;
volatile cyg_uint32 synth_masked_isrs = 0xFFFFFFFF;
// The vector of interrupt handlers.
typedef struct synth_isr_handler {
cyg_ISR_t* isr;
CYG_ADDRWORD data;
CYG_ADDRESS obj;
cyg_priority_t pri;
} synth_isr_handler;
static synth_isr_handler synth_isr_handlers[CYGNUM_HAL_ISR_COUNT];
static void synth_alrm_sighandler(int);
static void synth_io_sighandler(int);
// ----------------------------------------------------------------------------
// Basic ISR and VSR handling.
// The default ISR handler. The system should never receive an interrupt it
// does not know how to handle.
static cyg_uint32
synth_default_isr(cyg_vector_t vector, cyg_addrword_t data)
{
CYG_UNUSED_PARAM(cyg_vector_t, vector);
CYG_UNUSED_PARAM(cyg_addrword_t, data);
CYG_FAIL("Default isr handler should never get invoked");
return CYG_ISR_HANDLED;
}
// The VSR is invoked
// 1) directly by a SIGALRM or SIGIO signal handler, if interrupts
// were enabled.
// 2) indirectly by hal_enable_interrupts(), if a signal happened
// while interrupts were disabled. hal_enable_interrupts()
// will have re-invoked the signal handler.
//
// On entry interrupts are disabled, and there should be one or more
// pending ISRs which are not masked off.
//
// The implementation is as per the HAL specification, where
// applicable.
static void
synth_default_vsr(void)
{
int isr_vector;
cyg_uint32 isr_result;
CYG_ASSERT(!hal_interrupts_enabled, "VSRs should only be invoked when interrupts are disabled");
CYG_ASSERT(0 != (synth_pending_isrs & ~synth_masked_isrs), "VSRs should only be invoked when an interrupt is pending");
// No need to save the cpu state. Either we are in a signal
// handler and the system has done that for us, or we are called
// synchronously via enable_interrupts.
// Increment the kernel scheduler lock, if the kernel is present.
// This prevents context switching while interrupt handling is in
// progress.
#ifdef CYGFUN_HAL_COMMON_KERNEL_SUPPORT
cyg_scheduler_lock();
#endif
// Do not switch to an interrupt stack - functionality is not
// implemented
// Do not allow nested interrupts - functionality is not
// implemented.
// Decode the actual external interrupt being delivered. This is
// determined from the pending and masked variables. Only one isr
// source can be handled here, since interrupt_end must be invoked
// with details of that interrupt. Multiple pending interrupts
// will be handled by a recursive call
HAL_LSBIT_INDEX(isr_vector, (synth_pending_isrs & ~synth_masked_isrs));
CYG_ASSERT((CYGNUM_HAL_ISR_MIN <= isr_vector) && (isr_vector <= CYGNUM_HAL_ISR_MAX), "ISR vector must be valid");
isr_result = (*synth_isr_handlers[isr_vector].isr)(isr_vector, synth_isr_handlers[isr_vector].data);
// Do not switch back from the interrupt stack, there isn't one.
// Interrupts were not enabled before, so they must be enabled
// now. This may result in a recursive invocation if other IRQs
// are still pending. The ISR should have either acknowledged or
// masked the current interrupt source, to prevent a recursive
// call for the current interrupt.
hal_enable_interrupts();
// Now call interrupt_end() with the result of the isr and the
// ISR's object This may return straightaway, or it may result in
// a context switch to another thread. In the latter case, when
// the current thread is reactivated we end up back here. The
// third argument should be a pointer to the saved state, but that
// is only relevant for thread-aware debugging which is not yet
// supported by the synthetic target.
{
extern void interrupt_end(cyg_uint32, CYG_ADDRESS, HAL_SavedRegisters*);
interrupt_end(isr_result, synth_isr_handlers[isr_vector].obj, (HAL_SavedRegisters*) 0);
}
// Restore machine state and return to the interrupted thread.
// That requires no effort here.
}
// Enabling interrupts. If a SIGALRM or SIGIO arrived at an inconvenient
// time, e.g. when already interacting with the auxiliary, then these
// will have been left pending and must be serviced now. Next, enabling
// interrupts means checking the interrupt pending and mask registers
// and seeing if the VSR should be invoked.
void
hal_enable_interrupts(void)
{
hal_interrupts_enabled = true;
if (synth_sigalrm_pending) {
synth_sigalrm_pending = false;
synth_alrm_sighandler(CYG_HAL_SYS_SIGALRM);
}
if (synth_sigio_pending) {
synth_sigio_pending = false;
synth_io_sighandler(CYG_HAL_SYS_SIGIO);
}
// The interrupt mask "register" may have been modified while
// interrupts were disabled. If there are pending interrupts,
// invoke the VSR. The VSR must be invoked with interrupts
// disabled, and will return with interrupts enabled.
// An alternative implementation that might be more accurate
// is to raise a signal, e.g. SIGUSR1. That way all interrupts
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -