Home | History | Annotate | Line # | Download | only in bfin
dv-bfin_cec.c revision 1.1.1.10.2.1
      1 /* Blackfin Core Event Controller (CEC) model.
      2 
      3    Copyright (C) 2010-2024 Free Software Foundation, Inc.
      4    Contributed by Analog Devices, Inc.
      5 
      6    This file is part of simulators.
      7 
      8    This program is free software; you can redistribute it and/or modify
      9    it under the terms of the GNU General Public License as published by
     10    the Free Software Foundation; either version 3 of the License, or
     11    (at your option) any later version.
     12 
     13    This program is distributed in the hope that it will be useful,
     14    but WITHOUT ANY WARRANTY; without even the implied warranty of
     15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16    GNU General Public License for more details.
     17 
     18    You should have received a copy of the GNU General Public License
     19    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     20 
     21 /* This must come before any other includes.  */
     22 #include "defs.h"
     23 
     24 #include <strings.h>
     25 
     26 #include "sim-main.h"
     27 #include "sim-signal.h"
     28 #include "devices.h"
     29 #include "dv-bfin_cec.h"
     30 #include "dv-bfin_evt.h"
     31 #include "dv-bfin_mmu.h"
     32 
     33 struct bfin_cec
     34 {
     35   bu32 base;
     36   SIM_CPU *cpu;
     37   struct hw *me;
     38   struct hw_event *pending;
     39 
     40   /* Order after here is important -- matches hardware MMR layout.  */
     41   bu32 evt_override, imask, ipend, ilat, iprio;
     42 };
     43 #define mmr_base()      offsetof(struct bfin_cec, evt_override)
     44 #define mmr_offset(mmr) (offsetof(struct bfin_cec, mmr) - mmr_base())
     45 
     46 static const char * const mmr_names[] =
     47 {
     48   "EVT_OVERRIDE", "IMASK", "IPEND", "ILAT", "IPRIO",
     49 };
     50 #define mmr_name(off) mmr_names[(off) / 4]
     51 
     52 static void _cec_raise (SIM_CPU *, struct bfin_cec *, int);
     53 
     54 static void
     55 bfin_cec_hw_event_callback (struct hw *me, void *data)
     56 {
     57   struct bfin_cec *cec = data;
     58   hw_event_queue_deschedule (me, cec->pending);
     59   _cec_raise (cec->cpu, cec, -1);
     60   cec->pending = NULL;
     61 }
     62 static void
     63 bfin_cec_check_pending (struct hw *me, struct bfin_cec *cec)
     64 {
     65   if (cec->pending)
     66     return;
     67   cec->pending = hw_event_queue_schedule (me, 0, bfin_cec_hw_event_callback, cec);
     68 }
     69 static void
     70 _cec_check_pending (SIM_CPU *cpu, struct bfin_cec *cec)
     71 {
     72   bfin_cec_check_pending (cec->me, cec);
     73 }
     74 
     75 static void
     76 _cec_imask_write (struct bfin_cec *cec, bu32 value)
     77 {
     78   cec->imask = (value & IVG_MASKABLE_B) | (cec->imask & IVG_UNMASKABLE_B);
     79 }
     80 
     81 static unsigned
     82 bfin_cec_io_write_buffer (struct hw *me, const void *source,
     83 			  int space, address_word addr, unsigned nr_bytes)
     84 {
     85   struct bfin_cec *cec = hw_data (me);
     86   bu32 mmr_off;
     87   bu32 value;
     88 
     89   /* Invalid access mode is higher priority than missing register.  */
     90   if (!dv_bfin_mmr_require_32 (me, addr, nr_bytes, true))
     91     return 0;
     92 
     93   value = dv_load_4 (source);
     94   mmr_off = addr - cec->base;
     95 
     96   HW_TRACE_WRITE ();
     97 
     98   switch (mmr_off)
     99     {
    100     case mmr_offset(evt_override):
    101       cec->evt_override = value;
    102       break;
    103     case mmr_offset(imask):
    104       _cec_imask_write (cec, value);
    105       bfin_cec_check_pending (me, cec);
    106       break;
    107     case mmr_offset(ipend):
    108       /* Read-only register.  */
    109       break;
    110     case mmr_offset(ilat):
    111       dv_w1c_4 (&cec->ilat, value, 0xffee);
    112       break;
    113     case mmr_offset(iprio):
    114       cec->iprio = (value & IVG_UNMASKABLE_B);
    115       break;
    116     }
    117 
    118   return nr_bytes;
    119 }
    120 
    121 static unsigned
    122 bfin_cec_io_read_buffer (struct hw *me, void *dest,
    123 			 int space, address_word addr, unsigned nr_bytes)
    124 {
    125   struct bfin_cec *cec = hw_data (me);
    126   bu32 mmr_off;
    127   bu32 *valuep;
    128 
    129   /* Invalid access mode is higher priority than missing register.  */
    130   if (!dv_bfin_mmr_require_32 (me, addr, nr_bytes, false))
    131     return 0;
    132 
    133   mmr_off = addr - cec->base;
    134   valuep = (void *)((uintptr_t)cec + mmr_base() + mmr_off);
    135 
    136   HW_TRACE_READ ();
    137 
    138   dv_store_4 (dest, *valuep);
    139 
    140   return nr_bytes;
    141 }
    142 
    143 static const struct hw_port_descriptor bfin_cec_ports[] =
    144 {
    145   { "emu",   IVG_EMU,   0, input_port, },
    146   { "rst",   IVG_RST,   0, input_port, },
    147   { "nmi",   IVG_NMI,   0, input_port, },
    148   { "evx",   IVG_EVX,   0, input_port, },
    149   { "ivhw",  IVG_IVHW,  0, input_port, },
    150   { "ivtmr", IVG_IVTMR, 0, input_port, },
    151   { "ivg7",  IVG7,      0, input_port, },
    152   { "ivg8",  IVG8,      0, input_port, },
    153   { "ivg9",  IVG9,      0, input_port, },
    154   { "ivg10", IVG10,     0, input_port, },
    155   { "ivg11", IVG11,     0, input_port, },
    156   { "ivg12", IVG12,     0, input_port, },
    157   { "ivg13", IVG13,     0, input_port, },
    158   { "ivg14", IVG14,     0, input_port, },
    159   { "ivg15", IVG15,     0, input_port, },
    160   { NULL, 0, 0, 0, },
    161 };
    162 
    163 static void
    164 bfin_cec_port_event (struct hw *me, int my_port, struct hw *source,
    165 		     int source_port, int level)
    166 {
    167   struct bfin_cec *cec = hw_data (me);
    168   _cec_raise (cec->cpu, cec, my_port);
    169 }
    170 
    171 static void
    172 attach_bfin_cec_regs (struct hw *me, struct bfin_cec *cec)
    173 {
    174   address_word attach_address;
    175   int attach_space;
    176   unsigned attach_size;
    177   reg_property_spec reg;
    178 
    179   if (hw_find_property (me, "reg") == NULL)
    180     hw_abort (me, "Missing \"reg\" property");
    181 
    182   if (!hw_find_reg_array_property (me, "reg", 0, &reg))
    183     hw_abort (me, "\"reg\" property must contain three addr/size entries");
    184 
    185   hw_unit_address_to_attach_address (hw_parent (me),
    186 				     &reg.address,
    187 				     &attach_space, &attach_address, me);
    188   hw_unit_size_to_attach_size (hw_parent (me), &reg.size, &attach_size, me);
    189 
    190   if (attach_size != BFIN_COREMMR_CEC_SIZE)
    191     hw_abort (me, "\"reg\" size must be %#x", BFIN_COREMMR_CEC_SIZE);
    192 
    193   hw_attach_address (hw_parent (me),
    194 		     0, attach_space, attach_address, attach_size, me);
    195 
    196   cec->base = attach_address;
    197   /* XXX: should take from the device tree.  */
    198   cec->cpu = STATE_CPU (hw_system (me), 0);
    199   cec->me = me;
    200 }
    201 
    202 static void
    203 bfin_cec_finish (struct hw *me)
    204 {
    205   struct bfin_cec *cec;
    206 
    207   cec = HW_ZALLOC (me, struct bfin_cec);
    208 
    209   set_hw_data (me, cec);
    210   set_hw_io_read_buffer (me, bfin_cec_io_read_buffer);
    211   set_hw_io_write_buffer (me, bfin_cec_io_write_buffer);
    212   set_hw_ports (me, bfin_cec_ports);
    213   set_hw_port_event (me, bfin_cec_port_event);
    214 
    215   attach_bfin_cec_regs (me, cec);
    216 
    217   /* Initialize the CEC.  */
    218   cec->imask = IVG_UNMASKABLE_B;
    219   cec->ipend = IVG_RST_B | IVG_IRPTEN_B;
    220 }
    221 
    222 const struct hw_descriptor dv_bfin_cec_descriptor[] =
    223 {
    224   {"bfin_cec", bfin_cec_finish,},
    225   {NULL, NULL},
    226 };
    227 
    228 static const char * const excp_decoded[] =
    229 {
    230   [VEC_SYS        ] = "Custom exception 0 (system call)",
    231   [VEC_EXCPT01    ] = "Custom exception 1 (software breakpoint)",
    232   [VEC_EXCPT02    ] = "Custom exception 2 (KGDB hook)",
    233   [VEC_EXCPT03    ] = "Custom exception 3 (userspace stack overflow)",
    234   [VEC_EXCPT04    ] = "Custom exception 4 (dump trace buffer)",
    235   [VEC_EXCPT05    ] = "Custom exception 5",
    236   [VEC_EXCPT06    ] = "Custom exception 6",
    237   [VEC_EXCPT07    ] = "Custom exception 7",
    238   [VEC_EXCPT08    ] = "Custom exception 8",
    239   [VEC_EXCPT09    ] = "Custom exception 9",
    240   [VEC_EXCPT10    ] = "Custom exception 10",
    241   [VEC_EXCPT11    ] = "Custom exception 11",
    242   [VEC_EXCPT12    ] = "Custom exception 12",
    243   [VEC_EXCPT13    ] = "Custom exception 13",
    244   [VEC_EXCPT14    ] = "Custom exception 14",
    245   [VEC_EXCPT15    ] = "Custom exception 15",
    246   [VEC_STEP       ] = "Hardware single step",
    247   [VEC_OVFLOW     ] = "Trace buffer overflow",
    248   [VEC_UNDEF_I    ] = "Undefined instruction",
    249   [VEC_ILGAL_I    ] = "Illegal instruction combo (multi-issue)",
    250   [VEC_CPLB_VL    ] = "DCPLB protection violation",
    251   [VEC_MISALI_D   ] = "Unaligned data access",
    252   [VEC_UNCOV      ] = "Unrecoverable event (double fault)",
    253   [VEC_CPLB_M     ] = "DCPLB miss",
    254   [VEC_CPLB_MHIT  ] = "Multiple DCPLB hit",
    255   [VEC_WATCH      ] = "Watchpoint match",
    256   [VEC_ISTRU_VL   ] = "ADSP-BF535 only",
    257   [VEC_MISALI_I   ] = "Unaligned instruction access",
    258   [VEC_CPLB_I_VL  ] = "ICPLB protection violation",
    259   [VEC_CPLB_I_M   ] = "ICPLB miss",
    260   [VEC_CPLB_I_MHIT] = "Multiple ICPLB hit",
    261   [VEC_ILL_RES    ] = "Illegal supervisor resource",
    262 };
    263 
    264 #define CEC_STATE(cpu) DV_STATE_CACHED (cpu, cec)
    265 
    266 #define __cec_get_ivg(val) (ffs ((val) & ~IVG_IRPTEN_B) - 1)
    267 #define _cec_get_ivg(cec) __cec_get_ivg ((cec)->ipend & ~IVG_EMU_B)
    268 
    269 int
    270 cec_get_ivg (SIM_CPU *cpu)
    271 {
    272   switch (STATE_ENVIRONMENT (CPU_STATE (cpu)))
    273     {
    274     case OPERATING_ENVIRONMENT:
    275       return _cec_get_ivg (CEC_STATE (cpu));
    276     default:
    277       return IVG_USER;
    278     }
    279 }
    280 
    281 static bool
    282 _cec_is_supervisor_mode (struct bfin_cec *cec)
    283 {
    284   return (cec->ipend & ~(IVG_EMU_B | IVG_IRPTEN_B));
    285 }
    286 bool
    287 cec_is_supervisor_mode (SIM_CPU *cpu)
    288 {
    289   switch (STATE_ENVIRONMENT (CPU_STATE (cpu)))
    290     {
    291     case OPERATING_ENVIRONMENT:
    292       return _cec_is_supervisor_mode (CEC_STATE (cpu));
    293     case USER_ENVIRONMENT:
    294       return false;
    295     default:
    296       return true;
    297     }
    298 }
    299 static bool
    300 _cec_is_user_mode (struct bfin_cec *cec)
    301 {
    302   return !_cec_is_supervisor_mode (cec);
    303 }
    304 bool
    305 cec_is_user_mode (SIM_CPU *cpu)
    306 {
    307   return !cec_is_supervisor_mode (cpu);
    308 }
    309 static void
    310 _cec_require_supervisor (SIM_CPU *cpu, struct bfin_cec *cec)
    311 {
    312   if (_cec_is_user_mode (cec))
    313     cec_exception (cpu, VEC_ILL_RES);
    314 }
    315 void
    316 cec_require_supervisor (SIM_CPU *cpu)
    317 {
    318   /* Do not call _cec_require_supervisor() to avoid CEC_STATE()
    319      as that macro requires OS operating mode.  */
    320   if (cec_is_user_mode (cpu))
    321     cec_exception (cpu, VEC_ILL_RES);
    322 }
    323 
    324 #define excp_to_sim_halt(reason, sigrc) \
    325   sim_engine_halt (CPU_STATE (cpu), cpu, NULL, PCREG, reason, sigrc)
    326 void
    327 cec_exception (SIM_CPU *cpu, int excp)
    328 {
    329   SIM_DESC sd = CPU_STATE (cpu);
    330   int sigrc = -1;
    331 
    332   TRACE_EVENTS (cpu, "processing exception %#x in EVT%i", excp,
    333 		cec_get_ivg (cpu));
    334 
    335   /* Ideally what would happen here for real hardware exceptions (not
    336      fake sim ones) is that:
    337       - For service exceptions (excp <= 0x11):
    338          RETX is the _next_ PC which can be tricky with jumps/hardware loops/...
    339       - For error exceptions (excp > 0x11):
    340          RETX is the _current_ PC (i.e. the one causing the exception)
    341       - PC is loaded with EVT3 MMR
    342       - ILAT/IPEND in CEC is updated depending on current IVG level
    343       - the fault address MMRs get updated with data/instruction info
    344       - Execution continues on in the EVT3 handler  */
    345 
    346   /* Handle simulator exceptions first.  */
    347   switch (excp)
    348     {
    349     case VEC_SIM_HLT:
    350       excp_to_sim_halt (sim_exited, 0);
    351       return;
    352     case VEC_SIM_ABORT:
    353       excp_to_sim_halt (sim_exited, 1);
    354       return;
    355     case VEC_SIM_TRAP:
    356       /* GDB expects us to step over EMUEXCPT.  */
    357       /* XXX: What about hwloops and EMUEXCPT at the end?
    358               Pretty sure gdb doesn't handle this already...  */
    359       SET_PCREG (PCREG + 2);
    360       /* Only trap when we are running in gdb.  */
    361       if (STATE_OPEN_KIND (sd) == SIM_OPEN_DEBUG)
    362 	excp_to_sim_halt (sim_stopped, SIM_SIGTRAP);
    363       return;
    364     case VEC_SIM_DBGA:
    365       /* If running in gdb, simply trap.  */
    366       if (STATE_OPEN_KIND (sd) == SIM_OPEN_DEBUG)
    367 	excp_to_sim_halt (sim_stopped, SIM_SIGTRAP);
    368       else
    369 	excp_to_sim_halt (sim_exited, 2);
    370     }
    371 
    372   if (excp <= 0x3f)
    373     {
    374       SET_EXCAUSE (excp);
    375       if (STATE_ENVIRONMENT (sd) == OPERATING_ENVIRONMENT)
    376 	{
    377 	  /* ICPLB regs always get updated.  */
    378 	  /* XXX: Should optimize this call path ...  */
    379 	  if (excp != VEC_MISALI_I && excp != VEC_MISALI_D
    380 	      && excp != VEC_CPLB_I_M && excp != VEC_CPLB_M
    381 	      && excp != VEC_CPLB_I_VL && excp != VEC_CPLB_VL
    382 	      && excp != VEC_CPLB_I_MHIT && excp != VEC_CPLB_MHIT)
    383 	    mmu_log_ifault (cpu);
    384 	  _cec_raise (cpu, CEC_STATE (cpu), IVG_EVX);
    385 	  /* We need to restart the engine so that we don't return
    386 	     and continue processing this bad insn.  */
    387 	  if (EXCAUSE >= 0x20)
    388 	    sim_engine_restart (sd, cpu, NULL, PCREG);
    389 	  return;
    390 	}
    391     }
    392 
    393   TRACE_EVENTS (cpu, "running virtual exception handler");
    394 
    395   switch (excp)
    396     {
    397     case VEC_SYS:
    398       bfin_syscall (cpu);
    399       break;
    400 
    401     case VEC_EXCPT01:	/* Userspace gdb breakpoint.  */
    402       sigrc = SIM_SIGTRAP;
    403       break;
    404 
    405     case VEC_UNDEF_I:	/* Undefined instruction.  */
    406       sigrc = SIM_SIGILL;
    407       break;
    408 
    409     case VEC_ILL_RES:	/* Illegal supervisor resource.  */
    410     case VEC_MISALI_I:	/* Misaligned instruction.  */
    411       sigrc = SIM_SIGBUS;
    412       break;
    413 
    414     case VEC_CPLB_M:
    415     case VEC_CPLB_I_M:
    416       sigrc = SIM_SIGSEGV;
    417       break;
    418 
    419     default:
    420       sim_io_eprintf (sd, "Unhandled exception %#x at 0x%08x (%s)\n",
    421 		      excp, PCREG, excp_decoded[excp]);
    422       sigrc = SIM_SIGILL;
    423       break;
    424     }
    425 
    426   if (sigrc != -1)
    427     excp_to_sim_halt (sim_stopped, sigrc);
    428 }
    429 
    430 bu32 cec_cli (SIM_CPU *cpu)
    431 {
    432   struct bfin_cec *cec;
    433   bu32 old_mask;
    434 
    435   if (STATE_ENVIRONMENT (CPU_STATE (cpu)) != OPERATING_ENVIRONMENT)
    436     return 0;
    437 
    438   cec = CEC_STATE (cpu);
    439   _cec_require_supervisor (cpu, cec);
    440 
    441   /* XXX: what about IPEND[4] ?  */
    442   old_mask = cec->imask;
    443   _cec_imask_write (cec, 0);
    444 
    445   TRACE_EVENTS (cpu, "CLI changed IMASK from %#x to %#x", old_mask, cec->imask);
    446 
    447   return old_mask;
    448 }
    449 
    450 void cec_sti (SIM_CPU *cpu, bu32 ints)
    451 {
    452   struct bfin_cec *cec;
    453   bu32 old_mask;
    454 
    455   if (STATE_ENVIRONMENT (CPU_STATE (cpu)) != OPERATING_ENVIRONMENT)
    456     return;
    457 
    458   cec = CEC_STATE (cpu);
    459   _cec_require_supervisor (cpu, cec);
    460 
    461   /* XXX: what about IPEND[4] ?  */
    462   old_mask = cec->imask;
    463   _cec_imask_write (cec, ints);
    464 
    465   TRACE_EVENTS (cpu, "STI changed IMASK from %#x to %#x", old_mask, cec->imask);
    466 
    467   /* Check for pending interrupts that are now enabled.  */
    468   _cec_check_pending (cpu, cec);
    469 }
    470 
    471 static void
    472 cec_irpten_enable (SIM_CPU *cpu, struct bfin_cec *cec)
    473 {
    474   /* Globally mask interrupts.  */
    475   TRACE_EVENTS (cpu, "setting IPEND[4] to globally mask interrupts");
    476   cec->ipend |= IVG_IRPTEN_B;
    477 }
    478 
    479 static void
    480 cec_irpten_disable (SIM_CPU *cpu, struct bfin_cec *cec)
    481 {
    482   /* Clear global interrupt mask.  */
    483   TRACE_EVENTS (cpu, "clearing IPEND[4] to not globally mask interrupts");
    484   cec->ipend &= ~IVG_IRPTEN_B;
    485 }
    486 
    487 static void
    488 _cec_raise (SIM_CPU *cpu, struct bfin_cec *cec, int ivg)
    489 {
    490   SIM_DESC sd = CPU_STATE (cpu);
    491   int curr_ivg = _cec_get_ivg (cec);
    492   bool snen;
    493   bool irpten;
    494 
    495   TRACE_EVENTS (cpu, "processing request for EVT%i while at EVT%i",
    496 		ivg, curr_ivg);
    497 
    498   irpten = (cec->ipend & IVG_IRPTEN_B);
    499   snen = (SYSCFGREG & SYSCFG_SNEN);
    500 
    501   if (curr_ivg == -1)
    502     curr_ivg = IVG_USER;
    503 
    504   /* Just check for higher latched interrupts.  */
    505   if (ivg == -1)
    506     {
    507       if (irpten)
    508 	goto done; /* All interrupts are masked anyways.  */
    509 
    510       ivg = __cec_get_ivg (cec->ilat & cec->imask);
    511       if (ivg < 0)
    512 	goto done; /* Nothing latched.  */
    513 
    514       if (ivg > curr_ivg)
    515 	goto done; /* Nothing higher latched.  */
    516 
    517       if (!snen && ivg == curr_ivg)
    518 	goto done; /* Self nesting disabled.  */
    519 
    520       /* Still here, so fall through to raise to higher pending.  */
    521     }
    522 
    523   cec->ilat |= (1 << ivg);
    524 
    525   if (ivg <= IVG_EVX)
    526     {
    527       /* These two are always processed.  */
    528       if (ivg == IVG_EMU || ivg == IVG_RST)
    529 	goto process_int;
    530 
    531       /* Anything lower might trigger a double fault.  */
    532       if (curr_ivg <= ivg)
    533 	{
    534 	  /* Double fault ! :(  */
    535 	  SET_EXCAUSE (VEC_UNCOV);
    536 	  /* XXX: SET_RETXREG (...);  */
    537 	  sim_io_error (sd, "%s: double fault at 0x%08x ! :(", __func__, PCREG);
    538 	  excp_to_sim_halt (sim_stopped, SIM_SIGABRT);
    539 	}
    540 
    541       /* No double fault -> always process.  */
    542       goto process_int;
    543     }
    544   else if (irpten && curr_ivg != IVG_USER)
    545     {
    546       /* Interrupts are globally masked.  */
    547     }
    548   else if (!(cec->imask & (1 << ivg)))
    549     {
    550       /* This interrupt is masked.  */
    551     }
    552   else if (ivg < curr_ivg || (snen && ivg == curr_ivg))
    553     {
    554       /* Do transition!  */
    555       bu32 oldpc;
    556 
    557  process_int:
    558       cec->ipend |= (1 << ivg);
    559       cec->ilat &= ~(1 << ivg);
    560 
    561       /* Interrupts are processed in between insns which means the return
    562          point is the insn-to-be-executed (which is the current PC).  But
    563          exceptions are handled while executing an insn, so we may have to
    564          advance the PC ourselves when setting RETX.
    565          XXX: Advancing the PC should only be for "service" exceptions, and
    566               handling them after executing the insn should be OK, which
    567               means we might be able to use the event interface for it.  */
    568 
    569       oldpc = PCREG;
    570       switch (ivg)
    571 	{
    572 	case IVG_EMU:
    573 	  /* Signal the JTAG ICE.  */
    574 	  /* XXX: what happens with 'raise 0' ?  */
    575 	  SET_RETEREG (oldpc);
    576 	  excp_to_sim_halt (sim_stopped, SIM_SIGTRAP);
    577 	  /* XXX: Need an easy way for gdb to signal it isnt here.  */
    578 	  cec->ipend &= ~IVG_EMU_B;
    579 	  break;
    580 	case IVG_RST:
    581 	  /* Have the core reset simply exit (i.e. "shutdown").  */
    582 	  excp_to_sim_halt (sim_exited, 0);
    583 	  break;
    584 	case IVG_NMI:
    585 	  /* XXX: Should check this.  */
    586 	  SET_RETNREG (oldpc);
    587 	  break;
    588 	case IVG_EVX:
    589 	  /* Non-service exceptions point to the excepting instruction.  */
    590 	  if (EXCAUSE >= 0x20)
    591 	    SET_RETXREG (oldpc);
    592 	  else
    593 	    {
    594 	      bu32 nextpc = hwloop_get_next_pc (cpu, oldpc, INSN_LEN);
    595 	      SET_RETXREG (nextpc);
    596 	    }
    597 
    598 	  break;
    599 	case IVG_IRPTEN:
    600 	  /* XXX: what happens with 'raise 4' ?  */
    601 	  sim_io_error (sd, "%s: what to do with 'raise 4' ?", __func__);
    602 	  break;
    603 	default:
    604 	  SET_RETIREG (oldpc | (ivg == curr_ivg ? 1 : 0));
    605 	  break;
    606 	}
    607 
    608       /* If EVT_OVERRIDE is in effect (IVG7+), use the reset address.  */
    609       if ((cec->evt_override & 0xff80) & (1 << ivg))
    610 	SET_PCREG (cec_get_reset_evt (cpu));
    611       else
    612 	SET_PCREG (cec_get_evt (cpu, ivg));
    613 
    614       BFIN_TRACE_BRANCH (cpu, oldpc, PCREG, -1, "CEC changed PC (to EVT%i):", ivg);
    615       BFIN_CPU_STATE.did_jump = true;
    616 
    617       /* Enable the global interrupt mask upon interrupt entry.  */
    618       if (ivg >= IVG_IVHW)
    619 	cec_irpten_enable (cpu, cec);
    620     }
    621 
    622   /* When moving between states, don't let internal states bleed through.  */
    623   DIS_ALGN_EXPT &= ~1;
    624 
    625   /* When going from user to super, we set LSB in LB regs to avoid
    626      misbehavior and/or malicious code.
    627      Also need to load SP alias with KSP.  */
    628   if (curr_ivg == IVG_USER)
    629     {
    630       int i;
    631       for (i = 0; i < 2; ++i)
    632 	if (!(LBREG (i) & 1))
    633 	  SET_LBREG (i, LBREG (i) | 1);
    634       SET_USPREG (SPREG);
    635       SET_SPREG (KSPREG);
    636     }
    637 
    638  done:
    639   TRACE_EVENTS (cpu, "now at EVT%i", _cec_get_ivg (cec));
    640 }
    641 
    642 static bu32
    643 cec_read_ret_reg (SIM_CPU *cpu, int ivg)
    644 {
    645   switch (ivg)
    646     {
    647     case IVG_EMU: return RETEREG;
    648     case IVG_NMI: return RETNREG;
    649     case IVG_EVX: return RETXREG;
    650     default:      return RETIREG;
    651     }
    652 }
    653 
    654 void
    655 cec_latch (SIM_CPU *cpu, int ivg)
    656 {
    657   struct bfin_cec *cec;
    658 
    659   if (STATE_ENVIRONMENT (CPU_STATE (cpu)) != OPERATING_ENVIRONMENT)
    660     {
    661       bu32 oldpc = PCREG;
    662       SET_PCREG (cec_read_ret_reg (cpu, ivg));
    663       BFIN_TRACE_BRANCH (cpu, oldpc, PCREG, -1, "CEC changed PC");
    664       return;
    665     }
    666 
    667   cec = CEC_STATE (cpu);
    668   cec->ilat |= (1 << ivg);
    669   _cec_check_pending (cpu, cec);
    670 }
    671 
    672 void
    673 cec_hwerr (SIM_CPU *cpu, int hwerr)
    674 {
    675   SET_HWERRCAUSE (hwerr);
    676   cec_latch (cpu, IVG_IVHW);
    677 }
    678 
    679 void
    680 cec_return (SIM_CPU *cpu, int ivg)
    681 {
    682   SIM_DESC sd = CPU_STATE (cpu);
    683   struct bfin_cec *cec;
    684   bool snen;
    685   int curr_ivg;
    686   bu32 oldpc, newpc;
    687 
    688   oldpc = PCREG;
    689 
    690   BFIN_CPU_STATE.did_jump = true;
    691   if (STATE_ENVIRONMENT (sd) != OPERATING_ENVIRONMENT)
    692     {
    693       SET_PCREG (cec_read_ret_reg (cpu, ivg));
    694       BFIN_TRACE_BRANCH (cpu, oldpc, PCREG, -1, "CEC changed PC");
    695       return;
    696     }
    697 
    698   cec = CEC_STATE (cpu);
    699 
    700   /* XXX: This isn't entirely correct ...  */
    701   cec->ipend &= ~IVG_EMU_B;
    702 
    703   curr_ivg = _cec_get_ivg (cec);
    704   if (curr_ivg == -1)
    705     curr_ivg = IVG_USER;
    706   if (ivg == -1)
    707     ivg = curr_ivg;
    708 
    709   TRACE_EVENTS (cpu, "returning from EVT%i (should be EVT%i)", curr_ivg, ivg);
    710 
    711   /* Not allowed to return from usermode.  */
    712   if (curr_ivg == IVG_USER)
    713     cec_exception (cpu, VEC_ILL_RES);
    714 
    715   if (ivg > IVG15 || ivg < 0)
    716     sim_io_error (sd, "%s: ivg %i out of range !", __func__, ivg);
    717 
    718   _cec_require_supervisor (cpu, cec);
    719 
    720   switch (ivg)
    721     {
    722     case IVG_EMU:
    723       /* RTE -- only valid in emulation mode.  */
    724       /* XXX: What does the hardware do ?  */
    725       if (curr_ivg != IVG_EMU)
    726 	cec_exception (cpu, VEC_ILL_RES);
    727       break;
    728     case IVG_NMI:
    729       /* RTN -- only valid in NMI.  */
    730       /* XXX: What does the hardware do ?  */
    731       if (curr_ivg != IVG_NMI)
    732 	cec_exception (cpu, VEC_ILL_RES);
    733       break;
    734     case IVG_EVX:
    735       /* RTX -- only valid in exception.  */
    736       /* XXX: What does the hardware do ?  */
    737       if (curr_ivg != IVG_EVX)
    738 	cec_exception (cpu, VEC_ILL_RES);
    739       break;
    740     default:
    741       /* RTI -- not valid in emulation, nmi, exception, or user.  */
    742       /* XXX: What does the hardware do ?  */
    743       if (curr_ivg == IVG_EMU || curr_ivg == IVG_NMI
    744 	  || curr_ivg == IVG_EVX || curr_ivg == IVG_USER)
    745 	cec_exception (cpu, VEC_ILL_RES);
    746       break;
    747     case IVG_IRPTEN:
    748       /* XXX: Is this even possible ?  */
    749       excp_to_sim_halt (sim_stopped, SIM_SIGABRT);
    750       break;
    751     }
    752   newpc = cec_read_ret_reg (cpu, ivg);
    753 
    754   /* XXX: Does this nested trick work on EMU/NMI/EVX ?  */
    755   snen = (newpc & 1);
    756   /* XXX: Delayed clear shows bad PCREG register trace above ?  */
    757   SET_PCREG (newpc & ~1);
    758 
    759   BFIN_TRACE_BRANCH (cpu, oldpc, PCREG, -1, "CEC changed PC (from EVT%i)", ivg);
    760 
    761   /* Update ipend after the BFIN_TRACE_BRANCH so dv-bfin_trace
    762      knows current CEC state wrt overflow.  */
    763   if (!snen)
    764     cec->ipend &= ~(1 << ivg);
    765 
    766   /* Disable global interrupt mask to let any interrupt take over, but
    767      only when we were already in a RTI level.  Only way we could have
    768      raised at that point is if it was cleared in the first place.  */
    769   if (ivg >= IVG_IVHW || ivg == IVG_RST)
    770     cec_irpten_disable (cpu, cec);
    771 
    772   /* When going from super to user, we clear LSB in LB regs in case
    773      it was set on the transition up.
    774      Also need to load SP alias with USP.  */
    775   if (_cec_get_ivg (cec) == -1)
    776     {
    777       int i;
    778       for (i = 0; i < 2; ++i)
    779 	if (LBREG (i) & 1)
    780 	  SET_LBREG (i, LBREG (i) & ~1);
    781       SET_KSPREG (SPREG);
    782       SET_SPREG (USPREG);
    783     }
    784 
    785   /* Check for pending interrupts before we return to usermode.  */
    786   _cec_check_pending (cpu, cec);
    787 }
    788 
    789 void
    790 cec_push_reti (SIM_CPU *cpu)
    791 {
    792   /* XXX: Need to check hardware with popped RETI value
    793      and bit 1 is set (when handling nested interrupts).
    794      Also need to check behavior wrt SNEN in SYSCFG.  */
    795   struct bfin_cec *cec;
    796 
    797   if (STATE_ENVIRONMENT (CPU_STATE (cpu)) != OPERATING_ENVIRONMENT)
    798     return;
    799 
    800   TRACE_EVENTS (cpu, "pushing RETI");
    801 
    802   cec = CEC_STATE (cpu);
    803   cec_irpten_disable (cpu, cec);
    804   /* Check for pending interrupts.  */
    805   _cec_check_pending (cpu, cec);
    806 }
    807 
    808 void
    809 cec_pop_reti (SIM_CPU *cpu)
    810 {
    811   /* XXX: Need to check hardware with popped RETI value
    812      and bit 1 is set (when handling nested interrupts).
    813      Also need to check behavior wrt SNEN in SYSCFG.  */
    814   struct bfin_cec *cec;
    815 
    816   if (STATE_ENVIRONMENT (CPU_STATE (cpu)) != OPERATING_ENVIRONMENT)
    817     return;
    818 
    819   TRACE_EVENTS (cpu, "popping RETI");
    820 
    821   cec = CEC_STATE (cpu);
    822   cec_irpten_enable (cpu, cec);
    823 }
    824