1995-08-23 23:06:36 +02:00
|
|
|
/* This file is part of the program psim.
|
|
|
|
|
|
|
|
Copyright (C) 1994-1995, 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 _CORE_C_
|
|
|
|
#define _CORE_C_
|
|
|
|
|
|
|
|
#ifndef STATIC_INLINE_CORE
|
|
|
|
#define STATIC_INLINE_CORE STATIC_INLINE
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "basics.h"
|
|
|
|
#include "device_tree.h"
|
|
|
|
#include "memory_map.h"
|
|
|
|
#include "core.h"
|
|
|
|
|
|
|
|
|
|
|
|
struct _core {
|
|
|
|
/* attached devices */
|
|
|
|
device_node *device_tree;
|
|
|
|
/* different memory maps */
|
|
|
|
memory_map *readable; /* really everything */
|
|
|
|
memory_map *writeable;
|
|
|
|
memory_map *executable;
|
|
|
|
/* VEA model requires additional memory information */
|
|
|
|
unsigned_word data_upper_bound;
|
|
|
|
unsigned_word data_high_water;
|
|
|
|
unsigned_word stack_upper_bound;
|
|
|
|
unsigned_word stack_lower_bound;
|
|
|
|
unsigned_word stack_low_water;
|
|
|
|
/* misc */
|
|
|
|
int trace;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
STATIC_INLINE_CORE void
|
|
|
|
create_core_from_addresses(device_node *device,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
core *memory = (core*)data;
|
|
|
|
device_address *address;
|
|
|
|
for (address = device->addresses;
|
|
|
|
address != NULL;
|
|
|
|
address = address->next_address) {
|
|
|
|
switch (device->type) {
|
|
|
|
case memory_device:
|
|
|
|
{
|
|
|
|
void *ram = zalloc(address->size);
|
|
|
|
TRACE(trace_core,
|
|
|
|
("create_core_from_addresses() adding memory at 0x%.8x-0x%.8x, size %8d\n",
|
|
|
|
address->lower_bound, address->lower_bound + address->size - 1, address->size));
|
|
|
|
core_add_raw_memory(memory,
|
|
|
|
ram,
|
|
|
|
address->lower_bound,
|
|
|
|
address->size,
|
|
|
|
address->access);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case sequential_device:
|
|
|
|
case block_device:
|
|
|
|
case bus_device:
|
|
|
|
case other_device:
|
|
|
|
{
|
|
|
|
TRACE(trace_core,
|
|
|
|
("create_core_from_addresses() adding device at 0x%.8x-0x%.8x, size %8d\n",
|
|
|
|
address->lower_bound, address->lower_bound + address->size - 1, address->size));
|
|
|
|
ASSERT(device->callbacks != NULL);
|
|
|
|
core_add_callback_memory(memory,
|
|
|
|
device,
|
|
|
|
device->callbacks->read_callback,
|
|
|
|
device->callbacks->write_callback,
|
|
|
|
address->lower_bound,
|
|
|
|
address->size,
|
|
|
|
address->access);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
TRACE(trace_core,
|
|
|
|
("create_core_from_addresses() unknown type %d\n", (int)device->type));
|
|
|
|
break;
|
|
|
|
/* nothing happens here */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
INLINE_CORE core *
|
|
|
|
core_create(device_node *root,
|
|
|
|
int trace)
|
|
|
|
{
|
|
|
|
core *memory;
|
|
|
|
|
|
|
|
/* Initialize things */
|
|
|
|
memory = ZALLOC(core);
|
|
|
|
memory->trace = trace;
|
|
|
|
memory->device_tree = root;
|
|
|
|
|
|
|
|
/* allocate space for the separate virtual to physical maps */
|
|
|
|
memory->executable = new_memory_map();
|
|
|
|
memory->readable = new_memory_map();
|
|
|
|
memory->writeable = new_memory_map();
|
|
|
|
|
|
|
|
/* initial values for the water marks */
|
|
|
|
memory->data_high_water = 0;
|
|
|
|
memory->stack_low_water = memory->data_high_water - sizeof(unsigned_word);
|
|
|
|
|
|
|
|
/* go over the device tree looking for address ranges to add to
|
|
|
|
memory */
|
|
|
|
device_tree_traverse(root,
|
|
|
|
create_core_from_addresses,
|
|
|
|
NULL,
|
|
|
|
memory);
|
|
|
|
|
|
|
|
/* return the created core object */
|
|
|
|
return memory;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
STATIC_INLINE_CORE void
|
|
|
|
zero_core_from_addresses(device_node *device,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
core *memory = (core*)data;
|
|
|
|
device_address *address;
|
|
|
|
|
|
|
|
/* for memory nodes, copy or zero any data */
|
|
|
|
if (device->type == memory_device) {
|
|
|
|
for (address = device->addresses;
|
|
|
|
address != NULL;
|
|
|
|
address = address->next_address) {
|
|
|
|
if (memory_map_zero(memory->readable,
|
|
|
|
address->lower_bound,
|
|
|
|
address->size) != address->size)
|
|
|
|
error("init_core_from_addresses() - zero failed\n");
|
|
|
|
/* adjust high water mark (sbrk) */
|
|
|
|
if (memory->data_upper_bound < address->upper_bound)
|
|
|
|
memory->data_upper_bound = address->upper_bound;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
STATIC_INLINE_CORE void
|
|
|
|
load_core_from_addresses(device_node *device,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
core *memory = (core*)data;
|
|
|
|
device_address *address;
|
|
|
|
|
|
|
|
/* initialize the address range with the value attached to the
|
|
|
|
address. Even works for devices! */
|
|
|
|
for (address = device->addresses;
|
|
|
|
address != NULL;
|
|
|
|
address = address->next_address) {
|
|
|
|
/* (re)init the address range. I don't want to think about what
|
|
|
|
this is doing to callback devices! */
|
|
|
|
if (address->init) {
|
|
|
|
if (memory_map_write_buffer(memory->readable,
|
|
|
|
address->init,
|
|
|
|
address->lower_bound,
|
|
|
|
address->size,
|
|
|
|
raw_transfer) != address->size)
|
|
|
|
error("init_core_from_addresses() - write failed\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
INLINE_CORE void
|
|
|
|
core_init(core *memory)
|
|
|
|
{
|
|
|
|
unsigned nr_cleared;
|
|
|
|
unsigned_word clear_base;
|
|
|
|
unsigned_word clear_bound;
|
|
|
|
|
|
|
|
/* for vea, several memory break points */
|
|
|
|
memory->data_upper_bound = 0;
|
|
|
|
memory->stack_upper_bound = device_tree_find_int(memory->device_tree,
|
|
|
|
"/options/stack-pointer");;
|
|
|
|
memory->stack_lower_bound = memory->stack_upper_bound;
|
|
|
|
|
|
|
|
/* (re) clear all of memory that is specified by memory-address
|
|
|
|
entries. While we're at it determine the upper bound for memory
|
|
|
|
areas */
|
|
|
|
device_tree_traverse(memory->device_tree,
|
|
|
|
NULL,
|
|
|
|
zero_core_from_addresses,
|
|
|
|
memory);
|
|
|
|
|
|
|
|
/* May have grown the data sectioin (vea model), zero that too if
|
|
|
|
present */
|
|
|
|
clear_base = memory->data_upper_bound;
|
|
|
|
clear_bound = memory->data_high_water;
|
|
|
|
if (clear_bound > clear_base) {
|
|
|
|
while ((nr_cleared = memory_map_zero(memory->readable,
|
|
|
|
clear_base,
|
|
|
|
clear_bound - clear_base)) > 0) {
|
|
|
|
clear_base += nr_cleared;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* clear any part of the stack that was dynamically allocated */
|
|
|
|
clear_base = memory->stack_low_water;
|
|
|
|
clear_bound = memory->stack_upper_bound;
|
|
|
|
if (clear_bound > clear_base) {
|
|
|
|
while ((nr_cleared = memory_map_zero(memory->readable,
|
|
|
|
clear_base,
|
|
|
|
clear_bound - clear_base)) > 0) {
|
|
|
|
clear_base += nr_cleared;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* with everything zero'ed, now (re) load any data sections */
|
|
|
|
device_tree_traverse(memory->device_tree,
|
|
|
|
NULL,
|
|
|
|
load_core_from_addresses,
|
|
|
|
memory);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
INLINE_CORE void
|
|
|
|
core_add_raw_memory(core *memory,
|
|
|
|
void *buffer,
|
|
|
|
unsigned_word base,
|
|
|
|
unsigned size,
|
|
|
|
device_access access)
|
|
|
|
{
|
|
|
|
if (access & device_is_readable)
|
|
|
|
memory_map_add_raw_memory(memory->readable,
|
|
|
|
buffer, base, size);
|
|
|
|
if (access & device_is_writeable)
|
|
|
|
memory_map_add_raw_memory(memory->writeable,
|
|
|
|
buffer, base, size);
|
|
|
|
if (access & device_is_executable)
|
|
|
|
memory_map_add_raw_memory(memory->executable,
|
|
|
|
buffer, base, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
INLINE_CORE void
|
|
|
|
core_add_callback_memory(core *memory,
|
|
|
|
device_node *device,
|
|
|
|
device_reader_callback *reader,
|
|
|
|
device_writer_callback *writer,
|
|
|
|
unsigned_word base,
|
|
|
|
unsigned size,
|
|
|
|
device_access access)
|
|
|
|
{
|
|
|
|
if (access & device_is_readable)
|
|
|
|
memory_map_add_callback_memory(memory->readable,
|
|
|
|
device, reader, writer,
|
|
|
|
base, size);
|
|
|
|
if (access & device_is_writeable)
|
|
|
|
memory_map_add_callback_memory(memory->writeable,
|
|
|
|
device, reader, writer,
|
|
|
|
base, size);
|
|
|
|
if (access & device_is_executable)
|
|
|
|
memory_map_add_callback_memory(memory->executable,
|
|
|
|
device, reader, writer,
|
|
|
|
base, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
STATIC_INLINE_CORE void
|
|
|
|
malloc_core_memory(core *memory,
|
|
|
|
unsigned_word base,
|
|
|
|
unsigned size,
|
|
|
|
device_access access)
|
|
|
|
{
|
|
|
|
void *buffer = (void*)zalloc(size);
|
|
|
|
core_add_raw_memory(memory, buffer, base, size, access);
|
|
|
|
}
|
|
|
|
|
|
|
|
INLINE_CORE unsigned_word
|
|
|
|
core_data_upper_bound(core *memory)
|
|
|
|
{
|
|
|
|
return memory->data_upper_bound;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
INLINE_CORE unsigned_word
|
|
|
|
core_stack_lower_bound(core *memory)
|
|
|
|
{
|
|
|
|
return memory->stack_lower_bound;
|
|
|
|
}
|
|
|
|
|
|
|
|
INLINE_CORE unsigned_word
|
|
|
|
core_stack_size(core *memory)
|
|
|
|
{
|
|
|
|
return (memory->stack_upper_bound - memory->stack_lower_bound);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
INLINE_CORE void
|
|
|
|
core_add_data(core *memory, unsigned_word incr)
|
|
|
|
{
|
1995-09-06 16:00:16 +02:00
|
|
|
unsigned_word new_upper_bound = memory->data_upper_bound + incr;
|
|
|
|
if (new_upper_bound > memory->data_high_water) {
|
|
|
|
if (memory->data_upper_bound >= memory->data_high_water)
|
|
|
|
/* all the memory is new */
|
|
|
|
malloc_core_memory(memory,
|
|
|
|
memory->data_upper_bound,
|
|
|
|
incr,
|
|
|
|
device_is_readable | device_is_writeable);
|
|
|
|
else
|
|
|
|
/* some of the memory was already allocated, only need to add
|
|
|
|
missing bit */
|
|
|
|
malloc_core_memory(memory,
|
|
|
|
memory->data_high_water,
|
|
|
|
new_upper_bound - memory->data_high_water,
|
|
|
|
device_is_readable | device_is_writeable);
|
|
|
|
memory->data_high_water = new_upper_bound;
|
1995-08-23 23:06:36 +02:00
|
|
|
}
|
1995-09-06 16:00:16 +02:00
|
|
|
memory->data_upper_bound = new_upper_bound;
|
1995-08-23 23:06:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
INLINE_CORE void
|
|
|
|
core_add_stack(core *memory, unsigned_word incr)
|
|
|
|
{
|
1995-09-06 16:00:16 +02:00
|
|
|
unsigned_word new_lower_bound = memory->stack_lower_bound - incr;
|
|
|
|
if (new_lower_bound < memory->stack_low_water) {
|
|
|
|
if (memory->stack_lower_bound <= memory->stack_low_water)
|
|
|
|
/* all the memory is new */
|
|
|
|
malloc_core_memory(memory,
|
|
|
|
new_lower_bound,
|
|
|
|
incr,
|
|
|
|
device_is_readable | device_is_writeable);
|
|
|
|
else
|
|
|
|
/* allocate only the extra bit */
|
|
|
|
malloc_core_memory(memory,
|
|
|
|
new_lower_bound,
|
|
|
|
memory->stack_low_water - new_lower_bound,
|
|
|
|
device_is_readable | device_is_writeable);
|
|
|
|
memory->stack_low_water = new_lower_bound;
|
1995-08-23 23:06:36 +02:00
|
|
|
}
|
1995-09-06 16:00:16 +02:00
|
|
|
memory->stack_lower_bound = new_lower_bound;
|
1995-08-23 23:06:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
INLINE_CORE memory_map *
|
|
|
|
core_readable(core *core)
|
|
|
|
{
|
|
|
|
return core->readable;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
INLINE_CORE memory_map *
|
|
|
|
core_writeable(core *core)
|
|
|
|
{
|
|
|
|
return core->writeable;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
INLINE_CORE memory_map *
|
|
|
|
core_executable(core *core)
|
|
|
|
{
|
|
|
|
return core->executable;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* _CORE_ */
|