]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - sim/or1k/traps.c
Automatic date update in version.in
[thirdparty/binutils-gdb.git] / sim / or1k / traps.c
CommitLineData
fa8b7c21 1/* OpenRISC exception, interrupts, syscall and trap support
1d506c26 2 Copyright (C) 2017-2024 Free Software Foundation, Inc.
fa8b7c21
SH
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
6df01ab8
MF
19/* This must come before any other includes. */
20#include "defs.h"
21
fa8b7c21
SH
22#define WANT_CPU_OR1K32BF
23#define WANT_CPU
24
25#include "sim-main.h"
f5e82fac 26#include "sim-fpu.h"
1fef66b0 27#include "sim-signal.h"
fa8b7c21
SH
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
34SEM_PC
35sim_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. */
48void
49or1k32bf_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. */
906192d7 109 if (!PROFILE_ANY_P (current_cpu) && !TRACE_ANY_P (current_cpu))
fa8b7c21
SH
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
123void
124or1k32bf_exception (sim_cpu *current_cpu, USI pc, USI exnum)
125{
126 SIM_DESC sd = CPU_STATE (current_cpu);
4c3c3171 127 struct or1k_sim_cpu *or1k_cpu = OR1K_SIM_CPU (current_cpu);
fa8b7c21
SH
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 {
e998918e 137 IADDR handler_pc;
fa8b7c21
SH
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:
4c3c3171 147 SET_H_SYS_EPCR0 (pc + 4 - (or1k_cpu->delay_slot ? 4 : 0));
fa8b7c21
SH
148 break;
149
150 case EXCEPT_BUSERR:
151 case EXCEPT_ALIGN:
152 case EXCEPT_ILLEGAL:
153 case EXCEPT_RANGE:
4c3c3171 154 SET_H_SYS_EPCR0 (pc - (or1k_cpu->delay_slot ? 4 : 0));
fa8b7c21
SH
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. */
4c3c3171 167 SET_H_SYS_SR_DSX (or1k_cpu->delay_slot);
fa8b7c21 168
4c3c3171 169 or1k_cpu->next_delay_slot = 0;
fa8b7c21
SH
170
171 /* Jump program counter into handler. */
e998918e
MF
172 handler_pc =
173 (GET_H_SYS_SR_EPH () ? 0xf0000000 : 0x00000000) + (exnum << 8);
fa8b7c21
SH
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
182void
183or1k32bf_rfe (sim_cpu *current_cpu)
184{
4c3c3171
MF
185 struct or1k_sim_cpu *or1k_cpu = OR1K_SIM_CPU (current_cpu);
186
fa8b7c21
SH
187 SET_H_SYS_SR (GET_H_SYS_ESR0 ());
188 SET_H_SYS_SR_FO (1);
189
4c3c3171 190 or1k_cpu->next_delay_slot = 0;
fa8b7c21
SH
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
199USI
200or1k32bf_mfspr (sim_cpu *current_cpu, USI addr)
201{
202 SIM_DESC sd = CPU_STATE (current_cpu);
e998918e 203 SI val;
fa8b7c21
SH
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
e998918e 215 val = GET_H_SPR (addr);
fa8b7c21
SH
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
240bad_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
249void
250or1k32bf_mtspr (sim_cpu *current_cpu, USI addr, USI val)
251{
252 SIM_DESC sd = CPU_STATE (current_cpu);
4c3c3171 253 struct or1k_sim_cpu *or1k_cpu = OR1K_SIM_CPU (current_cpu);
fa8b7c21
SH
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):
4c3c3171 283 or1k_cpu->next_delay_slot = 0;
fa8b7c21 284
4c3c3171 285 sim_engine_restart (sd, current_cpu, NULL, val);
fa8b7c21
SH
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
307bad_address:
308 sim_io_eprintf (sd, "WARNING: l.mtspr with invalid SPR address 0x%x\n", addr);
309
310}