dv-tx3904cpu.c revision 1.6
1/*  This file is part of the program GDB, the GNU debugger.
2
3    Copyright (C) 1998-2016 Free Software Foundation, Inc.
4    Contributed by Cygnus Solutions.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19    */
20
21
22#include "sim-main.h"
23#include "hw-main.h"
24
25/* DEVICE
26
27
28   tx3904cpu - tx3904 cpu virtual device
29
30
31   DESCRIPTION
32
33
34   Implements the external tx3904 functionality.  This includes the
35   delivery of of interrupts generated from other devices and the
36   handling of device specific registers.
37
38
39   PROPERTIES
40
41   none
42
43
44   PORTS
45
46
47   reset (input)
48
49   Currently ignored.
50
51
52   nmi (input)
53
54   Deliver a non-maskable interrupt to the processor.
55
56
57   level (input)
58
59   Deliver a maskable interrupt of given level, corresponding to
60   IP[5:0], to processor.
61
62
63
64   BUGS
65
66
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.  An
73   alternative implementation might modify instructions that change
74   the PSW and have them check to see if the change makes an interrupt
75   delivery possible.
76
77   */
78
79
80
81struct tx3904cpu {
82  /* Pending interrupts for delivery by event handler */
83  int pending_reset, pending_nmi, pending_level;
84  struct hw_event* event;
85};
86
87
88
89/* input port ID's */
90
91enum {
92  RESET_PORT,
93  NMI_PORT,
94  LEVEL_PORT,
95};
96
97
98static const struct hw_port_descriptor tx3904cpu_ports[] = {
99
100  /* interrupt inputs */
101  { "reset", RESET_PORT, 0, input_port, },
102  { "nmi", NMI_PORT, 0, input_port, },
103  { "level", LEVEL_PORT, 0, input_port, },
104
105  { NULL, },
106};
107
108
109/* Finish off the partially created hw device.  Attach our local
110   callbacks.  Wire up our port names etc */
111
112static hw_port_event_method tx3904cpu_port_event;
113
114
115
116static void
117tx3904cpu_finish (struct hw *me)
118{
119  struct tx3904cpu *controller;
120
121  controller = HW_ZALLOC (me, struct tx3904cpu);
122  set_hw_data (me, controller);
123  set_hw_ports (me, tx3904cpu_ports);
124  set_hw_port_event (me, tx3904cpu_port_event);
125
126  /* Initialize the pending interrupt flags */
127  controller->pending_level = 0;
128  controller->pending_reset = 0;
129  controller->pending_nmi = 0;
130  controller->event = NULL;
131}
132
133
134
135/* An event arrives on an interrupt port */
136
137static void
138deliver_tx3904cpu_interrupt (struct hw *me,
139			    void *data)
140{
141  struct tx3904cpu *controller = hw_data (me);
142  SIM_DESC sd = hw_system (me);
143  sim_cpu *cpu = STATE_CPU (sd, 0); /* NB: fix CPU 0. */
144  address_word cia = CPU_PC_GET (cpu);
145
146  if (controller->pending_reset)
147    {
148      controller->pending_reset = 0;
149      HW_TRACE ((me, "reset pc=0x%08lx", (long) CPU_PC_GET (cpu)));
150      SignalExceptionNMIReset();
151    }
152  else if (controller->pending_nmi)
153    {
154      controller->pending_nmi = 0;
155      HW_TRACE ((me, "nmi pc=0x%08lx", (long) CPU_PC_GET (cpu)));
156      SignalExceptionNMIReset();
157    }
158  else if (controller->pending_level)
159    {
160      HW_TRACE ((me, "interrupt level=%d pc=0x%08lx sr=0x%08lx",
161		 controller->pending_level,
162		 (long) CPU_PC_GET (cpu), (long) SR));
163
164      /* Clear CAUSE register.  It may stay this way if the interrupt
165	 was cleared with a negative pending_level. */
166      CAUSE &= ~ (cause_IP_mask << cause_IP_shift);
167
168      if(controller->pending_level > 0) /* interrupt set */
169	{
170	  /* set hardware-interrupt subfields of CAUSE register */
171	  CAUSE |= (controller->pending_level & cause_IP_mask) << cause_IP_shift;
172
173	  /* check for enabled / unmasked interrupts */
174	  if((SR & status_IEc) &&
175	     (controller->pending_level & ((SR >> status_IM_shift) & status_IM_mask)))
176	    {
177	      controller->pending_level = 0;
178	      SignalExceptionInterrupt(0 /* dummy value */);
179	    }
180	  else
181	    {
182	      /* reschedule soon */
183	      if(controller->event != NULL)
184		hw_event_queue_deschedule(me, controller->event);
185	      controller->event =
186		hw_event_queue_schedule (me, 1, deliver_tx3904cpu_interrupt, NULL);
187	    }
188	} /* interrupt set */
189    }
190}
191
192
193static void
194tx3904cpu_port_event (struct hw *me,
195		     int my_port,
196		     struct hw *source,
197		     int source_port,
198		     int level)
199{
200  struct tx3904cpu *controller = hw_data (me);
201
202  switch (my_port)
203    {
204    case RESET_PORT:
205      controller->pending_reset = 1;
206      HW_TRACE ((me, "port-in reset"));
207      break;
208
209    case NMI_PORT:
210      controller->pending_nmi = 1;
211      HW_TRACE ((me, "port-in nmi"));
212      break;
213
214    case LEVEL_PORT:
215      /* level == 0 means that the interrupt was cleared */
216      if(level == 0)
217	controller->pending_level = -1; /* signal end of interrupt */
218      else
219	controller->pending_level = level;
220      HW_TRACE ((me, "port-in level=%d", level));
221      break;
222
223    default:
224      hw_abort (me, "bad switch");
225      break;
226    }
227
228  /* Schedule an event to be delivered immediately after current
229     instruction. */
230  if(controller->event != NULL)
231    hw_event_queue_deschedule(me, controller->event);
232  controller->event =
233    hw_event_queue_schedule (me, 0, deliver_tx3904cpu_interrupt, NULL);
234}
235
236
237const struct hw_descriptor dv_tx3904cpu_descriptor[] = {
238  { "tx3904cpu", tx3904cpu_finish, },
239  { NULL },
240};
241