/* cpu_asm.S * * This file contains the basic algorithms for all assembly code used * in the Blackfin port of RTEMS. These algorithms must be implemented * in assembly language * * Copyright (c) 2006 by Atos Automacao Industrial Ltda. * written by Alain Schaefer * and Antonio Giovanini * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.com/license/LICENSE. * * $Id$ */ #include #include #include #include #define LO(con32) ((con32) & 0xFFFF) #define HI(con32) (((con32) >> 16) & 0xFFFF) /* _CPU_Context_switch * * This routine performs a normal non-FP context switch. * * bfin Specific Information: * * For now we simply save all registers. * */ .globl __CPU_Context_switch __CPU_Context_switch: /* Start saving context R0 = current, R1=heir */ /*save P0 first*/ [FP+0x8] = P0; P0 = R0; [ P0 + R0_OFFSET ] = R0; [ P0 + R1_OFFSET] = R1; [ P0 + R2_OFFSET] = R2; [ P0 + R4_OFFSET] = R4; [ P0 + R3_OFFSET] = R3; [ P0 + R5_OFFSET] = R5; [ P0 + R6_OFFSET] = R6; [ P0 + R7_OFFSET] = R7; [ P0 + P1_OFFSET] = P1; /* save the original value of P0 */ P1 = [FP+0x8]; [ P0 + P0_OFFSET] = P1; [ P0 + P2_OFFSET] = P2; [ P0 + P3_OFFSET] = P3; [ P0 + P4_OFFSET] = P4; [ P0 + P5_OFFSET] = P5; [ P0 + FP_OFFSET] = FP; [ P0 + SP_OFFSET] = SP; /* save ASTAT */ R0 = ASTAT; [P0 + ASTAT_OFFSET] = R0; /* save Loop Counters */ R0 = LC0; [P0 + LC0_OFFSET] = R0; R0 = LC1; [P0 + LC1_OFFSET] = R0; /* save Accumulators */ R0 = A0.W; [P0 + A0W_OFFSET] = R0; R0 = A0.X; [P0 + A0X_OFFSET] = R0; R0 = A1.W; [P0 + A1W_OFFSET] = R0; R0 = A1.X; [P0 + A1X_OFFSET] = R0; /* save Index Registers */ R0 = I0; [P0 + I0_OFFSET] = R0; R0 = I1; [P0 + I1_OFFSET] = R0; R0 = I2; [P0 + I2_OFFSET] = R0; R0 = I3; [P0 + I3_OFFSET] = R0; /* save Modifier Registers */ R0 = M0; [P0 + M0_OFFSET] = R0; R0 = M1; [P0 + M1_OFFSET] = R0; R0 = M2; [P0 + M2_OFFSET] = R0; R0 = M3; [P0 + M3_OFFSET] = R0; /* save Length Registers */ R0 = L0; [P0 + L0_OFFSET] = R0; R0 = L1; [P0 + L1_OFFSET] = R0; R0 = L2; [P0 + L2_OFFSET] = R0; R0 = L3; [P0 + L3_OFFSET] = R0; /* Base Registers */ R0 = B0; [P0 + B0_OFFSET] = R0; R0 = B1; [P0 + B1_OFFSET] = R0; R0 = B2; [P0 + B2_OFFSET] = R0; R0 = B3; [P0 + B3_OFFSET] = R0; /* save RETS */ R0 = RETS; [ P0 + RETS_OFFSET] = R0; restore: P0 = R1; R1 = [P0 + R1_OFFSET]; R2 = [P0 + R2_OFFSET]; R3 = [P0 + R3_OFFSET]; R4 = [P0 + R4_OFFSET]; R5 = [P0 + R5_OFFSET]; R6 = [P0 + R6_OFFSET]; R7 = [P0 + R7_OFFSET]; P2 = [P0 + P2_OFFSET]; P3 = [P0 + P3_OFFSET]; P4 = [P0 + P4_OFFSET]; P5 = [P0 + P5_OFFSET]; /* might have to be placed more to the end */ FP = [P0 + FP_OFFSET]; SP = [P0 + SP_OFFSET]; /* save ASTAT */ R0 = [P0 + ASTAT_OFFSET]; ASTAT = R0; /* save Loop Counters */ R0 = [P0 + LC0_OFFSET]; LC0 = R0; R0 = [P0 + LC1_OFFSET]; LC1 = R0; /* save Accumulators */ R0 = [P0 + A0W_OFFSET]; A0.W = R0; R0 = [P0 + A0X_OFFSET]; A0.X = R0; R0 = [P0 + A1W_OFFSET]; A1.W = R0; R0 = [P0 + A1X_OFFSET]; A1.X = R0; /* save Index Registers */ R0 = [P0 + I0_OFFSET]; I0 = R0; R0 = [P0 + I1_OFFSET]; I1 = R0; R0 = [P0 + I2_OFFSET]; I2 = R0; R0 = [P0 + I3_OFFSET]; I3 = R0; /* save Modifier Registers */ R0 = [P0 + M0_OFFSET]; M0 = R0; R0 = [P0 + M1_OFFSET]; M1 = R0; R0 = [P0 + M2_OFFSET]; M2 = R0; R0 = [P0 + M3_OFFSET]; M3 = R0; /* save Length Registers */ R0 = [P0 + L0_OFFSET]; L0 = R0; R0 = [P0 + L1_OFFSET]; L1 = R0; R0 = [P0 + L2_OFFSET]; L2 = R0; R0 = [P0 + L3_OFFSET]; L3 = R0; /* Base Registers */ R0 = [P0 + B0_OFFSET]; B0 = R0; R0 = [P0 + B1_OFFSET]; B1 = R0; R0 = [P0 + B2_OFFSET]; B2 = R0; R0 = [P0 + B3_OFFSET]; B3 = R0; /* restore RETS */ P1 = [P0 + RETS_OFFSET]; RETS = P1; /* now restore the P1 + P0 */ P1 = [P0 + R1_OFFSET]; P0 = [P0 + P0_OFFSET]; rts; /* * _CPU_Context_restore * * This routine is generally used only to restart self in an * efficient manner. It may simply be a label in _CPU_Context_switch. * * NOTE: May be unnecessary to reload some registers. * * Blackfin Specific Information: * * none * */ .globl __CPU_Context_restore __CPU_Context_restore: jump restore; .globl __ISR_Thread_Dispatch __ISR_Thread_Dispatch: .extern __Thread_Dispatch R0.l = __Thread_Dispatch; R0.h = __Thread_Dispatch; /* Puts the address of th Thread_Dispatch function on Stack * Where it will be restored to the RTI register */ P0 = [FP]; /* save the old reti */ R1 = [P0+0xc]; [P0+0xc] = R0; /* * Overwriting the RETS Register is save because Thread_Dispatch is * disabled when we are between call/link or unlink/rts */ [P0+0x8] = R1; /* save old rets */ rts; .globl __ISR_Handler __ISR_Handler: /* First of all check the Stackpointer and */ /* switch to Scratchpad if necessary */ /* save P0 and R0 in the scratchpad */ USP = P0; /* load base adress of scratchpad */ P0.H = HI(SCRATCH); P0.L = LO(SCRATCH); [--SP] = ASTAT; /* save cc flag*/ /* if SP is already inside the SCRATCHPAD */ CC=SP>= 16;*/ R3 = 0; /* Check if RETI is a LINK instruction */ R1.h = HI(0x0000); R1.l = LO(0xE800); CC=R0==R1; if cc jump disablethreaddispatch; /* Check if RETI is a RTS instruction */ R1.h = HI(0x0000); R1.l = LO(0x0010); CC=R0==R1; if cc jump disablethreaddispatch; jump afterthreaddispatch; disablethreaddispatch: /* _Thread_Dispatch_disable_level++ */ .extern _Thread_Dispatch_disable_level P0.H = __Thread_Dispatch_disable_level; P0.L = __Thread_Dispatch_disable_level; R0 = [P0]; R0 += 1; [P0] = R0; R3 = 1; afterthreaddispatch: /* Put R3 on the stack */ [--SP] = R3; /* Obtain a bitlist of the pending interrupts. */ P0.H = HI(IPEND); P0.L = LO(IPEND); R1 = [P0]; /* * Search through the bit list stored in R0 to find the first enabled * bit. The offset of this bit is the index of the interrupt that is * to be handled. */ R0 = -1; intloop: R0 += 1; R1 = ROT R1 by -1; if !cc jump intloop; /* pass SP as parameter to the C function */ R1 = SP /* pass values by register as well as by stack */ /* to comply with the c calling conventions */ [--SP] = R0; [--SP] = R1; .extern _ISR_Handler2 call _ISR_Handler2 /* inc 2 to compensate the passing of arguments */ R3 = [SP++]; R3 = [SP++]; /* check if _Thread_Dispatch_disable_level has been incremented */ R3 = [SP++] CC=R3==0 if cc jump dont_decrement; .extern _Thread_Dispatch_disable_level P0.H = __Thread_Dispatch_disable_level; P0.L = __Thread_Dispatch_disable_level; R0 = [P0]; R0 += -1; [P0] = R0; dont_decrement: (R7:0, P5:0) = [SP++]; FP = [SP++]; ASTAT = [SP++]; RETS = [SP++]; RETI = [SP++]; /* Interrupts are now disabled again */ /*should restore the old stack !!!*/ /*if sp now points to SCRATCH_TOP */ /* load base adress of scratchpad */ USP = P0; P0.H = HI(SCRATCH_TOP); P0.L = LO(SCRATCH_TOP); CC=SP==P0 if !cc jump restoreP0 /* restore the stack */ SP=[P0]; restoreP0: P0 = USP; ASTAT = [SP++]; /* restore cc flag */ /*now we should be on the old "user-stack" again */ /* return from interrupt, will jump to adress stored in RETI */ RTI;