From 556954141eeeb4b11fe39c3e223840bccce222e3 Mon Sep 17 00:00:00 2001 From: Dimitry Kloper Date: Tue, 29 Dec 2015 19:01:06 +0200 Subject: [PATCH] AVR: Add support for AVR chips with 24-bit Program Counter register The main challenge is to change the context switch code to be aware of the extra byte that is saved on stack during call and intterupt. This relates also to the task startup and signal handling. --- arch/avr/include/avr/irq.h | 3 +++ arch/avr/src/avr/excptmacros.h | 35 ++++++++++++++++++++----- arch/avr/src/avr/up_dumpstate.c | 7 +++++ arch/avr/src/avr/up_initialstate.c | 6 +++++ arch/avr/src/avr/up_schedulesigaction.c | 32 +++++++++++++++++----- arch/avr/src/avr/up_sigdeliver.c | 7 +++-- arch/avr/src/avr/up_switchcontext.S | 2 ++ 7 files changed, 77 insertions(+), 15 deletions(-) diff --git a/arch/avr/include/avr/irq.h b/arch/avr/include/avr/irq.h index 65abae20128..9e52dd16ef7 100644 --- a/arch/avr/include/avr/irq.h +++ b/arch/avr/include/avr/irq.h @@ -93,6 +93,9 @@ #define REG_PC0 35 /* PC */ #define REG_PC1 36 +#if ATMEGA_PC_SIZE > 16 +#define REG_PC2 37 +#endif /**************************************************************************** * Public Types diff --git a/arch/avr/src/avr/excptmacros.h b/arch/avr/src/avr/excptmacros.h index a4674bbe859..265297a4fff 100644 --- a/arch/avr/src/avr/excptmacros.h +++ b/arch/avr/src/avr/excptmacros.h @@ -335,8 +335,11 @@ /* Pop the return address from the stack (PC0 then PC1). R18:19 are Call-used */ - pop r19 /* r19=PC0 */ - pop r18 /* r18=PC1 */ +#if ATMEGA_PC_SIZE > 16 + pop r20 +#endif /* ATMEGA_PC_SIZE */ + pop r19 + pop r18 /* Save the current stack pointer as it would be after the return(SPH then SPL). */ @@ -398,9 +401,11 @@ adiw r26, 2 /* Two registers: r24-r25 */ /* Save the return address that we have saved in r18:19*/ - - st x+, r19 /* r19=PC0 */ - st x+, r18 /* r18=PC1 */ +#if ATMEGA_PC_SIZE > 16 + st x+, r20 +#endif /* ATMEGA_PC_SIZE */ + st x+, r19 + st x+, r18 .endm /******************************************************************************************** @@ -424,12 +429,16 @@ .macro TCB_RESTORE, regs - /* X [r36:27] points to the register save block. Get an offset pointer to the PC in + /* X [r26:27] points to the register save block. Get an offset pointer to the PC in * Y [r28:29] */ movw r28, r26 /* Get a pointer to the PC0/PC1 storage location */ +#if ATMEGA_PC_SIZE <= 16 adiw r28, REG_PC0 +#else + adiw r28, REG_PC2 +#endif /* Fetch and set the new stack pointer */ @@ -441,19 +450,33 @@ /* Fetch the return address and save it at the bottom of the new stack so * that we can iret to switch contexts. The new stack is now: * + * PC2 (for 24-bit PC arch) * PC1 * PC0 * --- <- SP */ +#if ATMEGA_PC_SIZE <= 16 ld r25, y+ /* Load PC0 (r25) then PC1 (r24) */ ld r24, y+ push r24 /* Push PC0 and PC1 on the stack (PC1 then PC0) */ push r25 +#else + ld r25, y /* Load PC2 (r25) */ + subi r28,1 + push r25 + ld r25, y /* Load PC1 (r25) */ + subi r28,1 + push r25 + ld r25, y /* Load PC0 (r25) */ + subi r28,1 + push r25 +#endif /* Then get value of X [r26:r27]. Save X on the new stack where we can * recover it later. The new stack is now: * + * PC2 (for 24-bit PC arch) * PC1 * PC0 * R26 diff --git a/arch/avr/src/avr/up_dumpstate.c b/arch/avr/src/avr/up_dumpstate.c index 383734323fb..5feaeabd6f5 100644 --- a/arch/avr/src/avr/up_dumpstate.c +++ b/arch/avr/src/avr/up_dumpstate.c @@ -151,10 +151,17 @@ static inline void up_registerdump(void) current_regs[REG_R28], current_regs[REG_R29], current_regs[REG_R30], current_regs[REG_R31]); +#if !defined(REG_PC2) lldbg("PC: %02x%02x SP: %02x%02x SREG: %02x\n", current_regs[REG_PC0], current_regs[REG_PC1], current_regs[REG_SPH], current_regs[REG_SPL], current_regs[REG_SREG]); +#else + lldbg("PC: %02x%02x%02x SP: %02x%02x SREG: %02x\n", + current_regs[REG_PC0], current_regs[REG_PC1], current_regs[REG_PC2], + current_regs[REG_SPH], current_regs[REG_SPL], + current_regs[REG_SREG]); +#endif } } diff --git a/arch/avr/src/avr/up_initialstate.c b/arch/avr/src/avr/up_initialstate.c index 3fbfc557987..2d0cf145b76 100644 --- a/arch/avr/src/avr/up_initialstate.c +++ b/arch/avr/src/avr/up_initialstate.c @@ -96,8 +96,14 @@ void up_initial_state(struct tcb_s *tcb) /* Save the task entry point */ +#if !defined(REG_PC2) xcp->regs[REG_PC0] = (uint8_t)((uint16_t)tcb->start >> 8); xcp->regs[REG_PC1] = (uint8_t)((uint16_t)tcb->start & 0xff); +#else + xcp->regs[REG_PC0] = (uint8_t)((uint32_t)tcb->start >> 16); + xcp->regs[REG_PC1] = (uint8_t)((uint32_t)tcb->start >> 8); + xcp->regs[REG_PC2] = (uint8_t)((uint32_t)tcb->start & 0xff); +#endif /* Enable or disable interrupts, based on user configuration */ diff --git a/arch/avr/src/avr/up_schedulesigaction.c b/arch/avr/src/avr/up_schedulesigaction.c index 78386076f6e..c9629c52c5c 100644 --- a/arch/avr/src/avr/up_schedulesigaction.c +++ b/arch/avr/src/avr/up_schedulesigaction.c @@ -154,16 +154,24 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver) */ tcb->xcp.sigdeliver = sigdeliver; - tcb->xcp.saved_pc1 = current_regs[REG_PCL]; - tcb->xcp.saved_pc0 = current_regs[REG_PCH]; + tcb->xcp.saved_pc0 = current_regs[REG_PC0]; + tcb->xcp.saved_pc1 = current_regs[REG_PC1]; +#if defined(REG_PC2) + tcb->xcp.saved_pc2 = current_regs[REG_PC2]; +#endif tcb->xcp.saved_sreg = current_regs[REG_SREG]; /* Then set up to vector to the trampoline with interrupts * disabled */ - - current_regs[REG_PC1] = (uint16_t)up_sigdeliver & 0xff; +#if !defined(REG_PC2) current_regs[REG_PC0] = (uint16_t)up_sigdeliver >> 8; + current_regs[REG_PC1] = (uint16_t)up_sigdeliver & 0xff; +#else + current_regs[REG_PC0] = (uint32_t)up_sigdeliver >> 16; + current_regs[REG_PC1] = (uint32_t)up_sigdeliver >> 8; + current_regs[REG_PC2] = (uint32_t)up_sigdeliver & 0xff; +#endif current_regs[REG_SREG] &= ~(1 << SREG_I); /* And make sure that the saved context in the TCB @@ -188,16 +196,26 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver) */ tcb->xcp.sigdeliver = sigdeliver; - tcb->xcp.saved_pc1 = tcb->xcp.regs[REG_PCL]; - tcb->xcp.saved_pc0 = tcb->xcp.regs[REG_PCH]; + tcb->xcp.saved_pc0 = tcb->xcp.regs[REG_PC0]; + tcb->xcp.saved_pc1 = tcb->xcp.regs[REG_PC1]; +#if defined(REG_PC2) + tcb->xcp.saved_pc2 = tcb->xcp.regs[REG_PC2]; +#endif tcb->xcp.saved_sreg = tcb->xcp.regs[REG_SREG]; /* Then set up to vector to the trampoline with interrupts * disabled */ - tcb->xcp.regs[REG_PC1] = (uint16_t)up_sigdeliver & 0xff; +#if !defined(REG_PC2) tcb->xcp.regs[REG_PC0] = (uint16_t)up_sigdeliver >> 8; + tcb->xcp.regs[REG_PC1] = (uint16_t)up_sigdeliver & 0xff; +#else + tcb->xcp.regs[REG_PC0] = (uint32_t)up_sigdeliver >> 16; + tcb->xcp.regs[REG_PC1] = (uint32_t)up_sigdeliver >> 8; + tcb->xcp.regs[REG_PC2] = (uint32_t)up_sigdeliver & 0xff; + +#endif tcb->xcp.regs[REG_SREG] &= ~(1 << SREG_I); } } diff --git a/arch/avr/src/avr/up_sigdeliver.c b/arch/avr/src/avr/up_sigdeliver.c index 1901cf420bd..6720e5c43c1 100644 --- a/arch/avr/src/avr/up_sigdeliver.c +++ b/arch/avr/src/avr/up_sigdeliver.c @@ -101,8 +101,11 @@ void up_sigdeliver(void) /* Save the real return state on the stack. */ up_copystate(regs, rtcb->xcp.regs); - regs[REG_PC1] = rtcb->xcp.saved_pcl; - regs[REG_PC0] = rtcb->xcp.saved_pch; + regs[REG_PC0] = rtcb->xcp.saved_pc0; + regs[REG_PC1] = rtcb->xcp.saved_pc1; +#if defined(REG_PC2) + regs[REG_PC2] = rtcb->xcp.saved_pc2; +#endif regs[REG_SREG] = rtcb->xcp.saved_sreg; /* Get a local copy of the sigdeliver function pointer. We do this so that diff --git a/arch/avr/src/avr/up_switchcontext.S b/arch/avr/src/avr/up_switchcontext.S index da5f45e6171..67587c7ac43 100755 --- a/arch/avr/src/avr/up_switchcontext.S +++ b/arch/avr/src/avr/up_switchcontext.S @@ -39,6 +39,8 @@ #include +#include + #include "excptmacros.h" /************************************************************************************