123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662 |
- /* dv-m68hc11sio.c -- Simulation of the 68HC11 serial device.
- Copyright (C) 1999-2015 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 3 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, see <http://www.gnu.org/licenses/>.
-
- */
- #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), M6811_IO_LEVEL, 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);
- 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;
-
- 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 send. */
- 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;
- 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);
- }
- 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 %s (%d bit%s)\n",
- cycle_to_string (cpu, t, PRINT_TIME | PRINT_CYCLE),
- 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 %s\n",
- cycle_to_string (cpu, t, PRINT_TIME | PRINT_CYCLE));
- }
-
- }
- 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 },
- { "m68hc12sio", m68hc11sio_finish },
- { NULL },
- };
|