From 65280ba602609b3d79da2ee1c21f166525ecb7ed Mon Sep 17 00:00:00 2001 From: patacongo Date: Fri, 15 Feb 2008 19:54:58 +0000 Subject: [PATCH] Add lazay interrupt context saving logic for ez8 git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@687 42af7a65-404d-4744-a932-0658087f49c3 --- arch/z80/include/z8/irq.h | 6 +- arch/z80/include/z8/types.h | 2 +- arch/z80/src/z8/Make.defs | 2 +- arch/z80/src/z8/switch.h | 131 +++++++++++++++--- arch/z80/src/z8/{z8_copystate.c => z8_irq.c} | 43 ++++-- arch/z80/src/z8/z8_registerdump.c | 70 ++++++++-- arch/z80/src/z8/z8_saveirqcontext.c | 134 +++++++++++++++++++ arch/z80/src/z80/switch.h | 8 +- arch/z80/src/z80/z80_irq.c | 1 + 9 files changed, 344 insertions(+), 53 deletions(-) rename arch/z80/src/z8/{z8_copystate.c => z8_irq.c} (78%) create mode 100644 arch/z80/src/z8/z8_saveirqcontext.c diff --git a/arch/z80/include/z8/irq.h b/arch/z80/include/z8/irq.h index 962ed5662a..7e8dbf699e 100644 --- a/arch/z80/include/z8/irq.h +++ b/arch/z80/include/z8/irq.h @@ -235,7 +235,7 @@ #define XCPT_RR8 (4) #define XCPT_RR10 (5) #define XCPT_RR12 (6) -#define XCPT_R1R4 (7) +#define XCPT_RR14 (7) #define XCPT_IRQCTL (8) /* Index 8: IRQCTL register */ #define XCPT_SP (9) /* Index 9: SP[8:15] */ #define XCPT_RPFLAGS (10) /* Index 10: RP (MS) and FLAGS (LS) */ @@ -259,8 +259,8 @@ #define XCPT_R11_OFFS (2*XCPT_RR10+1) #define XCPT_R12_OFFS (2*XCPT_RR12) #define XCPT_R13_OFFS (2*XCPT_RR12+1) -#define XCPT_R14_OFFS (2*XCPT_R1R4) -#define XCPT_R15_OFFS (2*XCPT_R1R4+1) +#define XCPT_R14_OFFS (2*XCPT_RR14) +#define XCPT_R15_OFFS (2*XCPT_RR14+1) #define XCPT_UNUSED_OFFS (2*XCPT_IRQCTL) /* Offset 16: Unused (zero) */ #define XCPT_IRQCTL_OFFS (2*XCPT_IRQCTL+1) /* offset 17: IRQCTL register */ #define XCPT_SPH_OFFS (2*XCPT_SP) /* Offset 18: SP[8:15] */ diff --git a/arch/z80/include/z8/types.h b/arch/z80/include/z8/types.h index 97cb72d98d..5cf8f13414 100644 --- a/arch/z80/include/z8/types.h +++ b/arch/z80/include/z8/types.h @@ -82,7 +82,7 @@ typedef unsigned long uint32; /* This is the size of the interrupt state save returned by irqsave() */ -typedef uint16 irqstate_t; +typedef ubyte irqstate_t; #endif /* __ASSEMBLY__ */ diff --git a/arch/z80/src/z8/Make.defs b/arch/z80/src/z8/Make.defs index 328f785bcb..25b4d51a8f 100644 --- a/arch/z80/src/z8/Make.defs +++ b/arch/z80/src/z8/Make.defs @@ -43,6 +43,6 @@ CMN_CSRCS = up_initialize.c up_allocateheap.c up_createstack.c \ up_mdelay.c up_udelay.c up_usestack.c CHIP_SSRCS = z8_vector.S z8_saveusercontext.S z8_restorecontext.S -CHIP_CSRCS = z8_initialstate.c z8_copystate.c z8_irq.c \ +CHIP_CSRCS = z8_initialstate.c z8_irq.c z8_saveirqcontext.c \ z8_schedulesigaction.c z8_sigdeliver.c z8_registerdump.c diff --git a/arch/z80/src/z8/switch.h b/arch/z80/src/z8/switch.h index ff921858e4..2bd452e4e1 100644 --- a/arch/z80/src/z8/switch.h +++ b/arch/z80/src/z8/switch.h @@ -50,65 +50,154 @@ * Definitions ************************************************************************************/ -/* Macros for portability */ +/* Z8_IRQSTATE_* definitions ******************************************************** + * These are used in the state field of 'struct z8_irqstate_s' structure to define + * the current state of the interrupt handling. These definition support "lazy" + * interrupt context saving. See comments below associated with s'truct z8_irqstate_s'. + */ + +#define Z8_IRQSTATE_NONE 0 /* Not handling an interrupt */ +#define Z8_IRQSTATE_ENTRY 1 /* In interrupt, context has not been saved */ +#define Z8_IRQSTATE_SAVED 2 /* In interrupt, context has been saved */ + +/* The information saved on interrupt entry can be retained in a array of two + * uint16 values. These are + * + * value[0] = RP (MS byte) and Flags (LS) byte + * value[1] = PC + * + * The pointer to the save structure is a stack pointer at the time that up_doirq() + * was called: + * + * PC[7:0] + * PC[15:8] + * Flags Register + * SP -> RP + * + * The stack pointer on return from interrupt can be obtained by adding 4 to the + * pointer to the save structure. + */ + +#define Z8_IRQSAVE_RPFLAGS (0) /* Index 10: RP (MS) and FLAGS (LS) */ +#define Z8_IRQSAVE_PC (1) /* Index 2: PC[8:15] */ +#define Z8_IRQSAVE_REGS (2) /* Number 16-bit values saved */ + +/* Byte offsets */ + +#define Z8_IRQSAVE_RP_OFFS (2*Z8_IRQSAVE_RPFLAGS) /* Offset 0: RP */ +#define Z8_IRQSAVE_FLAGS_OFFS (2*Z8_IRQSAVE_RPFLAGS+1) /* Offset 1: FLAGS */ +#define Z8_IRQSAVE_PCH_OFFS (2*Z8_IRQSAVE_PC) /* Offset 2: PC[8:15] */ +#define Z8_IRQSAVE_PCL_OFFS (2*Z8_IRQSAVE_PC+1) /* Offset 3: PC[0:7] */ +#define Z8_IRQSAVE_SIZE (2*Z8_IRQSAVE_REGS) /* Number 8-bit values saved */ + +/* Macros for portability *********************************************************** + * + * Common logic in arch/z80/src/common is customized for the z8 context switching + * logic via the following macros. + */ /* Initialize the IRQ state */ -#define INIT_IRQCONTEXT() current_regs = NULL +#define INIT_IRQCONTEXT() \ + do { \ + g_z8irqstate.state = Z8_IRQSTATE_NONE; \ + } while (0) /* IN_INTERRUPT returns TRUE if the system is current operating in the interrupt * context. IN_INTERRUPT is the inline equivalent of up_interrupt_context(). */ -#define IN_INTERRUPT() (current_regs != NULL) +#define IN_INTERRUPT() \ + (g_z8irqstate.state != Z8_IRQSTATE_NONE) /* The following macro is used when the system enters interrupt handling logic */ -#define IRQ_ENTER(irq, regs) current_regs = (regs) +#define IRQ_ENTER(irq, regs) \ + do { \ + g_z8irqstate.state = Z8_IRQSTATE_ENTRY; \ + g_z8irqstate.regs = (regs); \ + } while (0) /* The following macro is used when the system exits interrupt handling logic */ -#define IRQ_LEAVE(irq) current_regs = NULL +#define IRQ_LEAVE(irq) \ + do { \ + g_z8irqstate.state = Z8_IRQSTATE_NONE; \ + } while (0) /* The following macro is used to sample the interrupt state (as a opaque handle) */ -#define IRQ_STATE() (current_regs) +#define IRQ_STATE() \ + (g_z8irqstate.regs) /* Save the current IRQ context in the specified TCB */ -#define SAVE_IRQCONTEXT(tcb) z8_copystate((tcb)->xcp.regs, current_regs) +#define SAVE_IRQCONTEXT(tcb) \ + z8_saveirqcontext((tcb)->xcp.regs) /* Set the current IRQ context to the state specified in the TCB */ -#define SET_IRQCONTEXT(tcb) z8_copystate(current_regs, (tcb)->xcp.regs) +#define SET_IRQCONTEXT(tcb) \ + do { \ + g_z8irqstate.state = Z8_IRQSTATE_SAVED; \ + g_z8irqstate.regs = (tcb)->xcp.regs; \ + } while (0) /* Save the user context in the specified TCB. User context saves can be simpler * because only those registers normally saved in a C called need be stored. */ -#define SAVE_USERCONTEXT(tcb) z8_saveusercontext((tcb)->xcp.regs) +#define SAVE_USERCONTEXT(tcb) \ + z8_saveusercontext((tcb)->xcp.regs) /* Restore the full context -- either a simple user state save or the full, * IRQ state save. */ -#define RESTORE_USERCONTEXT(tcb) z8_restorecontext((tcb)->xcp.regs) +#define RESTORE_USERCONTEXT(tcb) \ + z8_restorecontext((tcb)->xcp.regs) /* Dump the current machine registers */ -#define _REGISTER_DUMP() z8_registerdump() +#define _REGISTER_DUMP() \ + z8_registerdump() + +/************************************************************************************ + * Public Types + ************************************************************************************/ + +/* In order to provide faster interrupt handling, the interrupt logic does "lazy" + * context saving as described below: + * + * (1) At the time of the interrupt, minimum information is saved and the register + * pointer is changed so that the interrupt logic does not alter the state of + * the interrupted task's registers. + * (2) If no context switch occurs during the interrupt processing, then the return + * from interrupt is also simple. + * (3) If a context switch occurs during interrupt processing, then + * (a) The full context of the interrupt task is saved, and + * (b) A full context switch is performed when the interrupt exits (see + * z8_vector.S). + * + * The following structure is used to manage this "lazy" context saving. + */ + +#ifndef __ASSEMBLY__ +struct z8_irqstate_s +{ + ubyte state; /* See Z8_IRQSTATE_* definitions above */ + chipreg_t *regs; /* Saved register information */ +}; +#endif /************************************************************************************ * Public Variables ************************************************************************************/ #ifndef __ASSEMBLY__ -/* This holds a references to the current interrupt level - * register storage structure. If is non-NULL only during - * interrupt processing. - */ +/* This structure holds information about the current interrupt processing state */ -extern chipreg_t *current_regs; +extern struct z8_irqstate_s g_z8irqstate; #endif /************************************************************************************ @@ -123,17 +212,17 @@ extern "C" { #define EXTERN extern #endif -/* Defined in z8_copystate.c */ - -EXTERN void z8_copystate(FAR chipreg_t *dest, FAR const chipreg_t *src); - /* Defined in z8_saveusercontext.asm */ EXTERN int z8_saveusercontext(FAR chipreg_t *regs); +/* Defined in z8_saveirqcontext.c */ + +EXTERN void z8_saveirqcontext(FAR chipreg_t *regs); + /* Defined in z8_restorecontext.asm */ -EXTERN int z8_restorecontext(FAR chipreg_t *regs); +EXTERN void z8_restorecontext(FAR chipreg_t *regs); /* Defined in z8_sigsetup.c */ diff --git a/arch/z80/src/z8/z8_copystate.c b/arch/z80/src/z8/z8_irq.c similarity index 78% rename from arch/z80/src/z8/z8_copystate.c rename to arch/z80/src/z8/z8_irq.c index 881f0a4a50..d3a46bf178 100644 --- a/arch/z80/src/z8/z8_copystate.c +++ b/arch/z80/src/z8/z8_irq.c @@ -1,5 +1,5 @@ /**************************************************************************** - * arch/z80/src/z8/z8_copystate.c + * arch/z80/src/z8/z8_irq.c * * Copyright (C) 2008 Gregory Nutt. All rights reserved. * Author: Gregory Nutt @@ -40,16 +40,25 @@ #include #include -#include + +#include +#include #include "chip/switch.h" -#include "os_internal.h" #include "up_internal.h" /**************************************************************************** - * Definitions + * Private Definitions ****************************************************************************/ +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* This structure holds information about the current interrupt processing state */ + +struct z8_irqstate_s g_z8irqstate; + /**************************************************************************** * Private Data ****************************************************************************/ @@ -63,17 +72,25 @@ ****************************************************************************/ /**************************************************************************** - * Name: z8_copystate + * Name: irqsave + * + * Description: + * Disable all interrupts; return previous interrupt state + * ****************************************************************************/ -/* Maybe a little faster than most memcpy's */ - -void z8_copystate(FAR chipreg_t *dest, FAR const chipreg_t *src) +irqstate_t irqsave(void) { - int i; - for (i = 0; i < XCPTCONTEXT_REGS; i++) - { - *dest++ = *src++; - } } +/**************************************************************************** + * Name: irqrestore + * + * Description: + * Restore previous interrupt state + * + ****************************************************************************/ + +void irqrestore(irqstate_t flags) +{ +} diff --git a/arch/z80/src/z8/z8_registerdump.c b/arch/z80/src/z8/z8_registerdump.c index 0a77659a4c..b7e01a74d2 100644 --- a/arch/z80/src/z8/z8_registerdump.c +++ b/arch/z80/src/z8/z8_registerdump.c @@ -70,25 +70,71 @@ * Private Functions ****************************************************************************/ +#ifdef CONFIG_ARCH_STACKDUMP +static inline void z8_dumpregs(FAR chipret_t *regs) +{ + lldbg("REGS: %04x %04x %04x %04x %04x %04x %04x %04x\n", + regs[XCPT_RR0], regs[XCPT_RR2], regs[XCPT_RR4], regs[XCPT_RR6], + regs[XCPT_RR8], regs[XCPT_RR10], regs[XCPT_RR12], regs[XCPT_RR14]); +} + +static inline void z8_dumpstate(chipreg_t sp, chipreg_t pc, ubyte irqctl, chipreg_t rpflags) +{ + lldbg("SP: %04x PC: %04x IRQCTL: %02x RP: %02x FLAGS: %02x\n", + sp, pc, irqctl & 0xff, rpflags >> 8, rpflags & 0xff); +} + +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + /**************************************************************************** * Name: z8_registerdump ****************************************************************************/ #ifdef CONFIG_ARCH_STACKDUMP -static void z8_registerdump(void) +void z8_registerdump(void) { - if (current_regs) + FAR chipret_t *regs; + FAR chipret_t *state; + chipreg_t sp; + uint16 rp; + + switch (g_z8irqstate.state) { - lldbg("REGS: %04x %04x %04x %04x %04x %04x %04x %04x\n", - current_regs[XCPT_RR0], current_regs[XCPT_RR2], - current_regs[XCPT_RR4], current_regs[XCPT_RR6], - current_regs[XCPT_RR8], current_regs[XCPT_RR10], - current_regs[XCPT_RR12], current_regs[XCPT_RR14]); - lldbg("SP: %04x PC: %04x IRQCTL: %02x RP: %02x FLAGS: %02x\n", - current_regs[XCPT_SP], current_regs[XCPT_PC], - current_regs[XCPT_IRQCTL] & 0xff, - current_regs[XCPT_RPFLAGS] >> 8, - current_regs[XCPT_RPFLAGS] & 0xff); + case Z8_IRQSTATE_ENTRY: + /* Calculate the source address based on the saved RP value */ + + rp = g_z8irqstate.regs[Z8_IRQSAVE_RPFLAGS] >> 8; + regs = (FAR uint16*)(rp & 0xf0); + + /* Then dump the register values */ + + z8_dumpregs(regs); + + /* Dump the saved machine state: + * The g_z8irqstate.regs pointer is the value of the stack pointer at + * the time that up_doirq() was called. Therefore, we can calculate + * the correct value for the stack pointer on return from interrupt: + */ + + sp = ((chipreg_t)g_z8irqstate.regs) + Z8_IRQSAVE_SIZE; + z8_dumpstate(sp, g_z8irqstate.regs[Z8_IRQSAVE_PC], 0x80, + g_z8irqstate.regs[Z8_IRQSAVE_RPFLAGS]); + break; + + case Z8_IRQSTATE_SAVED: + regs = g_z8irqstate.regs; + z8_dumpregs(regs); + z8_dumpstate(regs[XCPT_SP], regs[XCPT_PC], + regs[XCPT_IRQCTL], regs[XCPT_RPFLAGS]; + break; + + case Z8_IRQSTATE_NONE: + default: + break; } } #endif diff --git a/arch/z80/src/z8/z8_saveirqcontext.c b/arch/z80/src/z8/z8_saveirqcontext.c new file mode 100644 index 0000000000..22f5da0ed5 --- /dev/null +++ b/arch/z80/src/z8/z8_saveirqcontext.c @@ -0,0 +1,134 @@ +/**************************************************************************** + * arch/z80/src/z8/z8_saveirqcontext.c + * + * Copyright (C) 2008 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "chip/switch.h" +#include "os_internal.h" +#include "up_internal.h" + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: z8_saveirqcontext + * + * Description: + * In order to provide faster interrupt handling, the interrupt logic does + * "lazy" context saving as described below: + * + * (1) At the time of the interrupt, minimum information is saved and the + * register pointer is changed so that the interrupt logic does not + * alter the state of the interrupted task's registers. + * (2) If no context switch occurs during the interrupt processing, then + * the return from interrupt is also simple. + * (3) If a context switch occurs during interrupt processing, then + * (a) The full context of the interrupt task is saved, and + * (b) A full context switch is performed when the interrupt exits + * (see z8_vector.S). + * + * This function implements the full-context switch of bullet 3a. + * + ****************************************************************************/ + +void z8_saveirqcontext(FAR chipreg_t *regs) +{ + /* If we have already saved the interrupted task's registers in the TCB, + * then we do not need to do anything. + */ + + if (g_z8irqstate.state == Z8_IRQSTATE_ENTRY) + { + /* Calculate the source address based on the saved RP value */ + + uint16 rp = g_z8irqstate.regs[Z8_IRQSAVE_RPFLAGS] >> 8; + FAR chipreg_t *src = (FAR uint16*)(rp & 0xf0); + FAR chipreg_t *dest = ®s[XCPT_RR0]; + + /* Copy the interrupted tasks register into the TCB register save area. */ + + int i; + for (i = 0; i < XCPTCONTEXT_REGS; i++) + { + *dest++ = *src++; + } + + /* Since the task was interrupted, we know that interrupts were enabled */ + + regs[XCPT_IRQCTL] = 0x0080; /* IRQE bit will enable interrupts */ + + /* The g_z8irqstate.regs pointer is the value of the stack pointer at + * the time that up_doirq() was called. Therefore, we can calculate + * the correct value for the stack pointer on return from interrupt: + */ + + regs[XCPT_SP] = ((chipreg_t)g_z8irqstate.regs) + Z8_IRQSAVE_SIZE; + + /* Copy the PC, RP, and FLAGS information from the lazy save to the TCB + * register save area. + */ + + regs[XCPT_RPFLAGS] = g_z8irqstate.regs[Z8_IRQSAVE_RPFLAGS]; + regs[XCPT_PC] = g_z8irqstate.regs[Z8_IRQSAVE_PC]; + + /* Now update the IRQ save area so that we will know that we have already + * done this. + */ + + g_z8irqstate.state = Z8_IRQSTATE_SAVED; + g_z8irqstate.regs = regs; + } +} + diff --git a/arch/z80/src/z80/switch.h b/arch/z80/src/z80/switch.h index 2ba18615be..73a88d4490 100644 --- a/arch/z80/src/z80/switch.h +++ b/arch/z80/src/z80/switch.h @@ -50,7 +50,11 @@ * Definitions ************************************************************************************/ -/* Macros for portability */ +/* Macros for portability *********************************************************** + * + * Common logic in arch/z80/src/common is customized for the z8 context switching + * logic via the following macros. + */ /* Initialize the IRQ state */ @@ -133,7 +137,7 @@ EXTERN int z80_saveusercontext(FAR chipreg_t *regs); /* Defined in z80_restoreusercontext.asm */ -EXTERN int z80_restoreusercontext(FAR chipreg_t *regs); +EXTERN void z80_restoreusercontext(FAR chipreg_t *regs); /* Defined in z80_sigsetup.c */ diff --git a/arch/z80/src/z80/z80_irq.c b/arch/z80/src/z80/z80_irq.c index d9aee3994e..4873abbf7f 100644 --- a/arch/z80/src/z80/z80_irq.c +++ b/arch/z80/src/z80/z80_irq.c @@ -44,6 +44,7 @@ #include #include +#include "chip/switch.h" #include "up_internal.h" /****************************************************************************