Spike, a RISC-V ISA Simulator
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

179 lines
4.6 KiB

#include "devices.h"
#include "mmu.h"
#include <stdexcept>
mmio_device_map_t& mmio_device_map()
{
static mmio_device_map_t device_map;
return device_map;
}
static auto empty_device = rom_device_t(std::vector<char>());
bus_t::bus_t()
: bus_t(&empty_device)
{
}
bus_t::bus_t(abstract_device_t* fallback)
: fallback(fallback)
{
}
void bus_t::add_device(reg_t addr, abstract_device_t* dev)
{
// Allow empty devices by omitting them
auto size = dev->size();
if (size == 0)
return;
// Reject devices that overflow address size
if (addr + size - 1 < addr) {
fprintf(stderr, "device at [%" PRIx64 ", %" PRIx64 ") overflows address size\n",
addr, addr + size);
abort();
}
// Reject devices that overlap other devices
if (auto it = devices.upper_bound(addr);
(it != devices.end() && addr + size - 1 >= it->first) ||
(it != devices.begin() && (it--, it->first + it->second->size() - 1 >= addr))) {
fprintf(stderr, "devices at [%" PRIx64 ", %" PRIx64 ") and [%" PRIx64 ", %" PRIx64 ") overlap\n",
it->first, it->first + it->second->size(), addr, addr + size);
abort();
}
devices[addr] = dev;
}
bool bus_t::load(reg_t addr, size_t len, uint8_t* bytes)
{
if (auto [base, dev] = find_device(addr, len); dev)
return dev->load(addr - base, len, bytes);
return false;
}
bool bus_t::store(reg_t addr, size_t len, const uint8_t* bytes)
{
if (auto [base, dev] = find_device(addr, len); dev)
return dev->store(addr - base, len, bytes);
return false;
}
reg_t bus_t::size()
{
if (auto last = devices.rbegin(); last != devices.rend())
return last->first + last->second->size();
return 0;
}
std::pair<reg_t, abstract_device_t*> bus_t::find_device(reg_t addr, size_t len)
{
if (unlikely(!len || addr + len - 1 < addr))
return std::make_pair(0, nullptr);
// Obtain iterator to device immediately after the one that might match
auto it_after = devices.upper_bound(addr);
reg_t base, size;
if (likely(it_after != devices.begin())) {
// Obtain iterator to device that might match
auto it = std::prev(it_after);
base = it->first;
size = it->second->size();
if (likely(addr - base + len - 1 < size)) {
// it fully contains [addr, addr + len)
return std::make_pair(it->first, it->second);
}
}
if (unlikely((it_after != devices.end() && addr + len - 1 >= it_after->first)
|| (it_after != devices.begin() && addr - base < size))) {
// it_after or it contains part of, but not all of, [addr, add + len)
return std::make_pair(0, nullptr);
}
// No matching device
return std::make_pair(0, fallback);
}
mem_t::mem_t(reg_t size)
: sz(size)
{
if (size == 0 || size % PGSIZE != 0)
throw std::runtime_error("memory size must be a positive multiple of 4 KiB");
}
mem_t::~mem_t()
{
for (auto& entry : sparse_memory_map)
free(entry.second);
}
bool mem_t::load_store(reg_t addr, size_t len, uint8_t* bytes, bool store)
{
if (addr + len < addr || addr + len > sz)
return false;
while (len > 0) {
auto n = std::min(PGSIZE - (addr % PGSIZE), reg_t(len));
if (store)
memcpy(this->contents(addr), bytes, n);
else
memcpy(bytes, this->contents(addr), n);
addr += n;
bytes += n;
len -= n;
}
return true;
}
char* mem_t::contents(reg_t addr) {
reg_t ppn = addr >> PGSHIFT, pgoff = addr % PGSIZE;
auto search = sparse_memory_map.find(ppn);
if (search == sparse_memory_map.end()) {
auto res = (char*)calloc(PGSIZE, 1);
if (res == nullptr)
throw std::bad_alloc();
sparse_memory_map[ppn] = res;
return res + pgoff;
}
return search->second + pgoff;
}
void mem_t::dump(std::ostream& o) {
const char empty[PGSIZE] = {0};
for (reg_t i = 0; i < sz; i += PGSIZE) {
reg_t ppn = i >> PGSHIFT;
auto search = sparse_memory_map.find(ppn);
if (search == sparse_memory_map.end()) {
o.write(empty, PGSIZE);
} else {
o.write(sparse_memory_map[ppn], PGSIZE);
}
}
}
external_sim_device_t::external_sim_device_t(abstract_sim_if_t* sim)
: external_simulator(sim) {}
void external_sim_device_t::set_simulator(abstract_sim_if_t* sim) {
external_simulator = sim;
}
bool external_sim_device_t::load(reg_t addr, size_t len, uint8_t* bytes) {
if (unlikely(external_simulator == nullptr)) return false;
return external_simulator->load(addr, len, bytes);
}
bool external_sim_device_t::store(reg_t addr, size_t len, const uint8_t* bytes) {
if (unlikely(external_simulator == nullptr)) return false;
return external_simulator->store(addr, len, bytes);
}
reg_t external_sim_device_t::size() {
if (unlikely(external_simulator == nullptr)) return 0;
return PGSIZE; // TODO: proper size
}