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