simulation framework refactoring
This commit is contained in:
305
sim/common/mem.cpp
Normal file
305
sim/common/mem.cpp
Normal file
@@ -0,0 +1,305 @@
|
||||
#include "mem.h"
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <assert.h>
|
||||
#include "util.h"
|
||||
|
||||
using namespace vortex;
|
||||
|
||||
RamMemDevice::RamMemDevice(const char *filename, uint32_t wordSize)
|
||||
: wordSize_(wordSize) {
|
||||
std::ifstream input(filename);
|
||||
|
||||
if (!input) {
|
||||
std::cout << "Error reading file \"" << filename << "\" into RamMemDevice.\n";
|
||||
std::abort();
|
||||
}
|
||||
|
||||
do {
|
||||
contents_.push_back(input.get());
|
||||
} while (input);
|
||||
|
||||
while (contents_.size() & (wordSize-1))
|
||||
contents_.push_back(0x00);
|
||||
}
|
||||
|
||||
RamMemDevice::RamMemDevice(uint64_t size, uint32_t wordSize)
|
||||
: contents_(size)
|
||||
, wordSize_(wordSize)
|
||||
{}
|
||||
|
||||
void RamMemDevice::read(void *data, uint64_t addr, uint64_t size) {
|
||||
auto addr_end = addr + size;
|
||||
if ((addr & (wordSize_-1))
|
||||
|| (addr_end & (wordSize_-1))
|
||||
|| (addr_end <= contents_.size())) {
|
||||
std::cout << "lookup of 0x" << std::hex << (addr_end-1) << " failed.\n";
|
||||
throw BadAddress();
|
||||
}
|
||||
|
||||
const uint8_t *s = contents_.data() + addr;
|
||||
for (uint8_t *d = (uint8_t*)data, *de = d + size; d != de;) {
|
||||
*d++ = *s++;
|
||||
}
|
||||
}
|
||||
|
||||
void RamMemDevice::write(const void *data, uint64_t addr, uint64_t size) {
|
||||
auto addr_end = addr + size;
|
||||
if ((addr & (wordSize_-1))
|
||||
|| (addr_end & (wordSize_-1))
|
||||
|| (addr_end <= contents_.size())) {
|
||||
std::cout << "lookup of 0x" << std::hex << (addr_end-1) << " failed.\n";
|
||||
throw BadAddress();
|
||||
}
|
||||
|
||||
const uint8_t *s = (const uint8_t*)data;
|
||||
for (uint8_t *d = contents_.data() + addr, *de = d + size; d != de;) {
|
||||
*d++ = *s++;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void RomMemDevice::write(const void* /*data*/, uint64_t /*addr*/, uint64_t /*size*/) {
|
||||
std::cout << "attempt to write to ROM.\n";
|
||||
std::abort();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool MemoryUnit::ADecoder::lookup(uint64_t a, uint32_t wordSize, mem_accessor_t* ma) {
|
||||
uint64_t e = a + (wordSize - 1);
|
||||
assert(e >= a);
|
||||
for (auto iter = entries_.rbegin(), iterE = entries_.rend(); iter != iterE; ++iter) {
|
||||
if (a >= iter->start && e <= iter->end) {
|
||||
ma->md = iter->md;
|
||||
ma->addr = a - iter->start;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MemoryUnit::ADecoder::map(uint64_t a, uint64_t e, MemDevice &m) {
|
||||
assert(e >= a);
|
||||
entry_t entry{&m, a, e};
|
||||
entries_.emplace_back(entry);
|
||||
}
|
||||
|
||||
void MemoryUnit::ADecoder::read(void *data, uint64_t addr, uint64_t size) {
|
||||
mem_accessor_t ma;
|
||||
if (!this->lookup(addr, size, &ma)) {
|
||||
std::cout << "lookup of 0x" << std::hex << addr << " failed.\n";
|
||||
throw BadAddress();
|
||||
}
|
||||
ma.md->read(data, ma.addr, size);
|
||||
}
|
||||
|
||||
void MemoryUnit::ADecoder::write(const void *data, uint64_t addr, uint64_t size) {
|
||||
mem_accessor_t ma;
|
||||
if (!this->lookup(addr, size, &ma)) {
|
||||
std::cout << "lookup of 0x" << std::hex << addr << " failed.\n";
|
||||
throw BadAddress();
|
||||
}
|
||||
ma.md->write(data, ma.addr, size);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MemoryUnit::MemoryUnit(uint64_t pageSize, uint64_t addrBytes, bool disableVm)
|
||||
: pageSize_(pageSize)
|
||||
, addrBytes_(addrBytes)
|
||||
, disableVM_(disableVm) {
|
||||
if (!disableVm) {
|
||||
tlb_[0] = TLBEntry(0, 077);
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryUnit::attach(MemDevice &m, uint64_t start, uint64_t end) {
|
||||
decoder_.map(start, end, m);
|
||||
}
|
||||
|
||||
MemoryUnit::TLBEntry MemoryUnit::tlbLookup(uint64_t vAddr, uint32_t flagMask) {
|
||||
auto iter = tlb_.find(vAddr / pageSize_);
|
||||
if (iter != tlb_.end()) {
|
||||
if (iter->second.flags & flagMask)
|
||||
return iter->second;
|
||||
else {
|
||||
throw PageFault(vAddr, false);
|
||||
}
|
||||
} else {
|
||||
throw PageFault(vAddr, true);
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryUnit::read(void *data, uint64_t addr, uint64_t size, bool sup) {
|
||||
uint64_t pAddr;
|
||||
if (disableVM_) {
|
||||
pAddr = addr;
|
||||
} else {
|
||||
uint32_t flagMask = sup ? 8 : 1;
|
||||
TLBEntry t = this->tlbLookup(addr, flagMask);
|
||||
pAddr = t.pfn * pageSize_ + addr % pageSize_;
|
||||
}
|
||||
return decoder_.read(data, pAddr, size);
|
||||
}
|
||||
|
||||
void MemoryUnit::write(const void *data, uint64_t addr, uint64_t size, bool sup) {
|
||||
uint64_t pAddr;
|
||||
if (disableVM_) {
|
||||
pAddr = addr;
|
||||
} else {
|
||||
uint32_t flagMask = sup ? 16 : 2;
|
||||
TLBEntry t = tlbLookup(addr, flagMask);
|
||||
pAddr = t.pfn * pageSize_ + addr % pageSize_;
|
||||
}
|
||||
decoder_.write(data, pAddr, size);
|
||||
}
|
||||
|
||||
void MemoryUnit::tlbAdd(uint64_t virt, uint64_t phys, uint32_t flags) {
|
||||
tlb_[virt / pageSize_] = TLBEntry(phys / pageSize_, flags);
|
||||
}
|
||||
|
||||
void MemoryUnit::tlbRm(uint64_t va) {
|
||||
if (tlb_.find(va / pageSize_) != tlb_.end())
|
||||
tlb_.erase(tlb_.find(va / pageSize_));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RAM::RAM(uint32_t num_pages, uint32_t page_size)
|
||||
: page_bits_(log2ceil(page_size)) {
|
||||
assert(ispow2(page_size));
|
||||
mem_.resize(num_pages, NULL);
|
||||
size_ = uint64_t(mem_.size()) << page_bits_;
|
||||
}
|
||||
|
||||
RAM::~RAM() {
|
||||
this->clear();
|
||||
}
|
||||
|
||||
void RAM::clear() {
|
||||
for (auto& page : mem_) {
|
||||
delete[] page;
|
||||
page = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t RAM::size() const {
|
||||
return size_;
|
||||
}
|
||||
|
||||
uint8_t *RAM::get(uint32_t address) const {
|
||||
uint32_t page_size = 1 << page_bits_;
|
||||
uint32_t page_index = address >> page_bits_;
|
||||
uint32_t byte_offset = address & ((1 << page_bits_) - 1);
|
||||
|
||||
auto &page = mem_.at(page_index);
|
||||
if (page == NULL) {
|
||||
uint8_t *ptr = new uint8_t[page_size];
|
||||
// set uninitialized data to "baadf00d"
|
||||
for (uint32_t i = 0; i < page_size; ++i) {
|
||||
ptr[i] = (0xbaadf00d >> ((i & 0x3) * 8)) & 0xff;
|
||||
}
|
||||
page = ptr;
|
||||
}
|
||||
return page + byte_offset;
|
||||
}
|
||||
|
||||
void RAM::read(void *data, uint64_t addr, uint64_t size) {
|
||||
uint8_t* d = (uint8_t*)data;
|
||||
for (uint64_t i = 0; i < size; i++) {
|
||||
d[i] = *this->get(addr + i);
|
||||
}
|
||||
}
|
||||
|
||||
void RAM::write(const void *data, uint64_t addr, uint64_t size) {
|
||||
const uint8_t* s = (const uint8_t*)data;
|
||||
for (uint64_t i = 0; i < size; i++) {
|
||||
*this->get(addr + i) = s[i];
|
||||
}
|
||||
}
|
||||
|
||||
void RAM::loadBinImage(const char* filename, uint64_t destination) {
|
||||
std::ifstream ifs(filename);
|
||||
if (!ifs) {
|
||||
std::cout << "error: " << filename << " not found" << std::endl;
|
||||
}
|
||||
|
||||
ifs.seekg(0, ifs.end);
|
||||
size_t size = ifs.tellg();
|
||||
std::vector<uint8_t> content(size);
|
||||
ifs.seekg(0, ifs.beg);
|
||||
ifs.read((char*)content.data(), size);
|
||||
|
||||
this->clear();
|
||||
this->write(content.data(), destination, size);
|
||||
}
|
||||
|
||||
void RAM::loadHexImage(const char* filename) {
|
||||
auto hti = [&](char c)->uint32_t {
|
||||
if (c >= 'A' && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
if (c >= 'a' && c <= 'f')
|
||||
return c - 'a' + 10;
|
||||
return c - '0';
|
||||
};
|
||||
|
||||
auto hToI = [&](const char *c, uint32_t size)->uint32_t {
|
||||
uint32_t value = 0;
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
value += hti(c[i]) << ((size - i - 1) * 4);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
std::ifstream ifs(filename);
|
||||
if (!ifs) {
|
||||
std::cout << "error: " << filename << " not found" << std::endl;
|
||||
}
|
||||
|
||||
ifs.seekg(0, ifs.end);
|
||||
size_t size = ifs.tellg();
|
||||
std::vector<char> content(size);
|
||||
ifs.seekg(0, ifs.beg);
|
||||
ifs.read(content.data(), size);
|
||||
|
||||
int offset = 0;
|
||||
char *line = content.data();
|
||||
|
||||
this->clear();
|
||||
|
||||
while (true) {
|
||||
if (line[0] == ':') {
|
||||
uint32_t byteCount = hToI(line + 1, 2);
|
||||
uint32_t nextAddr = hToI(line + 3, 4) + offset;
|
||||
uint32_t key = hToI(line + 7, 2);
|
||||
switch (key) {
|
||||
case 0:
|
||||
for (uint32_t i = 0; i < byteCount; i++) {
|
||||
uint32_t addr = nextAddr + i;
|
||||
uint32_t value = hToI(line + 9 + i * 2, 2);
|
||||
*this->get(addr) = value;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
offset = hToI(line + 9, 4) << 4;
|
||||
break;
|
||||
case 4:
|
||||
offset = hToI(line + 9, 4) << 16;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (*line != '\n' && size != 0) {
|
||||
++line;
|
||||
--size;
|
||||
}
|
||||
if (size <= 1)
|
||||
break;
|
||||
++line;
|
||||
--size;
|
||||
}
|
||||
}
|
||||
163
sim/common/mem.h
Normal file
163
sim/common/mem.h
Normal file
@@ -0,0 +1,163 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace vortex {
|
||||
struct BadAddress {};
|
||||
|
||||
class MemDevice {
|
||||
public:
|
||||
virtual ~MemDevice() {}
|
||||
virtual uint64_t size() const = 0;
|
||||
virtual void read(void *data, uint64_t addr, uint64_t size) = 0;
|
||||
virtual void write(const void *data, uint64_t addr, uint64_t size) = 0;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class RamMemDevice : public MemDevice {
|
||||
public:
|
||||
RamMemDevice(uint64_t size, uint32_t wordSize);
|
||||
RamMemDevice(const char *filename, uint32_t wordSize);
|
||||
~RamMemDevice() {}
|
||||
|
||||
void read(void *data, uint64_t addr, uint64_t size) override;
|
||||
void write(const void *data, uint64_t addr, uint64_t size) override;
|
||||
|
||||
virtual uint64_t size() const {
|
||||
return contents_.size();
|
||||
};
|
||||
|
||||
protected:
|
||||
std::vector<uint8_t> contents_;
|
||||
uint32_t wordSize_;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class RomMemDevice : public RamMemDevice {
|
||||
public:
|
||||
RomMemDevice(const char *filename, uint32_t wordSize)
|
||||
: RamMemDevice(filename, wordSize)
|
||||
{}
|
||||
|
||||
RomMemDevice(uint64_t size, uint32_t wordSize)
|
||||
: RamMemDevice(size, wordSize)
|
||||
{}
|
||||
|
||||
~RomMemDevice();
|
||||
|
||||
void write(const void *data, uint64_t addr, uint64_t size) override;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class MemoryUnit {
|
||||
public:
|
||||
|
||||
struct PageFault {
|
||||
PageFault(uint64_t a, bool nf)
|
||||
: faultAddr(a)
|
||||
, notFound(nf)
|
||||
{}
|
||||
uint64_t faultAddr;
|
||||
bool notFound;
|
||||
};
|
||||
|
||||
MemoryUnit(uint64_t pageSize, uint64_t addrBytes, bool disableVm = false);
|
||||
|
||||
void attach(MemDevice &m, uint64_t start, uint64_t end);
|
||||
|
||||
void read(void *data, uint64_t addr, uint64_t size, bool sup);
|
||||
void write(const void *data, uint64_t addr, uint64_t size, bool sup);
|
||||
|
||||
void tlbAdd(uint64_t virt, uint64_t phys, uint32_t flags);
|
||||
void tlbRm(uint64_t va);
|
||||
void tlbFlush() {
|
||||
tlb_.clear();
|
||||
}
|
||||
private:
|
||||
|
||||
class ADecoder {
|
||||
public:
|
||||
ADecoder() {}
|
||||
|
||||
void read(void *data, uint64_t addr, uint64_t size);
|
||||
void write(const void *data, uint64_t addr, uint64_t size);
|
||||
|
||||
void map(uint64_t start, uint64_t end, MemDevice &md);
|
||||
|
||||
private:
|
||||
|
||||
struct mem_accessor_t {
|
||||
MemDevice* md;
|
||||
uint64_t addr;
|
||||
};
|
||||
|
||||
struct entry_t {
|
||||
MemDevice *md;
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
};
|
||||
|
||||
bool lookup(uint64_t a, uint32_t wordSize, mem_accessor_t*);
|
||||
|
||||
std::vector<entry_t> entries_;
|
||||
};
|
||||
|
||||
struct TLBEntry {
|
||||
TLBEntry() {}
|
||||
TLBEntry(uint32_t pfn, uint32_t flags)
|
||||
: pfn(pfn)
|
||||
, flags(flags)
|
||||
{}
|
||||
uint32_t pfn;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
TLBEntry tlbLookup(uint64_t vAddr, uint32_t flagMask);
|
||||
|
||||
std::unordered_map<uint64_t, TLBEntry> tlb_;
|
||||
uint64_t pageSize_;
|
||||
uint64_t addrBytes_;
|
||||
ADecoder decoder_;
|
||||
bool disableVM_;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class RAM : public MemDevice {
|
||||
public:
|
||||
|
||||
RAM(uint32_t num_pages, uint32_t page_size);
|
||||
|
||||
~RAM();
|
||||
|
||||
void clear();
|
||||
|
||||
uint64_t size() const override;
|
||||
void read(void *data, uint64_t addr, uint64_t size) override;
|
||||
void write(const void *data, uint64_t addr, uint64_t size) override;
|
||||
|
||||
void loadBinImage(const char* filename, uint64_t destination);
|
||||
void loadHexImage(const char* filename);
|
||||
|
||||
uint8_t& operator[](uint64_t address) {
|
||||
return *this->get(address);
|
||||
}
|
||||
|
||||
const uint8_t& operator[](uint64_t address) const {
|
||||
return *this->get(address);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
uint8_t *get(uint32_t address) const;
|
||||
|
||||
mutable std::vector<uint8_t*> mem_;
|
||||
uint32_t page_bits_;
|
||||
uint64_t size_;
|
||||
};
|
||||
|
||||
} // namespace vortex
|
||||
92
sim/common/util.cpp
Normal file
92
sim/common/util.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "util.h"
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <math.h>
|
||||
#include <climits>
|
||||
#include <string.h>
|
||||
#include <bitset>
|
||||
#include <fcntl.h>
|
||||
|
||||
using namespace vortex;
|
||||
|
||||
// Apply integer sign extension
|
||||
uint32_t vortex::signExt(uint32_t w, uint32_t bit, uint32_t mask) {
|
||||
if (w >> (bit - 1))
|
||||
w |= ~mask;
|
||||
return w;
|
||||
}
|
||||
|
||||
// Convert a floating point number to IEEE-754 32-bit representation,
|
||||
// so that it could be stored in a 32-bit integer register file
|
||||
// Reference: https://www.wikihow.com/Convert-a-Number-from-Decimal-to-IEEE-754-Floating-Point-Representation
|
||||
// https://www.technical-recipes.com/2012/converting-between-binary-and-decimal-representations-of-ieee-754-floating-point-numbers-in-c/
|
||||
uint32_t vortex::floatToBin(float in_value) {
|
||||
union {
|
||||
float input; // assumes sizeof(float) == sizeof(int)
|
||||
int output;
|
||||
} data;
|
||||
|
||||
data.input = in_value;
|
||||
|
||||
std::bitset<sizeof(float) * CHAR_BIT> bits(data.output);
|
||||
std::string mystring = bits.to_string<char, std::char_traits<char>, std::allocator<char>>();
|
||||
// Convert binary to uint32_t
|
||||
uint32_t result = stoul(mystring, nullptr, 2);
|
||||
return result;
|
||||
}
|
||||
|
||||
// https://en.wikipedia.org/wiki/Single-precision_floating-point_format
|
||||
// check floating-point number in binary format is NaN
|
||||
uint8_t vortex::fpBinIsNan(uint32_t din) {
|
||||
bool fsign = din & 0x80000000;
|
||||
uint32_t expo = (din>>23) & 0x000000FF;
|
||||
uint32_t fraction = din & 0x007FFFFF;
|
||||
uint32_t bit_22 = din & 0x00400000;
|
||||
|
||||
if ((expo==0xFF) && (fraction!=0)) {
|
||||
// if (!fsign && (fraction == 0x00400000))
|
||||
if (!fsign && (bit_22))
|
||||
return 1; // quiet NaN, return 1
|
||||
else
|
||||
return 2; // signaling NaN, return 2
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check floating-point number in binary format is zero
|
||||
uint8_t vortex::fpBinIsZero(uint32_t din) {
|
||||
bool fsign = din & 0x80000000;
|
||||
uint32_t expo = (din>>23) & 0x000000FF;
|
||||
uint32_t fraction = din & 0x007FFFFF;
|
||||
|
||||
if ((expo==0) && (fraction==0)) {
|
||||
if (fsign)
|
||||
return 1; // negative 0
|
||||
else
|
||||
return 2; // positive 0
|
||||
}
|
||||
return 0; // not zero
|
||||
}
|
||||
|
||||
// check floating-point number in binary format is infinity
|
||||
uint8_t vortex::fpBinIsInf(uint32_t din) {
|
||||
bool fsign = din & 0x80000000;
|
||||
uint32_t expo = (din>>23) & 0x000000FF;
|
||||
uint32_t fraction = din & 0x007FFFFF;
|
||||
|
||||
if ((expo==0xFF) && (fraction==0)) {
|
||||
if (fsign)
|
||||
return 1; // negative infinity
|
||||
else
|
||||
return 2; // positive infinity
|
||||
}
|
||||
return 0; // not infinity
|
||||
}
|
||||
|
||||
// return file extension
|
||||
const char* vortex::fileExtension(const char* filepath) {
|
||||
const char *ext = strrchr(filepath, '.');
|
||||
if (ext == NULL || ext == filepath)
|
||||
return "";
|
||||
return ext + 1;
|
||||
}
|
||||
44
sim/common/util.h
Normal file
44
sim/common/util.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <assert.h>
|
||||
|
||||
namespace vortex {
|
||||
|
||||
template <typename... Args>
|
||||
void unused(Args&&...) {}
|
||||
|
||||
#define __unused(...) unused(__VA_ARGS__)
|
||||
|
||||
constexpr bool ispow2(uint64_t value) {
|
||||
return value && !(value & (value - 1));
|
||||
}
|
||||
|
||||
constexpr unsigned log2ceil(uint32_t value) {
|
||||
return 32 - __builtin_clz(value - 1);
|
||||
}
|
||||
|
||||
inline uint64_t align_size(uint64_t size, uint64_t alignment) {
|
||||
assert(0 == (alignment & (alignment - 1)));
|
||||
return (size + alignment - 1) & ~(alignment - 1);
|
||||
}
|
||||
|
||||
// Apply integer sign extension
|
||||
uint32_t signExt(uint32_t w, uint32_t bit, uint32_t mask);
|
||||
|
||||
// Convert a floating point number to IEEE-754 32-bit representation
|
||||
uint32_t floatToBin(float in_value);
|
||||
|
||||
// check floating-point number in binary format is NaN
|
||||
uint8_t fpBinIsNan(uint32_t din);
|
||||
|
||||
// check floating-point number in binary format is zero
|
||||
uint8_t fpBinIsZero(uint32_t din);
|
||||
|
||||
// check floating-point number in binary format is infinity
|
||||
uint8_t fpBinIsInf(uint32_t din);
|
||||
|
||||
// return file extension
|
||||
const char* fileExtension(const char* filepath);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user