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 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".  */
75enum
76 {
77   INT_PORT,
78 };
79
80static const struct hw_port_descriptor cris_ports[] =
81 {
82   { "int", INT_PORT, 0, input_port },
83   { NULL, 0, 0, 0 }
84 };
85
86struct cris_vec_tr
87 {
88   unsigned32 portval, vec;
89 };
90
91enum cris_multiple_ints
92  {
93    cris_multint_abort,
94    cris_multint_ignore_previous,
95    cris_multint_vector
96  };
97
98struct 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
110static void
111deliver_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
156static void
157cris_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
230static void
231cris_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
309const struct hw_descriptor dv_cris_descriptor[] = {
310  { "cris", cris_finish, },
311  { NULL },
312};
313