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