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