118 lines
2.9 KiB
C++
118 lines
2.9 KiB
C++
#pragma once
|
|
|
|
#include <array>
|
|
#include <optional>
|
|
|
|
#include "rv_csr.h"
|
|
#include "rv_types.h"
|
|
#include "rv_mem.h"
|
|
|
|
struct RVCore {
|
|
std::array<ux_t, 32> regs;
|
|
ux_t pc;
|
|
RVCSR csr;
|
|
bool load_reserved;
|
|
MemBase32 &mem;
|
|
|
|
// A single flat RAM is handled as a special case, in addition to whatever
|
|
// is in `mem`, because this avoids virtual calls for the majority of
|
|
// memory accesses. This RAM takes precedence over whatever is mapped at
|
|
// the same address in `mem`. (Note the size of this RAM may be zero, and
|
|
// RAM can also be added to the `mem` object.)
|
|
ux_t *ram;
|
|
ux_t ram_base;
|
|
ux_t ram_top;
|
|
|
|
RVCore(MemBase32 &_mem, ux_t reset_vector, ux_t ram_base_, ux_t ram_size_) : mem(_mem) {
|
|
std::fill(std::begin(regs), std::end(regs), 0);
|
|
pc = reset_vector;
|
|
load_reserved = false;
|
|
ram_base = ram_base_;
|
|
ram_top = ram_base_ + ram_size_;
|
|
ram = new ux_t[ram_size_ / sizeof(ux_t)];
|
|
assert(ram);
|
|
assert(!(ram_base_ & 0x3));
|
|
assert(!(ram_size_ & 0x3));
|
|
assert(ram_base_ + ram_size_ >= ram_base_);
|
|
for (ux_t i = 0; i < ram_size_ / sizeof(ux_t); ++i)
|
|
ram[i] = 0;
|
|
}
|
|
|
|
~RVCore() {
|
|
delete ram;
|
|
}
|
|
|
|
enum {
|
|
OPC_LOAD = 0b00'000,
|
|
OPC_MISC_MEM = 0b00'011,
|
|
OPC_OP_IMM = 0b00'100,
|
|
OPC_AUIPC = 0b00'101,
|
|
OPC_STORE = 0b01'000,
|
|
OPC_AMO = 0b01'011,
|
|
OPC_OP = 0b01'100,
|
|
OPC_LUI = 0b01'101,
|
|
OPC_BRANCH = 0b11'000,
|
|
OPC_JALR = 0b11'001,
|
|
OPC_JAL = 0b11'011,
|
|
OPC_SYSTEM = 0b11'100,
|
|
OPC_CUSTOM0 = 0b00'010
|
|
};
|
|
|
|
// Functions to read/write memory from this hart's point of view
|
|
std::optional<uint8_t> r8(ux_t addr) {
|
|
if (addr >= ram_base && addr < ram_top) {
|
|
return ram[(addr - ram_base) >> 2] >> 8 * (addr & 0x3) & 0xffu;
|
|
} else {
|
|
return mem.r8(addr);
|
|
}
|
|
}
|
|
|
|
bool w8(ux_t addr, uint8_t data) {
|
|
if (addr >= ram_base && addr < ram_top) {
|
|
ram[(addr - ram_base) >> 2] &= ~(0xffu << 8 * (addr & 0x3));
|
|
ram[(addr - ram_base) >> 2] |= (uint32_t)data << 8 * (addr & 0x3);
|
|
return true;
|
|
} else {
|
|
return mem.w8(addr, data);
|
|
}
|
|
}
|
|
|
|
std::optional<uint16_t> r16(ux_t addr) {
|
|
if (addr >= ram_base && addr < ram_top) {
|
|
return ram[(addr - ram_base) >> 2] >> 8 * (addr & 0x2) & 0xffffu;
|
|
} else {
|
|
return mem.r16(addr);
|
|
}
|
|
}
|
|
|
|
bool w16(ux_t addr, uint16_t data) {
|
|
if (addr >= ram_base && addr < ram_top) {
|
|
ram[(addr - ram_base) >> 2] &= ~(0xffffu << 8 * (addr & 0x2));
|
|
ram[(addr - ram_base) >> 2] |= (uint32_t)data << 8 * (addr & 0x2);
|
|
return true;
|
|
} else {
|
|
return mem.w16(addr, data);
|
|
}
|
|
}
|
|
|
|
std::optional<uint32_t> r32(ux_t addr) {
|
|
if (addr >= ram_base && addr < ram_top) {
|
|
return ram[(addr - ram_base) >> 2];
|
|
} else {
|
|
return mem.r32(addr);
|
|
}
|
|
}
|
|
|
|
bool w32(ux_t addr, uint32_t data) {
|
|
if (addr >= ram_base && addr < ram_top) {
|
|
ram[(addr - ram_base) >> 2] = data;
|
|
return true;
|
|
} else {
|
|
return mem.w32(addr, data);
|
|
}
|
|
}
|
|
|
|
// Fetch and execute one instruction from memory.
|
|
void step(bool trace=false);
|
|
};
|