1 /* dv-m68hc11.c -- CPU 68HC11 as a device.
2 Copyright (C) 1999, 2000 Free Software Foundation, Inc.
3 Written by Stephane Carrez (stcarrez@worldnet.fr)
4 (From a driver model Contributed by Cygnus Solutions.)
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 2 of the License, or
9 (at your option) any later version.
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.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 m68hc11cpu - m68hc11 cpu virtual device
33 Implements the external m68hc11 functionality. This includes the
34 delivery of of interrupts generated from other devices and the
35 handling of device specific registers.
42 Register base (should be 0x1000 0x03f).
46 Frequency of the quartz used by the processor.
48 mode [single | expanded | bootstrap | test]
50 Cpu operating mode (the MODA and MODB external pins).
57 Reset the cpu and generates a cpu-reset event (used to reset
62 Deliver a non-maskable interrupt to the processor.
67 Event generated after the CPU performs a reset.
72 When delivering an interrupt, this code assumes that there is only
73 one processor (number 0).
80 /* Pending interrupts for delivery by event handler. */
84 struct hw_event
*event
;
85 unsigned_word attach_address
;
102 static const struct hw_port_descriptor m68hc11cpu_ports
[] = {
104 /* Interrupt inputs. */
105 { "reset", RESET_PORT
, 0, input_port
, },
106 { "nmi", NMI_PORT
, 0, input_port
, },
107 { "irq", IRQ_PORT
, 0, input_port
, },
109 /* Events generated for connection to other devices. */
110 { "cpu-reset", CPU_RESET_PORT
, 0, output_port
, },
115 static hw_io_read_buffer_method m68hc11cpu_io_read_buffer
;
116 static hw_io_write_buffer_method m68hc11cpu_io_write_buffer
;
117 static hw_ioctl_method m68hc11_ioctl
;
119 /* Finish off the partially created hw device. Attach our local
120 callbacks. Wire up our port names etc. */
122 static hw_port_event_method m68hc11cpu_port_event
;
126 dv_m6811_attach_address_callback (struct hw
*me
,
130 address_word nr_bytes
,
133 HW_TRACE ((me
, "attach - level=%d, space=%d, addr=0x%lx, sz=%ld, client=%s",
134 level
, space
, (unsigned long) addr
, (unsigned long) nr_bytes
,
139 sim_core_attach (hw_system (me
),
142 access_read_write_exec
,
151 /*printf("Attach from sub device: %d\n", (long) addr);*/
152 sim_core_attach (hw_system (me
),
165 dv_m6811_detach_address_callback (struct hw
*me
,
169 address_word nr_bytes
,
172 sim_core_detach (hw_system (me
), NULL
, /*cpu*/
178 attach_m68hc11_regs (struct hw
*me
,
179 struct m68hc11cpu
*controller
)
183 reg_property_spec reg
;
184 const char *cpu_mode
;
186 if (hw_find_property (me
, "reg") == NULL
)
187 hw_abort (me
, "Missing \"reg\" property");
189 if (!hw_find_reg_array_property (me
, "reg", 0, ®
))
190 hw_abort (me
, "\"reg\" property must contain one addr/size entry");
192 hw_unit_address_to_attach_address (hw_parent (me
),
194 &controller
->attach_space
,
195 &controller
->attach_address
,
197 hw_unit_size_to_attach_size (hw_parent (me
),
199 &controller
->attach_size
, me
);
201 hw_attach_address (hw_parent (me
), M6811_IO_LEVEL
,
202 controller
->attach_space
,
203 controller
->attach_address
,
204 controller
->attach_size
,
208 /* Get cpu frequency. */
210 cpu
= STATE_CPU (sd
, 0);
211 if (hw_find_property (me
, "clock") != NULL
)
213 cpu
->cpu_frequency
= hw_find_integer_property (me
, "clock");
217 cpu
->cpu_frequency
= 8*1000*1000;
220 cpu_mode
= "expanded";
221 if (hw_find_property (me
, "mode") != NULL
)
222 cpu_mode
= hw_find_string_property (me
, "mode");
224 if (strcmp (cpu_mode
, "test") == 0)
225 cpu
->cpu_mode
= M6811_MDA
| M6811_SMOD
;
226 else if (strcmp (cpu_mode
, "bootstrap") == 0)
227 cpu
->cpu_mode
= M6811_SMOD
;
228 else if (strcmp (cpu_mode
, "single") == 0)
231 cpu
->cpu_mode
= M6811_MDA
;
235 m68hc11cpu_finish (struct hw
*me
)
237 struct m68hc11cpu
*controller
;
239 controller
= HW_ZALLOC (me
, struct m68hc11cpu
);
240 set_hw_data (me
, controller
);
241 set_hw_io_read_buffer (me
, m68hc11cpu_io_read_buffer
);
242 set_hw_io_write_buffer (me
, m68hc11cpu_io_write_buffer
);
243 set_hw_ports (me
, m68hc11cpu_ports
);
244 set_hw_port_event (me
, m68hc11cpu_port_event
);
245 set_hw_attach_address (me
, dv_m6811_attach_address_callback
);
246 set_hw_detach_address (me
, dv_m6811_detach_address_callback
);
248 set_hw_ioctl (me
, m68hc11_ioctl
);
250 me
->to_ioctl
= m68hc11_ioctl
;
253 /* Initialize the pending interrupt flags. */
254 controller
->pending_level
= 0;
255 controller
->pending_reset
= 0;
256 controller
->pending_nmi
= 0;
257 controller
->event
= NULL
;
259 attach_m68hc11_regs (me
, controller
);
264 /* An event arrives on an interrupt port. */
267 deliver_m68hc11cpu_interrupt (struct hw
*me
, void *data
)
273 m68hc11cpu_port_event (struct hw
*me
,
279 struct m68hc11cpu
*controller
= hw_data (me
);
284 cpu
= STATE_CPU (sd
, 0);
288 HW_TRACE ((me
, "port-in reset"));
290 /* The reset is made in 3 steps:
291 - First, cleanup the current sim_cpu struct.
293 - Restart the cpu for the reset (get the CPU mode from the
294 CONFIG register that gets initialized by EEPROM device). */
296 hw_port_event (me
, CPU_RESET_PORT
, 1);
301 controller
->pending_nmi
= 1;
302 HW_TRACE ((me
, "port-in nmi"));
306 /* level == 0 means that the interrupt was cleared. */
308 controller
->pending_level
= -1; /* signal end of interrupt */
310 controller
->pending_level
= level
;
311 HW_TRACE ((me
, "port-in level=%d", level
));
315 hw_abort (me
, "bad switch");
319 /* Schedule an event to be delivered immediately after current
321 if(controller
->event
!= NULL
)
322 hw_event_queue_deschedule(me
, controller
->event
);
324 hw_event_queue_schedule (me
, 0, deliver_m68hc11cpu_interrupt
, NULL
);
328 io_reg_desc config_desc
[] = {
329 { M6811_NOSEC
, "NOSEC ", "Security Mode Disable" },
330 { M6811_NOCOP
, "NOCOP ", "COP System Disable" },
331 { M6811_ROMON
, "ROMON ", "Enable On-chip Rom" },
332 { M6811_EEON
, "EEON ", "Enable On-chip EEprom" },
336 io_reg_desc hprio_desc
[] = {
337 { M6811_RBOOT
, "RBOOT ", "Read Bootstrap ROM" },
338 { M6811_SMOD
, "SMOD ", "Special Mode" },
339 { M6811_MDA
, "MDA ", "Mode Select A" },
340 { M6811_IRV
, "IRV ", "Internal Read Visibility" },
344 io_reg_desc option_desc
[] = {
345 { M6811_ADPU
, "ADPU ", "A/D Powerup" },
346 { M6811_CSEL
, "CSEL ", "A/D/EE Charge pump clock source select" },
347 { M6811_IRQE
, "IRQE ", "IRQ Edge/Level sensitive" },
348 { M6811_DLY
, "DLY ", "Stop exit turn on delay" },
349 { M6811_CME
, "CME ", "Clock Monitor Enable" },
350 { M6811_CR1
, "CR1 ", "COP timer rate select (CR1)" },
351 { M6811_CR0
, "CR0 ", "COP timer rate select (CR0)" },
356 m68hc11_info (struct hw
*me
)
361 struct m68hc11sio
*controller
;
365 cpu
= STATE_CPU (sd
, 0);
366 controller
= hw_data (me
);
368 base
= cpu_get_io_base (cpu
);
369 sim_io_printf (sd
, "M68HC11:\n");
371 val
= cpu
->ios
[M6811_HPRIO
];
372 print_io_byte (sd
, "HPRIO ", hprio_desc
, val
, base
+ M6811_HPRIO
);
373 sim_io_printf (sd
, "\n");
375 val
= cpu
->ios
[M6811_CONFIG
];
376 print_io_byte (sd
, "CONFIG", config_desc
, val
, base
+ M6811_CONFIG
);
377 sim_io_printf (sd
, "\n");
379 val
= cpu
->ios
[M6811_OPTION
];
380 print_io_byte (sd
, "OPTION", option_desc
, val
, base
+ M6811_OPTION
);
381 sim_io_printf (sd
, "\n");
383 val
= cpu
->ios
[M6811_INIT
];
384 print_io_byte (sd
, "INIT ", 0, val
, base
+ M6811_INIT
);
385 sim_io_printf (sd
, "Ram = 0x%04x IO = 0x%04x\n",
386 (((uint16
) (val
& 0xF0)) << 8),
387 (((uint16
) (val
& 0x0F)) << 12));
391 interrupts_info (sd
, &cpu
->cpu_interrupts
);
395 m68hc11_ioctl (struct hw
*me
,
396 hw_ioctl_request request
,
403 /* generic read/write */
406 m68hc11cpu_io_read_buffer (struct hw
*me
,
413 struct m68hc11cpu
*controller
= hw_data (me
);
418 HW_TRACE ((me
, "read 0x%08lx %d", (long) base
, (int) nr_bytes
));
421 cpu
= STATE_CPU (sd
, 0);
423 /* Handle reads for the sub-devices. */
424 base
-= controller
->attach_address
;
425 result
= sim_core_read_buffer (sd
, cpu
,
426 io_map
, dest
, base
, nr_bytes
);
435 memcpy (dest
, &cpu
->ios
[base
], 1);
446 m68hc11cpu_io_write (struct hw
*me
, sim_cpu
*cpu
,
447 unsigned_word addr
, uint8 val
)
479 /* Change the RAM and I/O mapping. */
482 uint8 old_bank
= cpu
->ios
[M6811_INIT
];
484 cpu
->ios
[M6811_INIT
] = val
;
486 /* Update IO mapping. Detach from the old address
487 and attach to the new one. */
488 if ((old_bank
& 0xF0) != (val
& 0xF0))
490 struct m68hc11cpu
*controller
= hw_data (me
);
492 hw_detach_address (hw_parent (me
), M6811_IO_LEVEL
,
493 controller
->attach_space
,
494 controller
->attach_address
,
495 controller
->attach_size
,
497 controller
->attach_address
= (val
& 0x0F0) << 12;
498 hw_attach_address (hw_parent (me
), M6811_IO_LEVEL
,
499 controller
->attach_space
,
500 controller
->attach_address
,
501 controller
->attach_size
,
504 if ((old_bank
& 0x0F) != (val
& 0x0F))
511 /* Writing the config is similar to programing the eeprom.
512 The config register value is the last byte of the EEPROM.
513 This last byte is not mapped in memory (that's why we have
514 to add '1' to 'end_addr'). */
523 if (val
== 0xAA && cpu
->ios
[addr
] == 0x55)
526 /* COP reset here. */
534 cpu
->ios
[addr
] = val
;
538 m68hc11cpu_io_write_buffer (struct hw
*me
,
545 struct m68hc11cpu
*controller
= hw_data (me
);
550 HW_TRACE ((me
, "write 0x%08lx %d", (long) base
, (int) nr_bytes
));
553 cpu
= STATE_CPU (sd
, 0);
554 base
-= controller
->attach_address
;
555 result
= sim_core_write_buffer (sd
, cpu
,
556 io_map
, source
, base
, nr_bytes
);
567 val
= *((uint8
*) source
);
568 m68hc11cpu_io_write (me
, cpu
, base
, val
);
577 const struct hw_descriptor dv_m68hc11_descriptor
[] = {
578 { "m68hc11", m68hc11cpu_finish
, },