1 /* OpenRISC exception, interrupts, syscall and trap support 2 Copyright (C) 2017-2024 Free Software Foundation, Inc. 3 4 This file is part of GDB, the GNU debugger. 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 18 19 /* This must come before any other includes. */ 20 #include "defs.h" 21 22 #define WANT_CPU_OR1K32BF 23 #define WANT_CPU 24 25 #include "sim-main.h" 26 #include "sim-fpu.h" 27 #include "sim-signal.h" 28 #include "cgen-ops.h" 29 30 /* Implement the sim invalid instruction function. This will set the error 31 effective address to that of the invalid instruction then call the 32 exception handler. */ 33 34 SEM_PC 35 sim_engine_invalid_insn (SIM_CPU *current_cpu, IADDR cia, SEM_PC vpc) 36 { 37 SET_H_SYS_EEAR0 (cia); 38 39 #ifdef WANT_CPU_OR1K32BF 40 or1k32bf_exception (current_cpu, cia, EXCEPT_ILLEGAL); 41 #endif 42 43 return vpc; 44 } 45 46 /* Generate the appropriate OpenRISC fpu exception based on the status code from 47 the sim fpu. */ 48 void 49 or1k32bf_fpu_error (CGEN_FPU* fpu, int status) 50 { 51 SIM_CPU *current_cpu = (SIM_CPU *)fpu->owner; 52 53 /* If floating point exceptions are enabled. */ 54 if (GET_H_SYS_FPCSR_FPEE() != 0) 55 { 56 /* Set all of the status flag bits. */ 57 if (status 58 & (sim_fpu_status_invalid_snan 59 | sim_fpu_status_invalid_qnan 60 | sim_fpu_status_invalid_isi 61 | sim_fpu_status_invalid_idi 62 | sim_fpu_status_invalid_zdz 63 | sim_fpu_status_invalid_imz 64 | sim_fpu_status_invalid_cvi 65 | sim_fpu_status_invalid_cmp 66 | sim_fpu_status_invalid_sqrt)) 67 SET_H_SYS_FPCSR_IVF (1); 68 69 if (status & sim_fpu_status_invalid_snan) 70 SET_H_SYS_FPCSR_SNF (1); 71 72 if (status & sim_fpu_status_invalid_qnan) 73 SET_H_SYS_FPCSR_QNF (1); 74 75 if (status & sim_fpu_status_overflow) 76 SET_H_SYS_FPCSR_OVF (1); 77 78 if (status & sim_fpu_status_underflow) 79 SET_H_SYS_FPCSR_UNF (1); 80 81 if (status 82 & (sim_fpu_status_invalid_isi 83 | sim_fpu_status_invalid_idi)) 84 SET_H_SYS_FPCSR_INF (1); 85 86 if (status & sim_fpu_status_invalid_div0) 87 SET_H_SYS_FPCSR_DZF (1); 88 89 if (status & sim_fpu_status_inexact) 90 SET_H_SYS_FPCSR_IXF (1); 91 92 /* If any of the exception bits were actually set. */ 93 if (GET_H_SYS_FPCSR() 94 & (SPR_FIELD_MASK_SYS_FPCSR_IVF 95 | SPR_FIELD_MASK_SYS_FPCSR_SNF 96 | SPR_FIELD_MASK_SYS_FPCSR_QNF 97 | SPR_FIELD_MASK_SYS_FPCSR_OVF 98 | SPR_FIELD_MASK_SYS_FPCSR_UNF 99 | SPR_FIELD_MASK_SYS_FPCSR_INF 100 | SPR_FIELD_MASK_SYS_FPCSR_DZF 101 | SPR_FIELD_MASK_SYS_FPCSR_IXF)) 102 { 103 SIM_DESC sd = CPU_STATE (current_cpu); 104 105 /* If the sim is running in fast mode, i.e. not profiling, 106 per-instruction callbacks are not triggered which would allow 107 us to track the PC. This means we cannot track which 108 instruction caused the FPU error. */ 109 if (!PROFILE_ANY_P (current_cpu) && !TRACE_ANY_P (current_cpu)) 110 sim_io_eprintf 111 (sd, "WARNING: ignoring fpu error caught in fast mode.\n"); 112 else 113 or1k32bf_exception (current_cpu, GET_H_SYS_PPC (), EXCEPT_FPE); 114 } 115 } 116 } 117 118 119 /* Implement the OpenRISC exception function. This is mostly used by the 120 CGEN generated files. For example, this is used when handling a 121 overflow exception during a multiplication instruction. */ 122 123 void 124 or1k32bf_exception (sim_cpu *current_cpu, USI pc, USI exnum) 125 { 126 SIM_DESC sd = CPU_STATE (current_cpu); 127 struct or1k_sim_cpu *or1k_cpu = OR1K_SIM_CPU (current_cpu); 128 129 if (exnum == EXCEPT_TRAP) 130 { 131 /* Trap, used for breakpoints, sends control back to gdb breakpoint 132 handling. */ 133 sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, SIM_SIGTRAP); 134 } 135 else 136 { 137 IADDR handler_pc; 138 139 /* Calculate the exception program counter. */ 140 switch (exnum) 141 { 142 case EXCEPT_RESET: 143 break; 144 145 case EXCEPT_FPE: 146 case EXCEPT_SYSCALL: 147 SET_H_SYS_EPCR0 (pc + 4 - (or1k_cpu->delay_slot ? 4 : 0)); 148 break; 149 150 case EXCEPT_BUSERR: 151 case EXCEPT_ALIGN: 152 case EXCEPT_ILLEGAL: 153 case EXCEPT_RANGE: 154 SET_H_SYS_EPCR0 (pc - (or1k_cpu->delay_slot ? 4 : 0)); 155 break; 156 157 default: 158 sim_io_error (sd, "unexpected exception 0x%x raised at PC 0x%08x", 159 exnum, pc); 160 break; 161 } 162 163 /* Store the current SR into ESR0. */ 164 SET_H_SYS_ESR0 (GET_H_SYS_SR ()); 165 166 /* Indicate in SR if the failed instruction is in delay slot or not. */ 167 SET_H_SYS_SR_DSX (or1k_cpu->delay_slot); 168 169 or1k_cpu->next_delay_slot = 0; 170 171 /* Jump program counter into handler. */ 172 handler_pc = 173 (GET_H_SYS_SR_EPH () ? 0xf0000000 : 0x00000000) + (exnum << 8); 174 175 sim_engine_restart (sd, current_cpu, NULL, handler_pc); 176 } 177 } 178 179 /* Implement the return from exception instruction. This is used to return 180 the CPU to its previous state from within an exception handler. */ 181 182 void 183 or1k32bf_rfe (sim_cpu *current_cpu) 184 { 185 struct or1k_sim_cpu *or1k_cpu = OR1K_SIM_CPU (current_cpu); 186 187 SET_H_SYS_SR (GET_H_SYS_ESR0 ()); 188 SET_H_SYS_SR_FO (1); 189 190 or1k_cpu->next_delay_slot = 0; 191 192 sim_engine_restart (CPU_STATE (current_cpu), current_cpu, NULL, 193 GET_H_SYS_EPCR0 ()); 194 } 195 196 /* Implement the move from SPR instruction. This is used to read from the 197 CPU's special purpose registers. */ 198 199 USI 200 or1k32bf_mfspr (sim_cpu *current_cpu, USI addr) 201 { 202 SIM_DESC sd = CPU_STATE (current_cpu); 203 SI val; 204 205 if (!GET_H_SYS_SR_SM () && !GET_H_SYS_SR_SUMRA ()) 206 { 207 sim_io_eprintf (sd, "WARNING: l.mfspr in user mode (SR 0x%x)\n", 208 GET_H_SYS_SR ()); 209 return 0; 210 } 211 212 if (addr >= NUM_SPR) 213 goto bad_address; 214 215 val = GET_H_SPR (addr); 216 217 switch (addr) 218 { 219 220 case SPR_ADDR (SYS, VR): 221 case SPR_ADDR (SYS, UPR): 222 case SPR_ADDR (SYS, CPUCFGR): 223 case SPR_ADDR (SYS, SR): 224 case SPR_ADDR (SYS, PPC): 225 case SPR_ADDR (SYS, FPCSR): 226 case SPR_ADDR (SYS, EPCR0): 227 case SPR_ADDR (MAC, MACLO): 228 case SPR_ADDR (MAC, MACHI): 229 break; 230 231 default: 232 if (addr < SPR_ADDR (SYS, GPR0) || addr > SPR_ADDR (SYS, GPR511)) 233 goto bad_address; 234 break; 235 236 } 237 238 return val; 239 240 bad_address: 241 sim_io_eprintf (sd, "WARNING: l.mfspr with invalid SPR address 0x%x\n", addr); 242 return 0; 243 244 } 245 246 /* Implement the move to SPR instruction. This is used to write too the 247 CPU's special purpose registers. */ 248 249 void 250 or1k32bf_mtspr (sim_cpu *current_cpu, USI addr, USI val) 251 { 252 SIM_DESC sd = CPU_STATE (current_cpu); 253 struct or1k_sim_cpu *or1k_cpu = OR1K_SIM_CPU (current_cpu); 254 255 if (!GET_H_SYS_SR_SM () && !GET_H_SYS_SR_SUMRA ()) 256 { 257 sim_io_eprintf 258 (sd, "WARNING: l.mtspr with address 0x%x in user mode (SR 0x%x)\n", 259 addr, GET_H_SYS_SR ()); 260 return; 261 } 262 263 if (addr >= NUM_SPR) 264 goto bad_address; 265 266 switch (addr) 267 { 268 269 case SPR_ADDR (SYS, FPCSR): 270 case SPR_ADDR (SYS, EPCR0): 271 case SPR_ADDR (SYS, ESR0): 272 case SPR_ADDR (MAC, MACHI): 273 case SPR_ADDR (MAC, MACLO): 274 SET_H_SPR (addr, val); 275 break; 276 277 case SPR_ADDR (SYS, SR): 278 SET_H_SPR (addr, val); 279 SET_H_SYS_SR_FO (1); 280 break; 281 282 case SPR_ADDR (SYS, NPC): 283 or1k_cpu->next_delay_slot = 0; 284 285 sim_engine_restart (sd, current_cpu, NULL, val); 286 break; 287 288 case SPR_ADDR (TICK, TTMR): 289 /* Allow some registers to be silently cleared. */ 290 if (val != 0) 291 sim_io_eprintf 292 (sd, "WARNING: l.mtspr to SPR address 0x%x with invalid value 0x%x\n", 293 addr, val); 294 break; 295 296 default: 297 if (addr >= SPR_ADDR (SYS, GPR0) && addr <= SPR_ADDR (SYS, GPR511)) 298 SET_H_SPR (addr, val); 299 else 300 goto bad_address; 301 break; 302 303 } 304 305 return; 306 307 bad_address: 308 sim_io_eprintf (sd, "WARNING: l.mtspr with invalid SPR address 0x%x\n", addr); 309 310 } 311