Replace *attach_address() arguments SPACEMASK:ADDR with SPACE:ADDR.

Add notes to hw-device.h that discuss the interpretation of SPACE:ADDR
on a BUS.
This commit is contained in:
Andrew Cagney 1998-03-22 04:18:52 +00:00
parent 6cfaad4d88
commit b1e9223cee
5 changed files with 1481 additions and 0 deletions

View File

@ -1,3 +1,18 @@
Sun Mar 22 15:09:52 1998 Andrew Cagney <cagney@b1.cygnus.com>
* hw-device.h (hw_attach_address_callback,
hw_detach_address_callback): Attach to a single space not a space
mask. Clarify interpretation of SPACE:ADDR parameters.
* hw-base.c (passthrough_hw_attach_address,
passthrough_hw_detach_address): Update.
* dv-core.c (dv_core_attach_address_callback): Ditto.
* dv-pal.c (hw_pal_attach_address): Ditto.
Thu Mar 19 00:41:00 1998 Andrew Cagney <cagney@b1.cygnus.com>
* sim-options.h: Document additional CPU arg to OPTION_HANDLER.
Wed Mar 18 14:13:02 1998 Andrew Cagney <cagney@b1.cygnus.com>
* Make-common.in (SIM_HW_OBJS, SIM_HW_SRC, SIM_DV_OBJS): Define.

116
sim/common/dv-core.c Normal file
View File

@ -0,0 +1,116 @@
/* This file is part of the program psim.
Copyright (C) 1994-1996, 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.
*/
#include "sim-main.h"
#include "hw-base.h"
/* DEVICE
core - root of the device tree
DESCRIPTION
The core device, positioned at the root of the device tree appears
to its child devices as a normal device just like every other
device in the tree.
Internally it is implemented using a core object. Requests to
attach (or detach) address spaces are passed to that core object.
Requests to transfer (DMA) data are reflected back down the device
tree using the core_map data transfer methods.
PROPERTIES
None.
*/
static void
dv_core_attach_address_callback (struct hw *me,
int level,
int space,
address_word addr,
address_word nr_bytes,
struct hw *client)
{
/* NOTE: At preset the space is assumed to be zero. Perhaphs the
space should be mapped onto something for instance: space0 -
unified memory; space1 - IO memory; ... */
if (space != 0)
hw_abort (me, "Hey! Unknown space %d", space);
sim_core_attach (hw_system (me),
NULL, /*cpu*/
level,
access_read_write_exec,
space, addr,
nr_bytes,
0, /* modulo */
client,
NULL);
}
static unsigned
dv_core_dma_read_buffer_callback (struct hw *me,
void *dest,
int space,
unsigned_word addr,
unsigned nr_bytes)
{
return sim_core_read_buffer (hw_system (me),
NULL, /*CPU*/
space, /*???*/
dest,
addr,
nr_bytes);
}
static unsigned
dv_core_dma_write_buffer_callback (struct hw *me,
const void *source,
int space,
unsigned_word addr,
unsigned nr_bytes,
int violate_read_only_section)
{
return sim_core_write_buffer (hw_system (me),
NULL, /*cpu*/
space, /*???*/
source,
addr,
nr_bytes);
}
static void
dv_core_finish (struct hw *me)
{
set_hw_attach_address (me, dv_core_attach_address_callback);
set_hw_dma_write_buffer (me, dv_core_dma_write_buffer_callback);
set_hw_dma_read_buffer (me, dv_core_dma_read_buffer_callback);
}
const struct hw_device_descriptor dv_core_descriptor[] = {
{ "core", dv_core_finish, },
{ NULL },
};

387
sim/common/dv-pal.c Normal file
View File

@ -0,0 +1,387 @@
/* 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.
*/
#include "sim-main.h"
#include "hw-base.h"
/* NOTE: pal is naughty and grubs around looking at things outside of
its immediate domain */
#include "hw-tree.h"
#ifdef HAVE_STRING_H
#include <string.h>
#else
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#define DTRACE(x,y)
/* 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
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.
Processor count register (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.
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.
PROPERTIES
reg = <address> <size> (required)
Specify the address (within the parent bus) that this device is to
live.
*/
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_address_mask = 0x1f,
};
typedef struct _hw_pal_console_buffer {
char buffer;
int status;
} hw_pal_console_buffer;
typedef struct _hw_pal_device {
hw_pal_console_buffer input;
hw_pal_console_buffer output;
struct hw *disk;
} hw_pal_device;
/* 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
char c;
int count;
count = sim_io_read_stdin (hw_system (me), &c, sizeof(c));
#if 0
switch (count)
{
case sim_io_not_ready:
case sim_io_eof:
hw_pal->input.buffer = 0;
hw_pal->input.status = 0;
break;
default:
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)
{
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;
}
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)
{
hw_pal_device *hw_pal = (hw_pal_device *) hw_data (me);
unsigned_1 val;
switch (addr & hw_pal_address_mask)
{
case hw_pal_cpu_nr_register:
#ifdef CPU_INDEX
val = CPU_INDEX (cpu);
#else
val = 0;
#endif
DTRACE (pal, ("read - cpu-nr %d\n", val));
break;
case hw_pal_nr_cpu_register:
val = hw_tree_find_integer_property (me, "/openprom/options/smp");
DTRACE (pal, ("read - nr-cpu %d\n", val));
break;
case hw_pal_read_fifo:
val = hw_pal->input.buffer;
DTRACE (pal, ("read - input-fifo %d\n", val));
break;
case hw_pal_read_status:
scan_hw_pal (me);
val = hw_pal->input.status;
DTRACE (pal, ("read - input-status %d\n", val));
break;
case hw_pal_write_fifo:
val = hw_pal->output.buffer;
DTRACE (pal, ("read - output-fifo %d\n", val));
break;
case hw_pal_write_status:
val = hw_pal->output.status;
DTRACE (pal, ("read - output-status %d\n", val));
break;
default:
val = 0;
DTRACE (pal, ("read - ???\n"));
}
memset (dest, 0, nr_bytes);
*(unsigned_1*)dest = val;
return nr_bytes;
}
static unsigned
hw_pal_io_write_buffer (struct hw *me,
const void *source,
int space,
unsigned_word addr,
unsigned nr_bytes,
sim_cpu *cpu,
sim_cia cia)
{
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:
sim_engine_halt (NULL, cpu, NULL, cia, 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);
break;
case hw_pal_read_fifo:
hw_pal->input.buffer = byte[0];
DTRACE (pal, ("write - input-fifo %d\n", byte[0]));
break;
case hw_pal_read_status:
hw_pal->input.status = byte[0];
DTRACE (pal, ("write - input-status %d\n", byte[0]));
break;
case hw_pal_write_fifo:
write_hw_pal (me, byte[0]);
DTRACE (pal, ("write - output-fifo %d\n", byte[0]));
break;
case hw_pal_write_status:
hw_pal->output.status = byte[0];
DTRACE (pal, ("write - output-status %d\n", byte[0]));
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 const struct hw_port_descriptor hw_pal_ports[] = {
{ "int", 0, MAX_NR_PROCESSORS },
{ NULL }
};
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 = ZALLOC (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);
}
const struct hw_device_descriptor dv_pal_descriptor[] = {
{ "pal", hw_pal_finish, },
{ NULL },
};

467
sim/common/hw-base.c Normal file
View File

@ -0,0 +1,467 @@
/* 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.
*/
#include "sim-main.h"
#include "hw-base.h"
/* LATER: #include "hwconfig.h" */
struct hw_base_data {
int finished_p;
const struct hw_device_descriptor *descriptor;
};
extern const struct hw_device_descriptor dv_core_descriptor[];
extern const struct hw_device_descriptor dv_pal_descriptor[];
const struct hw_device_descriptor *hw_descriptors[] = {
dv_core_descriptor,
dv_pal_descriptor,
NULL,
};
#ifdef HAVE_STRING_H
#include <string.h>
#else
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#endif
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <ctype.h>
static int
generic_hw_unit_decode (struct hw *bus,
const char *unit,
hw_unit *phys)
{
memset (phys, 0, sizeof (*phys));
if (unit == NULL)
return 0;
else
{
int nr_cells = 0;
const int max_nr_cells = hw_unit_nr_address_cells (bus);
while (1)
{
char *end = NULL;
unsigned long val;
val = strtoul (unit, &end, 0);
/* parse error? */
if (unit == end)
return -1;
/* two many cells? */
if (nr_cells >= max_nr_cells)
return -1;
/* save it */
phys->cells[nr_cells] = val;
nr_cells++;
unit = end;
/* more to follow? */
if (isspace (*unit) || *unit == '\0')
break;
if (*unit != ',')
return -1;
unit++;
}
if (nr_cells < max_nr_cells) {
/* shift everything to correct position */
int i;
for (i = 1; i <= nr_cells; i++)
phys->cells[max_nr_cells - i] = phys->cells[nr_cells - i];
for (i = 0; i < (max_nr_cells - nr_cells); i++)
phys->cells[i] = 0;
}
phys->nr_cells = max_nr_cells;
return max_nr_cells;
}
}
static int
generic_hw_unit_encode (struct hw *bus,
const hw_unit *phys,
char *buf,
int sizeof_buf)
{
int i;
int len;
char *pos = buf;
/* skip leading zero's */
for (i = 0; i < phys->nr_cells; i++)
{
if (phys->cells[i] != 0)
break;
}
/* don't output anything if empty */
if (phys->nr_cells == 0)
{
strcpy(pos, "");
len = 0;
}
else if (i == phys->nr_cells)
{
/* all zero */
strcpy(pos, "0");
len = 1;
}
else
{
for (; i < phys->nr_cells; i++)
{
if (pos != buf) {
strcat(pos, ",");
pos = strchr(pos, '\0');
}
if (phys->cells[i] < 10)
sprintf (pos, "%ld", (unsigned long)phys->cells[i]);
else
sprintf (pos, "0x%lx", (unsigned long)phys->cells[i]);
pos = strchr(pos, '\0');
}
len = pos - buf;
}
if (len >= sizeof_buf)
hw_abort (NULL, "generic_unit_encode - buffer overflow\n");
return len;
}
static int
generic_hw_unit_address_to_attach_address (struct hw *me,
const hw_unit *address,
int *attach_space,
unsigned_word *attach_address,
struct hw *client)
{
int i;
for (i = 0; i < address->nr_cells - 2; i++)
{
if (address->cells[i] != 0)
hw_abort (me, "Only 32bit addresses supported");
}
if (address->nr_cells >= 2)
*attach_space = address->cells[address->nr_cells - 2];
else
*attach_space = 0;
*attach_address = address->cells[address->nr_cells - 1];
return 1;
}
static int
generic_hw_unit_size_to_attach_size (struct hw *me,
const hw_unit *size,
unsigned *nr_bytes,
struct hw *client)
{
int i;
for (i = 0; i < size->nr_cells - 1; i++)
{
if (size->cells[i] != 0)
hw_abort (me, "Only 32bit sizes supported");
}
*nr_bytes = size->cells[0];
return *nr_bytes;
}
/* ignore/passthrough versions of each function */
static void
passthrough_hw_attach_address (struct hw *me,
int level,
int space,
address_word addr,
address_word nr_bytes,
struct hw *client) /*callback/default*/
{
if (hw_parent (me) == NULL)
hw_abort (client, "hw_attach_address: no parent attach method");
hw_attach_address (hw_parent (me), level,
space, addr, nr_bytes,
client);
}
static void
passthrough_hw_detach_address (struct hw *me,
int level,
int space,
address_word addr,
address_word nr_bytes,
struct hw *client) /*callback/default*/
{
if (hw_parent (me) == NULL)
hw_abort (client, "hw_attach_address: no parent attach method");
hw_detach_address (hw_parent (me), level,
space, addr, nr_bytes,
client);
}
static unsigned
panic_hw_io_read_buffer (struct hw *me,
void *dest,
int space,
unsigned_word addr,
unsigned nr_bytes,
sim_cpu *processor,
sim_cia cia)
{
hw_abort (me, "no io-read method");
return 0;
}
static unsigned
panic_hw_io_write_buffer (struct hw *me,
const void *source,
int space,
unsigned_word addr,
unsigned nr_bytes,
sim_cpu *processor,
sim_cia cia)
{
hw_abort (me, "no io-write method");
return 0;
}
static unsigned
passthrough_hw_dma_read_buffer (struct hw *me,
void *dest,
int space,
unsigned_word addr,
unsigned nr_bytes)
{
if (hw_parent (me) == NULL)
hw_abort (me, "no parent dma-read method");
return hw_dma_read_buffer (hw_parent (me), dest,
space, addr, nr_bytes);
}
static unsigned
passthrough_hw_dma_write_buffer (struct hw *me,
const void *source,
int space,
unsigned_word addr,
unsigned nr_bytes,
int violate_read_only_section)
{
if (hw_parent (me) == NULL)
hw_abort (me, "no parent dma-write method");
return hw_dma_write_buffer (hw_parent (me), source,
space, addr,
nr_bytes,
violate_read_only_section);
}
const struct hw_port_descriptor empty_hw_ports[] = {
{ NULL, },
};
static void
panic_hw_port_event (struct hw *me,
int my_port,
struct hw *source,
int source_port,
int level,
sim_cpu *processor,
sim_cia cia)
{
hw_abort (me, "no port method");
}
static const char *
full_name_of_hw (struct hw *leaf,
char *buf,
unsigned sizeof_buf)
{
/* get a buffer */
char full_name[1024];
if (buf == (char*)0)
{
buf = full_name;
sizeof_buf = sizeof (full_name);
}
/* use head recursion to construct the path */
if (hw_parent (leaf) == NULL)
/* root */
{
if (sizeof_buf < 1)
hw_abort (leaf, "buffer overflow");
*buf = '\0';
}
else
/* sub node */
{
char unit[1024];
full_name_of_hw (hw_parent (leaf), buf, sizeof_buf);
if (hw_unit_encode (hw_parent (leaf),
hw_unit_address (leaf),
unit + 1,
sizeof (unit) - 1)
> 0)
unit[0] = '@';
else
unit[0] = '\0';
if (strlen (buf) + strlen ("/") + strlen (hw_name (leaf)) + strlen (unit)
>= sizeof_buf)
hw_abort (leaf, "buffer overflow");
strcat (buf, "/");
strcat (buf, hw_name (leaf));
strcat (buf, unit);
}
/* return it usefully */
if (buf == full_name)
buf = (char *) strdup (full_name);
return buf;
}
struct hw *
hw_create (SIM_DESC sd,
struct hw *parent,
const char *family,
const char *name,
const char *unit,
const char *args)
{
struct hw *hw = ZALLOC (struct hw);
/* our identity */
hw->family_of_hw = family;
hw->name_of_hw = name;
hw->args_of_hw = args;
/* a hook into the system */
if (sd != NULL)
hw->system_of_hw = sd;
else if (parent != NULL)
hw->system_of_hw = hw_system (parent);
else
hw_abort (parent, "No system found");
/* in a tree */
if (parent != NULL)
{
struct hw **sibling = &parent->child_of_hw;
while ((*sibling) != NULL)
sibling = &(*sibling)->sibling_of_hw;
*sibling = hw;
hw->parent_of_hw = parent;
}
/* top of tree */
if (parent != NULL)
{
struct hw *root = parent;
while (root->parent_of_hw != NULL)
root = root->parent_of_hw;
hw->root_of_hw = root;
}
/* a unique identifier for the device on the parents bus */
if (parent != NULL)
{
hw_unit_decode (parent, unit, &hw->unit_address_of_hw);
}
/* Determine our path */
if (parent != NULL)
hw->path_of_hw = full_name_of_hw (hw, NULL, 0);
else
hw->path_of_hw = "/";
/* our callbacks */
set_hw_io_read_buffer (hw, panic_hw_io_read_buffer);
set_hw_io_write_buffer (hw, panic_hw_io_write_buffer);
set_hw_dma_read_buffer (hw, passthrough_hw_dma_read_buffer);
set_hw_dma_write_buffer (hw, passthrough_hw_dma_write_buffer);
set_hw_unit_decode (hw, generic_hw_unit_decode);
set_hw_unit_encode (hw, generic_hw_unit_encode);
set_hw_unit_address_to_attach_address (hw, generic_hw_unit_address_to_attach_address);
set_hw_unit_size_to_attach_size (hw, generic_hw_unit_size_to_attach_size);
set_hw_attach_address (hw, passthrough_hw_attach_address);
set_hw_detach_address (hw, passthrough_hw_detach_address);
/* locate a descriptor */
{
const struct hw_device_descriptor **table;
for (table = hw_descriptors;
*table != NULL;
table++)
{
const struct hw_device_descriptor *entry;
for (entry = *table;
entry->family != NULL;
entry++)
{
if (strcmp (family, entry->family) == 0)
{
hw->base_of_hw = ZALLOC (struct hw_base_data);
hw->base_of_hw->descriptor = entry;
hw->base_of_hw->finished_p = 0;
}
}
}
if (hw->base_of_hw == NULL)
{
hw_abort (parent, "Unknown device `%s'", family);
}
}
/* Attach dummy ports */
set_hw_ports (hw, empty_hw_ports);
set_hw_port_event (hw, panic_hw_port_event);
return hw;
}
int
hw_finished_p (struct hw *me)
{
return (me->base_of_hw->finished_p);
}
void
hw_finish (struct hw *me)
{
if (hw_finished_p (me))
hw_abort (me, "Attempt to finish finished device");
/* Fill in the (hopefully) defined address/size cells values */
if (hw_find_property (me, "#address-cells") != NULL)
me->nr_address_cells_of_hw_unit =
hw_find_integer_property (me, "#address-cells");
else
me->nr_address_cells_of_hw_unit = 2;
if (hw_find_property (me, "#size-cells") != NULL)
me->nr_size_cells_of_hw_unit =
hw_find_integer_property (me, "#size-cells");
else
me->nr_size_cells_of_hw_unit = 1;
/* Allow the real device to override any methods */
me->base_of_hw->descriptor->to_finish (me);
me->base_of_hw->finished_p = 1;
}

496
sim/common/hw-device.h Normal file
View File

@ -0,0 +1,496 @@
/* This file is part of the program psim.
Copyright (C) 1994-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.
*/
#ifndef HW_DEVICE_H
#define HW_DEVICE_H
/* declared in sim-basics.h, this object is used everywhere */
/* typedef struct _device device; */
/* Introduction:
As explained in earlier sections, the device, device instance,
property and ports lie at the heart of PSIM's device model.
In the below a synopsis of the device object and the operations it
supports are given.
*/
/* Creation:
The devices are created using a sequence of steps. In particular:
o A tree framework is created.
At this point, properties can be modified and extra
devices inserted (or removed?).
#if LATER
Any properties that have a run-time value (eg ihandle
or device instance pointer properties) are entered
into the device tree using a named reference to the
corresponding runtime object that is to be created.
#endif
o Real devices are created for all the dummy devices.
A device can assume that all of its parents have been
initialized.
A device can assume that all non run-time properties
have been initialized.
As part of being created, the device normally attaches
itself to its parent bus.
#if LATER
Device instance data is initialized.
#endif
#if LATER
o Any run-time properties are created.
#endif
#if MUCH_MUCH_LATER
o Some devices, as part of their initialization
might want to refer to ihandle properties
in the device tree.
#endif
NOTES:
o It is important to separate the creation
of an actual device from the creation
of the tree. The alternative creating
the device in two stages: As a separate
entity and then as a part of the tree.
#if LATER
o Run-time properties can not be created
until after the devices in the tree
have been created. Hence an extra pass
for handling them.
#endif
*/
/* Relationships:
A device is able to determine its relationship to other devices
within the tree. Operations include querying for a devices parent,
sibling, child, name, and path (from the root).
*/
#define hw_parent(hw) ((hw)->parent_of_hw + 0)
#define hw_sibling(hw) ((hw)->sibling_of_hw + 0)
#define hw_child(hw) ((hw)->child_of_hw + 0)
/* Herritage:
*/
#define hw_family(hw) ((hw)->family_of_hw + 0)
#define hw_name(hw) ((hw)->name_of_hw + 0)
#define hw_args(hw) ((hw)->args_of_hw + 0)
#define hw_path(hw) ((hw)->path_of_hw + 0)
/* Short cut to the root node of the tree */
#define hw_root(hw) ((hw)->root_of_hw + 0)
/* Short cut back to the simulator object */
#define hw_system(hw) ((hw)->system_of_hw + 0)
/* Device private data */
#define hw_data(hw) ((hw)->data_of_hw)
/* Perform a soft reset of the device */
typedef unsigned (hw_reset_callback)
(struct hw *me);
#define hw_reset(hw) ((hw)->to_reset (hw))
/* Hardware operations:
Connecting a parent to its children is a common bus. The parent
node is described as the bus owner and is responisble for
co-ordinating bus operations. On the bus, a SPACE:ADDR pair is used
to specify an address. A device that is both a bus owner (parent)
and bus client (child) are refered to as a bridging device.
A child performing a data (DMA) transfer will pass its request to
the bus owner (the devices parent). The bus owner will then either
reflect the request to one of the other devices attached to the bus
(a child of the bus owner) or bridge the request up the tree to the
next bus. */
/* Children attached to a bus can register (attach) themselves to
specific addresses on their attached bus.
(A device may also be implicitly attached to certain bus
addresses).
The SPACE:ADDR pair specify an address on the common bus that
connects the parent and child devices. */
typedef void (hw_attach_address_callback)
(struct hw *me,
int level,
int space,
address_word addr,
address_word nr_bytes,
struct hw *client); /*callback/default*/
#define hw_attach_address(me, level, space, addr, nr_bytes, client) \
((me)->to_attach_address (me, level, space, addr, nr_bytes, client))
typedef void (hw_detach_address_callback)
(struct hw *me,
int level,
int space,
address_word addr,
address_word nr_bytes,
struct hw *client); /*callback/default*/
#define hw_detach_address(me, level, space, addr, nr_bytes, client) \
((me)->to_detach_address (me, level, space, addr, nr_bytes, client))
/* An IO operation from a parent to a child via the conecting bus.
The SPACE:ADDR pair specify an address on the bus shared between
the parent and child devices. */
typedef unsigned (hw_io_read_buffer_callback)
(struct hw *me,
void *dest,
int space,
unsigned_word addr,
unsigned nr_bytes,
sim_cpu *processor,
sim_cia cia);
#define hw_io_read_buffer(hw, dest, space, addr, nr_bytes, processor, cia) \
((hw)->to_io_read_buffer (hw, dest, space, addr, nr_bytes, processor, cia))
typedef unsigned (hw_io_write_buffer_callback)
(struct hw *me,
const void *source,
int space,
unsigned_word addr,
unsigned nr_bytes,
sim_cpu *processor,
sim_cia cia);
#define hw_io_write_buffer(hw, src, space, addr, nr_bytes, processor, cia) \
((hw)->to_io_write_buffer (hw, src, space, addr, nr_bytes, processor, cia))
/* Conversly, the device pci1000,1@1 may need to perform a dma transfer
into the cpu/memory core. Just as I/O moves towards the leaves,
dma transfers move towards the core via the initiating devices
parent nodes. The root device (special) converts the DMA transfer
into reads/writes to memory.
The SPACE:ADDR pair specify an address on the common bus connecting
the parent and child devices. */
typedef unsigned (hw_dma_read_buffer_callback)
(struct hw *bus,
void *dest,
int space,
unsigned_word addr,
unsigned nr_bytes);
#define hw_dma_read_buffer(bus, dest, space, addr, nr_bytes) \
((bus)->to_dma_read_buffer (bus, dest, space, addr, nr_bytes))
typedef unsigned (hw_dma_write_buffer_callback)
(struct hw *bus,
const void *source,
int space,
unsigned_word addr,
unsigned nr_bytes,
int violate_read_only_section);
#define hw_dma_write_buffer(bus, src, space, addr, nr_bytes, violate_ro) \
((bus)->to_dma_write_buffer (bus, src, space, addr, nr_bytes, violate_ro))
/* Address/size specs for devices are encoded following a convention
similar to that used by OpenFirmware. In particular, an
address/size is packed into a sequence of up to four cell words.
The number of words determined by the number of {address,size}
cells attributes of the device. */
typedef struct _hw_unit {
int nr_cells;
unsigned_cell cells[4]; /* unused cells are zero */
} hw_unit;
/* For the given bus, the number of address and size cells used in a
hw_unit. */
#define hw_unit_nr_address_cells(bus) ((bus)->nr_address_cells_of_hw_unit + 0)
#define hw_unit_nr_size_cells(bus) ((bus)->nr_size_cells_of_hw_unit + 0)
/* For the given device, its identifying hw_unit address.
Each device has an identifying hw_unit address. That address is
used when identifying one of a number of identical devices on a
common controller bus. ex fd0&fd1. */
const hw_unit *hw_unit_address
(struct hw *me);
/* Convert between a textual and the internal representation of a
hw_unit address/size.
NOTE: A device asks its parent to translate between a hw_unit and
textual representation. This is because the textual address of a
device is specified using the parent busses notation. */
typedef int (hw_unit_decode_callback)
(struct hw *bus,
const char *encoded,
hw_unit *unit);
#define hw_unit_decode(bus, encoded, unit) \
((bus)->to_unit_decode (bus, encoded, unit))
typedef int (hw_unit_encode_callback)
(struct hw *bus,
const hw_unit *unit,
char *encoded,
int sizeof_buf);
#define hw_unit_encode(bus, unit, encoded, sizeof_encoded) \
((bus)->to_unit_encode (bus, unit, encoded, sizeof_encoded))
/* As the bus that the device is attached too, to translate a devices
hw_unit address/size into a form suitable for an attach address
call.
Return a zero result if the address should be ignored when looking
for attach addresses. */
typedef int (hw_unit_address_to_attach_address_callback)
(struct hw *bus,
const hw_unit *unit_addr,
int *attach_space,
unsigned_word *attach_addr,
struct hw *client);
#define hw_unit_address_to_attach_address(bus, unit_addr, attach_space, attach_addr, client) \
((bus)->to_unit_address_to_attach_address (bus, unit_addr, attach_space, attach_addr, client))
typedef int (hw_unit_size_to_attach_size_callback)
(struct hw *bus,
const hw_unit *unit_size,
unsigned *attach_size,
struct hw *client);
#define hw_unit_size_to_attach_size(bus, unit_size, attach_size, client) \
((bus)->to_unit_size_to_attach_size (bus, unit_size, attach_size, client))
/* Utilities:
*/
/* IOCTL::
Often devices require `out of band' operations to be performed.
For instance a pal device may need to notify a PCI bridge device
that an interrupt ack cycle needs to be performed on the PCI bus.
Within PSIM such operations are performed by using the generic
ioctl call <<hw_ioctl()>>.
*/
typedef enum {
hw_ioctl_break, /* unsigned_word requested_break */
hw_ioctl_set_trace, /* void */
hw_ioctl_create_stack, /* unsigned_word *sp, char **argv, char **envp */
hw_ioctl_change_media, /* const char *new_image (possibly NULL) */
nr_hw_ioctl_requests,
} hw_ioctl_request;
typedef int (hw_ioctl_callback)
(struct hw *me,
sim_cpu *processor,
sim_cia cia,
hw_ioctl_request request,
va_list ap);
int hw_ioctl
(struct hw *me,
sim_cpu *processor,
sim_cia cia,
hw_ioctl_request request,
...);
/* Event queue:
Device specific versions of certain event handlers */
typedef struct _hw_event hw_event;
typedef void (hw_event_handler) (struct hw *me, void *data);
hw_event *hw_event_queue_schedule
(struct hw *me,
signed64 delta_time,
hw_event_handler *handler,
void *data);
void hw_event_queue_deschedule
(struct hw *me,
hw_event *event_to_remove);
signed64 hw_event_queue_time
(struct hw *me);
/* Error reporting::
So that errors originating from devices appear in a consistent
format, the <<hw_abort()>> function can be used. Formats and
outputs the error message before aborting the simulation
Devices should use this function to abort the simulation except
when the abort reason leaves the simulation in a hazardous
condition (for instance a failed malloc).
*/
void volatile NORETURN hw_abort
(struct hw *me,
const char *fmt,
...) __attribute__ ((format (printf, 2, 3)));
#define hw_trace_p(hw) ((hw)->trace_of_hw_p + 0)
/* Some of the related functions require specific types */
struct hw_property_data;
struct hw_port_data;
struct hw_base_data;
/* Finally the hardware device - keep your grubby little mits off of
these internals! :-) */
struct hw {
/* our relatives */
struct hw *parent_of_hw;
struct hw *sibling_of_hw;
struct hw *child_of_hw;
/* our identity */
const char *name_of_hw;
const char *family_of_hw;
const char *args_of_hw;
const char *path_of_hw;
/* our data */
void *data_of_hw;
/* hot links */
struct hw *root_of_hw;
SIM_DESC system_of_hw;
/* identifying data */
hw_unit unit_address_of_hw;
int nr_address_cells_of_hw_unit;
int nr_size_cells_of_hw_unit;
/* Soft reset */
hw_reset_callback *to_reset;
/* Basic callbacks */
hw_io_read_buffer_callback *to_io_read_buffer;
hw_io_write_buffer_callback *to_io_write_buffer;
hw_dma_read_buffer_callback *to_dma_read_buffer;
hw_dma_write_buffer_callback *to_dma_write_buffer;
hw_attach_address_callback *to_attach_address;
hw_detach_address_callback *to_detach_address;
/* More complicated callbacks */
hw_ioctl_callback *to_ioctl;
int trace_of_hw_p;
/* address callbacks */
hw_unit_decode_callback *to_unit_decode;
hw_unit_encode_callback *to_unit_encode;
hw_unit_address_to_attach_address_callback *to_unit_address_to_attach_address;
hw_unit_size_to_attach_size_callback *to_unit_size_to_attach_size;
/* related data */
struct hw_property_data *properties_of_hw;
struct hw_port_data *ports_of_hw;
struct hw_base_data *base_of_hw;
};
#endif