/* memory.c -- Generic memory model
|
/* memory.c -- Generic memory model
|
Copyright (C) 1999 Damjan Lampret, lampret@opencores.org
|
Copyright (C) 1999 Damjan Lampret, lampret@opencores.org
|
Copyright (C) 2005 György `nog' Jeney, nog@sdf.lonestar.org
|
Copyright (C) 2005 György `nog' Jeney, nog@sdf.lonestar.org
|
|
|
This file is part of OpenRISC 1000 Architectural Simulator.
|
This file is part of OpenRISC 1000 Architectural Simulator.
|
|
|
This program is free software; you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
the Free Software Foundation; either version 2 of the License, or
|
the Free Software Foundation; either version 2 of the License, or
|
(at your option) any later version.
|
(at your option) any later version.
|
|
|
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
GNU General Public License for more details.
|
GNU General Public License for more details.
|
|
|
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
along with this program; if not, write to the Free Software
|
along with this program; if not, write to the Free Software
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|
|
#include <stdio.h>
|
#include <stdio.h>
|
#include <time.h>
|
#include <time.h>
|
#include <string.h>
|
#include <string.h>
|
|
|
#include "config.h"
|
#include "config.h"
|
|
|
#ifdef HAVE_INTTYPES_H
|
#ifdef HAVE_INTTYPES_H
|
#include <inttypes.h>
|
#include <inttypes.h>
|
#endif
|
#endif
|
|
|
#include "port.h"
|
#include "port.h"
|
#include "arch.h"
|
#include "arch.h"
|
#include "abstract.h"
|
#include "abstract.h"
|
#include "sim-config.h"
|
#include "sim-config.h"
|
|
|
struct mem_config {
|
struct mem_config {
|
int ce; /* Which ce this memory is associated with */
|
int ce; /* Which ce this memory is associated with */
|
int mc; /* Which mc this memory is connected to */
|
int mc; /* Which mc this memory is connected to */
|
oraddr_t baseaddr; /* Start address of the memory */
|
oraddr_t baseaddr; /* Start address of the memory */
|
unsigned int size; /* Memory size */
|
unsigned int size; /* Memory size */
|
char *name; /* Memory type string */
|
char *name; /* Memory type string */
|
char *log; /* Memory log filename */
|
char *log; /* Memory log filename */
|
int delayr; /* Read cycles */
|
int delayr; /* Read cycles */
|
int delayw; /* Write cycles */
|
int delayw; /* Write cycles */
|
|
|
void *mem; /* malloced memory for this memory */
|
void *mem; /* malloced memory for this memory */
|
|
|
int pattern; /* A user specified memory initialization
|
int pattern; /* A user specified memory initialization
|
* pattern */
|
* pattern */
|
int random_seed; /* Initialize the memory with random values,
|
int random_seed; /* Initialize the memory with random values,
|
* starting with seed */
|
* starting with seed */
|
enum {
|
enum {
|
MT_UNKNOWN,
|
MT_UNKNOWN,
|
MT_PATTERN,
|
MT_PATTERN,
|
MT_RANDOM
|
MT_RANDOM
|
} type;
|
} type;
|
};
|
};
|
|
|
uint32_t simmem_read32(oraddr_t addr, void *dat)
|
uint32_t simmem_read32(oraddr_t addr, void *dat)
|
{
|
{
|
return *(uint32_t *)(dat + addr);
|
return *(uint32_t *)(dat + addr);
|
}
|
}
|
|
|
uint16_t simmem_read16(oraddr_t addr, void *dat)
|
uint16_t simmem_read16(oraddr_t addr, void *dat)
|
{
|
{
|
return *(uint16_t *)(dat + (addr ^ 2));
|
return *(uint16_t *)(dat + (addr ^ 2));
|
}
|
}
|
|
|
uint8_t simmem_read8(oraddr_t addr, void *dat)
|
uint8_t simmem_read8(oraddr_t addr, void *dat)
|
{
|
{
|
return *(uint8_t *)(dat + ((addr & ~ADDR_C(3)) | (3 - addr & 3)));
|
return *(uint8_t *)(dat + ((addr & ~ADDR_C(3)) | (3 - (addr & 3))));
|
}
|
}
|
|
|
void simmem_write32(oraddr_t addr, uint32_t value, void *dat)
|
void simmem_write32(oraddr_t addr, uint32_t value, void *dat)
|
{
|
{
|
*(uint32_t *)(dat + addr) = value;
|
*(uint32_t *)(dat + addr) = value;
|
}
|
}
|
|
|
void simmem_write16(oraddr_t addr, uint16_t value, void *dat)
|
void simmem_write16(oraddr_t addr, uint16_t value, void *dat)
|
{
|
{
|
*(uint16_t *)(dat + (addr ^ 2)) = value;
|
*(uint16_t *)(dat + (addr ^ 2)) = value;
|
}
|
}
|
|
|
void simmem_write8(oraddr_t addr, uint8_t value, void *dat)
|
void simmem_write8(oraddr_t addr, uint8_t value, void *dat)
|
{
|
{
|
*(uint8_t *)(dat + ((addr & ~ADDR_C(3)) | (3 - addr & 3))) = value;
|
*(uint8_t *)(dat + ((addr & ~ADDR_C(3)) | (3 - (addr & 3)))) = value;
|
}
|
}
|
|
|
uint32_t simmem_read_zero32(oraddr_t addr, void *dat)
|
uint32_t simmem_read_zero32(oraddr_t addr, void *dat)
|
{
|
{
|
if (config.sim.verbose)
|
if (config.sim.verbose)
|
fprintf (stderr, "WARNING: 32-bit memory read from non-read memory area 0x%"
|
fprintf (stderr, "WARNING: 32-bit memory read from non-read memory area 0x%"
|
PRIxADDR".\n", addr);
|
PRIxADDR".\n", addr);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
uint16_t simmem_read_zero16(oraddr_t addr, void *dat)
|
uint16_t simmem_read_zero16(oraddr_t addr, void *dat)
|
{
|
{
|
if (config.sim.verbose)
|
if (config.sim.verbose)
|
fprintf (stderr, "WARNING: 16-bit memory read from non-read memory area 0x%"
|
fprintf (stderr, "WARNING: 16-bit memory read from non-read memory area 0x%"
|
PRIxADDR".\n", addr);
|
PRIxADDR".\n", addr);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
uint8_t simmem_read_zero8(oraddr_t addr, void *dat)
|
uint8_t simmem_read_zero8(oraddr_t addr, void *dat)
|
{
|
{
|
if (config.sim.verbose)
|
if (config.sim.verbose)
|
fprintf (stderr, "WARNING: 8-bit memory read from non-read memory area 0x%"
|
fprintf (stderr, "WARNING: 8-bit memory read from non-read memory area 0x%"
|
PRIxADDR".\n", addr);
|
PRIxADDR".\n", addr);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
void simmem_write_null32(oraddr_t addr, uint32_t value, void *dat)
|
void simmem_write_null32(oraddr_t addr, uint32_t value, void *dat)
|
{
|
{
|
if (config.sim.verbose)
|
if (config.sim.verbose)
|
fprintf (stderr, "WARNING: 32-bit memory write to 0x%"PRIxADDR", non-write "
|
fprintf (stderr, "WARNING: 32-bit memory write to 0x%"PRIxADDR", non-write "
|
"memory area (value 0x%08"PRIx32").\n", addr, value);
|
"memory area (value 0x%08"PRIx32").\n", addr, value);
|
}
|
}
|
|
|
void simmem_write_null16(oraddr_t addr, uint16_t value, void *dat)
|
void simmem_write_null16(oraddr_t addr, uint16_t value, void *dat)
|
{
|
{
|
if (config.sim.verbose)
|
if (config.sim.verbose)
|
fprintf (stderr, "WARNING: 16-bit memory write to 0x%"PRIxADDR", non-write "
|
fprintf (stderr, "WARNING: 16-bit memory write to 0x%"PRIxADDR", non-write "
|
"memory area (value 0x%08"PRIx32").\n", addr, value);
|
"memory area (value 0x%08"PRIx32").\n", addr, value);
|
}
|
}
|
|
|
void simmem_write_null8(oraddr_t addr, uint8_t value, void *dat)
|
void simmem_write_null8(oraddr_t addr, uint8_t value, void *dat)
|
{
|
{
|
if (config.sim.verbose)
|
if (config.sim.verbose)
|
fprintf (stderr, "WARNING: 8-bit memory write to 0x%"PRIxADDR", non-write "
|
fprintf (stderr, "WARNING: 8-bit memory write to 0x%"PRIxADDR", non-write "
|
"memory area (value 0x%08"PRIx32").\n", addr, value);
|
"memory area (value 0x%08"PRIx32").\n", addr, value);
|
}
|
}
|
|
|
void mem_reset(void *dat)
|
void mem_reset(void *dat)
|
{
|
{
|
struct mem_config *mem = dat;
|
struct mem_config *mem = dat;
|
int seed;
|
int seed;
|
int i;
|
int i;
|
uint8_t *mem_area = mem->mem;
|
uint8_t *mem_area = mem->mem;
|
|
|
/* Initialize memory */
|
/* Initialize memory */
|
switch(mem->type) {
|
switch(mem->type) {
|
case MT_RANDOM:
|
case MT_RANDOM:
|
if (mem->random_seed == -1) {
|
if (mem->random_seed == -1) {
|
seed = time(NULL);
|
seed = time(NULL);
|
/* Print out the seed just in case we ever need to debug */
|
/* Print out the seed just in case we ever need to debug */
|
PRINTF("Seeding random generator with value %d\n", seed);
|
PRINTF("Seeding random generator with value %d\n", seed);
|
} else
|
} else
|
seed = mem->random_seed;
|
seed = mem->random_seed;
|
srandom(seed);
|
srandom(seed);
|
|
|
for(i = 0; i < mem->size; i++, mem_area++)
|
for(i = 0; i < mem->size; i++, mem_area++)
|
*mem_area = random() & 0xFF;
|
*mem_area = random() & 0xFF;
|
break;
|
break;
|
case MT_PATTERN:
|
case MT_PATTERN:
|
for(i = 0; i < mem->size; i++, mem_area++)
|
for(i = 0; i < mem->size; i++, mem_area++)
|
*mem_area = mem->pattern;
|
*mem_area = mem->pattern;
|
break;
|
break;
|
case MT_UNKNOWN:
|
case MT_UNKNOWN:
|
break;
|
break;
|
default:
|
default:
|
fprintf(stderr, "Invalid memory configuration type.\n");
|
fprintf(stderr, "Invalid memory configuration type.\n");
|
exit(1);
|
exit(1);
|
}
|
}
|
}
|
}
|
|
|
/*-------------------------------------------------[ Memory configuration ]---*/
|
/*-------------------------------------------------[ Memory configuration ]---*/
|
void memory_random_seed(union param_val val, void *dat)
|
void memory_random_seed(union param_val val, void *dat)
|
{
|
{
|
struct mem_config *mem = dat;
|
struct mem_config *mem = dat;
|
mem->random_seed = val.int_val;
|
mem->random_seed = val.int_val;
|
}
|
}
|
|
|
void memory_pattern(union param_val val, void *dat)
|
void memory_pattern(union param_val val, void *dat)
|
{
|
{
|
struct mem_config *mem = dat;
|
struct mem_config *mem = dat;
|
mem->pattern = val.int_val;
|
mem->pattern = val.int_val;
|
}
|
}
|
|
|
void memory_type(union param_val val, void *dat)
|
void memory_type(union param_val val, void *dat)
|
{
|
{
|
struct mem_config *mem = dat;
|
struct mem_config *mem = dat;
|
if(!strcmp(val.str_val, "unknown"))
|
if(!strcmp(val.str_val, "unknown"))
|
mem->type = MT_UNKNOWN;
|
mem->type = MT_UNKNOWN;
|
else if(!strcmp (val.str_val, "random"))
|
else if(!strcmp (val.str_val, "random"))
|
mem->type = MT_RANDOM;
|
mem->type = MT_RANDOM;
|
else if(!strcmp (val.str_val, "pattern"))
|
else if(!strcmp (val.str_val, "pattern"))
|
mem->type = MT_PATTERN;
|
mem->type = MT_PATTERN;
|
else if(!strcmp (val.str_val, "zero")) {
|
else if(!strcmp (val.str_val, "zero")) {
|
mem->type = MT_PATTERN;
|
mem->type = MT_PATTERN;
|
mem->pattern = 0;
|
mem->pattern = 0;
|
} else {
|
} else {
|
char tmp[200];
|
char tmp[200];
|
sprintf (tmp, "invalid memory type '%s'.\n", val.str_val);
|
sprintf (tmp, "invalid memory type '%s'.\n", val.str_val);
|
CONFIG_ERROR(tmp);
|
CONFIG_ERROR(tmp);
|
}
|
}
|
}
|
}
|
|
|
void memory_ce(union param_val val, void *dat)
|
void memory_ce(union param_val val, void *dat)
|
{
|
{
|
struct mem_config *mem = dat;
|
struct mem_config *mem = dat;
|
mem->ce = val.int_val;
|
mem->ce = val.int_val;
|
}
|
}
|
|
|
void memory_mc(union param_val val, void *dat)
|
void memory_mc(union param_val val, void *dat)
|
{
|
{
|
struct mem_config *mem = dat;
|
struct mem_config *mem = dat;
|
mem->mc = val.int_val;
|
mem->mc = val.int_val;
|
}
|
}
|
|
|
void memory_baseaddr(union param_val val, void *dat)
|
void memory_baseaddr(union param_val val, void *dat)
|
{
|
{
|
struct mem_config *mem = dat;
|
struct mem_config *mem = dat;
|
mem->baseaddr = val.addr_val;
|
mem->baseaddr = val.addr_val;
|
}
|
}
|
|
|
void memory_size(union param_val val, void *dat)
|
void memory_size(union param_val val, void *dat)
|
{
|
{
|
struct mem_config *mem = dat;
|
struct mem_config *mem = dat;
|
mem->size = val.int_val;
|
mem->size = val.int_val;
|
}
|
}
|
|
|
/* FIXME: Check use */
|
/* FIXME: Check use */
|
void memory_name(union param_val val, void *dat)
|
void memory_name(union param_val val, void *dat)
|
{
|
{
|
struct mem_config *mem = dat;
|
struct mem_config *mem = dat;
|
mem->name = strdup(val.str_val);
|
mem->name = strdup(val.str_val);
|
}
|
}
|
|
|
void memory_log(union param_val val, void *dat)
|
void memory_log(union param_val val, void *dat)
|
{
|
{
|
struct mem_config *mem = dat;
|
struct mem_config *mem = dat;
|
mem->log = strdup(val.str_val);
|
mem->log = strdup(val.str_val);
|
}
|
}
|
|
|
void memory_delayr(union param_val val, void *dat)
|
void memory_delayr(union param_val val, void *dat)
|
{
|
{
|
struct mem_config *mem = dat;
|
struct mem_config *mem = dat;
|
mem->delayr = val.int_val;
|
mem->delayr = val.int_val;
|
}
|
}
|
|
|
void memory_delayw(union param_val val, void *dat)
|
void memory_delayw(union param_val val, void *dat)
|
{
|
{
|
struct mem_config *mem = dat;
|
struct mem_config *mem = dat;
|
mem->delayw = val.int_val;
|
mem->delayw = val.int_val;
|
}
|
}
|
|
|
void *memory_sec_start(void)
|
void *memory_sec_start(void)
|
{
|
{
|
struct mem_config *mem = malloc(sizeof(struct mem_config));
|
struct mem_config *mem = malloc(sizeof(struct mem_config));
|
|
|
if(!mem) {
|
if(!mem) {
|
fprintf(stderr, "Memory Peripheral: Run out of memory\n");
|
fprintf(stderr, "Memory Peripheral: Run out of memory\n");
|
exit(-1);
|
exit(-1);
|
}
|
}
|
mem->size = 0;
|
mem->size = 0;
|
mem->log = NULL;
|
mem->log = NULL;
|
mem->name = NULL;
|
mem->name = NULL;
|
mem->delayr = 1;
|
mem->delayr = 1;
|
mem->delayw = 1;
|
mem->delayw = 1;
|
mem->random_seed = -1;
|
mem->random_seed = -1;
|
mem->ce = -1;
|
mem->ce = -1;
|
mem->mc = 0;
|
mem->mc = 0;
|
|
|
return mem;
|
return mem;
|
}
|
}
|
|
|
void memory_sec_end(void *dat)
|
void memory_sec_end(void *dat)
|
{
|
{
|
struct mem_config *mem = dat;
|
struct mem_config *mem = dat;
|
struct dev_memarea *mema;
|
struct dev_memarea *mema;
|
|
|
struct mem_ops ops;
|
struct mem_ops ops;
|
|
|
if(!mem->size) {
|
if(!mem->size) {
|
free(dat);
|
free(dat);
|
return;
|
return;
|
}
|
}
|
|
|
/* Round up to the next 32-bit boundry */
|
/* Round up to the next 32-bit boundry */
|
if(mem->size & 3) {
|
if(mem->size & 3) {
|
mem->size &= ~3;
|
mem->size &= ~3;
|
mem->size += 4;
|
mem->size += 4;
|
}
|
}
|
|
|
if(!(mem->mem = malloc(mem->size))) {
|
if(!(mem->mem = malloc(mem->size))) {
|
fprintf(stderr, "Unable to allocate memory at %"PRIxADDR", length %i\n",
|
fprintf(stderr, "Unable to allocate memory at %"PRIxADDR", length %i\n",
|
mem->baseaddr, mem->size);
|
mem->baseaddr, mem->size);
|
exit(-1);
|
exit(-1);
|
}
|
}
|
|
|
if(mem->delayr > 0) {
|
if(mem->delayr > 0) {
|
ops.readfunc32 = simmem_read32;
|
ops.readfunc32 = simmem_read32;
|
ops.readfunc16 = simmem_read16;
|
ops.readfunc16 = simmem_read16;
|
ops.readfunc8 = simmem_read8;
|
ops.readfunc8 = simmem_read8;
|
} else {
|
} else {
|
ops.readfunc32 = simmem_read_zero32;
|
ops.readfunc32 = simmem_read_zero32;
|
ops.readfunc16 = simmem_read_zero16;
|
ops.readfunc16 = simmem_read_zero16;
|
ops.readfunc8 = simmem_read_zero8;
|
ops.readfunc8 = simmem_read_zero8;
|
}
|
}
|
|
|
if(mem->delayw > 0) {
|
if(mem->delayw > 0) {
|
ops.writefunc32 = simmem_write32;
|
ops.writefunc32 = simmem_write32;
|
ops.writefunc16 = simmem_write16;
|
ops.writefunc16 = simmem_write16;
|
ops.writefunc8 = simmem_write8;
|
ops.writefunc8 = simmem_write8;
|
} else {
|
} else {
|
ops.writefunc32 = simmem_write_null32;
|
ops.writefunc32 = simmem_write_null32;
|
ops.writefunc16 = simmem_write_null16;
|
ops.writefunc16 = simmem_write_null16;
|
ops.writefunc8 = simmem_write_null8;
|
ops.writefunc8 = simmem_write_null8;
|
}
|
}
|
|
|
ops.writeprog8 = simmem_write8;
|
ops.writeprog8 = simmem_write8;
|
ops.writeprog32 = simmem_write32;
|
ops.writeprog32 = simmem_write32;
|
ops.writeprog8_dat = mem->mem;
|
ops.writeprog8_dat = mem->mem;
|
ops.writeprog32_dat = mem->mem;
|
ops.writeprog32_dat = mem->mem;
|
|
|
ops.read_dat32 = mem->mem;
|
ops.read_dat32 = mem->mem;
|
ops.read_dat16 = mem->mem;
|
ops.read_dat16 = mem->mem;
|
ops.read_dat8 = mem->mem;
|
ops.read_dat8 = mem->mem;
|
|
|
ops.write_dat32 = mem->mem;
|
ops.write_dat32 = mem->mem;
|
ops.write_dat16 = mem->mem;
|
ops.write_dat16 = mem->mem;
|
ops.write_dat8 = mem->mem;
|
ops.write_dat8 = mem->mem;
|
|
|
ops.delayr = mem->delayr;
|
ops.delayr = mem->delayr;
|
ops.delayw = mem->delayw;
|
ops.delayw = mem->delayw;
|
|
|
ops.log = mem->log;
|
ops.log = mem->log;
|
|
|
mema = reg_mem_area(mem->baseaddr, mem->size, 0, &ops);
|
mema = reg_mem_area(mem->baseaddr, mem->size, 0, &ops);
|
|
|
/* Set valid */
|
/* Set valid */
|
/* FIXME: Should this be done during reset? */
|
/* FIXME: Should this be done during reset? */
|
set_mem_valid(mema, 1);
|
set_mem_valid(mema, 1);
|
|
|
if(mem->ce >= 0)
|
if(mem->ce >= 0)
|
mc_reg_mem_area(mema, mem->ce, mem->mc);
|
mc_reg_mem_area(mema, mem->ce, mem->mc);
|
|
|
reg_sim_reset(mem_reset, dat);
|
reg_sim_reset(mem_reset, dat);
|
}
|
}
|
|
|
void reg_memory_sec(void)
|
void reg_memory_sec(void)
|
{
|
{
|
struct config_section *sec = reg_config_sec("memory", memory_sec_start,
|
struct config_section *sec = reg_config_sec("memory", memory_sec_start,
|
memory_sec_end);
|
memory_sec_end);
|
|
|
reg_config_param(sec, "random_seed", paramt_int, memory_random_seed);
|
reg_config_param(sec, "random_seed", paramt_int, memory_random_seed);
|
reg_config_param(sec, "pattern", paramt_int, memory_pattern);
|
reg_config_param(sec, "pattern", paramt_int, memory_pattern);
|
reg_config_param(sec, "type", paramt_word, memory_type);
|
reg_config_param(sec, "type", paramt_word, memory_type);
|
reg_config_param(sec, "ce", paramt_int, memory_ce);
|
reg_config_param(sec, "ce", paramt_int, memory_ce);
|
reg_config_param(sec, "mc", paramt_int, memory_mc);
|
reg_config_param(sec, "mc", paramt_int, memory_mc);
|
reg_config_param(sec, "baseaddr", paramt_addr, memory_baseaddr);
|
reg_config_param(sec, "baseaddr", paramt_addr, memory_baseaddr);
|
reg_config_param(sec, "size", paramt_int, memory_size);
|
reg_config_param(sec, "size", paramt_int, memory_size);
|
reg_config_param(sec, "name", paramt_str, memory_name);
|
reg_config_param(sec, "name", paramt_str, memory_name);
|
reg_config_param(sec, "log", paramt_str, memory_log);
|
reg_config_param(sec, "log", paramt_str, memory_log);
|
reg_config_param(sec, "delayr", paramt_int, memory_delayr);
|
reg_config_param(sec, "delayr", paramt_int, memory_delayr);
|
reg_config_param(sec, "delayw", paramt_int, memory_delayw);
|
reg_config_param(sec, "delayw", paramt_int, memory_delayw);
|
}
|
}
|
|
|
|
|