| /* The common simulator framework for GDB, the GNU Debugger. |
| |
| Copyright 2002-2021 Free Software Foundation, Inc. |
| |
| Contributed by Andrew Cagney and Red Hat. |
| |
| This file is part of GDB. |
| |
| 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 "sim-io.h" |
| |
| /* NOTE: pal is naughty and grubs around looking at things outside of |
| its immediate domain */ |
| #include "hw-tree.h" |
| |
| #include <string.h> |
| #ifdef HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #include <stdlib.h> |
| |
| /* DEVICE |
| |
| |
| pal - glue logic device containing assorted junk |
| |
| |
| DESCRIPTION |
| |
| |
| Typical hardware dependant hack. This device allows the firmware |
| to gain access to all the things the firmware needs (but the OS |
| doesn't). |
| |
| The pal contains the following registers: |
| |
| |0 reset register (write, 8bit) |
| |4 processor id register (read, 8bit) |
| |8 interrupt register (8 - port, 9 - level) (write, 16bit) |
| |12 processor count register (read, 8bit) |
| |
| |16 tty input fifo register (read, 8bit) |
| |20 tty input status register (read, 8bit) |
| |24 tty output fifo register (write, 8bit) |
| |28 tty output status register (read, 8bit) |
| |
| |32 countdown register (read/write, 32bit, big-endian) |
| |36 countdown value register (read, 32bit, big-endian) |
| |40 timer register (read/write, 32bit, big-endian) |
| |44 timer value register (read, 32bit, big-endian) |
| |
| RESET (write): halts the simulator. The value written to the |
| register is used as an exit status. |
| |
| PROCESSOR ID (read): returns the processor identifier (0 .. N-1) of |
| the processor performing the read. |
| |
| INTERRUPT (write): This register must be written using a two byte |
| store. The low byte specifies a port and the upper byte specifies |
| the a level. LEVEL is driven on the specified port. By |
| convention, the pal's interrupt ports (int0, int1, ...) are wired |
| up to the corresponding processor's level sensative external |
| interrupt pin. Eg: A two byte write to address 8 of 0x0102 |
| (big-endian) will result in processor 2's external interrupt pin |
| being asserted. |
| |
| PROCESSOR COUNT (read): returns the total number of processors |
| active in the current simulation. |
| |
| TTY INPUT FIFO (read): if the TTY input status register indicates a |
| character is available by being nonzero, returns the next available |
| character from the pal's tty input port. |
| |
| TTY OUTPUT FIFO (write): if the TTY output status register |
| indicates the output fifo is not full by being nonzero, outputs the |
| character written to the tty's output port. |
| |
| COUNDOWN (read/write): The countdown registers provide a |
| non-repeating timed interrupt source. Writing a 32 bit big-endian |
| zero value to this register clears the countdown timer. Writing a |
| non-zero 32 bit big-endian value to this register sets the |
| countdown timer to expire in VALUE ticks (ticks is target |
| dependant). Reading the countdown register returns the last value |
| writen. |
| |
| COUNTDOWN VALUE (read): Reading this 32 bit big-endian register |
| returns the number of ticks remaining until the countdown timer |
| expires. |
| |
| TIMER (read/write): The timer registers provide a periodic timed |
| interrupt source. Writing a 32 bit big-endian zero value to this |
| register clears the periodic timer. Writing a 32 bit non-zero |
| value to this register sets the periodic timer to triger every |
| VALUE ticks (ticks is target dependant). Reading the timer |
| register returns the last value written. |
| |
| TIMER VALUE (read): Reading this 32 bit big-endian register returns |
| the number of ticks until the next periodic interrupt. |
| |
| |
| PROPERTIES |
| |
| |
| reg = <address> <size> (required) |
| |
| Specify the address (within the parent bus) that this device is to |
| be located. |
| |
| poll? = <boolean> |
| |
| If present and true, indicates that the device should poll its |
| input. |
| |
| |
| PORTS |
| |
| |
| int[0..NR_PROCESSORS] (output) |
| |
| Driven as a result of a write to the interrupt-port / |
| interrupt-level register pair. |
| |
| |
| countdown |
| |
| Driven whenever the countdown counter reaches zero. |
| |
| |
| timer |
| |
| Driven whenever the timer counter reaches zero. |
| |
| |
| BUGS |
| |
| |
| At present the common simulator framework does not support input |
| polling. |
| |
| */ |
| |
| |
| enum { |
| hw_pal_reset_register = 0x0, |
| hw_pal_cpu_nr_register = 0x4, |
| hw_pal_int_register = 0x8, |
| hw_pal_nr_cpu_register = 0xa, |
| hw_pal_read_fifo = 0x10, |
| hw_pal_read_status = 0x14, |
| hw_pal_write_fifo = 0x18, |
| hw_pal_write_status = 0x1a, |
| hw_pal_countdown = 0x20, |
| hw_pal_countdown_value = 0x24, |
| hw_pal_timer = 0x28, |
| hw_pal_timer_value = 0x2c, |
| hw_pal_address_mask = 0x3f, |
| }; |
| |
| |
| typedef struct _hw_pal_console_buffer { |
| char buffer; |
| int status; |
| } hw_pal_console_buffer; |
| |
| typedef struct _hw_pal_counter { |
| struct hw_event *handler; |
| signed64 start; |
| unsigned32 delta; |
| int periodic_p; |
| } hw_pal_counter; |
| |
| |
| typedef struct _hw_pal_device { |
| hw_pal_console_buffer input; |
| hw_pal_console_buffer output; |
| hw_pal_counter countdown; |
| hw_pal_counter timer; |
| struct hw *disk; |
| do_hw_poll_read_method *reader; |
| } hw_pal_device; |
| |
| enum { |
| COUNTDOWN_PORT, |
| TIMER_PORT, |
| INT_PORT, |
| }; |
| |
| static const struct hw_port_descriptor hw_pal_ports[] = { |
| { "countdown", COUNTDOWN_PORT, 0, output_port, }, |
| { "timer", TIMER_PORT, 0, output_port, }, |
| { "int", INT_PORT, MAX_NR_PROCESSORS, output_port, }, |
| { NULL, 0, 0, 0 } |
| }; |
| |
| |
| /* countdown and simple timer */ |
| |
| static void |
| do_counter_event (struct hw *me, |
| void *data) |
| { |
| hw_pal_counter *counter = (hw_pal_counter *) data; |
| if (counter->periodic_p) |
| { |
| HW_TRACE ((me, "timer expired")); |
| counter->start = hw_event_queue_time (me); |
| hw_port_event (me, TIMER_PORT, 1); |
| hw_event_queue_schedule (me, counter->delta, do_counter_event, counter); |
| } |
| else |
| { |
| HW_TRACE ((me, "countdown expired")); |
| counter->delta = 0; |
| hw_port_event (me, COUNTDOWN_PORT, 1); |
| } |
| } |
| |
| static void |
| do_counter_read (struct hw *me, |
| hw_pal_device *pal, |
| const char *reg, |
| hw_pal_counter *counter, |
| unsigned32 *word, |
| unsigned nr_bytes) |
| { |
| unsigned32 val; |
| if (nr_bytes != 4) |
| hw_abort (me, "%s - bad read size must be 4 bytes", reg); |
| val = counter->delta; |
| HW_TRACE ((me, "read - %s %ld", reg, (long) val)); |
| *word = H2BE_4 (val); |
| } |
| |
| static void |
| do_counter_value (struct hw *me, |
| hw_pal_device *pal, |
| const char *reg, |
| hw_pal_counter *counter, |
| unsigned32 *word, |
| unsigned nr_bytes) |
| { |
| unsigned32 val; |
| if (nr_bytes != 4) |
| hw_abort (me, "%s - bad read size must be 4 bytes", reg); |
| if (counter->delta != 0) |
| val = (counter->start + counter->delta |
| - hw_event_queue_time (me)); |
| else |
| val = 0; |
| HW_TRACE ((me, "read - %s %ld", reg, (long) val)); |
| *word = H2BE_4 (val); |
| } |
| |
| static void |
| do_counter_write (struct hw *me, |
| hw_pal_device *pal, |
| const char *reg, |
| hw_pal_counter *counter, |
| const unsigned32 *word, |
| unsigned nr_bytes) |
| { |
| if (nr_bytes != 4) |
| hw_abort (me, "%s - bad write size must be 4 bytes", reg); |
| if (counter->handler != NULL) |
| { |
| hw_event_queue_deschedule (me, counter->handler); |
| counter->handler = NULL; |
| } |
| counter->delta = BE2H_4 (*word); |
| counter->start = hw_event_queue_time (me); |
| HW_TRACE ((me, "write - %s %ld", reg, (long) counter->delta)); |
| if (counter->delta > 0) |
| hw_event_queue_schedule (me, counter->delta, do_counter_event, counter); |
| } |
| |
| |
| |
| |
| /* check the console for an available character */ |
| static void |
| scan_hw_pal (struct hw *me) |
| { |
| hw_pal_device *hw_pal = (hw_pal_device *)hw_data (me); |
| char c; |
| int count; |
| count = do_hw_poll_read (me, hw_pal->reader, 0/*STDIN*/, &c, sizeof (c)); |
| switch (count) |
| { |
| case HW_IO_NOT_READY: |
| case HW_IO_EOF: |
| hw_pal->input.buffer = 0; |
| hw_pal->input.status = 0; |
| break; |
| default: |
| hw_pal->input.buffer = c; |
| hw_pal->input.status = 1; |
| } |
| } |
| |
| /* write the character to the hw_pal */ |
| |
| static void |
| write_hw_pal (struct hw *me, |
| char val) |
| { |
| hw_pal_device *hw_pal = (hw_pal_device *) hw_data (me); |
| sim_io_write_stdout (hw_system (me), &val, 1); |
| hw_pal->output.buffer = val; |
| hw_pal->output.status = 1; |
| } |
| |
| |
| /* Reads/writes */ |
| |
| static unsigned |
| hw_pal_io_read_buffer (struct hw *me, |
| void *dest, |
| int space, |
| unsigned_word addr, |
| unsigned nr_bytes) |
| { |
| hw_pal_device *hw_pal = (hw_pal_device *) hw_data (me); |
| unsigned_1 *byte = (unsigned_1 *) dest; |
| memset (dest, 0, nr_bytes); |
| switch (addr & hw_pal_address_mask) |
| { |
| |
| case hw_pal_cpu_nr_register: |
| *byte = CPU_INDEX (hw_system_cpu (me)); |
| HW_TRACE ((me, "read - cpu-nr %d\n", *byte)); |
| break; |
| |
| case hw_pal_nr_cpu_register: |
| if (hw_tree_find_property (me, "/openprom/options/smp") == NULL) |
| { |
| *byte = 1; |
| HW_TRACE ((me, "read - nr-cpu %d (not defined)\n", *byte)); |
| } |
| else |
| { |
| *byte = hw_tree_find_integer_property (me, "/openprom/options/smp"); |
| HW_TRACE ((me, "read - nr-cpu %d\n", *byte)); |
| } |
| break; |
| |
| case hw_pal_read_fifo: |
| *byte = hw_pal->input.buffer; |
| HW_TRACE ((me, "read - input-fifo %d\n", *byte)); |
| break; |
| |
| case hw_pal_read_status: |
| scan_hw_pal (me); |
| *byte = hw_pal->input.status; |
| HW_TRACE ((me, "read - input-status %d\n", *byte)); |
| break; |
| |
| case hw_pal_write_fifo: |
| *byte = hw_pal->output.buffer; |
| HW_TRACE ((me, "read - output-fifo %d\n", *byte)); |
| break; |
| |
| case hw_pal_write_status: |
| *byte = hw_pal->output.status; |
| HW_TRACE ((me, "read - output-status %d\n", *byte)); |
| break; |
| |
| case hw_pal_countdown: |
| do_counter_read (me, hw_pal, "countdown", |
| &hw_pal->countdown, dest, nr_bytes); |
| break; |
| |
| case hw_pal_countdown_value: |
| do_counter_value (me, hw_pal, "countdown-value", |
| &hw_pal->countdown, dest, nr_bytes); |
| break; |
| |
| case hw_pal_timer: |
| do_counter_read (me, hw_pal, "timer", |
| &hw_pal->timer, dest, nr_bytes); |
| break; |
| |
| case hw_pal_timer_value: |
| do_counter_value (me, hw_pal, "timer-value", |
| &hw_pal->timer, dest, nr_bytes); |
| break; |
| |
| default: |
| HW_TRACE ((me, "read - ???\n")); |
| break; |
| |
| } |
| return nr_bytes; |
| } |
| |
| |
| static unsigned |
| hw_pal_io_write_buffer (struct hw *me, |
| const void *source, |
| int space, |
| unsigned_word addr, |
| unsigned nr_bytes) |
| { |
| hw_pal_device *hw_pal = (hw_pal_device*) hw_data (me); |
| unsigned_1 *byte = (unsigned_1 *) source; |
| |
| switch (addr & hw_pal_address_mask) |
| { |
| |
| case hw_pal_reset_register: |
| hw_halt (me, sim_exited, byte[0]); |
| break; |
| |
| case hw_pal_int_register: |
| hw_port_event (me, |
| INT_PORT + byte[0], /*port*/ |
| (nr_bytes > 1 ? byte[1] : 0)); /* val */ |
| break; |
| |
| case hw_pal_read_fifo: |
| hw_pal->input.buffer = byte[0]; |
| HW_TRACE ((me, "write - input-fifo %d\n", byte[0])); |
| break; |
| |
| case hw_pal_read_status: |
| hw_pal->input.status = byte[0]; |
| HW_TRACE ((me, "write - input-status %d\n", byte[0])); |
| break; |
| |
| case hw_pal_write_fifo: |
| write_hw_pal (me, byte[0]); |
| HW_TRACE ((me, "write - output-fifo %d\n", byte[0])); |
| break; |
| |
| case hw_pal_write_status: |
| hw_pal->output.status = byte[0]; |
| HW_TRACE ((me, "write - output-status %d\n", byte[0])); |
| break; |
| |
| case hw_pal_countdown: |
| do_counter_write (me, hw_pal, "countdown", |
| &hw_pal->countdown, source, nr_bytes); |
| break; |
| |
| case hw_pal_timer: |
| do_counter_write (me, hw_pal, "timer", |
| &hw_pal->timer, source, nr_bytes); |
| break; |
| |
| } |
| return nr_bytes; |
| } |
| |
| |
| /* instances of the hw_pal struct hw */ |
| |
| #if NOT_YET |
| static void |
| hw_pal_instance_delete_callback (hw_instance *instance) |
| { |
| /* nothing to delete, the hw_pal is attached to the struct hw */ |
| return; |
| } |
| #endif |
| |
| #if NOT_YET |
| static int |
| hw_pal_instance_read_callback (hw_instance *instance, |
| void *buf, |
| unsigned_word len) |
| { |
| DITRACE (pal, ("read - %s (%ld)", (const char*) buf, (long int) len)); |
| return sim_io_read_stdin (buf, len); |
| } |
| #endif |
| |
| #if NOT_YET |
| static int |
| hw_pal_instance_write_callback (hw_instance *instance, |
| const void *buf, |
| unsigned_word len) |
| { |
| int i; |
| const char *chp = buf; |
| hw_pal_device *hw_pal = hw_instance_data (instance); |
| DITRACE (pal, ("write - %s (%ld)", (const char*) buf, (long int) len)); |
| for (i = 0; i < len; i++) |
| write_hw_pal (hw_pal, chp[i]); |
| sim_io_flush_stdoutput (); |
| return i; |
| } |
| #endif |
| |
| #if NOT_YET |
| static const hw_instance_callbacks hw_pal_instance_callbacks = { |
| hw_pal_instance_delete_callback, |
| hw_pal_instance_read_callback, |
| hw_pal_instance_write_callback, |
| }; |
| #endif |
| |
| #if 0 |
| static hw_instance * |
| hw_pal_create_instance (struct hw *me, |
| const char *path, |
| const char *args) |
| { |
| return hw_create_instance_from (me, NULL, |
| hw_data (me), |
| path, args, |
| &hw_pal_instance_callbacks); |
| } |
| #endif |
| |
| |
| static void |
| hw_pal_attach_address (struct hw *me, |
| int level, |
| int space, |
| address_word addr, |
| address_word nr_bytes, |
| struct hw *client) |
| { |
| hw_pal_device *pal = (hw_pal_device*) hw_data (me); |
| pal->disk = client; |
| } |
| |
| |
| #if 0 |
| static hw_callbacks const hw_pal_callbacks = { |
| { generic_hw_init_address, }, |
| { hw_pal_attach_address, }, /* address */ |
| { hw_pal_io_read_buffer_callback, |
| hw_pal_io_write_buffer_callback, }, |
| { NULL, }, /* DMA */ |
| { NULL, NULL, hw_pal_interrupt_ports }, /* interrupt */ |
| { generic_hw_unit_decode, |
| generic_hw_unit_encode, |
| generic_hw_address_to_attach_address, |
| generic_hw_size_to_attach_size }, |
| hw_pal_create_instance, |
| }; |
| #endif |
| |
| |
| static void |
| hw_pal_finish (struct hw *hw) |
| { |
| /* create the descriptor */ |
| hw_pal_device *hw_pal = HW_ZALLOC (hw, hw_pal_device); |
| hw_pal->output.status = 1; |
| hw_pal->output.buffer = '\0'; |
| hw_pal->input.status = 0; |
| hw_pal->input.buffer = '\0'; |
| set_hw_data (hw, hw_pal); |
| set_hw_attach_address (hw, hw_pal_attach_address); |
| set_hw_io_read_buffer (hw, hw_pal_io_read_buffer); |
| set_hw_io_write_buffer (hw, hw_pal_io_write_buffer); |
| set_hw_ports (hw, hw_pal_ports); |
| /* attach ourselves */ |
| do_hw_attach_regs (hw); |
| /* If so configured, enable polled input */ |
| if (hw_find_property (hw, "poll?") != NULL |
| && hw_find_boolean_property (hw, "poll?")) |
| { |
| hw_pal->reader = sim_io_poll_read; |
| } |
| else |
| { |
| hw_pal->reader = sim_io_read; |
| } |
| /* tag the periodic timer */ |
| hw_pal->timer.periodic_p = 1; |
| } |
| |
| |
| const struct hw_descriptor dv_pal_descriptor[] = { |
| { "pal", hw_pal_finish, }, |
| { NULL, NULL }, |
| }; |