aboutsummaryrefslogtreecommitdiff
path: root/sim/m68hc11/dv-m68hc11spi.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-m68hc11spi.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-m68hc11spi.c')
-rw-r--r--sim/m68hc11/dv-m68hc11spi.c508
1 files changed, 508 insertions, 0 deletions
diff --git a/sim/m68hc11/dv-m68hc11spi.c b/sim/m68hc11/dv-m68hc11spi.c
new file mode 100644
index 0000000..42aaa70
--- /dev/null
+++ b/sim/m68hc11/dv-m68hc11spi.c
@@ -0,0 +1,508 @@
+/* dv-m68hc11spi.c -- Simulation of the 68HC11 SPI
+ Copyright (C) 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
+
+ m68hc11spi - m68hc11 SPI interface
+
+
+ DESCRIPTION
+
+ Implements the m68hc11 Synchronous Serial Peripheral Interface
+ described in the m68hc11 user guide (Chapter 8 in pink book).
+ The SPI I/O controller is directly connected to the CPU
+ interrupt. The simulator implements:
+
+ - SPI clock emulation
+ - Data transfer
+ - Write collision detection
+
+
+ PROPERTIES
+
+ None
+
+
+ PORTS
+
+ reset (input)
+
+ Reset port. This port is only used to simulate a reset of the SPI
+ 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 m68hc11spi_ports[] =
+{
+ { "reset", RESET_PORT, 0, input_port, },
+ { NULL, },
+};
+
+
+/* SPI */
+struct m68hc11spi
+{
+ /* Information about next character to be transmited. */
+ unsigned char tx_char;
+ int tx_bit;
+ unsigned char mode;
+
+ unsigned char rx_char;
+ unsigned char rx_clear_scsr;
+ unsigned char clk_pin;
+
+ /* SPI clock rate (twice the real clock). */
+ unsigned int clock;
+
+ /* Periodic SPI event. */
+ struct hw_event* spi_event;
+};
+
+
+
+/* Finish off the partially created hw device. Attach our local
+ callbacks. Wire up our port names etc */
+
+static hw_io_read_buffer_method m68hc11spi_io_read_buffer;
+static hw_io_write_buffer_method m68hc11spi_io_write_buffer;
+static hw_port_event_method m68hc11spi_port_event;
+static hw_ioctl_method m68hc11spi_ioctl;
+
+#define M6811_SPI_FIRST_REG (M6811_SPCR)
+#define M6811_SPI_LAST_REG (M6811_SPDR)
+
+
+static void
+attach_m68hc11spi_regs (struct hw *me,
+ struct m68hc11spi *controller)
+{
+ hw_attach_address (hw_parent (me), 0, io_map,
+ M6811_SPI_FIRST_REG,
+ M6811_SPI_LAST_REG - M6811_SPI_FIRST_REG + 1,
+ me);
+}
+
+static void
+m68hc11spi_finish (struct hw *me)
+{
+ struct m68hc11spi *controller;
+
+ controller = HW_ZALLOC (me, struct m68hc11spi);
+ me->overlap_mode_hw = 1;
+ set_hw_data (me, controller);
+ set_hw_io_read_buffer (me, m68hc11spi_io_read_buffer);
+ set_hw_io_write_buffer (me, m68hc11spi_io_write_buffer);
+ set_hw_ports (me, m68hc11spi_ports);
+ set_hw_port_event (me, m68hc11spi_port_event);
+#ifdef set_hw_ioctl
+ set_hw_ioctl (me, m68hc11spi_ioctl);
+#else
+ me->to_ioctl = m68hc11spi_ioctl;
+#endif
+
+ /* Attach ourself to our parent bus. */
+ attach_m68hc11spi_regs (me, controller);
+
+ /* Initialize to reset state. */
+ controller->spi_event = NULL;
+ controller->rx_clear_scsr = 0;
+}
+
+
+
+/* An event arrives on an interrupt port */
+
+static void
+m68hc11spi_port_event (struct hw *me,
+ int my_port,
+ struct hw *source,
+ int source_port,
+ int level)
+{
+ SIM_DESC sd;
+ struct m68hc11spi *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, "SPI reset"));
+
+ /* Reset the state of SPI registers. */
+ controller->rx_clear_scsr = 0;
+ if (controller->spi_event)
+ {
+ hw_event_queue_deschedule (me, controller->spi_event);
+ controller->spi_event = 0;
+ }
+
+ val = 0;
+ m68hc11spi_io_write_buffer (me, &val, io_map,
+ (unsigned_word) M6811_SPCR, 1);
+ break;
+ }
+
+ default:
+ hw_abort (me, "Event on unknown port %d", my_port);
+ break;
+ }
+}
+
+static void
+set_bit_port (struct hw *me, sim_cpu *cpu, int port, int mask, int value)
+{
+ /* TODO: Post an event to inform other devices that pin 'port' changes.
+ This has only a sense if we provide some device that is logically
+ connected to these pin ports (SCLK and MOSI) and that handles
+ the SPI protocol. */
+ if (value)
+ cpu->ios[port] |= mask;
+ else
+ cpu->ios[port] &= ~mask;
+}
+
+
+/* When a character is sent/received by the SPI, the PD2..PD5 line
+ are driven by the following signals:
+
+ B7 B6
+ -----+---------+--------+---/-+-------
+ MOSI | | | | | |
+ MISO +---------+--------+---/-+
+ ____ ___
+ CLK _______/ \____/ \__ CPOL=0, CPHA=0
+ _______ ____ __
+ \____/ \___/ CPOL=1, CPHA=0
+ ____ ____ __
+ __/ \____/ \___/ CPOL=0, CPHA=1
+ __ ____ ___
+ \____/ \____/ \__ CPOL=1, CPHA=1
+
+ SS ___ ____
+ \__________________________//___/
+
+ MISO = PD2
+ MOSI = PD3
+ SCK = PD4
+ SS = PD5
+
+*/
+
+#define SPI_START_BIT 0
+#define SPI_MIDDLE_BIT 1
+
+void
+m68hc11spi_clock (struct hw *me, void *data)
+{
+ SIM_DESC sd;
+ struct m68hc11spi* controller;
+ sim_cpu *cpu;
+ int check_interrupt = 0;
+
+ controller = hw_data (me);
+ sd = hw_system (me);
+ cpu = STATE_CPU (sd, 0);
+
+ /* Cleanup current event. */
+ if (controller->spi_event)
+ {
+ hw_event_queue_deschedule (me, controller->spi_event);
+ controller->spi_event = 0;
+ }
+
+ /* Change a bit of data at each two SPI event. */
+ if (controller->mode == SPI_START_BIT)
+ {
+ /* Reflect the bit value on bit 2 of port D. */
+ set_bit_port (me, cpu, M6811_PORTD, (1 << 2),
+ (controller->tx_char & (1 << controller->tx_bit)));
+ controller->tx_bit--;
+ controller->mode = SPI_MIDDLE_BIT;
+ }
+ else
+ {
+ controller->mode = SPI_START_BIT;
+ }
+
+ /* Change the SPI clock at each event on bit 4 of port D. */
+ controller->clk_pin = ~controller->clk_pin;
+ set_bit_port (me, cpu, M6811_PORTD, (1 << 4), controller->clk_pin);
+
+ /* Transmit is now complete for this byte. */
+ if (controller->mode == SPI_START_BIT && controller->tx_bit < 0)
+ {
+ controller->rx_clear_scsr = 0;
+ cpu->ios[M6811_SPSR] |= M6811_SPIF;
+ if (cpu->ios[M6811_SPCR] & M6811_SPIE)
+ check_interrupt = 1;
+ }
+ else
+ {
+ controller->spi_event = hw_event_queue_schedule (me, controller->clock,
+ m68hc11spi_clock,
+ NULL);
+ }
+
+ if (check_interrupt)
+ interrupts_update_pending (&cpu->cpu_interrupts);
+}
+
+/* Flags of the SPCR register. */
+io_reg_desc spcr_desc[] = {
+ { M6811_SPIE, "SPIE ", "Serial Peripheral Interrupt Enable" },
+ { M6811_SPE, "SPE ", "Serial Peripheral System Enable" },
+ { M6811_DWOM, "DWOM ", "Port D Wire-OR mode option" },
+ { M6811_MSTR, "MSTR ", "Master Mode Select" },
+ { M6811_CPOL, "CPOL ", "Clock Polarity" },
+ { M6811_CPHA, "CPHA ", "Clock Phase" },
+ { M6811_SPR1, "SPR1 ", "SPI Clock Rate Select" },
+ { M6811_SPR0, "SPR0 ", "SPI Clock Rate Select" },
+ { 0, 0, 0 }
+};
+
+
+/* Flags of the SPSR register. */
+io_reg_desc spsr_desc[] = {
+ { M6811_SPIF, "SPIF ", "SPI Transfer Complete flag" },
+ { M6811_WCOL, "WCOL ", "Write Collision" },
+ { M6811_MODF, "MODF ", "Mode Fault" },
+ { 0, 0, 0 }
+};
+
+static void
+m68hc11spi_info (struct hw *me)
+{
+ SIM_DESC sd;
+ uint16 base = 0;
+ sim_cpu *cpu;
+ struct m68hc11spi *controller;
+ uint8 val;
+
+ sd = hw_system (me);
+ cpu = STATE_CPU (sd, 0);
+ controller = hw_data (me);
+
+ sim_io_printf (sd, "M68HC11 SPI:\n");
+
+ base = cpu_get_io_base (cpu);
+
+ val = cpu->ios[M6811_SPCR];
+ print_io_byte (sd, "SPCR", spcr_desc, val, base + M6811_SPCR);
+ sim_io_printf (sd, "\n");
+
+ val = cpu->ios[M6811_SPSR];
+ print_io_byte (sd, "SPSR", spsr_desc, val, base + M6811_SPSR);
+ sim_io_printf (sd, "\n");
+
+ if (controller->spi_event)
+ {
+ signed64 t;
+
+ t = hw_event_remain_time (me, controller->spi_event);
+ sim_io_printf (sd, " SPI operation finished in %ld cycles\n",
+ (long) t);
+ }
+}
+
+static int
+m68hc11spi_ioctl (struct hw *me,
+ hw_ioctl_request request,
+ va_list ap)
+{
+ m68hc11spi_info (me);
+ return 0;
+}
+
+/* generic read/write */
+
+static unsigned
+m68hc11spi_io_read_buffer (struct hw *me,
+ void *dest,
+ int space,
+ unsigned_word base,
+ unsigned nr_bytes)
+{
+ SIM_DESC sd;
+ struct m68hc11spi *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_SPSR:
+ controller->rx_clear_scsr = cpu->ios[M6811_SCSR]
+ & (M6811_SPIF | M6811_WCOL | M6811_MODF);
+
+ case M6811_SPCR:
+ val = cpu->ios[base];
+ break;
+
+ case M6811_SPDR:
+ if (controller->rx_clear_scsr)
+ {
+ cpu->ios[M6811_SPSR] &= ~controller->rx_clear_scsr;
+ controller->rx_clear_scsr = 0;
+ }
+ val = controller->rx_char;
+ break;
+
+ default:
+ return 0;
+ }
+ *((unsigned8*) dest) = val;
+ return 1;
+}
+
+static unsigned
+m68hc11spi_io_write_buffer (struct hw *me,
+ const void *source,
+ int space,
+ unsigned_word base,
+ unsigned nr_bytes)
+{
+ SIM_DESC sd;
+ struct m68hc11spi *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_SPCR:
+ cpu->ios[M6811_SPCR] = val;
+
+ /* The SPI clock rate is 2, 4, 16, 32 of the internal CPU clock.
+ We have to drive the clock pin and need a 2x faster clock. */
+ switch (val & (M6811_SPR1 | M6811_SPR0))
+ {
+ case 0:
+ controller->clock = 1;
+ break;
+
+ case 1:
+ controller->clock = 2;
+ break;
+
+ case 2:
+ controller->clock = 8;
+ break;
+
+ default:
+ controller->clock = 16;
+ break;
+ }
+
+ /* Set the clock pin. */
+ if ((val & M6811_CPOL)
+ && (controller->spi_event == 0
+ || ((val & M6811_CPHA) && controller->mode == 1)))
+ controller->clk_pin = 1;
+ else
+ controller->clk_pin = 0;
+
+ set_bit_port (me, cpu, M6811_PORTD, (1 << 4), controller->clk_pin);
+ break;
+
+ /* Can't write to SPSR. */
+ case M6811_SPSR:
+ break;
+
+ case M6811_SPDR:
+ if (!(cpu->ios[M6811_SPCR] & M6811_SPE))
+ {
+ return 0;
+ }
+
+ /* If transfer is taking place, a write to SPDR
+ generates a collision. */
+ if (controller->spi_event)
+ {
+ cpu->ios[M6811_SPSR] |= M6811_WCOL;
+ break;
+ }
+
+ /* Refuse the write if there was no read of SPSR. */
+ /* ???? TBD. */
+
+ /* Prepare to send a byte. */
+ controller->tx_char = val;
+ controller->tx_bit = 7;
+ controller->mode = 0;
+
+ /* Toggle clock pin internal value when CPHA is 0 so that
+ it will really change in the middle of a bit. */
+ if (!(cpu->ios[M6811_SPCR] & M6811_CPHA))
+ controller->clk_pin = ~controller->clk_pin;
+
+ cpu->ios[M6811_SPDR] = val;
+
+ /* Activate transmission. */
+ m68hc11spi_clock (me, NULL);
+ break;
+
+ default:
+ return 0;
+ }
+ return nr_bytes;
+}
+
+
+const struct hw_descriptor dv_m68hc11spi_descriptor[] = {
+ { "m68hc11spi", m68hc11spi_finish, },
+ { NULL },
+};
+