From e6dec71c2703bd6b54e957746931b532d2fb44fa Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Fri, 1 Feb 2002 15:00:30 +0000 Subject: 2001-02-01 Greg Menke * cpu.c: Enhancements and fixes for modifying the SR when changing the interrupt level. * cpu_asm.S: Fixed handling of FP enable bit so it is properly managed on a per-task basis, improved handling of interrupt levels, and made deferred FP contexts work on the MIPS. * rtems/score/cpu.h: Modified to support above changes. --- cpukit/score/cpu/mips/ChangeLog | 9 ++ cpukit/score/cpu/mips/cpu.c | 51 +++--- cpukit/score/cpu/mips/cpu_asm.S | 269 +++++++++++++++++++------------- cpukit/score/cpu/mips/rtems/score/cpu.h | 63 +++++--- 4 files changed, 244 insertions(+), 148 deletions(-) (limited to 'cpukit') diff --git a/cpukit/score/cpu/mips/ChangeLog b/cpukit/score/cpu/mips/ChangeLog index c69cde159a..ff63919afb 100644 --- a/cpukit/score/cpu/mips/ChangeLog +++ b/cpukit/score/cpu/mips/ChangeLog @@ -1,3 +1,12 @@ +2001-02-01 Greg Menke + + * cpu.c: Enhancements and fixes for modifying the SR when changing + the interrupt level. + * cpu_asm.S: Fixed handling of FP enable bit so it is properly + managed on a per-task basis, improved handling of interrupt levels, + and made deferred FP contexts work on the MIPS. + * rtems/score/cpu.h: Modified to support above changes. + 2002-01-28 Ralf Corsepius * rtems/Makefile.am: Removed. diff --git a/cpukit/score/cpu/mips/cpu.c b/cpukit/score/cpu/mips/cpu.c index 9a5546f683..d9093ae85c 100644 --- a/cpukit/score/cpu/mips/cpu.c +++ b/cpukit/score/cpu/mips/cpu.c @@ -82,51 +82,66 @@ unsigned32 _CPU_ISR_Get_level( void ) mips_get_sr(sr); #if __mips == 3 - return ((sr & SR_EXL) >> 1); +/* EXL bit and shift down hardware ints into bits 1 thru 6 */ + return ((sr & SR_EXL) >> 1) + ((sr & 0xfc00) >> 9); #elif __mips == 1 - return ((sr & SR_IEC) ? 0 : 1); +/* IEC bit and shift down hardware ints into bits 1 thru 6 */ + return (sr & SR_IEC) + ((sr & 0xfc00) >> 9); #else #error "CPU ISR level: unknown MIPS level for SR handling" #endif } + void _CPU_ISR_Set_level( unsigned32 new_level ) { - unsigned int sr; + unsigned int sr, srbits; + + /* + ** mask off the int level bits only so we can + ** preserve software int settings and FP enable + ** for this thread. Note we don't force software ints + ** enabled when changing level, they were turned on + ** when this task was created, but may have been turned + ** off since, so we'll just leave them alone. + */ + mips_get_sr(sr); #if __mips == 3 + mips_set_sr(sr & ~SR_IE); /* first disable ie bit (recommended) */ + + srbits = sr & ~(0xfc00 | SR_EXL | SR_IE); + + sr = srbits | ((new_level==0)? (0xfc00 | SR_EXL | SR_IE): \ + (((new_level<<9) & 0xfc000) | \ + (new_level & 1)?(SR_EXL | SR_IE):0)); +/* if ( (new_level & SR_EXL) == (sr & SR_EXL) ) return; if ( (new_level & SR_EXL) == 0 ) { - sr &= ~SR_EXL; /* clear the EXL bit */ + sr &= ~SR_EXL; * clear the EXL bit * mips_set_sr(sr); } else { - sr &= ~SR_IE; - mips_set_sr(sr); /* first disable ie bit (recommended) */ - sr |= SR_EXL|SR_IE; /* enable exception level */ - mips_set_sr(sr); /* first disable ie bit (recommended) */ + sr |= SR_EXL|SR_IE; * enable exception level * + mips_set_sr(sr); * first disable ie bit (recommended) * } +*/ #elif __mips == 1 - if ( (new_level & SR_IEC) == (sr & SR_IEC) ) - return; - - sr &= ~SR_IEC; /* clear the IEC bit */ - if ( !new_level ) - sr |= SR_IEC; /* enable interrupts */ - - mips_set_sr(sr); + mips_set_sr( (sr & ~SR_IEC) ); + srbits = sr & ~(0xfc00 | SR_IEC); + sr = srbits | ((new_level==0)?0xfc01:( ((new_level<<9) & 0xfc000) | (new_level & 1))); #else #error "CPU ISR level: unknown MIPS level for SR handling" #endif - + mips_set_sr( sr ); } /*PAGE @@ -153,7 +168,7 @@ void _CPU_ISR_install_raw_handler( * table used by the CPU to dispatch interrupt handlers. * * Because all interrupts are vectored through the same exception handler - * this is not necessary on this port. + * this is not necessary on thi sport. */ } diff --git a/cpukit/score/cpu/mips/cpu_asm.S b/cpukit/score/cpu/mips/cpu_asm.S index a259bd9006..d28e3e3f7c 100644 --- a/cpukit/score/cpu/mips/cpu_asm.S +++ b/cpukit/score/cpu/mips/cpu_asm.S @@ -23,6 +23,13 @@ * 2001: Joel Sherrill continued this rework, * rewriting as much as possible in C and added the JMR3904 BSP * so testing could be performed on a simulator. + * 2001: Greg Menke , bench tested ISR + * performance, tweaking this code and the isr vectoring routines + * to reduce overhead & latencies. Added optional + * instrumentation as well. + * 2002: Greg Menke , overhauled cpu_asm.S, + * cpu.c and cpu.h to manage FP vs int only tasks, interrupt levels + * and deferred FP contexts. * * COPYRIGHT (c) 1989-2000. * On-Line Applications Research Corporation (OAR). @@ -42,8 +49,9 @@ * Mongoose-ism debug tool- but may be handy in the future so we * left it in... */ -/* #define INSTRUMENT */ - + +#define INSTRUMENT_ISR_VECTORING +//#define INSTRUMENT_EXECUTING_THREAD @@ -165,10 +173,28 @@ * ); */ -#if ( CPU_HARDWARE_FP == TRUE ) +#if ( CPU_HARDWARE_FP == FALSE ) FRAME(_CPU_Context_save_fp,sp,0,ra) .set noat - ld a1,(a0) + +#if ( CPU_USE_DEFERRED_FP_SWITCH == TRUE ) + /* + ** Make sure the FPU is on before we save state. This code is here + ** because the FPU context switch might occur when an integer + ** task is switching out w/ an FP task switching in, but the current + ** FPU state was left by a sometime previously scheduled FP task. + ** + ** In non-deferred FP context switch, if the exiting task is FP, then + ** the FPU is already on so we don't need to do this. + */ + + MFC0 t0,C0_SR + li k0,SR_CU1 + or t0,k0 + MTC0 t0,C0_SR +#endif + + ld a1,(a0) NOP swc1 $f0,FP0_OFFSET*F_SZ(a1) swc1 $f1,FP1_OFFSET*F_SZ(a1) @@ -226,10 +252,22 @@ ENDFRAME(_CPU_Context_save_fp) * ) */ -#if ( CPU_HARDWARE_FP == TRUE ) +#if ( CPU_HARDWARE_FP == FALSE ) FRAME(_CPU_Context_restore_fp,sp,0,ra) .set noat - ld a1,(a0) + + /* + ** Make sure the FPU is on before we retrieve state. This code + ** is here because the FPU context switch might occur when an + ** integer task is switching out with a FP task switching in. + */ + + MFC0 t0,C0_SR + li k0,SR_CU1 + or t0,k0 + MTC0 t0,C0_SR + + ld a1,(a0) NOP lwc1 $f0,FP0_OFFSET*4(a1) lwc1 $f1,FP1_OFFSET*4(a1) @@ -284,13 +322,12 @@ 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 */ + STREG t0,C0_SR_OFFSET*R_SZ(a0) 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 + ori t0,(SR_EXL|SR_IE) /* enable exception level to disable interrupts */ #endif + MTC0 t0,C0_SR STREG ra,RA_OFFSET*R_SZ(a0) /* save current context */ STREG sp,SP_OFFSET*R_SZ(a0) @@ -304,7 +341,7 @@ FRAME(_CPU_Context_switch,sp,0,ra) STREG s6,S6_OFFSET*R_SZ(a0) STREG s7,S7_OFFSET*R_SZ(a0) - /* + /* EPC is readonly... MFC0 t0,C0_EPC NOP STREG t0,C0_EPC_OFFSET*R_SZ(a0) @@ -323,35 +360,64 @@ _CPU_Context_switch_restore: LDREG s6,S6_OFFSET*R_SZ(a1) LDREG s7,S7_OFFSET*R_SZ(a1) - /* + /* EPC is readonly... 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 +// 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 */ +// NOP + + +/* +** Incorporate the new thread's FP coprocessor state and interrupt mask/enable +** into the status register. We jump thru the requisite hoops to ensure we +** maintain all other SR bits as global values. +** +** Get the thread's FPU enable, int mask & int enable bits. Although we keep the +** software int enables on a per-task basis, the rtems_task_create +** Interrupt Level & int level manipulation functions cannot enable/disable them, +** so they are automatically enabled for all tasks. To turn them off, a thread +** must itself manipulate the SR register. +*/ +#if __mips == 3 + li k0,(SR_CU1 | SR_IMASK | SR_EXL | SR_IE) #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 */ + li k0,(SR_CU1 | SR_IMASK | SR_IEC) #endif + and t0,k0 + + MFC0 t1,C0_SR /* grab the current SR */ + not k0 /* invert k0 so we can clear out the SR bits */ + and t1,k0 + + or t0,t1 /* setup the new task's SR value */ + MTC0 t0,C0_SR /* and load the new SR */ + NOP -_CPU_Context_1: +/* _CPU_Context_1: */ j ra NOP ENDFRAME(_CPU_Context_switch) @@ -450,42 +516,15 @@ FRAME(_ISR_Handler,sp,0,ra) MFC0 t1,C0_EPC STREG t0,R_SR*R_SZ(sp) STREG t1,R_EPC*R_SZ(sp) + - -#ifdef INSTRUMENT +#ifdef INSTRUMENT_EXECUTING_THREAD lw t2, _Thread_Executing nop sw t2, 0x8001FFF0 - - sw t0, 0x8001F050 - sw t1, 0x8001F054 - - li t0, 0xdeadbeef - li t1, 0xdeadbeef - li t2, 0xdeadbeef - - sw ra, 0x8001F000 - sw v0, 0x8001F004 - sw v1, 0x8001F008 - sw a0, 0x8001F00c - sw a1, 0x8001F010 - sw a2, 0x8001F014 - sw a3, 0x8001F018 - sw t0, 0x8001F01c - sw t1, 0x8001F020 - sw t2, 0x8001F024 - sw t3, 0x8001F028 - sw t4, 0x8001F02c - sw t5, 0x8001F030 - sw t6, 0x8001F034 - sw t7, 0x8001F038 - sw t8, 0x8001F03c - sw t9, 0x8001F040 - sw gp, 0x8001F044 - sw fp, 0x8001F048 #endif -/* determine if an interrupt generated this exception */ + /* determine if an interrupt generated this exception */ MFC0 k0,C0_CAUSE NOP @@ -515,9 +554,11 @@ _ISR_Handler_1: /* Then where did it come from??? */ beq k0,zero,_ISR_Handler_exit + - li t2,1 /* set a flag so we process interrupts */ + + /* * save some or all context on stack * may need to save some special interrupt information for exit @@ -547,10 +588,23 @@ _ISR_Handler_1: * Call the CPU model or BSP specific routine to decode the * interrupt source and actually vector to device ISR handlers. */ + +#ifdef INSTRUMENT_ISR_VECTORING + nop + li t1, 1 + sw t1, 0x8001e000 +#endif + move a0,sp jal mips_vector_isr_handlers nop - + +#ifdef INSTRUMENT_ISR_VECTORING + li t1, 0 + sw t1, 0x8001e000 + nop +#endif + /* * --_ISR_Nest_level; */ @@ -572,6 +626,14 @@ _ISR_Handler_1: or t0,t2,t1 bne t0,zero,_ISR_Handler_exit nop + + + + + + + + /* * #if ( CPU_HAS_SOFTWARE_INTERRUPT_STACK == TRUE ) * restore stack @@ -589,32 +651,35 @@ _ISR_Handler_1: -#ifdef INSTRUMENT - li t0,0x11111111 - sw t0,0x8001F104 +#ifdef INSTRUMENT_EXECUTING_THREAD + lw t0,_Thread_Executing + nop + sw t0,0x8001FFF4 #endif - - /* restore interrupt state from the saved status register, - * if the isr vectoring didn't so we allow nested interrupts to - * occur */ - - LDREG t0,R_SR*R_SZ(sp) - NOP - MTC0 t0,C0_SR - rfe - - jal _Thread_Dispatch - nop +/* +** Turn on interrupts before entering Thread_Dispatch which +** will run for a while, thus allowing new interrupts to +** be serviced. Observe the Thread_Dispatch_disable_level interlock +** that prevents recursive entry into Thread_Dispatch. +*/ + + MFC0 t0, C0_SR + NOP + or t0, SR_INTERRUPT_ENABLE_BITS + MTC0 t0, C0_SR + NOP + + jal _Thread_Dispatch + NOP -#ifdef INSTRUMENT - li t0,0x22222222 - sw t0,0x8001F100 +#ifdef INSTRUMENT_EXECUTING_THREAD + lw t0,_Thread_Executing + nop + sw t0,0x8001FFF8 #endif - - /* * prepare to get out of interrupt * return from interrupt (maybe to _ISR_Dispatch) @@ -625,16 +690,19 @@ _ISR_Handler_1: */ _ISR_Handler_exit: - LDREG t0, R_SR*R_SZ(sp) - NOP - MTC0 t0, C0_SR - -/* restore context from stack */ +/* +** Skip the SR restore because its a global register. _CPU_Context_switch_restore +** adjusts it according to each task's configuration. If we didn't dispatch, the +** SR value isn't changing, so all we need to do is return. +** +*/ -#ifdef INSTRUMENT + /* restore context from stack */ + +#ifdef INSTRUMENT_EXECUTING_THREAD lw t0,_Thread_Executing nop - sw t0, 0x8001FFF4 + sw t0, 0x8001FFFC #endif LDREG k0, R_MDLO*R_SZ(sp) @@ -660,33 +728,11 @@ _ISR_Handler_exit: LDREG a3, R_A3*R_SZ(sp) LDREG v1, R_V1*R_SZ(sp) LDREG v0, R_V0*R_SZ(sp) - -#ifdef INSTRUMENT - sw ra, 0x8001F000 - sw v0, 0x8001F004 - sw v1, 0x8001F008 - sw a0, 0x8001F00c - sw a1, 0x8001F010 - sw a2, 0x8001F014 - sw a3, 0x8001F018 - sw t0, 0x8001F01c - sw t1, 0x8001F020 - sw t2, 0x8001F024 - sw t3, 0x8001F028 - sw t4, 0x8001F02c - sw t5, 0x8001F030 - sw t6, 0x8001F034 - sw t7, 0x8001F038 - sw t8, 0x8001F03c - sw t9, 0x8001F040 - sw gp, 0x8001F044 - sw fp, 0x8001F048 -#endif LDREG k0, R_EPC*R_SZ(sp) .set noat - LDREG AT, R_AT*R_SZ(sp) + LDREG AT, R_AT*R_SZ(sp) .set at ADDIU sp,sp,EXCP_STACK_SIZE @@ -697,6 +743,7 @@ _ISR_Handler_exit: .set reorder ENDFRAME(_ISR_Handler) + FRAME(mips_break,sp,0,ra) #if 1 break 0x0 diff --git a/cpukit/score/cpu/mips/rtems/score/cpu.h b/cpukit/score/cpu/mips/rtems/score/cpu.h index a540528379..1ece7ebbe5 100644 --- a/cpukit/score/cpu/mips/rtems/score/cpu.h +++ b/cpukit/score/cpu/mips/rtems/score/cpu.h @@ -67,7 +67,7 @@ extern "C" { * one subroutine call is avoided entirely.] */ -#define CPU_INLINE_ENABLE_DISPATCH TRUE +#define CPU_INLINE_ENABLE_DISPATCH FALSE /* * Should the body of the search loops in _Thread_queue_Enqueue_priority @@ -207,7 +207,7 @@ extern "C" { * must be saved as part of the preemption. */ -#define CPU_IDLE_TASK_IS_FP FALSE +#define CPU_IDLE_TASK_IS_FP TRUE /* * Should the saving of the floating point registers be deferred @@ -554,13 +554,6 @@ extern unsigned int mips_interrupt_number_of_vectors; #define CPU_INTERRUPT_NUMBER_OF_VECTORS (mips_interrupt_number_of_vectors) #define CPU_INTERRUPT_MAXIMUM_VECTOR_NUMBER (CPU_INTERRUPT_NUMBER_OF_VECTORS - 1) -/* - * This is defined if the port has a special way to report the ISR nesting - * level. Most ports maintain the variable _ISR_Nest_level. - */ - -#define CPU_PROVIDES_ISR_IS_IN_PROGRESS FALSE - /* * Should be large enough to run all RTEMS tests. This insures * that a "reasonable" small application should not have any problems. @@ -632,7 +625,8 @@ extern unsigned int mips_interrupt_number_of_vectors; #define _CPU_ISR_Disable( _level ) \ do { \ mips_get_sr( _level ); \ - mips_set_sr( (_level) & ~SR_INTERRUPT_ENABLE_BITS ); \ + mips_set_sr( _level & ~SR_INTERRUPT_ENABLE_BITS ); \ + _level &= SR_INTERRUPT_ENABLE_BITS; \ } while(0) /* @@ -643,21 +637,22 @@ extern unsigned int mips_interrupt_number_of_vectors; #define _CPU_ISR_Enable( _level ) \ do { \ - mips_set_sr(_level); \ + unsigned int _scratch; \ + mips_get_sr( _scratch ); \ + mips_set_sr( (_scratch & ~SR_INTERRUPT_ENABLE_BITS) | (_level & SR_INTERRUPT_ENABLE_BITS) ); \ } while(0) /* * This temporarily restores the interrupt to _level before immediately * disabling them again. This is used to divide long RTEMS critical * sections into two or more parts. The parameter _level is not - * modified. + * modified. */ #define _CPU_ISR_Flash( _xlevel ) \ do { \ - unsigned int _scratch; \ _CPU_ISR_Enable( _xlevel ); \ - _CPU_ISR_Disable( _scratch ); \ + _CPU_ISR_Disable( _xlevel ); \ } while(0) /* @@ -701,22 +696,52 @@ void _CPU_ISR_Set_level( unsigned32 ); /* in cpu.c */ * point thread. This is typically only used on CPUs where the * FPU may be easily disabled by software such as on the SPARC * where the PSR contains an enable FPU bit. + * + * The per-thread status register holds the interrupt enable, FP enable + * and global interrupt enable for that thread. It means each thread can + * enable its own set of interrupts. If interrupts are disabled, RTEMS + * can still dispatch via blocking calls. This is the function of the + * "Interrupt Level", and on the MIPS, it controls the IEC bit and all + * the hardware interrupts as defined in the SR. Software ints + * are automatically enabled for all threads, as they will only occur under + * program control anyhow. Besides, the interrupt level parm is only 8 bits, + * and controlling the software ints plus the others would require 9. + * + * If the Interrupt Level is 0, all ints are on. Otherwise, the + * Interrupt Level should supply a bit pattern to impose on the SR + * interrupt bits; bit 0 applies to the mips1 IEC bit/mips3 EXL&IE, bits 1 thru 6 + * apply to the SR register Intr bits from bit 10 thru bit 15. Bit 7 of + * the Interrupt Level parameter is unused at this time. + * + * These are the only per-thread SR bits, the others are maintained + * globally & explicitly preserved by the Context Switch code in cpu_asm.s */ -#define _CPU_Context_Initialize( _the_context, _stack_base, _size, \ - _isr, _entry_point, _is_fp ) \ + +#if __mips == 3 +#define _INTON (SR_EXL | SR_IE) +#endif +#if __mips == 1 +#define _INTON SR_IEC +#endif + +#define _CPU_Context_Initialize( _the_context, _stack_base, _size, _isr, _entry_point, _is_fp ) \ { \ unsigned32 _stack_tmp = \ (unsigned32)(_stack_base) + (_size) - CPU_STACK_ALIGNMENT; \ + unsigned32 _intlvl = _isr & 0xff; \ _stack_tmp &= ~(CPU_STACK_ALIGNMENT - 1); \ (_the_context)->sp = _stack_tmp; \ (_the_context)->fp = _stack_tmp; \ (_the_context)->ra = (unsigned64)_entry_point; \ - (_the_context)->c0_sr = ((_the_context)->c0_sr & 0x0fff0000) | \ - ((_isr)?0xff00:0xff01) | \ - ((_is_fp)?0x20000000:0x10000000); \ + (_the_context)->c0_sr = ((_intlvl==0)?(0xFF00 | _INTON):( ((_intlvl<<9) & 0xfc00) | \ + 0x300 | \ + ((_intlvl & 1)?_INTON:0)) ) | \ + SR_CU0 | ((_is_fp)?SR_CU1:0); \ } + + /* * This routine is responsible for somehow restarting the currently * executing task. If you are lucky, then all that is necessary -- cgit v1.2.3