diff options
author | Andrew Cagney <cagney@redhat.com> | 2000-07-27 11:23:39 +0000 |
---|---|---|
committer | Andrew Cagney <cagney@redhat.com> | 2000-07-27 11:23:39 +0000 |
commit | e0709f5044a0e824475defef03f86346ed9a292e (patch) | |
tree | e06389fffbbc598f7e212d1eedd4688720a72ca9 /sim/m68hc11/dv-m68hc11sio.c | |
parent | 3c765a54971db6d8dbeafdf5230ad692cad271b3 (diff) | |
download | gdb-e0709f5044a0e824475defef03f86346ed9a292e.zip gdb-e0709f5044a0e824475defef03f86346ed9a292e.tar.gz gdb-e0709f5044a0e824475defef03f86346ed9a292e.tar.bz2 |
New simulator.
Diffstat (limited to 'sim/m68hc11/dv-m68hc11sio.c')
-rw-r--r-- | sim/m68hc11/dv-m68hc11sio.c | 664 |
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 0000000..df493d6 --- /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 }, +}; + |