From e0709f5044a0e824475defef03f86346ed9a292e Mon Sep 17 00:00:00 2001 From: Andrew Cagney Date: Thu, 27 Jul 2000 11:23:39 +0000 Subject: New simulator. --- sim/m68hc11/dv-m68hc11eepr.c | 620 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 620 insertions(+) create mode 100644 sim/m68hc11/dv-m68hc11eepr.c (limited to 'sim/m68hc11/dv-m68hc11eepr.c') 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 +#include +#include + + + +/* 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 of eeprom and its length. + + file + + 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, ®)) + hw_abort (me, "\"reg\" property must contain one addr/size entry"); + + hw_unit_address_to_attach_address (hw_parent (me), + ®.address, + &attach_space, + &attach_address, + me); + hw_unit_size_to_attach_size (hw_parent (me), + ®.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 }, +}; + -- cgit v1.1