/* Queue a division exception. */ enum frv_dtt frvbf_division_exception (SIM_CPU *current_cpu, enum frv_dtt dtt, int target_index, int non_excepting) { /* If there was an overflow and it is masked, then record it in ISR.AEXC. */ USI isr = GET_ISR (); if ((dtt & FRV_DTT_OVERFLOW) && GET_ISR_EDE (isr)) { dtt &= ~FRV_DTT_OVERFLOW; SET_ISR_AEXC (isr); SET_ISR (isr); } if (dtt != FRV_DTT_NO_EXCEPTION) { if (non_excepting) { /* Non excepting instruction, simply set the NE flag for the target register. */ SI NE_flags[2]; GET_NE_FLAGS (NE_flags, H_SPR_GNER0); SET_NE_FLAG (NE_flags, target_index); SET_NE_FLAGS (H_SPR_GNER0, NE_flags); } else frv_queue_division_exception_interrupt (current_cpu, dtt); } return dtt; }
/* Process any pending interrupt(s) after a group of parallel insns. */ void frv_process_interrupts (SIM_CPU *current_cpu) { SI NE_flags[2]; /* Need to save the pc here because writeback may change it (due to a branch). */ IADDR pc = CPU_PC_GET (current_cpu); /* Check for a reset before anything else. */ if (check_reset (current_cpu, pc)) return; /* First queue the writes for any accumulated NE flags. */ if (frv_interrupt_state.f_ne_flags[0] != 0 || frv_interrupt_state.f_ne_flags[1] != 0) { GET_NE_FLAGS (NE_flags, H_SPR_FNER0); NE_flags[0] |= frv_interrupt_state.f_ne_flags[0]; NE_flags[1] |= frv_interrupt_state.f_ne_flags[1]; SET_NE_FLAGS (H_SPR_FNER0, NE_flags); } /* If there is no interrupt pending, then perform parallel writeback. This may cause an interrupt. */ if (frv_interrupt_state.queue_index <= 0) frvbf_perform_writeback (current_cpu); /* If there is an interrupt pending, then process it. */ if (frv_interrupt_state.queue_index > 0) handle_interrupt (current_cpu, pc); }
void frvbf_commit (SIM_CPU *current_cpu, SI target_index, BI is_float) { SI NE_base; SI NE_flags[2]; BI NE_flag; int exception; int hi_available; int lo_available; USI necr; FRV_REGISTER_CONTROL *control; /* Check for availability of the target register(s). */ which_registers_available (current_cpu, & hi_available, & lo_available, is_float); /* Check to make sure that the target register is available. */ if (! frv_check_register_access (current_cpu, target_index, hi_available, lo_available)) return; /* Determine whether we're working with GR or FR registers. */ if (is_float) NE_base = H_SPR_FNER0; else NE_base = H_SPR_GNER0; /* Determine whether a ne exception is pending. */ GET_NE_FLAGS (NE_flags, NE_base); if (target_index >= 0) NE_flag = GET_NE_FLAG (NE_flags, target_index); else { NE_flag = hi_available && NE_flags[0] != 0 || lo_available && NE_flags[1] != 0; } /* Always clear the appropriate NE flags. */ clear_ne_flags (current_cpu, target_index, hi_available, lo_available, NE_base); control = CPU_REGISTER_CONTROL (current_cpu); if (control->spr[H_SPR_NECR].implemented) { necr = GET_NECR (); if (GET_NECR_VALID (necr) && GET_NECR_ELOS (necr) && NE_flag) { /* Clear the appropriate NESR and NEEAR registers. */ clear_nesr_neear (current_cpu, target_index, is_float); frv_queue_program_interrupt (current_cpu, FRV_COMMIT_EXCEPTION); } } }
static void clear_ne_flags ( SIM_CPU *current_cpu, SI target_index, int hi_available, int lo_available, SI NE_base ) { SI NE_flags[2]; int exception; GET_NE_FLAGS (NE_flags, NE_base); if (target_index >= 0) CLEAR_NE_FLAG (NE_flags, target_index); else { if (lo_available) NE_flags[1] = 0; if (hi_available) NE_flags[0] = 0; } SET_NE_FLAGS (NE_base, NE_flags); }
BI frvbf_check_non_excepting_load ( SIM_CPU *current_cpu, SI base_index, SI disp_index, SI target_index, SI immediate_disp, QI data_size, BI is_float ) { BI rc = 1; /* perform the load. */ SIM_DESC sd = CPU_STATE (current_cpu); int daec = 0; int rec = 0; int ec = 0; USI necr; int do_elos; SI NE_flags[2]; SI NE_base; SI nesr; SI ne_index; FRV_REGISTER_CONTROL *control; SI address = GET_H_GR (base_index); if (disp_index >= 0) address += GET_H_GR (disp_index); else address += immediate_disp; /* Check for interrupt factors. */ switch (data_size) { case NESR_UQI_SIZE: case NESR_QI_SIZE: break; case NESR_UHI_SIZE: case NESR_HI_SIZE: if (address & 1) ec = 1; break; case NESR_SI_SIZE: if (address & 3) ec = 1; break; case NESR_DI_SIZE: if (address & 7) ec = 1; if (target_index & 1) rec = 1; break; case NESR_XI_SIZE: if (address & 0xf) ec = 1; if (target_index & 3) rec = 1; break; default: { IADDR pc = GET_H_PC (); sim_engine_abort (sd, current_cpu, pc, "check_non_excepting_load: Incorrect data_size\n"); break; } } control = CPU_REGISTER_CONTROL (current_cpu); if (control->spr[H_SPR_NECR].implemented) { necr = GET_NECR (); do_elos = GET_NECR_VALID (necr) && GET_NECR_ELOS (necr); } else do_elos = 0; /* NECR, NESR, NEEAR are only implemented for the full frv machine. */ if (do_elos) { ne_index = next_available_nesr (current_cpu, NO_NESR); if (ne_index == NO_NESR) { IADDR pc = GET_H_PC (); sim_engine_abort (sd, current_cpu, pc, "No available NESR register\n"); } /* Fill in the basic fields of the NESR. */ nesr = GET_NESR (ne_index); SET_NESR_VALID (nesr); SET_NESR_EAV (nesr); SET_NESR_DRN (nesr, target_index); SET_NESR_SIZE (nesr, data_size); SET_NESR_NEAN (nesr, ne_index); if (is_float) SET_NESR_FR (nesr); else CLEAR_NESR_FR (nesr); /* Set the corresponding NEEAR. */ SET_NEEAR (ne_index, address); SET_NESR_DAEC (nesr, 0); SET_NESR_REC (nesr, 0); SET_NESR_EC (nesr, 0); } /* Set the NE flag corresponding to the target register if an interrupt factor was detected. daec is not checked here yet, but is declared for future reference. */ if (is_float) NE_base = H_SPR_FNER0; else NE_base = H_SPR_GNER0; GET_NE_FLAGS (NE_flags, NE_base); if (rec) { SET_NE_FLAG (NE_flags, target_index); if (do_elos) SET_NESR_REC (nesr, NESR_REGISTER_NOT_ALIGNED); } if (ec) { SET_NE_FLAG (NE_flags, target_index); if (do_elos) SET_NESR_EC (nesr, NESR_MEM_ADDRESS_NOT_ALIGNED); } if (do_elos) SET_NESR (ne_index, nesr); /* If no interrupt factor was detected then set the NE flag on the target register if the NE flag on one of the input registers is already set. */ if (! rec && ! ec && ! daec) { BI ne_flag = GET_NE_FLAG (NE_flags, base_index); if (disp_index >= 0) ne_flag |= GET_NE_FLAG (NE_flags, disp_index); if (ne_flag) { SET_NE_FLAG (NE_flags, target_index); rc = 0; /* Do not perform the load. */ } else CLEAR_NE_FLAG (NE_flags, target_index); } SET_NE_FLAGS (NE_base, NE_flags); return rc; /* perform the load? */ }