blob: 136622134b6c6864cc7663bade7ff9363d5034a9 [file] [log] [blame]
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef TESTS_VERILATOR_SIM_KELVIN_MEMORY_IF_H_
#define TESTS_VERILATOR_SIM_KELVIN_MEMORY_IF_H_
#include <stdint.h>
#include <stdio.h>
#include <algorithm>
#include <map>
#include "tests/verilator_sim/sysc_module.h"
// A memory model base class
struct Memory_if : Sysc_module {
const int kPageSize = 4 * 1024;
const int kPageMask = ~(kPageSize - 1);
struct memory_page_t {
uint32_t addr;
uint8_t data[4096];
};
Memory_if(sc_module_name n, const char* bin, int limit = -1) :
Sysc_module(n) {
FILE *f = fopen(bin, "rb");
fseek(f, 0, SEEK_END);
int64_t fsize = ftell(f);
fseek(f, 0, SEEK_SET);
uint8_t *fdata = new uint8_t[fsize];
fread(fdata, fsize, 1, f);
fclose(f);
if (limit > 0 && fsize > limit) {
printf("***ERROR Memory_if limit exceeded [%ld > %d]\n", fsize, limit);
exit(-1);
}
for (int addr = 0; addr < fsize; addr += kPageSize) {
const int64_t size = std::min(fsize - addr, int64_t(kPageSize));
AddPage(addr, size, fdata + addr);
}
delete [] fdata;
}
void Read(uint32_t addr, int bytes, uint8_t* data) {
while (bytes > 0) {
const uint32_t maddr = addr & kPageMask;
const uint32_t offset = addr - maddr;
const int limit = kPageSize - offset;
const int len = std::min(bytes, limit);
if (!HasPage(maddr)) {
#ifdef PRINT_ADD_PAGE
printf("MemoryModel::Read add_page %08x\n", addr);
#endif
AddPage(maddr, kPageSize);
}
auto& p = page_[maddr];
uint8_t* d = p.data;
memcpy(data, d + offset, len);
#if 0
printf("READ %08x", addr);
for (int i = 0; i < len; i++) {
printf(" %02x", data[i]);
}
printf("\n");
#endif
addr += len;
data += len;
bytes -= len;
assert(bytes >= 0);
}
}
void Write(uint32_t addr, int bytes, const uint8_t* data) {
while (bytes > 0) {
const uint32_t maddr = addr & kPageMask;
const uint32_t offset = addr - maddr;
const int limit = kPageSize - offset;
const int len = std::min(bytes, limit);
if (!HasPage(maddr)) {
#ifdef PRINT_ADD_PAGE
printf("MemoryModel::Write add_page %08x\n", addr);
#endif
AddPage(maddr, kPageSize);
}
auto& p = page_[maddr];
uint8_t* d = p.data;
memcpy(d + offset, data, len);
#if 0
printf("WRITE %08x", addr);
for (int i = 0; i < len; i++) {
printf(" %02x", data[i]);
}
printf("\n");
#endif
addr += len;
data += len;
bytes -= len;
assert(bytes >= 0);
}
}
protected:
void ReadSwizzle(const uint32_t addr, const int bytes, uint8_t* data) {
const int mask = bytes - 1;
const int alignment = (bytes - (addr & mask)) & mask; // left shuffle
uint8_t tmp[512/8];
if (!alignment) return;
for (int i = 0; i < bytes; ++i) {
tmp[i] = data[i];
}
for (int i = 0; i < bytes; ++i) {
data[i] = tmp[(i + alignment) & mask];
}
}
void WriteSwizzle(const uint32_t addr, const int bytes, uint8_t* data) {
const int mask = bytes - 1;
const int alignment = addr & mask; // right shuffle
uint8_t tmp[512/8];
if (!alignment) return;
for (int i = 0; i < bytes; ++i) {
tmp[i] = data[i];
}
for (int i = 0; i < bytes; ++i) {
data[i] = tmp[(i + alignment) & mask];
}
}
private:
std::map<uint32_t, memory_page_t> page_;
bool HasPage(const uint32_t addr) {
return page_.find(addr) != page_.end();
}
void AddPage(const uint32_t addr, const int bytes,
const uint8_t* data = nullptr) {
const uint32_t addrbase = addr & kPageMask;
if (addr != addrbase) {
printf("AddPage(%08x, %d)\n", addr, bytes);
assert(false && "AddPage: address not page aligned");
}
if (HasPage(addr)) {
printf("AddPage(%08x, %d)\n", addr, bytes);
assert(false && "AddPage: address already populated");
}
auto& p = page_[addr];
uint8_t* d = p.data;
if (bytes < kPageSize || data == nullptr) {
#if 1
// remove need for .bss (hacky?)
memset(d, 0x00, kPageSize);
#else
memset(d, 0xcc, kPageSize);
#endif
}
if (data) {
memcpy(d, data, bytes);
}
}
};
#endif // TESTS_VERILATOR_SIM_KELVIN_MEMORY_IF_H_