summaryrefslogtreecommitdiff
path: root/sim/m68hc11/dv-m68hc11sio.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-m68hc11sio.c
parent3c765a54971db6d8dbeafdf5230ad692cad271b3 (diff)
New simulator.
Diffstat (limited to 'sim/m68hc11/dv-m68hc11sio.c')
-rw-r--r--sim/m68hc11/dv-m68hc11sio.c664
1 files changed, 664 insertions, 0 deletions
diff --git a/sim/m68hc11/dv-m68hc11sio.c b/sim/m68hc11/dv-m68hc11sio.c
new file mode 100644
index 0000000000..df493d642c
--- /dev/null
+++ b/sim/m68hc11/dv-m68hc11sio.c
@@ -0,0 +1,664 @@
+/* dv-m68hc11sio.c -- Simulation of the 68HC11 serial device.
+ Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+ Written by Stephane Carrez (stcarrez@worldnet.fr)
+ (From a driver model Contributed by Cygnus Solutions.)
+
+ This file is part of the program GDB, the GNU debugger.
+
+ 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 "dv-sockser.h"
+#include "sim-assert.h"
+
+
+/* DEVICE
+
+ m68hc11sio - m68hc11 serial I/O
+
+
+ DESCRIPTION
+
+ Implements the m68hc11 serial I/O controller described in the m68hc11
+ user guide. The serial I/O controller is directly connected to the CPU
+ interrupt. The simulator implements:
+
+ - baud rate emulation
+ - 8-bits transfers
+
+ PROPERTIES
+
+ backend {tcp | stdio}
+
+ Use dv-sockser TCP-port backend or stdio for backend. Default: stdio.
+
+
+ PORTS
+
+ reset (input)
+
+ Reset port. This port is only used to simulate a reset of the serial
+ I/O controller. It should be connected to the RESET output of the cpu.
+
+ */
+
+
+
+/* port ID's */
+
+enum
+{
+ RESET_PORT
+};
+
+
+static const struct hw_port_descriptor m68hc11sio_ports[] =
+{
+ { "reset", RESET_PORT, 0, input_port, },
+ { NULL, },
+};
+
+
+/* Serial Controller information. */
+struct m68hc11sio
+{
+ enum {sio_tcp, sio_stdio} backend; /* backend */
+
+ /* Number of cpu cycles to send a bit on the wire. */
+ unsigned long baud_cycle;
+
+ /* Length in bits of characters sent, this includes the
+ start/stop and parity bits. Together with baud_cycle, this
+ is used to find the number of cpu cycles to send/receive a data. */
+ unsigned int data_length;
+
+ /* Information about next character to be transmited. */
+ unsigned char tx_has_char;
+ unsigned char tx_char;
+
+ unsigned char rx_char;
+ unsigned char rx_clear_scsr;
+
+ /* Periodic I/O polling. */
+ struct hw_event* tx_poll_event;
+ struct hw_event* rx_poll_event;
+};
+
+
+
+/* Finish off the partially created hw device. Attach our local
+ callbacks. Wire up our port names etc. */
+
+static hw_io_read_buffer_method m68hc11sio_io_read_buffer;
+static hw_io_write_buffer_method m68hc11sio_io_write_buffer;
+static hw_port_event_method m68hc11sio_port_event;
+static hw_ioctl_method m68hc11sio_ioctl;
+
+#define M6811_SCI_FIRST_REG (M6811_BAUD)
+#define M6811_SCI_LAST_REG (M6811_SCDR)
+
+
+static void
+attach_m68hc11sio_regs (struct hw *me,
+ struct m68hc11sio *controller)
+{
+ hw_attach_address (hw_parent (me), 0, io_map,
+ M6811_SCI_FIRST_REG,
+ M6811_SCI_LAST_REG - M6811_SCI_FIRST_REG + 1,
+ me);
+
+ if (hw_find_property(me, "backend") != NULL)
+ {
+ const char *value = hw_find_string_property(me, "backend");
+ if(! strcmp(value, "tcp"))
+ controller->backend = sio_tcp;
+ else if(! strcmp(value, "stdio"))
+ controller->backend = sio_stdio;
+ else
+ hw_abort (me, "illegal value for backend parameter `%s':"
+ "use tcp or stdio", value);
+ }
+}
+
+
+static void
+m68hc11sio_finish (struct hw *me)
+{
+ struct m68hc11sio *controller;
+
+ controller = HW_ZALLOC (me, struct m68hc11sio);
+ me->overlap_mode_hw = 1;
+ set_hw_data (me, controller);
+ set_hw_io_read_buffer (me, m68hc11sio_io_read_buffer);
+ set_hw_io_write_buffer (me, m68hc11sio_io_write_buffer);
+ set_hw_ports (me, m68hc11sio_ports);
+ set_hw_port_event (me, m68hc11sio_port_event);
+#ifdef set_hw_ioctl
+ set_hw_ioctl (me, m68hc11sio_ioctl);
+#else
+ me->to_ioctl = m68hc11sio_ioctl;
+#endif
+
+ /* Preset defaults. */
+ controller->backend = sio_stdio;
+
+ /* Attach ourself to our parent bus. */
+ attach_m68hc11sio_regs (me, controller);
+
+ /* Initialize to reset state. */
+ controller->tx_poll_event = NULL;
+ controller->rx_poll_event = NULL;
+ controller->tx_char = 0;
+ controller->tx_has_char = 0;
+ controller->rx_clear_scsr = 0;
+ controller->rx_char = 0;
+}
+
+
+
+/* An event arrives on an interrupt port. */
+
+static void
+m68hc11sio_port_event (struct hw *me,
+ int my_port,
+ struct hw *source,
+ int source_port,
+ int level)
+{
+ SIM_DESC sd;
+ struct m68hc11sio *controller;
+ sim_cpu *cpu;
+ unsigned8 val;
+
+ controller = hw_data (me);
+ sd = hw_system (me);
+ cpu = STATE_CPU (sd, 0);
+ switch (my_port)
+ {
+ case RESET_PORT:
+ {
+ HW_TRACE ((me, "SCI reset"));
+
+ /* Reset the state of SCI registers. */
+ val = 0;
+ m68hc11sio_io_write_buffer (me, &val, io_map,
+ (unsigned_word) M6811_BAUD, 1);
+ m68hc11sio_io_write_buffer (me, &val, io_map,
+ (unsigned_word) M6811_SCCR1, 1);
+ m68hc11sio_io_write_buffer (me, &val, io_map,
+ (unsigned_word) M6811_SCCR2, 1);
+
+ cpu->ios[M6811_SCSR] = M6811_TC | M6811_TDRE;
+ controller->rx_char = 0;
+ controller->tx_char = 0;
+ controller->tx_has_char = 0;
+ controller->rx_clear_scsr = 0;
+ if (controller->rx_poll_event)
+ {
+ hw_event_queue_deschedule (me, controller->rx_poll_event);
+ controller->rx_poll_event = 0;
+ }
+ if (controller->tx_poll_event)
+ {
+ hw_event_queue_deschedule (me, controller->tx_poll_event);
+ controller->tx_poll_event = 0;
+ }
+
+ /* In bootstrap mode, initialize the SCI to 1200 bauds to
+ simulate some initial setup by the internal rom. */
+ if (((cpu->ios[M6811_HPRIO]) & (M6811_SMOD | M6811_MDA)) == M6811_SMOD)
+ {
+ unsigned char val = 0x33;
+
+ m68hc11sio_io_write_buffer (me, &val, io_map,
+ (unsigned_word) M6811_BAUD, 1);
+ val = 0x12;
+ m68hc11sio_io_write_buffer (me, &val, io_map,
+ (unsigned_word) M6811_SCCR2, 1);
+ }
+ break;
+ }
+
+ default:
+ hw_abort (me, "Event on unknown port %d", my_port);
+ break;
+ }
+}
+
+
+void
+m68hc11sio_rx_poll (struct hw *me, void *data)
+{
+ SIM_DESC sd;
+ struct m68hc11sio *controller;
+ sim_cpu *cpu;
+ char cc;
+ int cnt;
+ int check_interrupt = 0;
+
+ controller = hw_data (me);
+ sd = hw_system (me);
+ cpu = STATE_CPU (sd, 0);
+ switch (controller->backend)
+ {
+ case sio_tcp:
+ cnt = dv_sockser_read (sd);
+ if (cnt != -1)
+ {
+ cc = (char) cnt;
+ cnt = 1;
+ }
+ break;
+
+ case sio_stdio:
+ cnt = sim_io_poll_read (sd, 0 /* stdin */, &cc, 1);
+ break;
+
+ default:
+ cnt = 0;
+ break;
+ }
+
+ if (cnt == 1)
+ {
+ /* Raise the overrun flag if the previous character was not read. */
+ if (cpu->ios[M6811_SCSR] & M6811_RDRF)
+ cpu->ios[M6811_SCSR] |= M6811_OR;
+
+ cpu->ios[M6811_SCSR] |= M6811_RDRF;
+ controller->rx_char = cc;
+ controller->rx_clear_scsr = 0;
+ check_interrupt = 1;
+ }
+ else
+ {
+ /* handle idle line detect here. */
+ ;
+ }
+
+ if (controller->rx_poll_event)
+ {
+ hw_event_queue_deschedule (me, controller->rx_poll_event);
+ controller->rx_poll_event = 0;
+ }
+
+ if (cpu->ios[M6811_SCCR2] & M6811_RE)
+ {
+ unsigned long clock_cycle;
+
+ /* Compute CPU clock cycles to wait for the next character. */
+ clock_cycle = controller->data_length * controller->baud_cycle;
+
+ controller->rx_poll_event = hw_event_queue_schedule (me, clock_cycle,
+ m68hc11sio_rx_poll,
+ NULL);
+ }
+
+ if (check_interrupt)
+ interrupts_update_pending (&cpu->cpu_interrupts);
+}
+
+
+void
+m68hc11sio_tx_poll (struct hw *me, void *data)
+{
+ SIM_DESC sd;
+ struct m68hc11sio *controller;
+ sim_cpu *cpu;
+ int check_interrupt = 0;
+
+ controller = hw_data (me);
+ sd = hw_system (me);
+ cpu = STATE_CPU (sd, 0);
+
+ cpu->ios[M6811_SCSR] |= M6811_TDRE;
+ cpu->ios[M6811_SCSR] |= M6811_TC;
+
+ /* Transmitter is enabled and we have something to sent. */
+ if ((cpu->ios[M6811_SCCR2] & M6811_TE) && controller->tx_has_char)
+ {
+ cpu->ios[M6811_SCSR] &= ~M6811_TDRE;
+ cpu->ios[M6811_SCSR] &= ~M6811_TC;
+ controller->tx_has_char = 0;
+ check_interrupt = 1;
+ switch (controller->backend)
+ {
+ case sio_tcp:
+ dv_sockser_write (sd, controller->tx_char);
+ break;
+
+ case sio_stdio:
+ sim_io_write_stdout (sd, &controller->tx_char, 1);
+ sim_io_flush_stdout (sd);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (controller->tx_poll_event)
+ {
+ hw_event_queue_deschedule (me, controller->tx_poll_event);
+ controller->tx_poll_event = 0;
+ }
+
+ if ((cpu->ios[M6811_SCCR2] & M6811_TE)
+ && ((cpu->ios[M6811_SCSR] & M6811_TC) == 0))
+ {
+ unsigned long clock_cycle;
+
+ /* Compute CPU clock cycles to wait for the next character. */
+ clock_cycle = controller->data_length * controller->baud_cycle;
+
+ controller->tx_poll_event = hw_event_queue_schedule (me, clock_cycle,
+ m68hc11sio_tx_poll,
+ NULL);
+ }
+
+ if (check_interrupt)
+ interrupts_update_pending (&cpu->cpu_interrupts);
+}
+
+/* Descriptions of the SIO I/O ports. These descriptions are only used to
+ give information of the SIO device under GDB. */
+io_reg_desc sccr2_desc[] = {
+ { M6811_TIE, "TIE ", "Transmit Interrupt Enable" },
+ { M6811_TCIE, "TCIE ", "Transmit Complete Interrupt Enable" },
+ { M6811_RIE, "RIE ", "Receive Interrupt Enable" },
+ { M6811_ILIE, "ILIE ", "Idle Line Interrupt Enable" },
+ { M6811_TE, "TE ", "Transmit Enable" },
+ { M6811_RE, "RE ", "Receive Enable" },
+ { M6811_RWU, "RWU ", "Receiver Wake Up" },
+ { M6811_SBK, "SBRK ", "Send Break" },
+ { 0, 0, 0 }
+};
+
+io_reg_desc sccr1_desc[] = {
+ { M6811_R8, "R8 ", "Receive Data bit 8" },
+ { M6811_T8, "T8 ", "Transmit Data bit 8" },
+ { M6811_M, "M ", "SCI Character length (0=8-bits, 1=9-bits)" },
+ { M6811_WAKE, "WAKE ", "Wake up method select (0=idle, 1=addr mark" },
+ { 0, 0, 0 }
+};
+
+io_reg_desc scsr_desc[] = {
+ { M6811_TDRE, "TDRE ", "Transmit Data Register Empty" },
+ { M6811_TC, "TC ", "Transmit Complete" },
+ { M6811_RDRF, "RDRF ", "Receive Data Register Full" },
+ { M6811_IDLE, "IDLE ", "Idle Line Detect" },
+ { M6811_OR, "OR ", "Overrun Error" },
+ { M6811_NF, "NF ", "Noise Flag" },
+ { M6811_FE, "FE ", "Framing Error" },
+ { 0, 0, 0 }
+};
+
+io_reg_desc baud_desc[] = {
+ { M6811_TCLR, "TCLR ", "Clear baud rate (test mode)" },
+ { M6811_SCP1, "SCP1 ", "SCI baud rate prescaler select (SCP1)" },
+ { M6811_SCP0, "SCP0 ", "SCI baud rate prescaler select (SCP0)" },
+ { M6811_RCKB, "RCKB ", "Baur Rate Clock Check (test mode)" },
+ { M6811_SCR2, "SCR2 ", "SCI Baud rate select (SCR2)" },
+ { M6811_SCR1, "SCR1 ", "SCI Baud rate select (SCR1)" },
+ { M6811_SCR0, "SCR0 ", "SCI Baud rate select (SCR0)" },
+ { 0, 0, 0 }
+};
+
+static void
+m68hc11sio_info (struct hw *me)
+{
+ SIM_DESC sd;
+ uint16 base = 0;
+ sim_cpu *cpu;
+ struct m68hc11sio *controller;
+ uint8 val;
+ long clock_cycle;
+
+ sd = hw_system (me);
+ cpu = STATE_CPU (sd, 0);
+ controller = hw_data (me);
+
+ sim_io_printf (sd, "M68HC11 SIO:\n");
+
+ base = cpu_get_io_base (cpu);
+
+ val = cpu->ios[M6811_BAUD];
+ print_io_byte (sd, "BAUD ", baud_desc, val, base + M6811_BAUD);
+ sim_io_printf (sd, " (%ld baud)\n",
+ (cpu->cpu_frequency / 4) / controller->baud_cycle);
+
+ val = cpu->ios[M6811_SCCR1];
+ print_io_byte (sd, "SCCR1", sccr1_desc, val, base + M6811_SCCR1);
+ sim_io_printf (sd, " (%d bits) (%dN1)\n",
+ controller->data_length, controller->data_length - 2);
+
+ val = cpu->ios[M6811_SCCR2];
+ print_io_byte (sd, "SCCR2", sccr2_desc, val, base + M6811_SCCR2);
+ sim_io_printf (sd, "\n");
+
+ val = cpu->ios[M6811_SCSR];
+ print_io_byte (sd, "SCSR ", scsr_desc, val, base + M6811_SCSR);
+ sim_io_printf (sd, "\n");
+
+ clock_cycle = controller->data_length * controller->baud_cycle;
+
+ if (controller->tx_poll_event)
+ {
+ signed64 t;
+ int n;
+
+ t = hw_event_remain_time (me, controller->tx_poll_event);
+ n = (clock_cycle - t) / controller->baud_cycle;
+ n = controller->data_length - n;
+ sim_io_printf (sd, " Transmit finished in %ld cycles (%d bit%s)\n",
+ (long) t, n, (n > 1 ? "s" : ""));
+ }
+ if (controller->rx_poll_event)
+ {
+ signed64 t;
+
+ t = hw_event_remain_time (me, controller->rx_poll_event);
+ sim_io_printf (sd, " Receive finished in %ld cycles\n",
+ (long) t);
+ }
+
+}
+
+static int
+m68hc11sio_ioctl (struct hw *me,
+ hw_ioctl_request request,
+ va_list ap)
+{
+ m68hc11sio_info (me);
+ return 0;
+}
+
+/* generic read/write */
+
+static unsigned
+m68hc11sio_io_read_buffer (struct hw *me,
+ void *dest,
+ int space,
+ unsigned_word base,
+ unsigned nr_bytes)
+{
+ SIM_DESC sd;
+ struct m68hc11sio *controller;
+ sim_cpu *cpu;
+ unsigned8 val;
+
+ HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
+
+ sd = hw_system (me);
+ cpu = STATE_CPU (sd, 0);
+ controller = hw_data (me);
+
+ switch (base)
+ {
+ case M6811_SCSR:
+ controller->rx_clear_scsr = cpu->ios[M6811_SCSR]
+ & (M6811_RDRF | M6811_IDLE | M6811_OR | M6811_NF | M6811_FE);
+
+ case M6811_BAUD:
+ case M6811_SCCR1:
+ case M6811_SCCR2:
+ val = cpu->ios[base];
+ break;
+
+ case M6811_SCDR:
+ if (controller->rx_clear_scsr)
+ {
+ cpu->ios[M6811_SCSR] &= ~controller->rx_clear_scsr;
+ }
+ val = controller->rx_char;
+ break;
+
+ default:
+ return 0;
+ }
+ *((unsigned8*) dest) = val;
+ return 1;
+}
+
+static unsigned
+m68hc11sio_io_write_buffer (struct hw *me,
+ const void *source,
+ int space,
+ unsigned_word base,
+ unsigned nr_bytes)
+{
+ SIM_DESC sd;
+ struct m68hc11sio *controller;
+ sim_cpu *cpu;
+ unsigned8 val;
+
+ HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
+
+ sd = hw_system (me);
+ cpu = STATE_CPU (sd, 0);
+ controller = hw_data (me);
+
+ val = *((const unsigned8*) source);
+ switch (base)
+ {
+ case M6811_BAUD:
+ {
+ long divisor;
+ long baud;
+
+ cpu->ios[M6811_BAUD] = val;
+ switch (val & (M6811_SCP1|M6811_SCP0))
+ {
+ case M6811_BAUD_DIV_1:
+ divisor = 1 * 16;
+ break;
+
+ case M6811_BAUD_DIV_3:
+ divisor = 3 * 16;
+ break;
+
+ case M6811_BAUD_DIV_4:
+ divisor = 4 * 16;
+ break;
+
+ default:
+ case M6811_BAUD_DIV_13:
+ divisor = 13 * 16;
+ break;
+ }
+ val &= (M6811_SCR2|M6811_SCR1|M6811_SCR0);
+ divisor *= (1 << val);
+
+ baud = (cpu->cpu_frequency / 4) / divisor;
+
+ HW_TRACE ((me, "divide rate %ld, baud rate %ld",
+ divisor, baud));
+
+ controller->baud_cycle = divisor;
+ }
+ break;
+
+ case M6811_SCCR1:
+ {
+ if (val & M6811_M)
+ controller->data_length = 11;
+ else
+ controller->data_length = 10;
+
+ cpu->ios[M6811_SCCR1] = val;
+ }
+ break;
+
+ case M6811_SCCR2:
+ if ((val & M6811_RE) == 0)
+ {
+ val &= ~(M6811_RDRF|M6811_IDLE|M6811_OR|M6811_NF|M6811_NF);
+ val |= (cpu->ios[M6811_SCCR2]
+ & (M6811_RDRF|M6811_IDLE|M6811_OR|M6811_NF|M6811_NF));
+ cpu->ios[M6811_SCCR2] = val;
+ break;
+ }
+
+ /* Activate reception. */
+ if (controller->rx_poll_event == 0)
+ {
+ long clock_cycle;
+
+ /* Compute CPU clock cycles to wait for the next character. */
+ clock_cycle = controller->data_length * controller->baud_cycle;
+
+ controller->rx_poll_event = hw_event_queue_schedule (me, clock_cycle,
+ m68hc11sio_rx_poll,
+ NULL);
+ }
+ cpu->ios[M6811_SCCR2] = val;
+ interrupts_update_pending (&cpu->cpu_interrupts);
+ break;
+
+ /* No effect. */
+ case M6811_SCSR:
+ return 1;
+
+ case M6811_SCDR:
+ if (!(cpu->ios[M6811_SCSR] & M6811_TDRE))
+ {
+ return 0;
+ }
+
+ controller->tx_char = val;
+ controller->tx_has_char = 1;
+ if ((cpu->ios[M6811_SCCR2] & M6811_TE)
+ && controller->tx_poll_event == 0)
+ {
+ m68hc11sio_tx_poll (me, NULL);
+ }
+ return 1;
+
+ default:
+ return 0;
+ }
+ return nr_bytes;
+}
+
+
+const struct hw_descriptor dv_m68hc11sio_descriptor[] = {
+ { "m68hc11sio", m68hc11sio_finish, },
+ { NULL },
+};
+