-/* This file is part of the program psim.
-
- Copyright (C) 1994-1996,1998, Andrew Cagney <cagney@highland.com.au>
-
- 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.
-
- */
+/* The common simulator framework for GDB, the GNU Debugger.
+ Copyright 2002-2020 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/>. */
+
+#include "config.h"
#include "sim-main.h"
-#include "hw-base.h"
+#include "hw-main.h"
+#include "sim-io.h"
/* NOTE: pal is naughty and grubs around looking at things outside of
its immediate domain */
/* 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. Except for the interrupt
- level register, each of the below is 8 bytes in size and must be
- accessed using correct alignment. For 16 and 32 bit accesses the
- bytes not directed to the register are ignored:
-
- |0 reset register (write)
- |4 processor id register (read)
- |8 interrupt port (write)
- |9 interrupt level (write)
- |12 processor count register (read)
- |16 tty input fifo register (read)
- |20 tty input status register (read)
- |24 tty output fifo register (write)
- |28 tty output status register (read)
-
- Reset register (write) halts the simulator exiting with the
- value written.
-
- Processor id register (read) returns the processor number (0
- .. N-1) of the processor performing the read.
-
- The interrupt registers should be accessed as a pair (using a 16 or
- 32 bit store). The low byte specifies the interrupt port while the
- high byte specifies the level to drive that port at. By
+ 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 to
- be asserted.
+ (big-endian) will result in processor 2's external interrupt pin
+ being asserted.
- Processor count register (read) returns the total number of
- processors active in the current simulation.
+ PROCESSOR COUNT (read): returns the total number of processors
+ active in the current simulation.
- TTY input fifo register (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 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.
- Similarly, the TTY output fifo register (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.
+ 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
- live.
+ be located.
+
+ poll? = <boolean>
+
+ If present and true, indicates that the device should poll its
+ input.
PORTS
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.
+
*/
hw_pal_read_status = 0x14,
hw_pal_write_fifo = 0x18,
hw_pal_write_status = 0x1a,
- hw_pal_address_mask = 0x1f,
+ hw_pal_countdown = 0x20,
+ hw_pal_countdown_value = 0x24,
+ hw_pal_timer = 0x28,
+ hw_pal_timer_value = 0x2c,
+ hw_pal_address_mask = 0x3f,
};
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)
{
-#if 0
- hw_pal_struct hw *hw_pal = (hw_pal_struct hw *) hw_data (me);
-#endif
+ hw_pal_device *hw_pal = (hw_pal_device *)hw_data (me);
char c;
int count;
- count = sim_io_read_stdin (hw_system (me), &c, sizeof(c));
-#if 0
+ count = do_hw_poll_read (me, hw_pal->reader, 0/*STDIN*/, &c, sizeof (c));
switch (count)
{
- case sim_io_not_ready:
- case sim_io_eof:
+ case HW_IO_NOT_READY:
+ case HW_IO_EOF:
hw_pal->input.buffer = 0;
hw_pal->input.status = 0;
break;
hw_pal->input.buffer = c;
hw_pal->input.status = 1;
}
-#endif
}
/* write the character to the hw_pal */
+
static void
write_hw_pal (struct hw *me,
char val)
}
+/* Reads/writes */
+
static unsigned
hw_pal_io_read_buffer (struct hw *me,
void *dest,
int space,
unsigned_word addr,
- unsigned nr_bytes,
- sim_cpu *cpu,
- sim_cia cia)
+ unsigned nr_bytes)
{
hw_pal_device *hw_pal = (hw_pal_device *) hw_data (me);
- unsigned_1 val;
+ unsigned_1 *byte = (unsigned_1 *) dest;
+ memset (dest, 0, nr_bytes);
switch (addr & hw_pal_address_mask)
{
+
case hw_pal_cpu_nr_register:
-#ifdef CPU_INDEX
- val = CPU_INDEX (cpu);
-#else
- val = 0;
-#endif
- HW_TRACE ((me, "read - cpu-nr %d\n", val));
+ *byte = CPU_INDEX (hw_system_cpu (me));
+ HW_TRACE ((me, "read - cpu-nr %d\n", *byte));
break;
+
case hw_pal_nr_cpu_register:
- val = hw_tree_find_integer_property (me, "/openprom/options/smp");
- HW_TRACE ((me, "read - nr-cpu %d\n", val));
+ 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:
- val = hw_pal->input.buffer;
- HW_TRACE ((me, "read - input-fifo %d\n", val));
+ *byte = hw_pal->input.buffer;
+ HW_TRACE ((me, "read - input-fifo %d\n", *byte));
break;
+
case hw_pal_read_status:
scan_hw_pal (me);
- val = hw_pal->input.status;
- HW_TRACE ((me, "read - input-status %d\n", val));
+ *byte = hw_pal->input.status;
+ HW_TRACE ((me, "read - input-status %d\n", *byte));
break;
+
case hw_pal_write_fifo:
- val = hw_pal->output.buffer;
- HW_TRACE ((me, "read - output-fifo %d\n", val));
+ *byte = hw_pal->output.buffer;
+ HW_TRACE ((me, "read - output-fifo %d\n", *byte));
break;
+
case hw_pal_write_status:
- val = hw_pal->output.status;
- HW_TRACE ((me, "read - output-status %d\n", val));
+ *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:
- val = 0;
HW_TRACE ((me, "read - ???\n"));
+ break;
+
}
- memset (dest, 0, nr_bytes);
- *(unsigned_1*)dest = val;
return nr_bytes;
}
const void *source,
int space,
unsigned_word addr,
- unsigned nr_bytes,
- sim_cpu *cpu,
- sim_cia cia)
+ unsigned nr_bytes)
{
hw_pal_device *hw_pal = (hw_pal_device*) hw_data (me);
- unsigned_1 *byte = (unsigned_1*) source;
-
+ unsigned_1 *byte = (unsigned_1 *) source;
+
switch (addr & hw_pal_address_mask)
{
+
case hw_pal_reset_register:
- sim_engine_halt (NULL, cpu, NULL, cia, sim_exited, byte[0]);
+ hw_halt (me, sim_exited, byte[0]);
break;
+
case hw_pal_int_register:
hw_port_event (me,
- byte[0], /*port*/
- (nr_bytes > 1 ? byte[1] : 0), /* val */
- cpu, cia);
+ 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;
}
#if NOT_YET
static void
-hw_pal_instance_delete_callback(hw_instance *instance)
+hw_pal_instance_delete_callback (hw_instance *instance)
{
/* nothing to delete, the hw_pal is attached to the struct hw */
return;
}
#endif
-static const struct hw_port_descriptor hw_pal_ports[] = {
- { "int", 0, MAX_NR_PROCESSORS },
- { NULL }
-};
-
static void
hw_pal_attach_address (struct hw *me,
set_hw_io_write_buffer (hw, hw_pal_io_write_buffer);
set_hw_ports (hw, hw_pal_ports);
/* attach ourselves */
- do_hw_attach_regs (me);
+ 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_device_descriptor dv_pal_descriptor[] = {
+const struct hw_descriptor dv_pal_descriptor[] = {
{ "pal", hw_pal_finish, },
- { NULL },
+ { NULL, NULL },
};