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