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