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