2017-12-08 21:57:25 +01:00
|
|
|
/* OpenRISC exception, interrupts, syscall and trap support
|
2019-01-01 07:01:51 +01:00
|
|
|
Copyright (C) 2017-2019 Free Software Foundation, Inc.
|
2017-12-08 21:57:25 +01:00
|
|
|
|
|
|
|
This file is part of GDB, the GNU debugger.
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
|
|
|
|
#define WANT_CPU_OR1K32BF
|
|
|
|
#define WANT_CPU
|
|
|
|
|
|
|
|
#include "sim-main.h"
|
|
|
|
#include "cgen-ops.h"
|
|
|
|
|
|
|
|
/* Implement the sim invalid instruction function. This will set the error
|
|
|
|
effective address to that of the invalid instruction then call the
|
|
|
|
exception handler. */
|
|
|
|
|
|
|
|
SEM_PC
|
|
|
|
sim_engine_invalid_insn (SIM_CPU *current_cpu, IADDR cia, SEM_PC vpc)
|
|
|
|
{
|
|
|
|
SET_H_SYS_EEAR0 (cia);
|
|
|
|
|
|
|
|
#ifdef WANT_CPU_OR1K32BF
|
|
|
|
or1k32bf_exception (current_cpu, cia, EXCEPT_ILLEGAL);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return vpc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Generate the appropriate OpenRISC fpu exception based on the status code from
|
|
|
|
the sim fpu. */
|
|
|
|
void
|
|
|
|
or1k32bf_fpu_error (CGEN_FPU* fpu, int status)
|
|
|
|
{
|
|
|
|
SIM_CPU *current_cpu = (SIM_CPU *)fpu->owner;
|
|
|
|
|
|
|
|
/* If floating point exceptions are enabled. */
|
|
|
|
if (GET_H_SYS_FPCSR_FPEE() != 0)
|
|
|
|
{
|
|
|
|
/* Set all of the status flag bits. */
|
|
|
|
if (status
|
|
|
|
& (sim_fpu_status_invalid_snan
|
|
|
|
| sim_fpu_status_invalid_qnan
|
|
|
|
| sim_fpu_status_invalid_isi
|
|
|
|
| sim_fpu_status_invalid_idi
|
|
|
|
| sim_fpu_status_invalid_zdz
|
|
|
|
| sim_fpu_status_invalid_imz
|
|
|
|
| sim_fpu_status_invalid_cvi
|
|
|
|
| sim_fpu_status_invalid_cmp
|
|
|
|
| sim_fpu_status_invalid_sqrt))
|
|
|
|
SET_H_SYS_FPCSR_IVF (1);
|
|
|
|
|
|
|
|
if (status & sim_fpu_status_invalid_snan)
|
|
|
|
SET_H_SYS_FPCSR_SNF (1);
|
|
|
|
|
|
|
|
if (status & sim_fpu_status_invalid_qnan)
|
|
|
|
SET_H_SYS_FPCSR_QNF (1);
|
|
|
|
|
|
|
|
if (status & sim_fpu_status_overflow)
|
|
|
|
SET_H_SYS_FPCSR_OVF (1);
|
|
|
|
|
|
|
|
if (status & sim_fpu_status_underflow)
|
|
|
|
SET_H_SYS_FPCSR_UNF (1);
|
|
|
|
|
|
|
|
if (status
|
|
|
|
& (sim_fpu_status_invalid_isi
|
|
|
|
| sim_fpu_status_invalid_idi))
|
|
|
|
SET_H_SYS_FPCSR_INF (1);
|
|
|
|
|
|
|
|
if (status & sim_fpu_status_invalid_div0)
|
|
|
|
SET_H_SYS_FPCSR_DZF (1);
|
|
|
|
|
|
|
|
if (status & sim_fpu_status_inexact)
|
|
|
|
SET_H_SYS_FPCSR_IXF (1);
|
|
|
|
|
|
|
|
/* If any of the exception bits were actually set. */
|
|
|
|
if (GET_H_SYS_FPCSR()
|
|
|
|
& (SPR_FIELD_MASK_SYS_FPCSR_IVF
|
|
|
|
| SPR_FIELD_MASK_SYS_FPCSR_SNF
|
|
|
|
| SPR_FIELD_MASK_SYS_FPCSR_QNF
|
|
|
|
| SPR_FIELD_MASK_SYS_FPCSR_OVF
|
|
|
|
| SPR_FIELD_MASK_SYS_FPCSR_UNF
|
|
|
|
| SPR_FIELD_MASK_SYS_FPCSR_INF
|
|
|
|
| SPR_FIELD_MASK_SYS_FPCSR_DZF
|
|
|
|
| SPR_FIELD_MASK_SYS_FPCSR_IXF))
|
|
|
|
{
|
|
|
|
SIM_DESC sd = CPU_STATE (current_cpu);
|
|
|
|
|
|
|
|
/* If the sim is running in fast mode, i.e. not profiling,
|
|
|
|
per-instruction callbacks are not triggered which would allow
|
|
|
|
us to track the PC. This means we cannot track which
|
|
|
|
instruction caused the FPU error. */
|
|
|
|
if (STATE_RUN_FAST_P (sd) == 1)
|
|
|
|
sim_io_eprintf
|
|
|
|
(sd, "WARNING: ignoring fpu error caught in fast mode.\n");
|
|
|
|
else
|
|
|
|
or1k32bf_exception (current_cpu, GET_H_SYS_PPC (), EXCEPT_FPE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Implement the OpenRISC exception function. This is mostly used by the
|
|
|
|
CGEN generated files. For example, this is used when handling a
|
|
|
|
overflow exception during a multiplication instruction. */
|
|
|
|
|
|
|
|
void
|
|
|
|
or1k32bf_exception (sim_cpu *current_cpu, USI pc, USI exnum)
|
|
|
|
{
|
|
|
|
SIM_DESC sd = CPU_STATE (current_cpu);
|
|
|
|
|
|
|
|
if (exnum == EXCEPT_TRAP)
|
|
|
|
{
|
|
|
|
/* Trap, used for breakpoints, sends control back to gdb breakpoint
|
|
|
|
handling. */
|
|
|
|
sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, SIM_SIGTRAP);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
|
|
|
|
/* Calculate the exception program counter. */
|
|
|
|
switch (exnum)
|
|
|
|
{
|
|
|
|
case EXCEPT_RESET:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EXCEPT_FPE:
|
|
|
|
case EXCEPT_SYSCALL:
|
|
|
|
SET_H_SYS_EPCR0 (pc + 4 - (current_cpu->delay_slot ? 4 : 0));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EXCEPT_BUSERR:
|
|
|
|
case EXCEPT_ALIGN:
|
|
|
|
case EXCEPT_ILLEGAL:
|
|
|
|
case EXCEPT_RANGE:
|
|
|
|
SET_H_SYS_EPCR0 (pc - (current_cpu->delay_slot ? 4 : 0));
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
sim_io_error (sd, "unexpected exception 0x%x raised at PC 0x%08x",
|
|
|
|
exnum, pc);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Store the current SR into ESR0. */
|
|
|
|
SET_H_SYS_ESR0 (GET_H_SYS_SR ());
|
|
|
|
|
|
|
|
/* Indicate in SR if the failed instruction is in delay slot or not. */
|
|
|
|
SET_H_SYS_SR_DSX (current_cpu->delay_slot);
|
|
|
|
|
|
|
|
current_cpu->next_delay_slot = 0;
|
|
|
|
|
|
|
|
/* Jump program counter into handler. */
|
|
|
|
IADDR handler_pc =
|
|
|
|
(GET_H_SYS_SR_EPH ()? 0xf0000000 : 0x00000000) + (exnum << 8);
|
|
|
|
|
|
|
|
sim_engine_restart (sd, current_cpu, NULL, handler_pc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Implement the return from exception instruction. This is used to return
|
|
|
|
the CPU to its previous state from within an exception handler. */
|
|
|
|
|
|
|
|
void
|
|
|
|
or1k32bf_rfe (sim_cpu *current_cpu)
|
|
|
|
{
|
|
|
|
SET_H_SYS_SR (GET_H_SYS_ESR0 ());
|
|
|
|
SET_H_SYS_SR_FO (1);
|
|
|
|
|
|
|
|
current_cpu->next_delay_slot = 0;
|
|
|
|
|
|
|
|
sim_engine_restart (CPU_STATE (current_cpu), current_cpu, NULL,
|
|
|
|
GET_H_SYS_EPCR0 ());
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Implement the move from SPR instruction. This is used to read from the
|
|
|
|
CPU's special purpose registers. */
|
|
|
|
|
|
|
|
USI
|
|
|
|
or1k32bf_mfspr (sim_cpu *current_cpu, USI addr)
|
|
|
|
{
|
|
|
|
SIM_DESC sd = CPU_STATE (current_cpu);
|
|
|
|
|
|
|
|
if (!GET_H_SYS_SR_SM () && !GET_H_SYS_SR_SUMRA ())
|
|
|
|
{
|
|
|
|
sim_io_eprintf (sd, "WARNING: l.mfspr in user mode (SR 0x%x)\n",
|
|
|
|
GET_H_SYS_SR ());
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (addr >= NUM_SPR)
|
|
|
|
goto bad_address;
|
|
|
|
|
|
|
|
SI val = GET_H_SPR (addr);
|
|
|
|
|
|
|
|
switch (addr)
|
|
|
|
{
|
|
|
|
|
|
|
|
case SPR_ADDR (SYS, VR):
|
|
|
|
case SPR_ADDR (SYS, UPR):
|
|
|
|
case SPR_ADDR (SYS, CPUCFGR):
|
|
|
|
case SPR_ADDR (SYS, SR):
|
|
|
|
case SPR_ADDR (SYS, PPC):
|
|
|
|
case SPR_ADDR (SYS, FPCSR):
|
|
|
|
case SPR_ADDR (SYS, EPCR0):
|
|
|
|
case SPR_ADDR (MAC, MACLO):
|
|
|
|
case SPR_ADDR (MAC, MACHI):
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (addr < SPR_ADDR (SYS, GPR0) || addr > SPR_ADDR (SYS, GPR511))
|
|
|
|
goto bad_address;
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return val;
|
|
|
|
|
|
|
|
bad_address:
|
|
|
|
sim_io_eprintf (sd, "WARNING: l.mfspr with invalid SPR address 0x%x\n", addr);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Implement the move to SPR instruction. This is used to write too the
|
|
|
|
CPU's special purpose registers. */
|
|
|
|
|
|
|
|
void
|
|
|
|
or1k32bf_mtspr (sim_cpu *current_cpu, USI addr, USI val)
|
|
|
|
{
|
|
|
|
SIM_DESC sd = CPU_STATE (current_cpu);
|
|
|
|
|
|
|
|
if (!GET_H_SYS_SR_SM () && !GET_H_SYS_SR_SUMRA ())
|
|
|
|
{
|
|
|
|
sim_io_eprintf
|
|
|
|
(sd, "WARNING: l.mtspr with address 0x%x in user mode (SR 0x%x)\n",
|
|
|
|
addr, GET_H_SYS_SR ());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (addr >= NUM_SPR)
|
|
|
|
goto bad_address;
|
|
|
|
|
|
|
|
switch (addr)
|
|
|
|
{
|
|
|
|
|
|
|
|
case SPR_ADDR (SYS, FPCSR):
|
|
|
|
case SPR_ADDR (SYS, EPCR0):
|
|
|
|
case SPR_ADDR (SYS, ESR0):
|
|
|
|
case SPR_ADDR (MAC, MACHI):
|
|
|
|
case SPR_ADDR (MAC, MACLO):
|
|
|
|
SET_H_SPR (addr, val);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_ADDR (SYS, SR):
|
|
|
|
SET_H_SPR (addr, val);
|
|
|
|
SET_H_SYS_SR_FO (1);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_ADDR (SYS, NPC):
|
|
|
|
current_cpu->next_delay_slot = 0;
|
|
|
|
|
|
|
|
sim_engine_restart (CPU_STATE (current_cpu), current_cpu, NULL, val);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_ADDR (TICK, TTMR):
|
|
|
|
/* Allow some registers to be silently cleared. */
|
|
|
|
if (val != 0)
|
|
|
|
sim_io_eprintf
|
|
|
|
(sd, "WARNING: l.mtspr to SPR address 0x%x with invalid value 0x%x\n",
|
|
|
|
addr, val);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (addr >= SPR_ADDR (SYS, GPR0) && addr <= SPR_ADDR (SYS, GPR511))
|
|
|
|
SET_H_SPR (addr, val);
|
|
|
|
else
|
|
|
|
goto bad_address;
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
bad_address:
|
|
|
|
sim_io_eprintf (sd, "WARNING: l.mtspr with invalid SPR address 0x%x\n", addr);
|
|
|
|
|
|
|
|
}
|