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.
This commit is contained in:
Dimitry Kloper
2015-12-29 19:01:06 +02:00
parent 99aab135b2
commit 556954141e
7 changed files with 77 additions and 15 deletions
+3
View File
@@ -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
+29 -6
View File
@@ -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
+7
View File
@@ -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
}
}
+6
View File
@@ -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 */
+25 -7
View File
@@ -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);
}
}
+5 -2
View File
@@ -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
+2
View File
@@ -39,6 +39,8 @@
#include <nuttx/config.h>
#include <arch/irq.h>
#include "excptmacros.h"
/************************************************************************************