/*
* This file contains the basic algorithms for all assembly code used
* in an specific CPU port of RTEMS. These algorithms must be implemented
* in assembly language
*
* History:
* Baseline: no_cpu
* 1996: Ported to MIPS64ORION by Craig Lebakken <craigl@transition.com>
* COPYRIGHT (c) 1996 by Transition Networks Inc.
* To anyone who acknowledges that the modifications to this file to
* port it to the MIPS64ORION are provided "AS IS" without any
* express or implied warranty:
* permission to use, copy, modify, and distribute this file
* for any purpose is hereby granted without fee, provided that
* the above copyright notice and this notice appears in all
* copies, and that the name of Transition Networks not be used in
* advertising or publicity pertaining to distribution of the
* software without specific, written prior permission. Transition
* Networks makes no representations about the suitability
* of this software for any purpose.
* 2000: Reworked by Alan Cudmore <alanc@linuxstart.com> to become
* the baseline of the more general MIPS port.
* 2001: Joel Sherrill <joel@OARcorp.com> continued this rework,
* rewriting as much as possible in C and added the JMR3904 BSP
* so testing could be performed on a simulator.
*
* COPYRIGHT (c) 1989-2000.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.OARcorp.com/rtems/license.html.
*
* $Id$
*/
#include <asm.h>
#include "iregdef.h"
#include "idtcpu.h"
/* Ifdefs prevent the duplication of code for MIPS ISA Level 3 ( R4xxx )
* and MIPS ISA Level 1 (R3xxx).
*/
#if __mips == 3
/* 64 bit register operations */
#define NOP
#define ADD dadd
#define STREG sd
#define LDREG ld
#define MFCO dmfc0
#define MTCO dmtc0
#define ADDU addu
#define ADDIU addiu
#define R_SZ 8
#define F_SZ 8
#define SZ_INT 8
#define SZ_INT_POW2 3
/* XXX if we don't always want 64 bit register ops, then another ifdef */
#elif __mips == 1
/* 32 bit register operations*/
#define NOP nop
#define ADD add
#define STREG sw
#define LDREG lw
#define MFCO mfc0
#define MTCO mtc0
#define ADDU add
#define ADDIU addi
#define R_SZ 4
#define F_SZ 4
#define SZ_INT 4
#define SZ_INT_POW2 2
#else
#error "mips assembly: what size registers do I deal with?"
#endif
#define ISR_VEC_SIZE 4
#define EXCP_STACK_SIZE (NREGS*R_SZ)
#ifdef __GNUC__
#define ASM_EXTERN(x,size) .extern x,size
#else
#define ASM_EXTERN(x,size)
#endif
/* NOTE: these constants must match the Context_Control structure in cpu.h */
#define S0_OFFSET 0
#define S1_OFFSET 1
#define S2_OFFSET 2
#define S3_OFFSET 3
#define S4_OFFSET 4
#define S5_OFFSET 5
#define S6_OFFSET 6
#define S7_OFFSET 7
#define SP_OFFSET 8
#define FP_OFFSET 9
#define RA_OFFSET 10
#define C0_SR_OFFSET 11
#define C0_EPC_OFFSET 12
/* NOTE: these constants must match the Context_Control_fp structure in cpu.h */
#define FP0_OFFSET 0
#define FP1_OFFSET 1
#define FP2_OFFSET 2
#define FP3_OFFSET 3
#define FP4_OFFSET 4
#define FP5_OFFSET 5
#define FP6_OFFSET 6
#define FP7_OFFSET 7
#define FP8_OFFSET 8
#define FP9_OFFSET 9
#define FP10_OFFSET 10
#define FP11_OFFSET 11
#define FP12_OFFSET 12
#define FP13_OFFSET 13
#define FP14_OFFSET 14
#define FP15_OFFSET 15
#define FP16_OFFSET 16
#define FP17_OFFSET 17
#define FP18_OFFSET 18
#define FP19_OFFSET 19
#define FP20_OFFSET 20
#define FP21_OFFSET 21
#define FP22_OFFSET 22
#define FP23_OFFSET 23
#define FP24_OFFSET 24
#define FP25_OFFSET 25
#define FP26_OFFSET 26
#define FP27_OFFSET 27
#define FP28_OFFSET 28
#define FP29_OFFSET 29
#define FP30_OFFSET 30
#define FP31_OFFSET 31
/*
* _CPU_Context_save_fp_context
*
* This routine is responsible for saving the FP context
* at *fp_context_ptr. If the point to load the FP context
* from is changed then the pointer is modified by this routine.
*
* Sometimes a macro implementation of this is in cpu.h which dereferences
* the ** and a similarly named routine in this file is passed something
* like a (Context_Control_fp *). The general rule on making this decision
* is to avoid writing assembly language.
*/
/* void _CPU_Context_save_fp(
* void **fp_context_ptr
* );
*/
#if ( CPU_HARDWARE_FP == FALSE )
FRAME(_CPU_Context_save_fp,sp,0,ra)
.set noat
ld a1,(a0)
NOP
swc1 $f0,FP0_OFFSET*F_SZ(a1)
swc1 $f1,FP1_OFFSET*F_SZ(a1)
swc1 $f2,FP2_OFFSET*F_SZ(a1)
swc1 $f3,FP3_OFFSET*F_SZ(a1)
swc1 $f4,FP4_OFFSET*F_SZ(a1)
swc1 $f5,FP5_OFFSET*F_SZ(a1)
swc1 $f6,FP6_OFFSET*F_SZ(a1)
swc1 $f7,FP7_OFFSET*F_SZ(a1)
swc1 $f8,FP8_OFFSET*F_SZ(a1)
swc1 $f9,FP9_OFFSET*F_SZ(a1)
swc1 $f10,FP10_OFFSET*F_SZ(a1)
swc1 $f11,FP11_OFFSET*F_SZ(a1)
swc1 $f12,FP12_OFFSET*F_SZ(a1)
swc1 $f13,FP13_OFFSET*F_SZ(a1)
swc1 $f14,FP14_OFFSET*F_SZ(a1)
swc1 $f15,FP15_OFFSET*F_SZ(a1)
swc1 $f16,FP16_OFFSET*F_SZ(a1)
swc1 $f17,FP17_OFFSET*F_SZ(a1)
swc1 $f18,FP18_OFFSET*F_SZ(a1)
swc1 $f19,FP19_OFFSET*F_SZ(a1)
swc1 $f20,FP20_OFFSET*F_SZ(a1)
swc1 $f21,FP21_OFFSET*F_SZ(a1)
swc1 $f22,FP22_OFFSET*F_SZ(a1)
swc1 $f23,FP23_OFFSET*F_SZ(a1)
swc1 $f24,FP24_OFFSET*F_SZ(a1)
swc1 $f25,FP25_OFFSET*F_SZ(a1)
swc1 $f26,FP26_OFFSET*F_SZ(a1)
swc1 $f27,FP27_OFFSET*F_SZ(a1)
swc1 $f28,FP28_OFFSET*F_SZ(a1)
swc1 $f29,FP29_OFFSET*F_SZ(a1)
swc1 $f30,FP30_OFFSET*F_SZ(a1)
swc1 $f31,FP31_OFFSET*F_SZ(a1)
j ra
nop
.set at
ENDFRAME(_CPU_Context_save_fp)
#endif
/*
* _CPU_Context_restore_fp_context
*
* This routine is responsible for restoring the FP context
* at *fp_context_ptr. If the point to load the FP context
* from is changed then the pointer is modified by this routine.
*
* Sometimes a macro implementation of this is in cpu.h which dereferences
* the ** and a similarly named routine in this file is passed something
* like a (Context_Control_fp *). The general rule on making this decision
* is to avoid writing assembly language.
*/
/* void _CPU_Context_restore_fp(
* void **fp_context_ptr
* )
*/
#if ( CPU_HARDWARE_FP == FALSE )
FRAME(_CPU_Context_restore_fp,sp,0,ra)
.set noat
ld a1,(a0)
NOP
lwc1 $f0,FP0_OFFSET*4(a1)
lwc1 $f1,FP1_OFFSET*4(a1)
lwc1 $f2,FP2_OFFSET*4(a1)
lwc1 $f3,FP3_OFFSET*4(a1)
lwc1 $f4,FP4_OFFSET*4(a1)
lwc1 $f5,FP5_OFFSET*4(a1)
lwc1 $f6,FP6_OFFSET*4(a1)
lwc1 $f7,FP7_OFFSET*4(a1)
lwc1 $f8,FP8_OFFSET*4(a1)
lwc1 $f9,FP9_OFFSET*4(a1)
lwc1 $f10,FP10_OFFSET*4(a1)
lwc1 $f11,FP11_OFFSET*4(a1)
lwc1 $f12,FP12_OFFSET*4(a1)
lwc1 $f13,FP13_OFFSET*4(a1)
lwc1 $f14,FP14_OFFSET*4(a1)
lwc1 $f15,FP15_OFFSET*4(a1)
lwc1 $f16,FP16_OFFSET*4(a1)
lwc1 $f17,FP17_OFFSET*4(a1)
lwc1 $f18,FP18_OFFSET*4(a1)
lwc1 $f19,FP19_OFFSET*4(a1)
lwc1 $f20,FP20_OFFSET*4(a1)
lwc1 $f21,FP21_OFFSET*4(a1)
lwc1 $f22,FP22_OFFSET*4(a1)
lwc1 $f23,FP23_OFFSET*4(a1)
lwc1 $f24,FP24_OFFSET*4(a1)
lwc1 $f25,FP25_OFFSET*4(a1)
lwc1 $f26,FP26_OFFSET*4(a1)
lwc1 $f27,FP27_OFFSET*4(a1)
lwc1 $f28,FP28_OFFSET*4(a1)
lwc1 $f29,FP29_OFFSET*4(a1)
lwc1 $f30,FP30_OFFSET*4(a1)
lwc1 $f31,FP31_OFFSET*4(a1)
j ra
nop
.set at
ENDFRAME(_CPU_Context_restore_fp)
#endif
/* _CPU_Context_switch
*
* This routine performs a normal non-FP context switch.
*/
/* void _CPU_Context_switch(
* Context_Control *run,
* Context_Control *heir
* )
*/
FRAME(_CPU_Context_switch,sp,0,ra)
MFC0 t0,C0_SR
li t1,~(SR_INTERRUPT_ENABLE_BITS)
STREG t0,C0_SR_OFFSET*4(a0) /* save status register */
and t0,t1
MTC0 t0,C0_SR /* first disable ie bit (recommended) */
#if __mips == 3
ori t0,SR_EXL|SR_IE /* enable exception level to disable interrupts */
MTC0 t0,C0_SR
#endif
STREG ra,RA_OFFSET*R_SZ(a0) /* save current context */
STREG sp,SP_OFFSET*R_SZ(a0)
STREG fp,FP_OFFSET*R_SZ(a0)
STREG s0,S0_OFFSET*R_SZ(a0)
STREG s1,S1_OFFSET*R_SZ(a0)
STREG s2,S2_OFFSET*R_SZ(a0)
STREG s3,S3_OFFSET*R_SZ(a0)
STREG s4,S4_OFFSET*R_SZ(a0)
STREG s5,S5_OFFSET*R_SZ(a0)
STREG s6,S6_OFFSET*R_SZ(a0)
STREG s7,S7_OFFSET*R_SZ(a0)
MFC0 t0,C0_EPC
NOP
STREG t0,C0_EPC_OFFSET*R_SZ(a0)
_CPU_Context_switch_restore:
LDREG ra,RA_OFFSET*R_SZ(a1)
LDREG sp,SP_OFFSET*R_SZ(a1)
LDREG fp,FP_OFFSET*R_SZ(a1)
LDREG s0,S0_OFFSET*R_SZ(a1) /* restore context */
LDREG s1,S1_OFFSET*R_SZ(a1)
LDREG s2,S2_OFFSET*R_SZ(a1)
LDREG s3,S3_OFFSET*R_SZ(a1)
LDREG s4,S4_OFFSET*R_SZ(a1)
LDREG s5,S5_OFFSET*R_SZ(a1)
LDREG s6,S6_OFFSET*R_SZ(a1)
LDREG s7,S7_OFFSET*R_SZ(a1)
LDREG t0,C0_EPC_OFFSET*R_SZ(a1)
NOP
MTC0 t0,C0_EPC
LDREG t0, C0_SR_OFFSET*R_SZ(a1)
NOP
#if __mips == 3
andi t0,SR_EXL
bnez t0,_CPU_Context_1 /* set exception level from restore context */
li t0,~SR_EXL
MFC0 t1,C0_SR
NOP
and t1,t0
MTC0 t1,C0_SR
#elif __mips == 1
andi t0,(SR_INTERRUPT_ENABLE_BITS) /* we know 0 disabled */
beq t0,$0,_CPU_Context_1 /* set level from restore context */
MFC0 t0,C0_SR
NOP
or t0,(SR_INTERRUPT_ENABLE_BITS) /* new_sr = old sr with enabled */
MTC0 t0,C0_SR /* set with enabled */
#endif
_CPU_Context_1:
j ra
NOP
ENDFRAME(_CPU_Context_switch)
/*
* _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.
*
* void _CPU_Context_restore(
* Context_Control *new_context
* );
*/
FRAME(_CPU_Context_restore,sp,0,ra)
ADD a1,a0,zero
j _CPU_Context_switch_restore
NOP
ENDFRAME(_CPU_Context_restore)
ASM_EXTERN(_ISR_Nest_level, SZ_INT)
ASM_EXTERN(_Thread_Dispatch_disable_level,SZ_INT)
ASM_EXTERN(_Context_Switch_necessary,SZ_INT)
ASM_EXTERN(_ISR_Signals_to_thread_executing,SZ_INT)
.extern _Thread_Dispatch
.extern _ISR_Vector_table
/* void __ISR_Handler()
*
* This routine provides the RTEMS interrupt management.
*
* void _ISR_Handler()
*
*
* This discussion ignores a lot of the ugly details in a real
* implementation such as saving enough registers/state to be
* able to do something real. Keep in mind that the goal is
* to invoke a user's ISR handler which is written in C and
* uses a certain set of registers.
*
* Also note that the exact order is to a large extent flexible.
* Hardware will dictate a sequence for a certain subset of
* _ISR_Handler while requirements for setting
*
* At entry to "common" _ISR_Handler, the vector number must be
* available. On some CPUs the hardware puts either the vector
* number or the offset into the vector table for this ISR in a
* known place. If the hardware does not give us this information,
* then the assembly portion of RTEMS for this port will contain
* a set of distinct interrupt entry points which somehow place
* the vector number in a known place (which is safe if another
* interrupt nests this one) and branches to _ISR_Handler.
*
*/
FRAME(_ISR_Handler,sp,0,ra)
.set noreorder
/* Q: _ISR_Handler, not using IDT/SIM ...save extra regs? */
/* wastes a lot of stack space for context?? */
ADDIU sp,sp,-EXCP_STACK_SIZE
STREG ra, R_RA*R_SZ(sp) /* store ra on the stack */
STREG v0, R_V0*R_SZ(sp)
STREG v1, R_V1*R_SZ(sp)
STREG a0, R_A0*R_SZ(sp)
STREG a1, R_A1*R_SZ(sp)
STREG a2, R_A2*R_SZ(sp)
STREG a3, R_A3*R_SZ(sp)
STREG t0, R_T0*R_SZ(sp)
STREG t1, R_T1*R_SZ(sp)
STREG t2, R_T2*R_SZ(sp)
STREG t3, R_T3*R_SZ(sp)
STREG t4, R_T4*R_SZ(sp)
STREG t5, R_T5*R_SZ(sp)
STREG t6, R_T6*R_SZ(sp)
STREG t7, R_T7*R_SZ(sp)
mflo k0
STREG t8, R_T8*R_SZ(sp)
STREG k0, R_MDLO*R_SZ(sp)
STREG t9, R_T9*R_SZ(sp)
mfhi k0
STREG gp, R_GP*R_SZ(sp)
STREG k0, R_MDHI*R_SZ(sp)
STREG fp, R_FP*R_SZ(sp)
.set noat
STREG AT, R_AT*R_SZ(sp)
.set at
MFC0 t0,C0_EPC /* XXX */
MFC0 t1,C0_SR
STREG t0,R_EPC*R_SZ(sp) /* XXX store EPC on the stack */
STREG t1,R_SR*R_SZ(sp) /* XXX store SR on the stack */
/* determine if an interrupt generated this exception */
MFC0 k0,C0_CAUSE
NOP
and k1,k0,CAUSE_EXCMASK
beq k1, 0, _ISR_Handler_1
_ISR_Handler_Exception:
nop
jal mips_vector_exceptions
nop
_ISR_Handler_1:
MFC0 k1,C0_SR
and k0,CAUSE_IPMASK
and k0,k1
beq k0,zero,_ISR_Handler_exit
/* external interrupt not enabled, ignore */
/* but if it's not an exception or an interrupt, */
/* Then where did it come from??? */
nop
/*
* save some or all context on stack
* may need to save some special interrupt information for exit
*
* #if ( CPU_HAS_SOFTWARE_INTERRUPT_STACK == TRUE )
* if ( _ISR_Nest_level == 0 )
* switch to software interrupt stack
* #endif
*/
/*
* _ISR_Nest_level++;
*/
LDREG t0,_ISR_Nest_level
NOP
ADD t0,t0,1
STREG t0,_ISR_Nest_level
/*
* _Thread_Dispatch_disable_level++;
*/
LDREG t1,_Thread_Dispatch_disable_level
NOP
ADD t1,t1,1
STREG t1,_Thread_Dispatch_disable_level
/*
* Call the CPU model or BSP specific routine to decode the
* interrupt source and actually vector to device ISR handlers.
*/
jal mips_vector_isr_handlers
nop
/*
* --_ISR_Nest_level;
*/
LDREG t2,_ISR_Nest_level
NOP
ADD t2,t2,-1
STREG t2,_ISR_Nest_level
/*
* --_Thread_Dispatch_disable_level;
*/
LDREG t1,_Thread_Dispatch_disable_level
NOP
ADD t1,t1,-1
STREG t1,_Thread_Dispatch_disable_level
/*
* if ( _Thread_Dispatch_disable_level || _ISR_Nest_level )
* goto the label "exit interrupt (simple case)"
*/
or t0,t2,t1
bne t0,zero,_ISR_Handler_exit
nop
/*
* #if ( CPU_HAS_SOFTWARE_INTERRUPT_STACK == TRUE )
* restore stack
* #endif
*
* if ( !_Context_Switch_necessary && !_ISR_Signals_to_thread_executing )
* goto the label "exit interrupt (simple case)"
*/
LDREG t0,_Context_Switch_necessary
LDREG t1,_ISR_Signals_to_thread_executing
NOP
or t0,t0,t1
beq t0,zero,_ISR_Handler_exit
nop
/*
* call _Thread_Dispatch() or prepare to return to _ISR_Dispatch
*/
LDREG t0,R_SR*R_SZ(sp) /* XXX restore SR on the stack */
NOP
MTC0 t0,C0_SR
la t0,_ISR_Dispatch
MTC0 t0, C0_EPC /* XXX */
NOP
j t0
rfe /* go to _ISR_Dispatch in task mode */
_ISR_Dispatch:
jal _Thread_Dispatch
nop
li t0,0x10011001
sw t0,0x8001ff00
nop
/*
* prepare to get out of interrupt
* return from interrupt (maybe to _ISR_Dispatch)
*
* LABEL "exit interrupt (simple case):"
* prepare to get out of interrupt
* return from interrupt
*/
_ISR_Handler_exit:
LDREG t0, R_EPC*R_SZ(sp) /* XXX restore EPC on the stack */
LDREG t1, R_SR*R_SZ(sp) /* XXX restore SR on the stack */
MTC0 t0, C0_EPC /* XXX */
MTC0 t1, C0_SR
/* restore interrupt context from stack */
LDREG k0, R_MDLO*R_SZ(sp)
LDREG a2, R_A2*R_SZ(sp)
mtlo k0
LDREG k0, R_MDHI*R_SZ(sp)
LDREG a3, R_A3*R_SZ(sp)
mthi k0
LDREG t0, R_T0*R_SZ(sp)
LDREG t1, R_T1*R_SZ(sp)
LDREG t2, R_T2*R_SZ(sp)
LDREG t3, R_T3*R_SZ(sp)
LDREG t4, R_T4*R_SZ(sp)
LDREG t5, R_T5*R_SZ(sp)
LDREG t6, R_T6*R_SZ(sp)
LDREG t7, R_T7*R_SZ(sp)
LDREG t8, R_T8*R_SZ(sp)
LDREG t9, R_T9*R_SZ(sp)
LDREG gp, R_GP*R_SZ(sp)
LDREG fp, R_FP*R_SZ(sp)
LDREG ra, R_RA*R_SZ(sp)
LDREG a0, R_A0*R_SZ(sp)
LDREG a1, R_A1*R_SZ(sp)
LDREG v1, R_V1*R_SZ(sp)
LDREG v0, R_V0*R_SZ(sp)
.set noat
LDREG AT, R_AT*R_SZ(sp)
.set at
ADDIU sp,sp,EXCP_STACK_SIZE
MFC0 k0, C0_EPC
NOP
j k0
rfe /* Might not need to do RFE here... */
nop
.set reorder
ENDFRAME(_ISR_Handler)
FRAME(mips_break,sp,0,ra)
#if 1
break 0x0
j mips_break
#else
j ra
#endif
nop
ENDFRAME(mips_break)