aboutsummaryrefslogtreecommitdiff
path: root/sim/m68hc11/dv-m68hc11eepr.c
diff options
context:
space:
mode:
authorAndrew Cagney <cagney@redhat.com>2000-07-27 11:23:39 +0000
committerAndrew Cagney <cagney@redhat.com>2000-07-27 11:23:39 +0000
commite0709f5044a0e824475defef03f86346ed9a292e (patch)
treee06389fffbbc598f7e212d1eedd4688720a72ca9 /sim/m68hc11/dv-m68hc11eepr.c
parent3c765a54971db6d8dbeafdf5230ad692cad271b3 (diff)
downloadfsf-binutils-gdb-e0709f5044a0e824475defef03f86346ed9a292e.zip
fsf-binutils-gdb-e0709f5044a0e824475defef03f86346ed9a292e.tar.gz
fsf-binutils-gdb-e0709f5044a0e824475defef03f86346ed9a292e.tar.bz2
New simulator.
Diffstat (limited to 'sim/m68hc11/dv-m68hc11eepr.c')
-rw-r--r--sim/m68hc11/dv-m68hc11eepr.c620
1 files changed, 620 insertions, 0 deletions
diff --git a/sim/m68hc11/dv-m68hc11eepr.c b/sim/m68hc11/dv-m68hc11eepr.c
new file mode 100644
index 0000000..1e26d85
--- /dev/null
+++ b/sim/m68hc11/dv-m68hc11eepr.c
@@ -0,0 +1,620 @@
+/* dv-m68hc11eepr.c -- Simulation of the 68HC11 Internal EEPROM.
+ Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+ Written by Stephane Carrez (stcarrez@worldnet.fr)
+ (From a driver model Contributed by Cygnus Solutions.)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+
+#include "sim-main.h"
+#include "hw-main.h"
+#include "sim-assert.h"
+#include "sim-events.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+
+
+/* DEVICE
+
+ m68hc11eepr - m68hc11 EEPROM
+
+
+ DESCRIPTION
+
+ Implements the 68HC11 eeprom device described in the m68hc11
+ user guide (Chapter 4 in the pink book).
+
+
+ PROPERTIES
+
+ reg <base> <length>
+
+ Base of eeprom and its length.
+
+ file <path>
+
+ Path of the EEPROM file. The default is 'm6811.eeprom'.
+
+
+ PORTS
+
+ None
+
+ */
+
+
+
+/* static functions */
+
+
+/* port ID's */
+
+enum
+{
+ RESET_PORT
+};
+
+
+static const struct hw_port_descriptor m68hc11eepr_ports[] =
+{
+ { "reset", RESET_PORT, 0, input_port, },
+ { NULL, },
+};
+
+
+
+/* The timer/counter register internal state. Note that we store
+ state using the control register images, in host endian order. */
+
+struct m68hc11eepr
+{
+ address_word base_address; /* control register base */
+ int attach_space;
+ unsigned size;
+
+ /* Current state of the eeprom programing:
+ - eeprom_wmode indicates whether the EEPROM address and byte have
+ been latched.
+ - eeprom_waddr indicates the EEPROM address that was latched
+ and eeprom_wbyte is the byte that was latched.
+ - eeprom_wcycle indicates the CPU absolute cycle type when
+ the high voltage was applied (successfully) on the EEPROM.
+
+ These data members are setup only when we detect good EEPROM programing
+ conditions (see Motorola EEPROM Programming and PPROG register usage).
+ When the high voltage is switched off, we look at the CPU absolute
+ cycle time to see if the EEPROM command must succeeds or not.
+ The EEPROM content is updated and saved only at that time.
+ (EEPROM command is: byte zero bits program, byte erase, row erase
+ and bulk erase).
+
+ The CONFIG register is programmed in the same way. It is physically
+ located at the end of the EEPROM (eeprom size + 1). It is not mapped
+ in memory but it's saved in the EEPROM file. */
+ unsigned long eeprom_wcycle;
+ uint16 eeprom_waddr;
+ uint8 eeprom_wbyte;
+ uint8 eeprom_wmode;
+
+ uint8* eeprom;
+
+ /* Minimum time in CPU cycles for programming the EEPROM. */
+ unsigned long eeprom_min_cycles;
+
+ char* file_name;
+};
+
+
+
+/* Finish off the partially created hw device. Attach our local
+ callbacks. Wire up our port names etc. */
+
+static hw_io_read_buffer_method m68hc11eepr_io_read_buffer;
+static hw_io_write_buffer_method m68hc11eepr_io_write_buffer;
+static hw_ioctl_method m68hc11eepr_ioctl;
+
+/* Read or write the memory bank content from/to a file.
+ Returns 0 if the operation succeeded and -1 if it failed. */
+static int
+m6811eepr_memory_rw (struct m68hc11eepr *controller, int mode)
+{
+ const char *name = controller->file_name;
+ int fd;
+ size_t size;
+
+ size = controller->size;
+ fd = open (name, mode, 0644);
+ if (fd < 0)
+ {
+ if (mode == O_RDONLY)
+ {
+ memset (controller->eeprom, 0xFF, size);
+ /* Default value for CONFIG register (0xFF should be ok):
+ controller->eeprom[size - 1] = M6811_NOSEC | M6811_NOCOP
+ | M6811_ROMON | M6811_EEON; */
+ return 0;
+ }
+ return -1;
+ }
+
+ if (mode == O_RDONLY)
+ {
+ if (read (fd, controller->eeprom, size) != size)
+ {
+ close (fd);
+ return -1;
+ }
+ }
+ else
+ {
+ if (write (fd, controller->eeprom, size) != size)
+ {
+ close (fd);
+ return -1;
+ }
+ }
+ close (fd);
+
+ return 0;
+}
+
+
+
+
+static void
+attach_m68hc11eepr_regs (struct hw *me,
+ struct m68hc11eepr *controller)
+{
+ unsigned_word attach_address;
+ int attach_space;
+ unsigned attach_size;
+ reg_property_spec reg;
+
+ if (hw_find_property (me, "reg") == NULL)
+ hw_abort (me, "Missing \"reg\" property");
+
+ if (!hw_find_reg_array_property (me, "reg", 0, &reg))
+ hw_abort (me, "\"reg\" property must contain one addr/size entry");
+
+ hw_unit_address_to_attach_address (hw_parent (me),
+ &reg.address,
+ &attach_space,
+ &attach_address,
+ me);
+ hw_unit_size_to_attach_size (hw_parent (me),
+ &reg.size,
+ &attach_size, me);
+
+ /* Attach the two IO registers that control the EEPROM.
+ The EEPROM is only attached at reset time because it may
+ be enabled/disabled by the EEON bit in the CONFIG register. */
+ hw_attach_address (hw_parent (me), 0, io_map, M6811_PPROG, 1, me);
+ hw_attach_address (hw_parent (me), 0, io_map, M6811_CONFIG, 1, me);
+
+ if (hw_find_property (me, "file") == NULL)
+ controller->file_name = "m6811.eeprom";
+ else
+ controller->file_name = hw_find_string_property (me, "file");
+
+ controller->attach_space = attach_space;
+ controller->base_address = attach_address;
+ controller->eeprom = (char*) malloc (attach_size + 1);
+ controller->eeprom_min_cycles = 10000;
+ controller->size = attach_size + 1;
+
+ m6811eepr_memory_rw (controller, O_RDONLY);
+}
+
+
+/* An event arrives on an interrupt port. */
+
+static void
+m68hc11eepr_port_event (struct hw *me,
+ int my_port,
+ struct hw *source,
+ int source_port,
+ int level)
+{
+ SIM_DESC sd;
+ struct m68hc11eepr *controller;
+ sim_cpu *cpu;
+
+ controller = hw_data (me);
+ sd = hw_system (me);
+ cpu = STATE_CPU (sd, 0);
+ switch (my_port)
+ {
+ case RESET_PORT:
+ {
+ HW_TRACE ((me, "EEPROM reset"));
+
+ /* Re-read the EEPROM from the file. This gives the chance
+ to users to erase this file before doing a reset and have
+ a fresh EEPROM taken into account. */
+ m6811eepr_memory_rw (controller, O_RDONLY);
+
+ /* Reset the state of EEPROM programmer. The CONFIG register
+ is also initialized from the EEPROM/file content. */
+ cpu->ios[M6811_PPROG] = 0;
+ if (cpu->cpu_use_local_config)
+ cpu->ios[M6811_CONFIG] = cpu->cpu_config;
+ else
+ cpu->ios[M6811_CONFIG] = controller->eeprom[controller->size-1];
+ controller->eeprom_wmode = 0;
+ controller->eeprom_waddr = 0;
+ controller->eeprom_wbyte = 0;
+
+ /* Attach or detach to the bus depending on the EEPROM enable bit.
+ The EEPROM CONFIG register is still enabled and can be programmed
+ for a next configuration (taken into account only after a reset,
+ see Motorola spec). */
+ if (cpu->ios[M6811_CONFIG] & M6811_EEON)
+ {
+ hw_attach_address (hw_parent (me), 0,
+ controller->attach_space,
+ controller->base_address,
+ controller->size - 1,
+ me);
+ }
+ else
+ {
+ hw_detach_address (hw_parent (me), 0,
+ controller->attach_space,
+ controller->base_address,
+ controller->size - 1,
+ me);
+ }
+ break;
+ }
+
+ default:
+ hw_abort (me, "Event on unknown port %d", my_port);
+ break;
+ }
+}
+
+
+static void
+m68hc11eepr_finish (struct hw *me)
+{
+ struct m68hc11eepr *controller;
+
+ controller = HW_ZALLOC (me, struct m68hc11eepr);
+ me->overlap_mode_hw = 1;
+ set_hw_data (me, controller);
+ set_hw_io_read_buffer (me, m68hc11eepr_io_read_buffer);
+ set_hw_io_write_buffer (me, m68hc11eepr_io_write_buffer);
+ set_hw_ports (me, m68hc11eepr_ports);
+ set_hw_port_event (me, m68hc11eepr_port_event);
+#ifdef set_hw_ioctl
+ set_hw_ioctl (me, m68hc11eepr_ioctl);
+#else
+ me->to_ioctl = m68hc11eepr_ioctl;
+#endif
+
+ attach_m68hc11eepr_regs (me, controller);
+}
+
+
+
+static io_reg_desc pprog_desc[] = {
+ { M6811_BYTE, "BYTE ", "Byte Program Mode" },
+ { M6811_ROW, "ROW ", "Row Program Mode" },
+ { M6811_ERASE, "ERASE ", "Erase Mode" },
+ { M6811_EELAT, "EELAT ", "EEProm Latch Control" },
+ { M6811_EEPGM, "EEPGM ", "EEProm Programming Voltable Enable" },
+ { 0, 0, 0 }
+};
+extern io_reg_desc config_desc[];
+
+
+/* Describe the state of the EEPROM device. */
+static void
+m68hc11eepr_info (struct hw *me)
+{
+ SIM_DESC sd;
+ uint16 base = 0;
+ sim_cpu *cpu;
+ struct m68hc11eepr *controller;
+ uint8 val;
+
+ sd = hw_system (me);
+ cpu = STATE_CPU (sd, 0);
+ controller = hw_data (me);
+ base = cpu_get_io_base (cpu);
+
+ sim_io_printf (sd, "M68HC11 EEprom:\n");
+
+ val = cpu->ios[M6811_PPROG];
+ print_io_byte (sd, "PPROG ", pprog_desc, val, base + M6811_PPROG);
+ sim_io_printf (sd, "\n");
+
+ val = cpu->ios[M6811_CONFIG];
+ print_io_byte (sd, "CONFIG ", config_desc, val, base + M6811_CONFIG);
+ sim_io_printf (sd, "\n");
+
+ val = controller->eeprom[controller->size - 1];
+ print_io_byte (sd, "(*NEXT*) ", config_desc, val, base + M6811_CONFIG);
+ sim_io_printf (sd, "\n");
+
+ /* Describe internal state of EEPROM. */
+ if (controller->eeprom_wmode)
+ {
+ if (controller->eeprom_waddr == controller->size - 1)
+ sim_io_printf (sd, " Programming CONFIG register ");
+ else
+ sim_io_printf (sd, " Programming: 0x%04x ",
+ controller->eeprom_waddr);
+
+ sim_io_printf (sd, "with 0x%02x\n",
+ controller->eeprom_wbyte);
+ }
+
+ sim_io_printf (sd, " EEProm file: %s\n",
+ controller->file_name);
+}
+
+static int
+m68hc11eepr_ioctl (struct hw *me,
+ hw_ioctl_request request,
+ va_list ap)
+{
+ m68hc11eepr_info (me);
+ return 0;
+}
+
+/* generic read/write */
+
+static unsigned
+m68hc11eepr_io_read_buffer (struct hw *me,
+ void *dest,
+ int space,
+ unsigned_word base,
+ unsigned nr_bytes)
+{
+ SIM_DESC sd;
+ struct m68hc11eepr *controller;
+ sim_cpu *cpu;
+
+ HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
+
+ sd = hw_system (me);
+ controller = hw_data (me);
+ cpu = STATE_CPU (sd, 0);
+
+ if (space == io_map)
+ {
+ unsigned cnt = 0;
+
+ while (nr_bytes != 0)
+ {
+ switch (base)
+ {
+ case M6811_PPROG:
+ case M6811_CONFIG:
+ *((uint8*) dest) = cpu->ios[base];
+ break;
+
+ default:
+ hw_abort (me, "reading wrong register 0x%04x", base);
+ }
+ dest = (uint8*) (dest) + 1;
+ base++;
+ nr_bytes--;
+ cnt++;
+ }
+ return cnt;
+ }
+
+ /* In theory, we can't read the EEPROM when it's being programmed. */
+ if ((cpu->ios[M6811_PPROG] & M6811_EELAT) != 0
+ && cpu_is_running (cpu))
+ {
+ sim_memory_error (cpu, SIM_SIGBUS, base,
+ "EEprom not configured for reading");
+ }
+
+ base = base - controller->base_address;
+ memcpy (dest, &controller->eeprom[base], nr_bytes);
+ return nr_bytes;
+}
+
+
+static unsigned
+m68hc11eepr_io_write_buffer (struct hw *me,
+ const void *source,
+ int space,
+ unsigned_word base,
+ unsigned nr_bytes)
+{
+ SIM_DESC sd;
+ struct m68hc11eepr *controller;
+ sim_cpu *cpu;
+ uint8 val;
+
+ HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
+
+ sd = hw_system (me);
+ controller = hw_data (me);
+ cpu = STATE_CPU (sd, 0);
+
+ /* Programming several bytes at a time is not possible. */
+ if (space != io_map && nr_bytes != 1)
+ {
+ sim_memory_error (cpu, SIM_SIGBUS, base,
+ "EEprom write error (only 1 byte can be programmed)");
+ return 0;
+ }
+
+ if (nr_bytes != 1)
+ hw_abort (me, "Cannot write more than 1 byte to EEPROM device at a time");
+
+ val = *((const uint8*) source);
+
+ /* Write to the EEPROM control register. */
+ if (space == io_map && base == M6811_PPROG)
+ {
+ uint8 wrong_bits;
+ uint16 addr;
+
+ addr = base + cpu_get_io_base (cpu);
+
+ /* Setting EELAT and EEPGM at the same time is an error.
+ Clearing them both is ok. */
+ wrong_bits = (cpu->ios[M6811_PPROG] ^ val) & val;
+ wrong_bits &= (M6811_EELAT | M6811_EEPGM);
+
+ if (wrong_bits == (M6811_EEPGM|M6811_EELAT))
+ {
+ sim_memory_error (cpu, SIM_SIGBUS, addr,
+ "Wrong eeprom programing value");
+ return 0;
+ }
+
+ if ((val & M6811_EELAT) == 0)
+ {
+ val = 0;
+ }
+ if ((val & M6811_EEPGM) && !(cpu->ios[M6811_PPROG] & M6811_EELAT))
+ {
+ sim_memory_error (cpu, SIM_SIGBUS, addr,
+ "EEProm high voltage applied after EELAT");
+ }
+ if ((val & M6811_EEPGM) && controller->eeprom_wmode == 0)
+ {
+ sim_memory_error (cpu, SIM_SIGSEGV, addr,
+ "EEProm high voltage applied without address");
+ }
+ if (val & M6811_EEPGM)
+ {
+ controller->eeprom_wcycle = cpu_current_cycle (cpu);
+ }
+ else if (cpu->ios[M6811_PPROG] & M6811_PPROG)
+ {
+ int i;
+ unsigned long t = cpu_current_cycle (cpu);
+
+ t -= controller->eeprom_wcycle;
+ if (t < controller->eeprom_min_cycles)
+ {
+ sim_memory_error (cpu, SIM_SIGILL, addr,
+ "EEprom programmed only for %lu cycles",
+ t);
+ }
+
+ /* Program the byte by clearing some bits. */
+ if (!(cpu->ios[M6811_PPROG] & M6811_ERASE))
+ {
+ controller->eeprom[controller->eeprom_waddr]
+ &= controller->eeprom_wbyte;
+ }
+
+ /* Erase a byte, row or the complete eeprom. Erased value is 0xFF.
+ Ignore row or complete eeprom erase when we are programming the
+ CONFIG register (last EEPROM byte). */
+ else if ((cpu->ios[M6811_PPROG] & M6811_BYTE)
+ || controller->eeprom_waddr == controller->size - 1)
+ {
+ controller->eeprom[controller->eeprom_waddr] = 0xff;
+ }
+ else if (cpu->ios[M6811_BYTE] & M6811_ROW)
+ {
+ size_t max_size;
+
+ /* Size of EEPROM (-1 because the last byte is the
+ CONFIG register. */
+ max_size = controller->size;
+ controller->eeprom_waddr &= 0xFFF0;
+ for (i = 0; i < 16
+ && controller->eeprom_waddr < max_size; i++)
+ {
+ controller->eeprom[controller->eeprom_waddr] = 0xff;
+ controller->eeprom_waddr ++;
+ }
+ }
+ else
+ {
+ size_t max_size;
+
+ max_size = controller->size;
+ for (i = 0; i < max_size; i++)
+ {
+ controller->eeprom[i] = 0xff;
+ }
+ }
+
+ /* Save the eeprom in a file. We have to save after each
+ change because the simulator can be stopped or crash... */
+ if (m6811eepr_memory_rw (controller, O_WRONLY | O_CREAT) != 0)
+ {
+ sim_memory_error (cpu, SIM_SIGABRT, addr,
+ "EEPROM programing failed: errno=%d", errno);
+ }
+ controller->eeprom_wmode = 0;
+ }
+ cpu->ios[M6811_PPROG] = val;
+ return 1;
+ }
+
+ /* The CONFIG IO register is mapped at end of EEPROM.
+ It's not visible. */
+ if (space == io_map && base == M6811_CONFIG)
+ {
+ base = controller->size - 1;
+ }
+ else
+ {
+ base = base - controller->base_address;
+ }
+
+ /* Writing the memory is allowed for the Debugger or simulator
+ (cpu not running). */
+ if (cpu_is_running (cpu))
+ {
+ if ((cpu->ios[M6811_PPROG] & M6811_EELAT) == 0)
+ {
+ sim_memory_error (cpu, SIM_SIGSEGV, base,
+ "EEprom not configured for writing");
+ return 0;
+ }
+ if (controller->eeprom_wmode != 0)
+ {
+ sim_memory_error (cpu, SIM_SIGSEGV, base,
+ "EEprom write error");
+ return 0;
+ }
+ controller->eeprom_wmode = 1;
+ controller->eeprom_waddr = base;
+ controller->eeprom_wbyte = val;
+ }
+ else
+ {
+ controller->eeprom[base] = val;
+ m6811eepr_memory_rw (controller, O_WRONLY);
+ }
+
+ return 1;
+}
+
+const struct hw_descriptor dv_m68hc11eepr_descriptor[] = {
+ { "m68hc11eepr", m68hc11eepr_finish, },
+ { NULL },
+};
+