]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - sim/m68hc11/dv-m68hc11eepr.c
New simulator.
[thirdparty/binutils-gdb.git] / sim / m68hc11 / dv-m68hc11eepr.c
1 /* dv-m68hc11eepr.c -- Simulation of the 68HC11 Internal EEPROM.
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.)
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 2 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, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 */
21
22
23 #include "sim-main.h"
24 #include "hw-main.h"
25 #include "sim-assert.h"
26 #include "sim-events.h"
27
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <errno.h>
31
32
33
34 /* DEVICE
35
36 m68hc11eepr - m68hc11 EEPROM
37
38
39 DESCRIPTION
40
41 Implements the 68HC11 eeprom device described in the m68hc11
42 user guide (Chapter 4 in the pink book).
43
44
45 PROPERTIES
46
47 reg <base> <length>
48
49 Base of eeprom and its length.
50
51 file <path>
52
53 Path of the EEPROM file. The default is 'm6811.eeprom'.
54
55
56 PORTS
57
58 None
59
60 */
61
62
63
64 /* static functions */
65
66
67 /* port ID's */
68
69 enum
70 {
71 RESET_PORT
72 };
73
74
75 static const struct hw_port_descriptor m68hc11eepr_ports[] =
76 {
77 { "reset", RESET_PORT, 0, input_port, },
78 { NULL, },
79 };
80
81
82
83 /* The timer/counter register internal state. Note that we store
84 state using the control register images, in host endian order. */
85
86 struct m68hc11eepr
87 {
88 address_word base_address; /* control register base */
89 int attach_space;
90 unsigned size;
91
92 /* Current state of the eeprom programing:
93 - eeprom_wmode indicates whether the EEPROM address and byte have
94 been latched.
95 - eeprom_waddr indicates the EEPROM address that was latched
96 and eeprom_wbyte is the byte that was latched.
97 - eeprom_wcycle indicates the CPU absolute cycle type when
98 the high voltage was applied (successfully) on the EEPROM.
99
100 These data members are setup only when we detect good EEPROM programing
101 conditions (see Motorola EEPROM Programming and PPROG register usage).
102 When the high voltage is switched off, we look at the CPU absolute
103 cycle time to see if the EEPROM command must succeeds or not.
104 The EEPROM content is updated and saved only at that time.
105 (EEPROM command is: byte zero bits program, byte erase, row erase
106 and bulk erase).
107
108 The CONFIG register is programmed in the same way. It is physically
109 located at the end of the EEPROM (eeprom size + 1). It is not mapped
110 in memory but it's saved in the EEPROM file. */
111 unsigned long eeprom_wcycle;
112 uint16 eeprom_waddr;
113 uint8 eeprom_wbyte;
114 uint8 eeprom_wmode;
115
116 uint8* eeprom;
117
118 /* Minimum time in CPU cycles for programming the EEPROM. */
119 unsigned long eeprom_min_cycles;
120
121 char* file_name;
122 };
123
124
125
126 /* Finish off the partially created hw device. Attach our local
127 callbacks. Wire up our port names etc. */
128
129 static hw_io_read_buffer_method m68hc11eepr_io_read_buffer;
130 static hw_io_write_buffer_method m68hc11eepr_io_write_buffer;
131 static hw_ioctl_method m68hc11eepr_ioctl;
132
133 /* Read or write the memory bank content from/to a file.
134 Returns 0 if the operation succeeded and -1 if it failed. */
135 static int
136 m6811eepr_memory_rw (struct m68hc11eepr *controller, int mode)
137 {
138 const char *name = controller->file_name;
139 int fd;
140 size_t size;
141
142 size = controller->size;
143 fd = open (name, mode, 0644);
144 if (fd < 0)
145 {
146 if (mode == O_RDONLY)
147 {
148 memset (controller->eeprom, 0xFF, size);
149 /* Default value for CONFIG register (0xFF should be ok):
150 controller->eeprom[size - 1] = M6811_NOSEC | M6811_NOCOP
151 | M6811_ROMON | M6811_EEON; */
152 return 0;
153 }
154 return -1;
155 }
156
157 if (mode == O_RDONLY)
158 {
159 if (read (fd, controller->eeprom, size) != size)
160 {
161 close (fd);
162 return -1;
163 }
164 }
165 else
166 {
167 if (write (fd, controller->eeprom, size) != size)
168 {
169 close (fd);
170 return -1;
171 }
172 }
173 close (fd);
174
175 return 0;
176 }
177
178
179
180
181 static void
182 attach_m68hc11eepr_regs (struct hw *me,
183 struct m68hc11eepr *controller)
184 {
185 unsigned_word attach_address;
186 int attach_space;
187 unsigned attach_size;
188 reg_property_spec reg;
189
190 if (hw_find_property (me, "reg") == NULL)
191 hw_abort (me, "Missing \"reg\" property");
192
193 if (!hw_find_reg_array_property (me, "reg", 0, &reg))
194 hw_abort (me, "\"reg\" property must contain one addr/size entry");
195
196 hw_unit_address_to_attach_address (hw_parent (me),
197 &reg.address,
198 &attach_space,
199 &attach_address,
200 me);
201 hw_unit_size_to_attach_size (hw_parent (me),
202 &reg.size,
203 &attach_size, me);
204
205 /* Attach the two IO registers that control the EEPROM.
206 The EEPROM is only attached at reset time because it may
207 be enabled/disabled by the EEON bit in the CONFIG register. */
208 hw_attach_address (hw_parent (me), 0, io_map, M6811_PPROG, 1, me);
209 hw_attach_address (hw_parent (me), 0, io_map, M6811_CONFIG, 1, me);
210
211 if (hw_find_property (me, "file") == NULL)
212 controller->file_name = "m6811.eeprom";
213 else
214 controller->file_name = hw_find_string_property (me, "file");
215
216 controller->attach_space = attach_space;
217 controller->base_address = attach_address;
218 controller->eeprom = (char*) malloc (attach_size + 1);
219 controller->eeprom_min_cycles = 10000;
220 controller->size = attach_size + 1;
221
222 m6811eepr_memory_rw (controller, O_RDONLY);
223 }
224
225
226 /* An event arrives on an interrupt port. */
227
228 static void
229 m68hc11eepr_port_event (struct hw *me,
230 int my_port,
231 struct hw *source,
232 int source_port,
233 int level)
234 {
235 SIM_DESC sd;
236 struct m68hc11eepr *controller;
237 sim_cpu *cpu;
238
239 controller = hw_data (me);
240 sd = hw_system (me);
241 cpu = STATE_CPU (sd, 0);
242 switch (my_port)
243 {
244 case RESET_PORT:
245 {
246 HW_TRACE ((me, "EEPROM reset"));
247
248 /* Re-read the EEPROM from the file. This gives the chance
249 to users to erase this file before doing a reset and have
250 a fresh EEPROM taken into account. */
251 m6811eepr_memory_rw (controller, O_RDONLY);
252
253 /* Reset the state of EEPROM programmer. The CONFIG register
254 is also initialized from the EEPROM/file content. */
255 cpu->ios[M6811_PPROG] = 0;
256 if (cpu->cpu_use_local_config)
257 cpu->ios[M6811_CONFIG] = cpu->cpu_config;
258 else
259 cpu->ios[M6811_CONFIG] = controller->eeprom[controller->size-1];
260 controller->eeprom_wmode = 0;
261 controller->eeprom_waddr = 0;
262 controller->eeprom_wbyte = 0;
263
264 /* Attach or detach to the bus depending on the EEPROM enable bit.
265 The EEPROM CONFIG register is still enabled and can be programmed
266 for a next configuration (taken into account only after a reset,
267 see Motorola spec). */
268 if (cpu->ios[M6811_CONFIG] & M6811_EEON)
269 {
270 hw_attach_address (hw_parent (me), 0,
271 controller->attach_space,
272 controller->base_address,
273 controller->size - 1,
274 me);
275 }
276 else
277 {
278 hw_detach_address (hw_parent (me), 0,
279 controller->attach_space,
280 controller->base_address,
281 controller->size - 1,
282 me);
283 }
284 break;
285 }
286
287 default:
288 hw_abort (me, "Event on unknown port %d", my_port);
289 break;
290 }
291 }
292
293
294 static void
295 m68hc11eepr_finish (struct hw *me)
296 {
297 struct m68hc11eepr *controller;
298
299 controller = HW_ZALLOC (me, struct m68hc11eepr);
300 me->overlap_mode_hw = 1;
301 set_hw_data (me, controller);
302 set_hw_io_read_buffer (me, m68hc11eepr_io_read_buffer);
303 set_hw_io_write_buffer (me, m68hc11eepr_io_write_buffer);
304 set_hw_ports (me, m68hc11eepr_ports);
305 set_hw_port_event (me, m68hc11eepr_port_event);
306 #ifdef set_hw_ioctl
307 set_hw_ioctl (me, m68hc11eepr_ioctl);
308 #else
309 me->to_ioctl = m68hc11eepr_ioctl;
310 #endif
311
312 attach_m68hc11eepr_regs (me, controller);
313 }
314
315
316
317 static io_reg_desc pprog_desc[] = {
318 { M6811_BYTE, "BYTE ", "Byte Program Mode" },
319 { M6811_ROW, "ROW ", "Row Program Mode" },
320 { M6811_ERASE, "ERASE ", "Erase Mode" },
321 { M6811_EELAT, "EELAT ", "EEProm Latch Control" },
322 { M6811_EEPGM, "EEPGM ", "EEProm Programming Voltable Enable" },
323 { 0, 0, 0 }
324 };
325 extern io_reg_desc config_desc[];
326
327
328 /* Describe the state of the EEPROM device. */
329 static void
330 m68hc11eepr_info (struct hw *me)
331 {
332 SIM_DESC sd;
333 uint16 base = 0;
334 sim_cpu *cpu;
335 struct m68hc11eepr *controller;
336 uint8 val;
337
338 sd = hw_system (me);
339 cpu = STATE_CPU (sd, 0);
340 controller = hw_data (me);
341 base = cpu_get_io_base (cpu);
342
343 sim_io_printf (sd, "M68HC11 EEprom:\n");
344
345 val = cpu->ios[M6811_PPROG];
346 print_io_byte (sd, "PPROG ", pprog_desc, val, base + M6811_PPROG);
347 sim_io_printf (sd, "\n");
348
349 val = cpu->ios[M6811_CONFIG];
350 print_io_byte (sd, "CONFIG ", config_desc, val, base + M6811_CONFIG);
351 sim_io_printf (sd, "\n");
352
353 val = controller->eeprom[controller->size - 1];
354 print_io_byte (sd, "(*NEXT*) ", config_desc, val, base + M6811_CONFIG);
355 sim_io_printf (sd, "\n");
356
357 /* Describe internal state of EEPROM. */
358 if (controller->eeprom_wmode)
359 {
360 if (controller->eeprom_waddr == controller->size - 1)
361 sim_io_printf (sd, " Programming CONFIG register ");
362 else
363 sim_io_printf (sd, " Programming: 0x%04x ",
364 controller->eeprom_waddr);
365
366 sim_io_printf (sd, "with 0x%02x\n",
367 controller->eeprom_wbyte);
368 }
369
370 sim_io_printf (sd, " EEProm file: %s\n",
371 controller->file_name);
372 }
373
374 static int
375 m68hc11eepr_ioctl (struct hw *me,
376 hw_ioctl_request request,
377 va_list ap)
378 {
379 m68hc11eepr_info (me);
380 return 0;
381 }
382
383 /* generic read/write */
384
385 static unsigned
386 m68hc11eepr_io_read_buffer (struct hw *me,
387 void *dest,
388 int space,
389 unsigned_word base,
390 unsigned nr_bytes)
391 {
392 SIM_DESC sd;
393 struct m68hc11eepr *controller;
394 sim_cpu *cpu;
395
396 HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
397
398 sd = hw_system (me);
399 controller = hw_data (me);
400 cpu = STATE_CPU (sd, 0);
401
402 if (space == io_map)
403 {
404 unsigned cnt = 0;
405
406 while (nr_bytes != 0)
407 {
408 switch (base)
409 {
410 case M6811_PPROG:
411 case M6811_CONFIG:
412 *((uint8*) dest) = cpu->ios[base];
413 break;
414
415 default:
416 hw_abort (me, "reading wrong register 0x%04x", base);
417 }
418 dest = (uint8*) (dest) + 1;
419 base++;
420 nr_bytes--;
421 cnt++;
422 }
423 return cnt;
424 }
425
426 /* In theory, we can't read the EEPROM when it's being programmed. */
427 if ((cpu->ios[M6811_PPROG] & M6811_EELAT) != 0
428 && cpu_is_running (cpu))
429 {
430 sim_memory_error (cpu, SIM_SIGBUS, base,
431 "EEprom not configured for reading");
432 }
433
434 base = base - controller->base_address;
435 memcpy (dest, &controller->eeprom[base], nr_bytes);
436 return nr_bytes;
437 }
438
439
440 static unsigned
441 m68hc11eepr_io_write_buffer (struct hw *me,
442 const void *source,
443 int space,
444 unsigned_word base,
445 unsigned nr_bytes)
446 {
447 SIM_DESC sd;
448 struct m68hc11eepr *controller;
449 sim_cpu *cpu;
450 uint8 val;
451
452 HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
453
454 sd = hw_system (me);
455 controller = hw_data (me);
456 cpu = STATE_CPU (sd, 0);
457
458 /* Programming several bytes at a time is not possible. */
459 if (space != io_map && nr_bytes != 1)
460 {
461 sim_memory_error (cpu, SIM_SIGBUS, base,
462 "EEprom write error (only 1 byte can be programmed)");
463 return 0;
464 }
465
466 if (nr_bytes != 1)
467 hw_abort (me, "Cannot write more than 1 byte to EEPROM device at a time");
468
469 val = *((const uint8*) source);
470
471 /* Write to the EEPROM control register. */
472 if (space == io_map && base == M6811_PPROG)
473 {
474 uint8 wrong_bits;
475 uint16 addr;
476
477 addr = base + cpu_get_io_base (cpu);
478
479 /* Setting EELAT and EEPGM at the same time is an error.
480 Clearing them both is ok. */
481 wrong_bits = (cpu->ios[M6811_PPROG] ^ val) & val;
482 wrong_bits &= (M6811_EELAT | M6811_EEPGM);
483
484 if (wrong_bits == (M6811_EEPGM|M6811_EELAT))
485 {
486 sim_memory_error (cpu, SIM_SIGBUS, addr,
487 "Wrong eeprom programing value");
488 return 0;
489 }
490
491 if ((val & M6811_EELAT) == 0)
492 {
493 val = 0;
494 }
495 if ((val & M6811_EEPGM) && !(cpu->ios[M6811_PPROG] & M6811_EELAT))
496 {
497 sim_memory_error (cpu, SIM_SIGBUS, addr,
498 "EEProm high voltage applied after EELAT");
499 }
500 if ((val & M6811_EEPGM) && controller->eeprom_wmode == 0)
501 {
502 sim_memory_error (cpu, SIM_SIGSEGV, addr,
503 "EEProm high voltage applied without address");
504 }
505 if (val & M6811_EEPGM)
506 {
507 controller->eeprom_wcycle = cpu_current_cycle (cpu);
508 }
509 else if (cpu->ios[M6811_PPROG] & M6811_PPROG)
510 {
511 int i;
512 unsigned long t = cpu_current_cycle (cpu);
513
514 t -= controller->eeprom_wcycle;
515 if (t < controller->eeprom_min_cycles)
516 {
517 sim_memory_error (cpu, SIM_SIGILL, addr,
518 "EEprom programmed only for %lu cycles",
519 t);
520 }
521
522 /* Program the byte by clearing some bits. */
523 if (!(cpu->ios[M6811_PPROG] & M6811_ERASE))
524 {
525 controller->eeprom[controller->eeprom_waddr]
526 &= controller->eeprom_wbyte;
527 }
528
529 /* Erase a byte, row or the complete eeprom. Erased value is 0xFF.
530 Ignore row or complete eeprom erase when we are programming the
531 CONFIG register (last EEPROM byte). */
532 else if ((cpu->ios[M6811_PPROG] & M6811_BYTE)
533 || controller->eeprom_waddr == controller->size - 1)
534 {
535 controller->eeprom[controller->eeprom_waddr] = 0xff;
536 }
537 else if (cpu->ios[M6811_BYTE] & M6811_ROW)
538 {
539 size_t max_size;
540
541 /* Size of EEPROM (-1 because the last byte is the
542 CONFIG register. */
543 max_size = controller->size;
544 controller->eeprom_waddr &= 0xFFF0;
545 for (i = 0; i < 16
546 && controller->eeprom_waddr < max_size; i++)
547 {
548 controller->eeprom[controller->eeprom_waddr] = 0xff;
549 controller->eeprom_waddr ++;
550 }
551 }
552 else
553 {
554 size_t max_size;
555
556 max_size = controller->size;
557 for (i = 0; i < max_size; i++)
558 {
559 controller->eeprom[i] = 0xff;
560 }
561 }
562
563 /* Save the eeprom in a file. We have to save after each
564 change because the simulator can be stopped or crash... */
565 if (m6811eepr_memory_rw (controller, O_WRONLY | O_CREAT) != 0)
566 {
567 sim_memory_error (cpu, SIM_SIGABRT, addr,
568 "EEPROM programing failed: errno=%d", errno);
569 }
570 controller->eeprom_wmode = 0;
571 }
572 cpu->ios[M6811_PPROG] = val;
573 return 1;
574 }
575
576 /* The CONFIG IO register is mapped at end of EEPROM.
577 It's not visible. */
578 if (space == io_map && base == M6811_CONFIG)
579 {
580 base = controller->size - 1;
581 }
582 else
583 {
584 base = base - controller->base_address;
585 }
586
587 /* Writing the memory is allowed for the Debugger or simulator
588 (cpu not running). */
589 if (cpu_is_running (cpu))
590 {
591 if ((cpu->ios[M6811_PPROG] & M6811_EELAT) == 0)
592 {
593 sim_memory_error (cpu, SIM_SIGSEGV, base,
594 "EEprom not configured for writing");
595 return 0;
596 }
597 if (controller->eeprom_wmode != 0)
598 {
599 sim_memory_error (cpu, SIM_SIGSEGV, base,
600 "EEprom write error");
601 return 0;
602 }
603 controller->eeprom_wmode = 1;
604 controller->eeprom_waddr = base;
605 controller->eeprom_wbyte = val;
606 }
607 else
608 {
609 controller->eeprom[base] = val;
610 m6811eepr_memory_rw (controller, O_WRONLY);
611 }
612
613 return 1;
614 }
615
616 const struct hw_descriptor dv_m68hc11eepr_descriptor[] = {
617 { "m68hc11eepr", m68hc11eepr_finish, },
618 { NULL },
619 };
620