Imported Upstream version 7.9
[platform/upstream/gdb.git] / sim / m68hc11 / dv-m68hc11eepr.c
1 /*  dv-m68hc11eepr.c -- Simulation of the 68HC11 Internal EEPROM.
2     Copyright (C) 1999-2015 Free Software Foundation, Inc.
3     Written by Stephane Carrez (stcarrez@nerim.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 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 #include "sim-assert.h"
25 #include "sim-events.h"
26
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <errno.h>
30
31
32
33 /* DEVICE
34
35         m68hc11eepr - m68hc11 EEPROM
36
37    
38    DESCRIPTION
39
40         Implements the 68HC11 eeprom device described in the m68hc11
41         user guide (Chapter 4 in the pink book).
42
43
44    PROPERTIES
45
46    reg <base> <length>
47
48         Base of eeprom and its length.
49
50    file <path>
51
52         Path of the EEPROM file.  The default is 'm6811.eeprom'.
53
54
55    PORTS
56
57         None
58
59    */
60
61
62
63 /* static functions */
64
65
66 /* port ID's */
67
68 enum
69 {
70   RESET_PORT
71 };
72
73
74 static const struct hw_port_descriptor m68hc11eepr_ports[] = 
75 {
76   { "reset", RESET_PORT, 0, input_port, },
77   { NULL, },
78 };
79
80
81
82 /* The timer/counter register internal state.  Note that we store
83    state using the control register images, in host endian order.  */
84
85 struct m68hc11eepr 
86 {
87   address_word  base_address; /* control register base */
88   int           attach_space;
89   unsigned      size;
90   int           mapped;
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   const 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), M6811_IO_LEVEL,
209                      io_map, M6811_PPROG, 1, me);
210   hw_attach_address (hw_parent (me), M6811_IO_LEVEL,
211                      io_map, M6811_CONFIG, 1, me);
212
213   if (hw_find_property (me, "file") == NULL)
214     controller->file_name = "m6811.eeprom";
215   else
216     controller->file_name = hw_find_string_property (me, "file");
217   
218   controller->attach_space = attach_space;
219   controller->base_address = attach_address;
220   controller->eeprom = (char*) hw_malloc (me, attach_size + 1);
221   controller->eeprom_min_cycles = 10000;
222   controller->size = attach_size + 1;
223   controller->mapped = 0;
224   
225   m6811eepr_memory_rw (controller, O_RDONLY);
226 }
227
228
229 /* An event arrives on an interrupt port.  */
230
231 static void
232 m68hc11eepr_port_event (struct hw *me,
233                         int my_port,
234                         struct hw *source,
235                         int source_port,
236                         int level)
237 {
238   SIM_DESC sd;
239   struct m68hc11eepr *controller;
240   sim_cpu *cpu;
241   
242   controller = hw_data (me);
243   sd         = hw_system (me);
244   cpu        = STATE_CPU (sd, 0);
245   switch (my_port)
246     {
247     case RESET_PORT:
248       {
249         HW_TRACE ((me, "EEPROM reset"));
250
251         /* Re-read the EEPROM from the file.  This gives the chance
252            to users to erase this file before doing a reset and have
253            a fresh EEPROM taken into account.  */
254         m6811eepr_memory_rw (controller, O_RDONLY);
255
256         /* Reset the state of EEPROM programmer.  The CONFIG register
257            is also initialized from the EEPROM/file content.  */
258         cpu->ios[M6811_PPROG]    = 0;
259         if (cpu->cpu_use_local_config)
260           cpu->ios[M6811_CONFIG] = cpu->cpu_config;
261         else
262           cpu->ios[M6811_CONFIG]   = controller->eeprom[controller->size-1];
263         controller->eeprom_wmode = 0;
264         controller->eeprom_waddr = 0;
265         controller->eeprom_wbyte = 0;
266
267         /* Attach or detach to the bus depending on the EEPROM enable bit.
268            The EEPROM CONFIG register is still enabled and can be programmed
269            for a next configuration (taken into account only after a reset,
270            see Motorola spec).  */
271         if (!(cpu->ios[M6811_CONFIG] & M6811_EEON))
272           {
273             if (controller->mapped)
274               hw_detach_address (hw_parent (me), M6811_EEPROM_LEVEL,
275                                  controller->attach_space,
276                                  controller->base_address,
277                                  controller->size - 1,
278                                  me);
279             controller->mapped = 0;
280           }
281         else
282           {
283             if (!controller->mapped)
284               hw_attach_address (hw_parent (me), M6811_EEPROM_LEVEL,
285                                  controller->attach_space,
286                                  controller->base_address,
287                                  controller->size - 1,
288                                  me);
289             controller->mapped = 1;
290           }
291         break;
292       }
293
294     default:
295       hw_abort (me, "Event on unknown port %d", my_port);
296       break;
297     }
298 }
299
300
301 static void
302 m68hc11eepr_finish (struct hw *me)
303 {
304   struct m68hc11eepr *controller;
305
306   controller = HW_ZALLOC (me, struct m68hc11eepr);
307   set_hw_data (me, controller);
308   set_hw_io_read_buffer (me, m68hc11eepr_io_read_buffer);
309   set_hw_io_write_buffer (me, m68hc11eepr_io_write_buffer);
310   set_hw_ports (me, m68hc11eepr_ports);
311   set_hw_port_event (me, m68hc11eepr_port_event);
312 #ifdef set_hw_ioctl
313   set_hw_ioctl (me, m68hc11eepr_ioctl);
314 #else
315   me->to_ioctl = m68hc11eepr_ioctl;
316 #endif
317
318   attach_m68hc11eepr_regs (me, controller);
319 }
320  
321
322
323 static io_reg_desc pprog_desc[] = {
324   { M6811_BYTE,  "BYTE  ", "Byte Program Mode" },
325   { M6811_ROW,   "ROW   ", "Row Program Mode" },
326   { M6811_ERASE, "ERASE ", "Erase Mode" },
327   { M6811_EELAT, "EELAT ", "EEProm Latch Control" },
328   { M6811_EEPGM, "EEPGM ", "EEProm Programming Voltable Enable" },
329   { 0,  0, 0 }
330 };
331 extern io_reg_desc config_desc[];
332
333
334 /* Describe the state of the EEPROM device.  */
335 static void
336 m68hc11eepr_info (struct hw *me)
337 {
338   SIM_DESC sd;
339   uint16 base = 0;
340   sim_cpu *cpu;
341   struct m68hc11eepr *controller;
342   uint8 val;
343   
344   sd         = hw_system (me);
345   cpu        = STATE_CPU (sd, 0);
346   controller = hw_data (me);
347   base       = cpu_get_io_base (cpu);
348   
349   sim_io_printf (sd, "M68HC11 EEprom:\n");
350
351   val = cpu->ios[M6811_PPROG];
352   print_io_byte (sd, "PPROG  ", pprog_desc, val, base + M6811_PPROG);
353   sim_io_printf (sd, "\n");
354
355   val = cpu->ios[M6811_CONFIG];
356   print_io_byte (sd, "CONFIG ", config_desc, val, base + M6811_CONFIG);
357   sim_io_printf (sd, "\n");
358
359   val = controller->eeprom[controller->size - 1];
360   print_io_byte (sd, "(*NEXT*) ", config_desc, val, base + M6811_CONFIG);
361   sim_io_printf (sd, "\n");
362   
363   /* Describe internal state of EEPROM.  */
364   if (controller->eeprom_wmode)
365     {
366       if (controller->eeprom_waddr == controller->size - 1)
367         sim_io_printf (sd, "  Programming CONFIG register ");
368       else
369         sim_io_printf (sd, "  Programming: 0x%04x ",
370                        controller->eeprom_waddr + controller->base_address);
371
372       sim_io_printf (sd, "with 0x%02x\n",
373                      controller->eeprom_wbyte);
374     }
375
376   sim_io_printf (sd, "  EEProm file: %s\n",
377                  controller->file_name);
378 }
379
380 static int
381 m68hc11eepr_ioctl (struct hw *me,
382                    hw_ioctl_request request,
383                    va_list ap)
384 {
385   m68hc11eepr_info (me);
386   return 0;
387 }
388
389 /* generic read/write */
390
391 static unsigned
392 m68hc11eepr_io_read_buffer (struct hw *me,
393                             void *dest,
394                             int space,
395                             unsigned_word base,
396                             unsigned nr_bytes)
397 {
398   SIM_DESC sd;
399   struct m68hc11eepr *controller;
400   sim_cpu *cpu;
401   
402   HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
403
404   sd         = hw_system (me);
405   controller = hw_data (me);
406   cpu        = STATE_CPU (sd, 0);
407
408   if (space == io_map)
409     {
410       unsigned cnt = 0;
411       
412       while (nr_bytes != 0)
413         {
414           switch (base)
415             {
416             case M6811_PPROG:
417             case M6811_CONFIG:
418               *((uint8*) dest) = cpu->ios[base];
419               break;
420
421             default:
422               hw_abort (me, "reading wrong register 0x%04x", base);
423             }
424           dest = (uint8*) (dest) + 1;
425           base++;
426           nr_bytes--;
427           cnt++;
428         }
429       return cnt;
430     }
431
432   /* In theory, we can't read the EEPROM when it's being programmed.  */
433   if ((cpu->ios[M6811_PPROG] & M6811_EELAT) != 0
434       && cpu_is_running (cpu))
435     {
436       sim_memory_error (cpu, SIM_SIGBUS, base,
437                         "EEprom not configured for reading");
438     }
439
440   base = base - controller->base_address;
441   memcpy (dest, &controller->eeprom[base], nr_bytes);
442   return nr_bytes;
443 }
444
445
446 static unsigned
447 m68hc11eepr_io_write_buffer (struct hw *me,
448                              const void *source,
449                              int space,
450                              unsigned_word base,
451                              unsigned nr_bytes)
452 {
453   SIM_DESC sd;
454   struct m68hc11eepr *controller;
455   sim_cpu *cpu;
456   uint8 val;
457
458   HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
459
460   sd         = hw_system (me);
461   controller = hw_data (me);
462   cpu        = STATE_CPU (sd, 0);
463
464   /* Programming several bytes at a time is not possible.  */
465   if (space != io_map && nr_bytes != 1)
466     {
467       sim_memory_error (cpu, SIM_SIGBUS, base,
468                         "EEprom write error (only 1 byte can be programmed)");
469       return 0;
470     }
471   
472   if (nr_bytes != 1)
473     hw_abort (me, "Cannot write more than 1 byte to EEPROM device at a time");
474
475   val = *((const uint8*) source);
476
477   /* Write to the EEPROM control register.  */
478   if (space == io_map && base == M6811_PPROG)
479     {
480       uint8 wrong_bits;
481       uint16 addr;
482       
483       addr = base + cpu_get_io_base (cpu);
484
485       /* Setting EELAT and EEPGM at the same time is an error.
486          Clearing them both is ok.  */
487       wrong_bits = (cpu->ios[M6811_PPROG] ^ val) & val;
488       wrong_bits &= (M6811_EELAT | M6811_EEPGM);
489
490       if (wrong_bits == (M6811_EEPGM|M6811_EELAT))
491         {
492           sim_memory_error (cpu, SIM_SIGBUS, addr,
493                             "Wrong eeprom programing value");
494           return 0;
495         }
496
497       if ((val & M6811_EELAT) == 0)
498         {
499           val = 0;
500         }
501       if ((val & M6811_EEPGM) && !(cpu->ios[M6811_PPROG] & M6811_EELAT))
502         {
503           sim_memory_error (cpu, SIM_SIGBUS, addr,
504                             "EEProm high voltage applied after EELAT");
505         }
506       if ((val & M6811_EEPGM) && controller->eeprom_wmode == 0)
507         {
508           sim_memory_error (cpu, SIM_SIGSEGV, addr,
509                             "EEProm high voltage applied without address");
510         }
511       if (val & M6811_EEPGM)
512         {
513           controller->eeprom_wcycle = cpu_current_cycle (cpu);
514         }
515       else if (cpu->ios[M6811_PPROG] & M6811_PPROG)
516         {
517           int i;
518           unsigned long t = cpu_current_cycle (cpu);
519
520           t -= controller->eeprom_wcycle;
521           if (t < controller->eeprom_min_cycles)
522             {
523               sim_memory_error (cpu, SIM_SIGILL, addr,
524                                 "EEprom programmed only for %lu cycles",
525                                 t);
526             }
527
528           /* Program the byte by clearing some bits.  */
529           if (!(cpu->ios[M6811_PPROG] & M6811_ERASE))
530             {
531               controller->eeprom[controller->eeprom_waddr]
532                 &= controller->eeprom_wbyte;
533             }
534
535           /* Erase a byte, row or the complete eeprom.  Erased value is 0xFF.
536              Ignore row or complete eeprom erase when we are programming the
537              CONFIG register (last EEPROM byte).  */
538           else if ((cpu->ios[M6811_PPROG] & M6811_BYTE)
539                    || controller->eeprom_waddr == controller->size - 1)
540             {
541               controller->eeprom[controller->eeprom_waddr] = 0xff;
542             }
543           else if (cpu->ios[M6811_BYTE] & M6811_ROW)
544             {
545               size_t max_size;
546
547               /* Size of EEPROM (-1 because the last byte is the
548                  CONFIG register.  */
549               max_size = controller->size;
550               controller->eeprom_waddr &= 0xFFF0;
551               for (i = 0; i < 16
552                      && controller->eeprom_waddr < max_size; i++)
553                 {
554                   controller->eeprom[controller->eeprom_waddr] = 0xff;
555                   controller->eeprom_waddr ++;
556                 }
557             }
558           else
559             {
560               size_t max_size;
561
562               max_size = controller->size;
563               for (i = 0; i < max_size; i++)
564                 {
565                   controller->eeprom[i] = 0xff;
566                 }
567             }
568
569           /* Save the eeprom in a file.  We have to save after each
570              change because the simulator can be stopped or crash...  */
571           if (m6811eepr_memory_rw (controller, O_WRONLY | O_CREAT) != 0)
572             {
573               sim_memory_error (cpu, SIM_SIGABRT, addr,
574                                 "EEPROM programing failed: errno=%d", errno);
575             }
576           controller->eeprom_wmode = 0;
577         }
578       cpu->ios[M6811_PPROG] = val;
579       return 1;
580     }
581
582   /* The CONFIG IO register is mapped at end of EEPROM.
583      It's not visible.  */
584   if (space == io_map && base == M6811_CONFIG)
585     {
586       base = controller->size - 1;
587     }
588   else
589     {
590       base = base - controller->base_address;
591     }
592
593   /* Writing the memory is allowed for the Debugger or simulator
594      (cpu not running).  */
595   if (cpu_is_running (cpu))
596     {
597       if ((cpu->ios[M6811_PPROG] & M6811_EELAT) == 0)
598         {
599           sim_memory_error (cpu, SIM_SIGSEGV, base,
600                             "EEprom not configured for writing");
601           return 0;
602         }
603       if (controller->eeprom_wmode != 0)
604         {
605           sim_memory_error (cpu, SIM_SIGSEGV, base,
606                             "EEprom write error");
607           return 0;
608         }
609       controller->eeprom_wmode = 1;
610       controller->eeprom_waddr = base;
611       controller->eeprom_wbyte = val;
612     }
613   else
614     {
615       controller->eeprom[base] = val;
616       m6811eepr_memory_rw (controller, O_WRONLY);
617     }
618   
619   return 1;
620 }
621
622 const struct hw_descriptor dv_m68hc11eepr_descriptor[] = {
623   { "m68hc11eepr", m68hc11eepr_finish },
624   { "m68hc12eepr", m68hc11eepr_finish },
625   { NULL },
626 };
627