summaryrefslogblamecommitdiffstats
path: root/cpukit/score/cpu/bfin/cpu_asm.S
blob: e3b253bf230c4de4470a3af8b52fcf8cd654df7f (plain) (tree)




















                                                                        
                            
 

                                            




















































































































































































































































                                                                       

                                          
































                                                                                       








                                     




                                                              
                      




                                            
                      





















































































                                                                         
                                         





                                                                   
/*  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 <alain.schaefer@easc.ch>
 *                    and Antonio Giovanini <antonio@atos.com.br>
 *
 *  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 <rtems/asm.h>
#include <rtems/score/cpu_asm.h>
#include <rtems/score/bfin.h>
#include <rtems/bfin/bfin.h>

#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<P0 (iu)
    if !CC jump continue;
    
    /* set PO to top of scratchpad */   
    P0.h=HI(SCRATCH_TOP);
    P0.l=LO(SCRATCH_TOP);
    /*save the old SP*/ 
    [P0] = SP;
    /*P0 += -4;*/
    /*set the new Stackpointer*/
    SP = P0;
    /*restore the old PO*/
    
    /* The Stackpointer is now setup as we want */  
    continue:
    /* restore P0 and save some context */
    P0 = USP;
    /* save some state on the isr stack (scratchpad), this enables interrupt nesting */
    [--SP] = RETI;
    [--SP] = RETS;
    [--SP] = ASTAT;
    [--SP] = FP;
    FP = SP;
    [--SP] = (R7:0, P5:0) ;
    
    
    /* Context is saved, now check which Instruction we were executing
     * If we were between a call and link or between a unlink and rts
     * we have to disable Thread_Dispatch because correct restore of context after
     * Thread_Dispatch would not be possible. */
    
    P0 = RETI;
    R0 = P0;
    R0.L = 0x0000;
    R1.H = 0xffa0;
    R1.L = 0x0000;
    CC = R0 == R1;
    if CC jump disablethreaddispatch;
        
    R0 = W[P0](Z);    
    
    /* shift 16 bits to the right (select the high nibble ) */
    /*R0 >>= 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;