--- trunk/src/cpus/cpu_sh.c 2007/10/08 16:21:34 36 +++ trunk/src/cpus/cpu_sh.c 2007/10/08 16:21:53 38 @@ -25,7 +25,7 @@ * SUCH DAMAGE. * * - * $Id: cpu_sh.c,v 1.62 2007/03/08 19:04:09 debug Exp $ + * $Id: cpu_sh.c,v 1.66 2007/04/13 07:06:31 debug Exp $ * * Hitachi SuperH ("SH") CPU emulation. * @@ -185,51 +185,113 @@ } /* SH4-specific memory mapped registers, TLBs, caches, etc: */ - if (cpu->cd.sh.cpu_type.arch == 4) + if (cpu->cd.sh.cpu_type.arch == 4) { device_add(machine, "sh4"); + /* + * Interrupt Controller initial values, according to the + * SH7760 manual: + */ + cpu->cd.sh.intc_iprd = 0xda74; + cpu->cd.sh.intc_intmsk00 = 0xf3ff7fff; + cpu->cd.sh.intc_intmsk04 = 0x00ffffff; + /* All others are zero. */ + + /* TODO: Initial priorities? */ + cpu->cd.sh.intc_intpri00 = 0x33333333; + cpu->cd.sh.intc_intpri04 = 0x33333333; + cpu->cd.sh.intc_intpri08 = 0x33333333; + cpu->cd.sh.intc_intpri0c = 0x33333333; + } + + sh_update_interrupt_priorities(cpu); + return 1; } /* + * sh_update_interrupt_priorities(): + * + * SH interrupts are a bit complicated; there are several intc registers + * controlling priorities for various peripherals: + * + * Register: Bits 15..12 11..8 7..4 3..0 + * --------- ----------- ----- ---- ---- + * ipra TMU0 TMU1 TMU2 Reserved + * iprb WDT REF Reserved Reserved + * iprc GPIO DMAC Reserved H-UDI + * iprd IRL0 IRL1 IRL2 IRL3 + * + * Register: 31..28 27..24 23..20 19..16 15..12 11..8 7..4 3..0 + * --------- ------ ------ ------ ------ ------ ----- ---- ---- + * intpri00 IRQ4 IRQ5 IRQ6 IRQ7 Rsrvd. Rsrvd. Rsrvd. Reserved + * intpri04 HCAN2,0 HCAN2,1 SSI(0) SSI(1) HAC(0) HAC(1) I2C(0) I2C(1) + * intpri08 USB LCDC DMABRG SCIF(0) SCIF(1) SCIF(2) SIM HSPI + * intpri0c Reserv. Reserv. MMCIF Reserv. MFI Rsrvd. ADC CMT + */ +void sh_update_interrupt_priorities(struct cpu *cpu) +{ + int i; + + /* + * Set priorities of known interrupts, without affecting the + * SH_INT_ASSERTED bit: + */ + + for (i=SH4_INTEVT_IRQ0; i<=SH4_INTEVT_IRQ14; i+=0x20) { + cpu->cd.sh.int_prio_and_pending[i/0x20] &= ~SH_INT_PRIO_MASK; + cpu->cd.sh.int_prio_and_pending[i/0x20] |= (15 - ((i - + SH4_INTEVT_IRQ0) / 0x20)); + } + + cpu->cd.sh.int_prio_and_pending[SH_INTEVT_TMU0_TUNI0 / 0x20] &= + ~SH_INT_PRIO_MASK; + cpu->cd.sh.int_prio_and_pending[SH_INTEVT_TMU0_TUNI0 / 0x20] |= + (cpu->cd.sh.intc_ipra >> 12) & 0xf; + + cpu->cd.sh.int_prio_and_pending[SH_INTEVT_TMU1_TUNI1 / 0x20] &= + ~SH_INT_PRIO_MASK; + cpu->cd.sh.int_prio_and_pending[SH_INTEVT_TMU1_TUNI1 / 0x20] |= + (cpu->cd.sh.intc_ipra >> 8) & 0xf; + + cpu->cd.sh.int_prio_and_pending[SH_INTEVT_TMU2_TUNI2 / 0x20] &= + ~SH_INT_PRIO_MASK; + cpu->cd.sh.int_prio_and_pending[SH_INTEVT_TMU2_TUNI2 / 0x20] |= + (cpu->cd.sh.intc_ipra >> 4) & 0xf; + + for (i=SH4_INTEVT_SCIF_ERI; i<=SH4_INTEVT_SCIF_TXI; i+=0x20) { + cpu->cd.sh.int_prio_and_pending[i/0x20] &= ~SH_INT_PRIO_MASK; + cpu->cd.sh.int_prio_and_pending[i/0x20] |= + ((cpu->cd.sh.intc_intpri08 >> 16) & 0xf); + } +} + + +/* * sh_cpu_interrupt_assert(): */ void sh_cpu_interrupt_assert(struct interrupt *interrupt) { struct cpu *cpu = interrupt->extra; int irq_nr = interrupt->line; - int word_index, bit_index; + int index = irq_nr / 0x20; + int prio; - /* - * Note: This gives higher interrupt priority to lower number - * interrupts. Hopefully this is correct. - */ + /* Assert the interrupt, and check its priority level: */ + cpu->cd.sh.int_prio_and_pending[index] |= SH_INT_ASSERTED; + prio = cpu->cd.sh.int_prio_and_pending[index] & SH_INT_PRIO_MASK; + + if (prio == 0) { + /* Interrupt not implemented? Hm. */ + fatal("[ SH interrupt 0x%x, prio 0 (?), aborting ]\n", irq_nr); + exit(1); + } - if (cpu->cd.sh.int_to_assert == 0 || irq_nr < cpu->cd.sh.int_to_assert) + if (cpu->cd.sh.int_to_assert == 0 || prio > cpu->cd.sh.int_level) { cpu->cd.sh.int_to_assert = irq_nr; - - /* - * TODO: Keep track of all pending interrupts at multiple levels... - * - * This is just a quick hack: - */ - cpu->cd.sh.int_level = 1; - if (irq_nr == SH_INTEVT_TMU0_TUNI0) - cpu->cd.sh.int_level = (cpu->cd.sh.intc_ipra >> 12) & 0xf; - if (irq_nr == SH_INTEVT_TMU1_TUNI1) - cpu->cd.sh.int_level = (cpu->cd.sh.intc_ipra >> 8) & 0xf; - if (irq_nr == SH_INTEVT_TMU2_TUNI2) - cpu->cd.sh.int_level = (cpu->cd.sh.intc_ipra >> 4) & 0xf; - if (irq_nr >= SH4_INTEVT_SCIF_ERI && - irq_nr <= SH4_INTEVT_SCIF_TXI) - cpu->cd.sh.int_level = (cpu->cd.sh.intc_iprc >> 4) & 0xf; - - irq_nr /= 0x20; - word_index = irq_nr / (sizeof(uint32_t)*8); - bit_index = irq_nr & ((sizeof(uint32_t)*8) - 1); - - cpu->cd.sh.int_pending[word_index] |= (1 << bit_index); + cpu->cd.sh.int_level = prio; + } } @@ -240,56 +302,27 @@ { struct cpu *cpu = interrupt->extra; int irq_nr = interrupt->line; - int word_index, bit_index; + int index = irq_nr / 0x20; - if (cpu->cd.sh.int_to_assert == irq_nr) { - /* - * Rescan all interrupts to see if any are still asserted. - * - * Note: The scan only has to go from irq_nr + 0x20 to the max - * index, since any lower interrupt cannot be asserted - * at this time. - */ - int i, max = 0x1000; - cpu->cd.sh.int_to_assert = 0; - - for (i=irq_nr+0x20; icd.sh.int_pending[word_index] == 0) - i += (sizeof(uint32_t)*8 - 1) * 0x20; - else if (cpu->cd.sh.int_pending[word_index] - & (1 << bit_index)) { - cpu->cd.sh.int_to_assert = i; - - -/* Hack. TODO: Fix. */ - cpu->cd.sh.int_level = 1; - if (i == SH_INTEVT_TMU0_TUNI0) - cpu->cd.sh.int_level = (cpu->cd.sh.intc_ipra >> 12) & 0xf; - if (i == SH_INTEVT_TMU1_TUNI1) - cpu->cd.sh.int_level = (cpu->cd.sh.intc_ipra >> 8) & 0xf; - if (i == SH_INTEVT_TMU2_TUNI2) - cpu->cd.sh.int_level = (cpu->cd.sh.intc_ipra >> 4) & 0xf; - if (i >= SH4_INTEVT_SCIF_ERI && - i <= SH4_INTEVT_SCIF_TXI) - cpu->cd.sh.int_level = (cpu->cd.sh.intc_iprc >> 4) & 0xf; + /* Deassert the interrupt: */ + if (cpu->cd.sh.int_prio_and_pending[index] & SH_INT_ASSERTED) { + cpu->cd.sh.int_prio_and_pending[index] &= ~SH_INT_ASSERTED; + /* Calculate new interrupt assertion: */ + cpu->cd.sh.int_to_assert = 0; + cpu->cd.sh.int_level = 0; - break; + /* NOTE/TODO: This is slow, but should hopefully work: */ + for (index=0; index<0x1000/0x20; index++) { + uint8_t x = cpu->cd.sh.int_prio_and_pending[index]; + uint8_t prio = x & SH_INT_PRIO_MASK; + if (x & SH_INT_ASSERTED && + prio > cpu->cd.sh.int_level) { + cpu->cd.sh.int_to_assert = index * 0x20; + cpu->cd.sh.int_level = prio; } } } - - irq_nr /= 0x20; - word_index = irq_nr / (sizeof(uint32_t)*8); - bit_index = irq_nr & ((sizeof(uint32_t)*8) - 1); - - cpu->cd.sh.int_pending[word_index] &= ~(1 << bit_index); } @@ -508,19 +541,6 @@ /* - * sh_cpu_gdb_stub(): - * - * Execute a "remote GDB" command. Returns a newly allocated response string - * on success, NULL on failure. - */ -char *sh_cpu_gdb_stub(struct cpu *cpu, char *cmd) -{ - fatal("sh_cpu_gdb_stub(): TODO\n"); - return NULL; -} - - -/* * sh_update_sr(): * * Writes a new value to the status register. @@ -569,6 +589,8 @@ * * Causes a transfer of control to an exception or interrupt handler. * If intevt > 0, then it is an interrupt, otherwise an exception. + * + * vaddr contains the faulting address, on TLB exceptions. */ void sh_exception(struct cpu *cpu, int expevt, int intevt, uint32_t vaddr) { @@ -580,7 +602,7 @@ else debug("[ exception 0x%03x", expevt); - debug(", pc=0x%08"PRIx32" ", (uint32_t)vaddr); + debug(", pc=0x%08"PRIx32" ", (uint32_t)cpu->pc); if (intevt == 0) debug("vaddr=0x%08"PRIx32" ", vaddr); @@ -588,7 +610,7 @@ } if (cpu->cd.sh.sr & SH_SR_BL) { - fatal("sh_exception(): BL bit already set. TODO\n"); + fatal("[ sh_exception(): BL bit already set. ]\n"); /* This is actually OK in two cases: a User Break, or on NMI interrupts if a special flag is set? */ @@ -613,20 +635,28 @@ cpu->pc -= sizeof(uint16_t); } - /* Stuff common to all exceptions: */ + + /* + * Stuff common to all exceptions: + */ + cpu->cd.sh.spc = cpu->pc; cpu->cd.sh.ssr = cpu->cd.sh.sr; cpu->cd.sh.sgr = cpu->cd.sh.r[15]; + if (intevt > 0) { cpu->cd.sh.intevt = intevt; expevt = -1; - } else + } else { cpu->cd.sh.expevt = expevt; + } + sh_update_sr(cpu, cpu->cd.sh.sr | SH_SR_MD | SH_SR_RB | SH_SR_BL); /* Most exceptions set PC to VBR + 0x100. */ cpu->pc = vbr + 0x100; + /* Specific cases: */ switch (expevt) { @@ -653,9 +683,13 @@ break; case EXPEVT_TRAPA: - /* Note: The TRA register is already set by the - implementation of the trapa instruction. See - cpu_sh_instr.c. */ + /* + * Note: The TRA register is already set by the implementation + * of the trapa instruction. See cpu_sh_instr.c for details. + * Here, spc is incremented, so that a return from the trap + * handler transfers control to the instruction _following_ + * the trapa. + */ cpu->cd.sh.spc += sizeof(uint16_t); break;