123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537 |
- /* dv-m68hc11spi.c -- Simulation of the 68HC11 SPI
- Copyright (C) 2000-2022 Free Software Foundation, Inc.
- Written by Stephane Carrez (stcarrez@nerim.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/>.
-
- */
- /* This must come before any other includes. */
- #include "defs.h"
- #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), M6811_IO_LEVEL, 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);
- 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;
- uint8_t 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)
- {
- uint8_t val;
-
- if (value)
- val = cpu->ios[port] | mask;
- else
- val = cpu->ios[port] & ~mask;
- /* Set the new value and post an event to inform other devices
- that pin 'port' changed. */
- m68hc11cpu_set_port (me, cpu, port, val);
- }
- /* 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_BYTE 0
- #define SPI_START_BIT 1
- #define SPI_MIDDLE_BIT 2
- static 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 if (controller->mode == SPI_MIDDLE_BIT)
- {
- controller->mode = SPI_START_BIT;
- }
- if (controller->mode == SPI_START_BYTE)
- {
- /* Start a new SPI transfer. */
-
- /* TBD: clear SS output. */
- controller->mode = SPI_START_BIT;
- controller->tx_bit = 7;
- set_bit_port (me, cpu, M6811_PORTD, (1 << 4), ~controller->clk_pin);
- }
- else
- {
- /* 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_t base = 0;
- sim_cpu *cpu;
- struct m68hc11spi *controller;
- uint8_t 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)
- {
- int64_t t;
- sim_io_printf (sd, " SPI has %d bits to send\n",
- controller->tx_bit + 1);
- t = hw_event_remain_time (me, controller->spi_event);
- sim_io_printf (sd, " SPI current bit-cycle finished in %s\n",
- cycle_to_string (cpu, t, PRINT_TIME | PRINT_CYCLE));
- t += (controller->tx_bit + 1) * 2 * controller->clock;
- sim_io_printf (sd, " SPI operation finished in %s\n",
- cycle_to_string (cpu, t, PRINT_TIME | PRINT_CYCLE));
- }
- }
- 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;
- uint8_t 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;
- interrupts_update_pending (&cpu->cpu_interrupts);
- }
- val = controller->rx_char;
- break;
-
- default:
- return 0;
- }
- *((uint8_t*) 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;
- uint8_t 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 uint8_t*) 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 (controller->rx_clear_scsr)
- {
- cpu->ios[M6811_SPSR] &= ~controller->rx_clear_scsr;
- controller->rx_clear_scsr = 0;
- interrupts_update_pending (&cpu->cpu_interrupts);
- }
- /* 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->mode = SPI_START_BYTE;
- /* 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 },
- { "m68hc12spi", m68hc11spi_finish },
- { NULL },
- };
|