xref: /netbsd-src/external/gpl3/gdb.old/dist/sim/cris/dv-cris.c (revision 8b657b0747480f8989760d71343d6dd33f8d4cf9)
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".  */
78 enum
79  {
80    INT_PORT,
81  };
82 
83 static const struct hw_port_descriptor cris_ports[] =
84  {
85    { "int", INT_PORT, 0, input_port },
86    { NULL, 0, 0, 0 }
87  };
88 
89 struct cris_vec_tr
90  {
91    uint32_t portval, vec;
92  };
93 
94 enum cris_multiple_ints
95   {
96     cris_multint_abort,
97     cris_multint_ignore_previous,
98     cris_multint_vector
99   };
100 
101 struct 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 
113 static void
114 deliver_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 
159 static void
160 cris_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 
233 static void
234 cris_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 
312 const struct hw_descriptor dv_cris_descriptor[] = {
313   { "cris", cris_finish, },
314   { NULL },
315 };
316