/*/////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2004 Xilinx, Inc. All rights reserved. // // Xilinx, Inc. // XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A // COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS // ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR // STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION // IS FREE FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE // FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. // XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO // THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO // ANY WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE // FROM CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY // AND FITNESS FOR A PARTICULAR PURPOSE. // // $Id: hw_exception_handler.S,v 1.1.2.2 2010/07/01 19:48:59 haibing Exp $ // //////////////////////////////////////////////////////////////////////////////*/ /* * Microblaze HW Exception Handler * - Non self-modifying exception handler for the following exception conditions * - Unalignment * - Instruction bus error * - Data bus error * - Illegal instruction opcode * - Divide-by-zero */ #include "microblaze_exceptions_g.h" /* Helpful Macros */ #define EX_HANDLER_STACK_SIZ (4*21) #define RMSR_OFFSET (20 * 4) #define R17_OFFSET (0) #define REG_OFFSET(regnum) (4 * (regnum + 1)) #define NUM_TO_REG(num) r ## num #define R3_TO_STACK(regnum) swi r3, r1, REG_OFFSET(regnum) #define R3_FROM_STACK(regnum) lwi r3, r1, REG_OFFSET(regnum) #define PUSH_REG(regnum) swi NUM_TO_REG(regnum), r1, REG_OFFSET(regnum) #define POP_REG(regnum) lwi NUM_TO_REG(regnum), r1, REG_OFFSET(regnum) /* Uses r5 */ #define PUSH_MSR \ mfs r5, rmsr; \ swi r5, r1, RMSR_OFFSET; #define PUSH_MSR_AND_ENABLE_EXC \ mfs r5, rmsr; \ swi r5, r1, RMSR_OFFSET; \ ori r5, r5, 0x100; /* Turn ON the EE bit*/ \ mts rmsr, r5; /* Uses r5 */ #define POP_MSR \ lwi r5, r1, RMSR_OFFSET; \ mts rmsr, r5; /* Push r17 */ #define PUSH_R17 swi r17, r1, R17_OFFSET /* Pop r17 */ #define POP_R17 lwi r17, r1, R17_OFFSET #define LWREG_NOP \ bri ex_handler_unhandled; \ nop; #define SWREG_NOP \ bri ex_handler_unhandled; \ nop; /* r3 is the source */ #define R3_TO_LWREG_V(regnum) \ R3_TO_STACK (regnum); \ bri ex_handler_done; /* r3 is the source */ #define R3_TO_LWREG(regnum) \ or NUM_TO_REG (regnum), r0, r3; \ bri ex_handler_done; /* r3 is the target */ #define SWREG_TO_R3_V(regnum) \ R3_FROM_STACK (regnum); \ bri ex_sw_tail; /* r3 is the target */ #define SWREG_TO_R3(regnum) \ or r3, r0, NUM_TO_REG (regnum); \ bri ex_sw_tail; /* regnum is the source */ #define FP_EX_OPB_SAVE(regnum) \ swi NUM_TO_REG (regnum), r0, mb_fpex_op_b; \ nop; \ bri handle_fp_ex_opa; /* regnum is the source */ #define FP_EX_OPB_SAVE_V(regnum) \ R3_FROM_STACK (regnum); \ swi r3, r0, mb_fpex_op_b; \ bri handle_fp_ex_opa; /* regnum is the source */ #define FP_EX_OPA_SAVE(regnum) \ swi NUM_TO_REG (regnum), r0, mb_fpex_op_a; \ nop; \ bri handle_fp_ex_done; /* regnum is the source */ #define FP_EX_OPA_SAVE_V(regnum) \ R3_FROM_STACK (regnum); \ swi r3, r0, mb_fpex_op_a; \ bri handle_fp_ex_done; #define FP_EX_UNHANDLED \ bri fp_ex_unhandled; \ nop; \ nop; /* ESR masks */ #define ESR_EXC_MASK 0x0000001F #define ESR_REG_MASK 0x000003E0 #define ESR_LW_SW_MASK 0x00000400 #define ESR_WORD_MASK 0x00000800 #define ESR_DS_MASK 0x00001000 /* Extern declarations */ .extern XNullHandler #ifdef MICROBLAZE_EXCEPTIONS_ENABLED /* If exceptions are enabled in the processor */ /* * hw_exception_handler - Handler for unaligned exceptions * Exception handler notes: * - Does not handle exceptions other than unaligned exceptions * - Does not handle exceptions during load into r17, r1, r0. * - Does not handle exceptions during store from r17 (cannot be done) and r1 (slows down common case) * * Relevant register structures * * EAR - |----|----|----|----|----|----|----|----| * - < ## 32 bit faulting address ## > * * ESR - |----|----|----|----|----| - | - |-----|-----| * - W S REG EXC * * * STACK FRAME STRUCTURE * --------------------- * * +-------------+ + 0 * | r17 | * +-------------+ + 4 * | Args for | * | next func | * +-------------+ + 8 * | r1 | * | . | * | . | * | . | * | . | * | r18 | * +-------------+ + 80 * | MSR | * +-------------+ + 84 * | . | * | . | */ .global _hw_exception_handler .section .text .align 2 .ent _hw_exception_handler .type _hw_exception_handler, @function _hw_exception_handler: addik r1, r1, -(EX_HANDLER_STACK_SIZ); /* Create stack frame */ PUSH_REG(3); PUSH_REG(4); PUSH_REG(5); PUSH_REG(6); #ifdef MICROBLAZE_CAN_HANDLE_EXCEPTIONS_IN_DELAY_SLOTS mfs r6, resr; andi r6, r6, ESR_DS_MASK; beqi r6, ex_handler_no_ds; mfs r17, rbtr; ex_handler_no_ds: #endif PUSH_R17; PUSH_MSR_AND_ENABLE_EXC; /* Exceptions enabled here. This will allow nested exceptions */ mfs r3, resr; andi r5, r3, ESR_EXC_MASK; /* Extract ESR[EXC] */ #ifndef NO_UNALIGNED_EXCEPTIONS xori r6, r5, 1; /* 00001 = Unaligned Exception */ bnei r6, handle_ex_regular; la r4, r0, MB_ExceptionVectorTable; /* Check if user has registered an unaligned exception handler */ lwi r4, r4, 8; la r6, r0, XNullHandler; /* If exceptionvectortable entry is still XNullHandler, use */ xor r6, r4, r6; /* the default exception handler */ beqi r6, handle_unaligned_ex ; handle_ex_regular: #endif /* ! NO_UNALIGNED_EXCEPTIONS */ #if defined (MICROBLAZE_FP_EXCEPTION_ENABLED) && defined (MICROBLAZE_FP_EXCEPTION_DECODE) xori r6, r5, 6; /* 00110 = FPU exception */ beqi r6, handle_fp_ex; /* Go and decode the FP exception */ #endif /* defined (MICROBLAZE_FP_EXCEPTION_ENABLED) && defined (MICROBLAZE_FP_EXCEPTION_DECODE) */ handle_other_ex: /* Handle Other exceptions here */ ori r6, r0, 20; cmp r6, r5, r6; /* >= 20 are exceptions we do not handle. */ blei r6, ex_handler_unhandled; ori r6, r0, 7; cmp r6, r5, r6; /* Convert MMU exception indices into an ordinal of 7 */ bgti r6, handle_other_ex_tail; ori r5, r0, 0x7; handle_other_ex_tail: PUSH_REG(7); /* Save other volatiles before we make procedure calls below */ PUSH_REG(8); PUSH_REG(9); PUSH_REG(10); PUSH_REG(11); PUSH_REG(12); PUSH_REG(15); PUSH_REG(18); la r4, r0, MB_ExceptionVectorTable; /* Load the Exception vector table base address */ addk r7, r5, r5; /* Calculate exception vector offset = r5 * 8 */ addk r7, r7, r7; addk r7, r7, r7; addk r7, r7, r4; /* Get pointer to exception vector */ lwi r5, r7, 4; /* Load argument to exception handler from table */ lw r7, r7, r0; /* Load vector itself here */ brald r15, r7; /* Branch to handler */ nop; POP_REG(7); /* Restore other volatiles */ POP_REG(8); POP_REG(9); POP_REG(10); POP_REG(11); POP_REG(12); POP_REG(15); POP_REG(18); bri ex_handler_done; /* Complete exception handling */ #ifndef NO_UNALIGNED_EXCEPTIONS handle_unaligned_ex: andi r6, r3, ESR_REG_MASK; /* Mask and extract the register operand */ srl r6, r6; /* r6 >> 5 */ srl r6, r6; srl r6, r6; srl r6, r6; srl r6, r6; sbi r6, r0, ex_reg_op; /* Store the register operand in a temporary location */ mfs r4, rear; andi r6, r3, ESR_LW_SW_MASK; /* Extract ESR[S] */ bnei r6, ex_sw; ex_lw: andi r6, r3, ESR_WORD_MASK; /* Extract ESR[W] */ beqi r6, ex_lhw; lbui r5, r4, 0; /* Exception address in r4 */ sbi r5, r0, ex_tmp_data_loc_0; /* Load a word, byte-by-byte from destination address and save it in tmp space */ lbui r5, r4, 1; sbi r5, r0, ex_tmp_data_loc_1; lbui r5, r4, 2; sbi r5, r0, ex_tmp_data_loc_2; lbui r5, r4, 3; sbi r5, r0, ex_tmp_data_loc_3; lwi r3, r0, ex_tmp_data_loc_0; /* Get the destination register value into r3 */ bri ex_lw_tail; ex_lhw: lbui r5, r4, 0; /* Exception address in r4 */ sbi r5, r0, ex_tmp_data_loc_0; /* Load a half-word, byte-by-byte from destination address and save it in tmp space */ lbui r5, r4, 1; sbi r5, r0, ex_tmp_data_loc_1; lhui r3, r0, ex_tmp_data_loc_0; /* Get the destination register value into r3 */ ex_lw_tail: lbui r5, r0, ex_reg_op; /* Get the destination register number into r5 */ la r6, r0, lw_table; /* Form load_word jump table offset (lw_table + (8 * regnum)) */ addk r5, r5, r5; addk r5, r5, r5; addk r5, r5, r5; addk r5, r5, r6; bra r5; ex_lw_end: /* Exception handling of load word, ends */ ex_sw: lbui r5, r0, ex_reg_op; /* Get the destination register number into r5 */ la r6, r0, sw_table; /* Form store_word jump table offset (sw_table + (8 * regnum)) */ add r5, r5, r5; add r5, r5, r5; add r5, r5, r5; add r5, r5, r6; bra r5; ex_sw_tail: mfs r6, resr; andi r6, r6, ESR_WORD_MASK; /* Extract ESR[W] */ beqi r6, ex_shw; swi r3, r0, ex_tmp_data_loc_0; lbui r3, r0, ex_tmp_data_loc_0; /* Store the word, byte-by-byte into destination address */ sbi r3, r4, 0; lbui r3, r0, ex_tmp_data_loc_1; sbi r3, r4, 1; lbui r3, r0, ex_tmp_data_loc_2; sbi r3, r4, 2; lbui r3, r0, ex_tmp_data_loc_3; sbi r3, r4, 3; bri ex_handler_done; ex_shw: swi r3, r0, ex_tmp_data_loc_0; /* Store the lower half-word, byte-by-byte into destination address */ #ifdef __LITTLE_ENDIAN__ lbui r3, r0, ex_tmp_data_loc_0; #else lbui r3, r0, ex_tmp_data_loc_2; #endif sbi r3, r4, 0; #ifdef __LITTLE_ENDIAN__ lbui r3, r0, ex_tmp_data_loc_1; #else lbui r3, r0, ex_tmp_data_loc_3; #endif sbi r3, r4, 1; ex_sw_end: /* Exception handling of store word, ends. */ bri ex_handler_done; #endif /* !NO_UNALIGNED_EXCEPTIONS */ #if defined (MICROBLAZE_FP_EXCEPTION_ENABLED) && defined (MICROBLAZE_FP_EXCEPTION_DECODE) handle_fp_ex: addik r3, r17, -4; /* r17 contains (addr of exception causing FP instruction + 4) */ lw r4, r0, r3; /* We might find ourselves in a spot here. Unguaranteed load */ handle_fp_ex_opb: la r6, r0, fp_table_opb; /* Decode opB and store its value in mb_fpex_op_b */ srl r4, r4; srl r4, r4; srl r4, r4; srl r4, r4; srl r4, r4; srl r4, r4; srl r4, r4; srl r4, r4; srl r4, r4; srl r4, r4; srl r4, r4; andi r3, r4, 0x1F; add r3, r3, r3; /* Calculate (fp_table_opb + (regno * 12)) in r5 */ add r3, r3, r3; add r5, r3, r3; add r5, r5, r3; add r5, r5, r6; bra r5; handle_fp_ex_opa: la r6, r0, fp_table_opa; /* Decode opA and store its value in mb_fpex_op_a */ srl r4, r4; srl r4, r4; srl r4, r4; srl r4, r4; srl r4, r4; andi r3, r4, 0x1F; add r3, r3, r3; /* Calculate (fp_table_opb + (regno * 12)) in r5 */ add r3, r3, r3; add r5, r3, r3; add r5, r5, r3; add r5, r5, r6; bra r5; handle_fp_ex_done: ori r5, r0, 6; /* Set exception number back to 6 */ bri handle_other_ex_tail; fp_ex_unhandled: bri 0; #endif /* defined (MICROBLAZE_FP_EXCEPTION_ENABLED) && defined (MICROBLAZE_FP_EXCEPTION_DECODE) */ ex_handler_done: POP_R17; POP_MSR; POP_REG(3); POP_REG(4); POP_REG(5); POP_REG(6); rted r17, 0 addik r1, r1, (EX_HANDLER_STACK_SIZ); /* Restore stack frame */ ex_handler_unhandled: bri 0 /* UNHANDLED. TRAP HERE */ .end _hw_exception_handler #ifndef NO_UNALIGNED_EXCEPTIONS /* * hw_exception_handler Jump Table * - Contains code snippets for each register that caused the unaligned exception. * - Hence exception handler is NOT self-modifying * - Separate table for load exceptions and store exceptions. * - Each table is of size: (8 * 32) = 256 bytes */ .section .text .align 4 lw_table: lw_r0: R3_TO_LWREG (0); lw_r1: LWREG_NOP; lw_r2: R3_TO_LWREG (2); lw_r3: R3_TO_LWREG_V (3); lw_r4: R3_TO_LWREG_V (4); lw_r5: R3_TO_LWREG_V (5); lw_r6: R3_TO_LWREG_V (6); lw_r7: R3_TO_LWREG (7); lw_r8: R3_TO_LWREG (8); lw_r9: R3_TO_LWREG (9); lw_r10: R3_TO_LWREG (10); lw_r11: R3_TO_LWREG (11); lw_r12: R3_TO_LWREG (12); lw_r13: R3_TO_LWREG (13); lw_r14: R3_TO_LWREG (14); lw_r15: R3_TO_LWREG (15); lw_r16: R3_TO_LWREG (16); lw_r17: LWREG_NOP; lw_r18: R3_TO_LWREG (18); lw_r19: R3_TO_LWREG (19); lw_r20: R3_TO_LWREG (20); lw_r21: R3_TO_LWREG (21); lw_r22: R3_TO_LWREG (22); lw_r23: R3_TO_LWREG (23); lw_r24: R3_TO_LWREG (24); lw_r25: R3_TO_LWREG (25); lw_r26: R3_TO_LWREG (26); lw_r27: R3_TO_LWREG (27); lw_r28: R3_TO_LWREG (28); lw_r29: R3_TO_LWREG (29); lw_r30: R3_TO_LWREG (30); lw_r31: R3_TO_LWREG (31); sw_table: sw_r0: SWREG_TO_R3 (0); sw_r1: SWREG_NOP; sw_r2: SWREG_TO_R3 (2); sw_r3: SWREG_TO_R3_V (3); sw_r4: SWREG_TO_R3_V (4); sw_r5: SWREG_TO_R3_V (5); sw_r6: SWREG_TO_R3_V (6); sw_r7: SWREG_TO_R3 (7); sw_r8: SWREG_TO_R3 (8); sw_r9: SWREG_TO_R3 (9); sw_r10: SWREG_TO_R3 (10); sw_r11: SWREG_TO_R3 (11); sw_r12: SWREG_TO_R3 (12); sw_r13: SWREG_TO_R3 (13); sw_r14: SWREG_TO_R3 (14); sw_r15: SWREG_TO_R3 (15); sw_r16: SWREG_TO_R3 (16); sw_r17: SWREG_NOP; sw_r18: SWREG_TO_R3 (18); sw_r19: SWREG_TO_R3 (19); sw_r20: SWREG_TO_R3 (20); sw_r21: SWREG_TO_R3 (21); sw_r22: SWREG_TO_R3 (22); sw_r23: SWREG_TO_R3 (23); sw_r24: SWREG_TO_R3 (24); sw_r25: SWREG_TO_R3 (25); sw_r26: SWREG_TO_R3 (26); sw_r27: SWREG_TO_R3 (27); sw_r28: SWREG_TO_R3 (28); sw_r29: SWREG_TO_R3 (29); sw_r30: SWREG_TO_R3 (30); sw_r31: SWREG_TO_R3 (31); /* Temporary data structures used in the handler */ .section .data .align 2 ex_tmp_data_loc_0: .byte 0 ex_tmp_data_loc_1: .byte 0 ex_tmp_data_loc_2: .byte 0 ex_tmp_data_loc_3: .byte 0 ex_reg_op: .byte 0 #endif /* ! NO_UNALIGNED_EXCEPTIONS */ #if defined (MICROBLAZE_FP_EXCEPTION_ENABLED) && defined (MICROBLAZE_FP_EXCEPTION_DECODE) /* * FP exception decode jump table. * - Contains code snippets for each register that could have been a source operand for an excepting FP instruction * - Hence exception handler is NOT self-modifying * - Separate table for opA and opB * - Each table is of size: (12 * 32) = 384 bytes */ .section .text .align 4 fp_table_opa: opa_r0: FP_EX_OPA_SAVE (0); opa_r1: FP_EX_UNHANDLED; opa_r2: FP_EX_OPA_SAVE (2); opa_r3: FP_EX_OPA_SAVE_V (3); opa_r4: FP_EX_OPA_SAVE_V (4); opa_r5: FP_EX_OPA_SAVE_V (5); opa_r6: FP_EX_OPA_SAVE_V (6); opa_r7: FP_EX_OPA_SAVE (7); opa_r8: FP_EX_OPA_SAVE (8); opa_r9: FP_EX_OPA_SAVE (9); opa_r10: FP_EX_OPA_SAVE (10); opa_r11: FP_EX_OPA_SAVE (11); opa_r12: FP_EX_OPA_SAVE (12); opa_r13: FP_EX_OPA_SAVE (13); opa_r14: FP_EX_UNHANDLED; opa_r15: FP_EX_UNHANDLED; opa_r16: FP_EX_UNHANDLED; opa_r17: FP_EX_UNHANDLED; opa_r18: FP_EX_OPA_SAVE (18); opa_r19: FP_EX_OPA_SAVE (19); opa_r20: FP_EX_OPA_SAVE (20); opa_r21: FP_EX_OPA_SAVE (21); opa_r22: FP_EX_OPA_SAVE (22); opa_r23: FP_EX_OPA_SAVE (23); opa_r24: FP_EX_OPA_SAVE (24); opa_r25: FP_EX_OPA_SAVE (25); opa_r26: FP_EX_OPA_SAVE (26); opa_r27: FP_EX_OPA_SAVE (27); opa_r28: FP_EX_OPA_SAVE (28); opa_r29: FP_EX_OPA_SAVE (29); opa_r30: FP_EX_OPA_SAVE (30); opa_r31: FP_EX_OPA_SAVE (31); fp_table_opb: opb_r0: FP_EX_OPB_SAVE (0); opb_r1: FP_EX_UNHANDLED; opb_r2: FP_EX_OPB_SAVE (2); opb_r3: FP_EX_OPB_SAVE_V (3); opb_r4: FP_EX_OPB_SAVE_V (4); opb_r5: FP_EX_OPB_SAVE_V (5); opb_r6: FP_EX_OPB_SAVE_V (6); opb_r7: FP_EX_OPB_SAVE (7); opb_r8: FP_EX_OPB_SAVE (8); opb_r9: FP_EX_OPB_SAVE (9); opb_r10: FP_EX_OPB_SAVE (10); opb_r11: FP_EX_OPB_SAVE (11); opb_r12: FP_EX_OPB_SAVE (12); opb_r13: FP_EX_OPB_SAVE (13); opb_r14: FP_EX_UNHANDLED; opb_r15: FP_EX_UNHANDLED; opb_r16: FP_EX_UNHANDLED; opb_r17: FP_EX_UNHANDLED; opb_r18: FP_EX_OPB_SAVE (18); opb_r19: FP_EX_OPB_SAVE (19); opb_r20: FP_EX_OPB_SAVE (20); opb_r21: FP_EX_OPB_SAVE (21); opb_r22: FP_EX_OPB_SAVE (22); opb_r23: FP_EX_OPB_SAVE (23); opb_r24: FP_EX_OPB_SAVE (24); opb_r25: FP_EX_OPB_SAVE (25); opb_r26: FP_EX_OPB_SAVE (26); opb_r27: FP_EX_OPB_SAVE (27); opb_r28: FP_EX_OPB_SAVE (28); opb_r29: FP_EX_OPB_SAVE (29); opb_r30: FP_EX_OPB_SAVE (30); opb_r31: FP_EX_OPB_SAVE (31); #endif /* defined (MICROBLAZE_FP_EXCEPTION_ENABLED) && defined (MICROBLAZE_FP_EXCEPTION_DECODE) */ #if defined(MICROBLAZE_FP_EXCEPTION_ENABLED) && defined(MICROBLAZE_FP_EXCEPTION_DECODE) /* This is where we store the opA and opB of the last excepting FP instruction */ .section .data .align 2 .global mb_fpex_op_a .global mb_fpex_op_b mb_fpex_op_a: .long 0 mb_fpex_op_b: .long 0 #endif /* defined (MICROBLAZE_FP_EXCEPTION_ENABLED) && defined (MICROBLAZE_FP_EXCEPTION_DECODE) */ /* The exception vector table */ .section .data .align 2 .global MB_ExceptionVectorTable MB_ExceptionVectorTable: .long XNullHandler .long 0 /* -- FSL Exception -- */ .long XNullHandler .long 1 /* -- Unaligned Access Exception -- */ .long XNullHandler .long 2 /* -- Illegal Opcode Exception -- */ .long XNullHandler .long 3 /* -- IOPB Exception -- */ .long XNullHandler .long 4 /* -- DOPB Exception -- */ .long XNullHandler .long 5 /* -- Div-by-0 Exception -- */ .long XNullHandler .long 6 /* -- FPU Exception -- */ .long XNullHandler .long 7 /* -- MMU Exceptions -- */ #else /* Dummy exception handler, in case exceptions are not present in the processor */ .global _hw_exception_handler .section .text .align 2 .ent _hw_exception_handler _hw_exception_handler: bri 0; .end _hw_exception_handler #endif /* MICROBLAZE_EXCEPTIONS_ENABLED */