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