forked from cores/microwatt
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
259 lines
4.9 KiB
C
259 lines
4.9 KiB
C
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#define DEBUG
|
|
|
|
#define ALIGN_UP(VAL, SIZE) (((VAL) + ((SIZE)-1)) & ~((SIZE)-1))
|
|
|
|
#define vhpi0 2 /* forcing 0 */
|
|
#define vhpi1 3 /* forcing 1 */
|
|
|
|
struct int_bounds
|
|
{
|
|
int left;
|
|
int right;
|
|
char dir;
|
|
unsigned int len;
|
|
};
|
|
|
|
struct fat_pointer
|
|
{
|
|
void *base;
|
|
struct int_bounds *bounds;
|
|
};
|
|
|
|
static char *from_string(void *__p)
|
|
{
|
|
struct fat_pointer *p = __p;
|
|
unsigned long len = p->bounds->len;
|
|
char *m;
|
|
|
|
m = malloc(len+1);
|
|
if (!m) {
|
|
perror("malloc");
|
|
exit(1);
|
|
}
|
|
|
|
memcpy(m, p->base, len);
|
|
m[len] = 0x0;
|
|
|
|
return m;
|
|
}
|
|
|
|
static uint64_t from_std_logic_vector(unsigned char *p, unsigned long len)
|
|
{
|
|
unsigned long ret = 0;
|
|
|
|
if (len > 64) {
|
|
fprintf(stderr, "%s: invalid length %lu\n", __func__, len);
|
|
exit(1);
|
|
}
|
|
|
|
for (unsigned long i = 0; i < len; i++) {
|
|
unsigned char bit;
|
|
|
|
if (*p == vhpi0) {
|
|
bit = 0;
|
|
} else if (*p == vhpi1) {
|
|
bit = 1;
|
|
} else {
|
|
fprintf(stderr, "%s: bad bit %d\n", __func__, *p);
|
|
bit = 0;
|
|
}
|
|
|
|
ret = (ret << 1) | bit;
|
|
p++;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void to_std_logic_vector(unsigned long val, unsigned char *p,
|
|
unsigned long len)
|
|
{
|
|
if (len > 64) {
|
|
fprintf(stderr, "%s: invalid length %lu\n", __func__, len);
|
|
exit(1);
|
|
}
|
|
|
|
for (unsigned long i = 0; i < len; i++) {
|
|
if ((val >> (len-1-i) & 1))
|
|
*p = vhpi1;
|
|
else
|
|
*p = vhpi0;
|
|
|
|
p++;
|
|
}
|
|
}
|
|
|
|
#define MAX_REGIONS 128
|
|
|
|
struct ram_behavioural {
|
|
char *filename;
|
|
unsigned long size;
|
|
void *m;
|
|
};
|
|
|
|
static struct ram_behavioural behavioural_regions[MAX_REGIONS];
|
|
static unsigned long region_nr;
|
|
|
|
unsigned long behavioural_initialize(void *__f, unsigned long size)
|
|
{
|
|
struct ram_behavioural *r;
|
|
int fd;
|
|
struct stat buf;
|
|
unsigned long tmp_size;
|
|
void *mem;
|
|
|
|
if (region_nr == MAX_REGIONS) {
|
|
fprintf(stderr, "%s: too many regions, bump MAX_REGIONS\n", __func__);
|
|
exit(1);
|
|
}
|
|
|
|
r = &behavioural_regions[region_nr];
|
|
|
|
r->filename = from_string(__f);
|
|
r->size = ALIGN_UP(size, getpagesize());
|
|
|
|
fd = open(r->filename, O_RDWR);
|
|
if (fd == -1) {
|
|
fprintf(stderr, "%s: could not open %s\n", __func__,
|
|
r->filename);
|
|
exit(1);
|
|
}
|
|
|
|
if (fstat(fd, &buf)) {
|
|
perror("fstat");
|
|
exit(1);
|
|
}
|
|
|
|
/* XXX Do we need to truncate the underlying file? */
|
|
tmp_size = ALIGN_UP(buf.st_size, getpagesize());
|
|
|
|
if (r->size > tmp_size) {
|
|
void *m;
|
|
|
|
/*
|
|
* We have to pad the file. Allocate the total size, then
|
|
* create a space for the file.
|
|
*/
|
|
mem = mmap(NULL, r->size, PROT_READ|PROT_WRITE,
|
|
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
|
if (mem == MAP_FAILED) {
|
|
perror("mmap");
|
|
exit(1);
|
|
}
|
|
|
|
if (tmp_size) {
|
|
munmap(mem, tmp_size);
|
|
|
|
m = mmap(mem, tmp_size, PROT_READ|PROT_WRITE,
|
|
MAP_PRIVATE|MAP_FIXED, fd, 0);
|
|
if (m == MAP_FAILED) {
|
|
perror("mmap");
|
|
exit(1);
|
|
}
|
|
if (m != mem) {
|
|
fprintf(stderr, "%s: mmap(MAP_FIXED) failed\n",
|
|
__func__);
|
|
exit(1);
|
|
}
|
|
}
|
|
} else {
|
|
mem = mmap(NULL, tmp_size, PROT_READ|PROT_WRITE, MAP_PRIVATE,
|
|
fd, 0);
|
|
if (mem == MAP_FAILED) {
|
|
perror("mmap");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
behavioural_regions[region_nr].m = mem;
|
|
return region_nr++;
|
|
}
|
|
|
|
void behavioural_read(unsigned char *__val, unsigned char *__addr,
|
|
unsigned long sel, int identifier)
|
|
{
|
|
struct ram_behavioural *r;
|
|
unsigned long val = 0;
|
|
unsigned long addr = from_std_logic_vector(__addr, 64);
|
|
unsigned char *p;
|
|
|
|
if (identifier > region_nr) {
|
|
fprintf(stderr, "%s: bad index %d\n", __func__, identifier);
|
|
exit(1);
|
|
}
|
|
|
|
r = &behavioural_regions[identifier];
|
|
|
|
for (unsigned long i = 0; i < 8; i++) {
|
|
#if 0
|
|
/* sel only used on writes */
|
|
if (!(sel & (1UL << i)))
|
|
continue;
|
|
#endif
|
|
|
|
if ((addr + i) > r->size) {
|
|
fprintf(stderr, "%s: bad memory access %lx %lx\n", __func__,
|
|
addr+i, r->size);
|
|
exit(1);
|
|
}
|
|
|
|
p = (unsigned char *)(((unsigned long)r->m) + addr + i);
|
|
val |= (((unsigned long)*p) << (i*8));
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("MEM behave %d read %016lx addr %016lx sel %02lx\n", identifier, val,
|
|
addr, sel);
|
|
#endif
|
|
|
|
to_std_logic_vector(val, __val, 64);
|
|
}
|
|
|
|
void behavioural_write(unsigned char *__val, unsigned char *__addr,
|
|
unsigned int sel, int identifier)
|
|
{
|
|
struct ram_behavioural *r;
|
|
unsigned long val = from_std_logic_vector(__val, 64);
|
|
unsigned long addr = from_std_logic_vector(__addr, 64);
|
|
unsigned char *p;
|
|
|
|
if (identifier > region_nr) {
|
|
fprintf(stderr, "%s: bad index %d\n", __func__, identifier);
|
|
exit(1);
|
|
}
|
|
|
|
r = &behavioural_regions[identifier];
|
|
|
|
p = (unsigned char *)(((unsigned long)r->m) + addr);
|
|
|
|
#ifdef DEBUG
|
|
printf("MEM behave %d write %016lx addr %016lx sel %02x\n", identifier, val,
|
|
addr, sel);
|
|
#endif
|
|
|
|
for (unsigned long i = 0; i < 8; i++) {
|
|
if (!(sel & (1UL << i)))
|
|
continue;
|
|
|
|
if ((addr + i) > r->size) {
|
|
fprintf(stderr, "%s: bad memory access %lx %lx\n", __func__,
|
|
addr+i, r->size);
|
|
exit(1);
|
|
}
|
|
|
|
p = (unsigned char *)(((unsigned long)r->m) + addr + i);
|
|
*p = (val >> (i*8)) & 0xff;
|
|
}
|
|
}
|