1/* The CRIS interrupt framework for GDB, the GNU Debugger. 2 3 Copyright 2006-2023 Free Software Foundation, Inc. 4 5 This file is part of GDB. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 19 20/* This must come before any other includes. */ 21#include "defs.h" 22 23#include "sim-main.h" 24#include "hw-main.h" 25 26/* DEVICE 27 28 CRIS cpu virtual device (very rudimental; generic enough for all 29 currently used CRIS versions). 30 31 32 DESCRIPTION 33 34 Implements the external CRIS functionality. This includes the 35 delivery of interrupts generated from other devices. 36 37 38 PROPERTIES 39 40 vec-for-int = <int-a> <vec-a> <int-b> <vec-b> ... 41 These are the translations to interrupt vector for values appearing 42 on the "int" port, as pairs of the value and the corresponding 43 vector. Defaults to no translation. All values that may appear on 44 the "int" port must be defined, or the device aborts. 45 46 multiple-int = ("abort" | "ignore_previous" | <vector>) 47 If multiple interrupt values are dispatched, this property decides 48 what to do. The value is either a number corresponding to the 49 vector to use, or the string "abort" to cause a hard abort, or the 50 string "ignore_previous", to silently use the new vector instead. 51 The default is "abort". 52 53 54 PORTS 55 56 int (input) 57 Interrupt port. An event with a non-zero value on this port causes 58 an interrupt. If, after an event but before the interrupt has been 59 properly dispatched, a non-zero value appears that is different 60 after mapping than the previous, then the property multiple_int 61 decides what to do. 62 63 FIXME: reg port so internal registers can be read. Requires 64 chip-specific versions, though. Ports "nmi" and "reset". 65 66 67 BUGS 68 When delivering an interrupt, this code assumes that there is only 69 one processor (number 0). 70 71 This code does not attempt to be efficient at handling pending 72 interrupts. It simply schedules the interrupt delivery handler 73 every instruction cycle until all pending interrupts go away. 74 It also works around a bug in sim_events_process when doing so. 75 */ 76 77/* Keep this an enum for simple addition of "reset" and "nmi". */ 78enum 79 { 80 INT_PORT, 81 }; 82 83static const struct hw_port_descriptor cris_ports[] = 84 { 85 { "int", INT_PORT, 0, input_port }, 86 { NULL, 0, 0, 0 } 87 }; 88 89struct cris_vec_tr 90 { 91 uint32_t portval, vec; 92 }; 93 94enum cris_multiple_ints 95 { 96 cris_multint_abort, 97 cris_multint_ignore_previous, 98 cris_multint_vector 99 }; 100 101struct cris_hw 102 { 103 struct hw_event *pending_handler; 104 uint32_t pending_vector; 105 struct cris_vec_tr *int_to_vec; 106 enum cris_multiple_ints multi_int_action; 107 uint32_t multiple_int_vector; 108 }; 109 110/* An event function, calling the actual CPU-model-specific 111 interrupt-delivery function. */ 112 113static void 114deliver_cris_interrupt (struct hw *me, void *data) 115{ 116 struct cris_hw *crishw = hw_data (me); 117 SIM_DESC simulator = hw_system (me); 118 sim_cpu *cpu = STATE_CPU (simulator, 0); 119 unsigned int intno = crishw->pending_vector; 120 121 if (CPU_CRIS_DELIVER_INTERRUPT (cpu) (cpu, CRIS_INT_INT, intno)) 122 { 123 crishw->pending_vector = 0; 124 crishw->pending_handler = NULL; 125 return; 126 } 127 128 { 129 /* Bug workaround: at time T with a pending number of cycles N to 130 process, if re-scheduling an event at time T+M, M < N, 131 sim_events_process gets stuck at T (updating the "time" to 132 before the event rather than after the event, or somesuch). 133 134 Hacking this locally is thankfully easy: if we see the same 135 simulation time, increase the number of cycles. Do this every 136 time we get here, until a new time is seen (supposedly unstuck 137 re-delivery). (Fixing in SIM/GDB source will hopefully then 138 also be easier, having a tangible test-case.) */ 139 static int64_t last_events_time = 0; 140 static int64_t delta = 1; 141 int64_t this_events_time = hw_event_queue_time (me); 142 143 if (this_events_time == last_events_time) 144 delta++; 145 else 146 { 147 delta = 1; 148 last_events_time = this_events_time; 149 } 150 151 crishw->pending_handler 152 = hw_event_queue_schedule (me, delta, deliver_cris_interrupt, NULL); 153 } 154} 155 156 157/* A port-event function for events arriving to an interrupt port. */ 158 159static void 160cris_port_event (struct hw *me, 161 int my_port, 162 struct hw *source, 163 int source_port, 164 int intparam) 165{ 166 struct cris_hw *crishw = hw_data (me); 167 uint32_t vec; 168 169 /* A few placeholders; only the INT port is implemented. */ 170 switch (my_port) 171 { 172 case INT_PORT: 173 HW_TRACE ((me, "INT value=0x%x", intparam)); 174 break; 175 176 default: 177 hw_abort (me, "bad switch"); 178 break; 179 } 180 181 if (intparam == 0) 182 return; 183 184 if (crishw->int_to_vec != NULL) 185 { 186 unsigned int i; 187 for (i = 0; crishw->int_to_vec[i].portval != 0; i++) 188 if (crishw->int_to_vec[i].portval == intparam) 189 break; 190 191 if (crishw->int_to_vec[i].portval == 0) 192 hw_abort (me, "unsupported value for int port: 0x%x", intparam); 193 194 vec = crishw->int_to_vec[i].vec; 195 } 196 else 197 vec = (uint32_t) intparam; 198 199 if (crishw->pending_vector != 0) 200 { 201 if (vec == crishw->pending_vector) 202 return; 203 204 switch (crishw->multi_int_action) 205 { 206 case cris_multint_abort: 207 hw_abort (me, "int 0x%x (0x%x) while int 0x%x hasn't been delivered", 208 vec, intparam, crishw->pending_vector); 209 break; 210 211 case cris_multint_ignore_previous: 212 break; 213 214 case cris_multint_vector: 215 vec = crishw->multiple_int_vector; 216 break; 217 218 default: 219 hw_abort (me, "bad switch"); 220 } 221 } 222 223 crishw->pending_vector = vec; 224 225 /* Schedule our event handler *now*. */ 226 if (crishw->pending_handler == NULL) 227 crishw->pending_handler 228 = hw_event_queue_schedule (me, 0, deliver_cris_interrupt, NULL); 229} 230 231/* Instance initializer function. */ 232 233static void 234cris_finish (struct hw *me) 235{ 236 struct cris_hw *crishw; 237 const struct hw_property *vec_for_int; 238 const struct hw_property *multiple_int; 239 240 crishw = HW_ZALLOC (me, struct cris_hw); 241 set_hw_data (me, crishw); 242 set_hw_ports (me, cris_ports); 243 set_hw_port_event (me, cris_port_event); 244 245 vec_for_int = hw_find_property (me, "vec-for-int"); 246 if (vec_for_int != NULL) 247 { 248 uint32_t vecsize; 249 uint32_t i; 250 251 if (hw_property_type (vec_for_int) != array_property) 252 hw_abort (me, "property \"vec-for-int\" has the wrong type"); 253 254 vecsize = hw_property_sizeof_array (vec_for_int) / sizeof (signed_cell); 255 256 if ((vecsize % 2) != 0) 257 hw_abort (me, "translation vector does not consist of even pairs"); 258 259 crishw->int_to_vec 260 = hw_malloc (me, (vecsize/2 + 1) * sizeof (crishw->int_to_vec[0])); 261 262 for (i = 0; i < vecsize/2; i++) 263 { 264 signed_cell portval_sc; 265 signed_cell vec_sc; 266 267 if (!hw_find_integer_array_property (me, "vec-for-int", i*2, 268 &portval_sc) 269 || !hw_find_integer_array_property (me, "vec-for-int", i*2 + 1, 270 &vec_sc) 271 || portval_sc < 0 272 || vec_sc < 0) 273 hw_abort (me, "no valid vector translation pair %u", i); 274 275 crishw->int_to_vec[i].portval = (uint32_t) portval_sc; 276 crishw->int_to_vec[i].vec = (uint32_t) vec_sc; 277 } 278 279 crishw->int_to_vec[i].portval = 0; 280 crishw->int_to_vec[i].vec = 0; 281 } 282 283 multiple_int = hw_find_property (me, "multiple-int"); 284 if (multiple_int != NULL) 285 { 286 if (hw_property_type (multiple_int) == integer_property) 287 { 288 crishw->multiple_int_vector 289 = hw_find_integer_property (me, "multiple-int"); 290 crishw->multi_int_action = cris_multint_vector; 291 } 292 else 293 { 294 const char *action = hw_find_string_property (me, "multiple-int"); 295 296 if (action == NULL) 297 hw_abort (me, "property \"multiple-int\" has the wrong type"); 298 299 if (strcmp (action, "abort") == 0) 300 crishw->multi_int_action = cris_multint_abort; 301 else if (strcmp (action, "ignore_previous") == 0) 302 crishw->multi_int_action = cris_multint_ignore_previous; 303 else 304 hw_abort (me, "property \"multiple-int\" must be one of <vector number>\n" 305 "\"abort\" and \"ignore_previous\", not \"%s\"", action); 306 } 307 } 308 else 309 crishw->multi_int_action = cris_multint_abort; 310} 311 312const struct hw_descriptor dv_cris_descriptor[] = { 313 { "cris", cris_finish, }, 314 { NULL }, 315}; 316