#define IRQ_COP 12
#define IRQ_HOST 13
-#define DEFAULT_RSTVEC 0x0
-#define DEFAULT_NMIVEC 0x4
-#define DEFAULT_MTVEC 0x8
+#define DEFAULT_RSTVEC 0x1000
+#define DEFAULT_NMIVEC 0x1004
+#define CFGSTRING_ADDR 0x100C
+#define DEFAULT_MTVEC 0x1010
+#define IO_BASE 0x40000000
+#define MEM_BASE 0x80000000
// page table entry (PTE) fields
#define PTE_V 0x001 // Valid
#include "htif.h"
#include "sim.h"
+#include "mmu.h"
#include "encoding.h"
#include <unistd.h>
#include <stdexcept>
send(&ack, sizeof(ack));
uint64_t buf[hdr.data_size];
- for (size_t i = 0; i < hdr.data_size; i++)
- buf[i] = sim->debug_mmu->load_uint64((hdr.addr+i)*HTIF_DATA_ALIGN);
+ for (size_t i = 0; i < hdr.data_size; i++) {
+ reg_t addr = (hdr.addr + i) * HTIF_DATA_ALIGN;
+ try {
+ buf[i] = sim->debug_mmu->load_uint64(addr);
+ } catch (trap_load_access_fault& e) {
+ fprintf(stderr, "HTIF: attempt to read from illegal address 0x%" PRIx64 "\n", addr);
+ exit(-1);
+ }
+ }
send(buf, hdr.data_size * sizeof(buf[0]));
break;
}
case HTIF_CMD_WRITE_MEM:
{
const uint64_t* buf = (const uint64_t*)p.get_payload();
- for (size_t i = 0; i < hdr.data_size; i++)
- sim->debug_mmu->store_uint64((hdr.addr+i)*HTIF_DATA_ALIGN, buf[i]);
-
+ for (size_t i = 0; i < hdr.data_size; i++) {
+ reg_t addr = (hdr.addr + i) * HTIF_DATA_ALIGN;
+ try {
+ sim->debug_mmu->store_uint64(addr, buf[i]);
+ } catch (trap_load_access_fault& e) {
+ fprintf(stderr, "HTIF: attempt to write to illegal address 0x%" PRIx64 "\n", addr);
+ exit(-1);
+ }
+ }
packet_header_t ack(HTIF_CMD_ACK, seqno, 0, 0);
send(&ack, sizeof(ack));
break;
#include "decode.h"
#include "disasm.h"
#include "sim.h"
+#include "mmu.h"
#include "htif.h"
#include <sys/mman.h>
#include <termios.h>
#include "sim.h"
#include "processor.h"
-mmu_t::mmu_t(char* _mem, size_t _memsz)
- : mem(_mem), memsz(_memsz), proc(NULL)
+mmu_t::mmu_t(sim_t* sim, processor_t* proc)
+ : sim(sim), proc(proc)
{
flush_tlb();
}
const uint16_t* mmu_t::fetch_slow_path(reg_t addr)
{
reg_t paddr = translate(addr, FETCH);
- if (paddr < memsz)
+ if (sim->addr_is_mem(paddr)) {
refill_tlb(addr, paddr, FETCH);
- else
- throw trap_instruction_access_fault(addr);
- return (const uint16_t*)(mem + paddr);
+ return (const uint16_t*)sim->addr_to_mem(paddr);
+ } else {
+ if (!sim->mmio_load(paddr, sizeof fetch_temp, (uint8_t*)&fetch_temp))
+ throw trap_instruction_access_fault(addr);
+ return &fetch_temp;
+ }
}
void mmu_t::load_slow_path(reg_t addr, reg_t len, uint8_t* bytes)
{
reg_t paddr = translate(addr, LOAD);
- if (paddr < memsz) {
- memcpy(bytes, mem + paddr, len);
+ if (sim->addr_is_mem(paddr)) {
+ memcpy(bytes, sim->addr_to_mem(paddr), len);
if (tracer.interested_in_range(paddr, paddr + PGSIZE, LOAD))
tracer.trace(paddr, len, LOAD);
else
refill_tlb(addr, paddr, LOAD);
- } else if (!proc || !proc->sim->mmio_load(paddr, len, bytes)) {
+ } else if (!sim->mmio_load(paddr, len, bytes)) {
throw trap_load_access_fault(addr);
}
}
void mmu_t::store_slow_path(reg_t addr, reg_t len, const uint8_t* bytes)
{
reg_t paddr = translate(addr, STORE);
- if (paddr < memsz) {
- memcpy(mem + paddr, bytes, len);
+ if (sim->addr_is_mem(paddr)) {
+ memcpy(sim->addr_to_mem(paddr), bytes, len);
if (tracer.interested_in_range(paddr, paddr + PGSIZE, STORE))
tracer.trace(paddr, len, STORE);
else
refill_tlb(addr, paddr, STORE);
- } else if (!proc || !proc->sim->mmio_store(paddr, len, bytes)) {
+ } else if (!sim->mmio_store(paddr, len, bytes)) {
throw trap_store_access_fault(addr);
}
}
else if (type == STORE) tlb_store_tag[idx] = expected_tag;
else tlb_load_tag[idx] = expected_tag;
- tlb_data[idx] = mem + paddr - vaddr;
+ tlb_data[idx] = sim->addr_to_mem(paddr) - vaddr;
}
reg_t mmu_t::walk(reg_t addr, access_type type, bool supervisor, bool pum)
// check that physical address of PTE is legal
reg_t pte_addr = base + idx * ptesize;
- if (pte_addr >= memsz)
+ if (!sim->addr_is_mem(pte_addr))
break;
- void* ppte = mem + pte_addr;
+ void* ppte = sim->addr_to_mem(pte_addr);
reg_t pte = ptesize == 4 ? *(uint32_t*)ppte : *(uint64_t*)ppte;
reg_t ppn = pte >> PTE_PPN_SHIFT;
#include "trap.h"
#include "common.h"
#include "config.h"
+#include "sim.h"
#include "processor.h"
#include "memtracer.h"
#include <stdlib.h>
class mmu_t
{
public:
- mmu_t(char* _mem, size_t _memsz);
+ mmu_t(sim_t* sim, processor_t* proc);
~mmu_t();
// template for functions that load an aligned value from memory
int length = insn_length(insn);
if (likely(length == 4)) {
- if (likely(addr % PGSIZE < PGSIZE-2))
- insn |= (insn_bits_t)*(const int16_t*)(iaddr + 1) << 16;
- else
- insn |= (insn_bits_t)*(const int16_t*)translate_insn_addr(addr + 2) << 16;
+ insn |= (insn_bits_t)*(const int16_t*)translate_insn_addr(addr + 2) << 16;
} else if (length == 2) {
insn = (int16_t)insn;
} else if (length == 6) {
entry->tag = addr;
entry->data = fetch;
- reg_t paddr = (const char*)iaddr - mem;
+ reg_t paddr = sim->mem_to_addr((char*)iaddr);
if (tracer.interested_in_range(paddr, paddr + 1, FETCH)) {
entry->tag = -1;
tracer.trace(paddr, length, FETCH);
return access_icache(addr)->data;
}
- void set_processor(processor_t* p) { proc = p; flush_tlb(); }
-
void flush_tlb();
void flush_icache();
void register_memtracer(memtracer_t*);
private:
- char* mem;
- size_t memsz;
+ sim_t* sim;
processor_t* proc;
memtracer_list_t tracer;
+ uint16_t fetch_temp;
// implement an instruction cache for simulator performance
icache_entry_t icache[ICACHE_ENTRIES];
#include "common.h"
#include "config.h"
#include "sim.h"
+#include "mmu.h"
#include "htif.h"
#include "disasm.h"
#include <cinttypes>
{
parse_isa_string(isa);
- mmu = new mmu_t(sim->mem, sim->memsz);
- mmu->set_processor(this);
+ mmu = new mmu_t(sim, this);
reset(true);
set_csr(CSR_MSTATUS, s);
set_privilege(PRV_S);
} else {
- state.pc = DEFAULT_MTVEC;
+ state.pc = state.mtvec;
state.mcause = t.cause();
state.mepc = epc;
if (t.has_badaddr())
case CSR_SCAUSE: state.scause = val; break;
case CSR_SBADADDR: state.sbadaddr = val; break;
case CSR_MEPC: state.mepc = val; break;
+ case CSR_MTVEC: state.mtvec = val >> 2 << 2; break;
case CSR_MSCRATCH: state.mscratch = val; break;
case CSR_MCAUSE: state.mcause = val; break;
case CSR_MBADADDR: state.mbadaddr = val; break;
case CSR_MIMPID: return 0;
case CSR_MVENDORID: return 0;
case CSR_MHARTID: return id;
- case CSR_MTVEC: return DEFAULT_MTVEC;
+ case CSR_MTVEC: return state.mtvec;
case CSR_MEDELEG: return state.medeleg;
case CSR_MIDELEG: return state.mideleg;
case CSR_MTOHOST:
reg_t mepc;
reg_t mbadaddr;
reg_t mscratch;
+ reg_t mtvec;
reg_t mcause;
reg_t minstret;
reg_t mie;
// See LICENSE for license details.
#include "sim.h"
+#include "mmu.h"
#include "htif.h"
#include <map>
#include <iostream>
fprintf(stderr, "warning: only got %lu bytes of target mem (wanted %lu)\n",
(unsigned long)memsz, (unsigned long)memsz0);
- debug_mmu = new mmu_t(mem, memsz);
+ debug_mmu = new mmu_t(this, NULL);
for (size_t i = 0; i < procs.size(); i++)
procs[i] = new processor_t(isa, this, i);
void sim_t::make_config_string()
{
- size_t csr_size = NCSR * 16 /* RV128 */;
- size_t device_tree_addr = memsz;
- size_t cpu_addr = memsz + csr_size;
-
- reg_t rtc_addr = memsz;
+ reg_t rtc_addr = IO_BASE;
bus.add_device(rtc_addr, rtc.get());
- config_string_addr = rtc_addr + rtc->size();
+
+ uint32_t reset_vec[8] = {
+ 0x297 + MEM_BASE - DEFAULT_RSTVEC, // reset vector
+ 0x00028067, // jump straight to MEM_BASE
+ 0x00000000, // reserved
+ 0, // pointer to configuration string
+ 0, 0, 0, 0 // trap vector
+ };
+ config_string_addr = DEFAULT_RSTVEC + sizeof(reset_vec);
+ reset_vec[3] = config_string_addr;
+
+ std::vector<char> rom((char*)reset_vec, (char*)reset_vec + sizeof(reset_vec));
std::stringstream s;
s << std::hex <<
"};\n"
"ram {\n"
" 0 {\n"
- " addr 0;\n"
- " size 0x" << memsz << ";\n"
+ " addr 0x" << MEM_BASE << ";\n"
+ " size 0x" << (MEM_BASE + memsz) << ";\n"
" };\n"
"};\n"
"core {\n";
" " << i << " {\n"
" " << "0 {\n" << // hart 0 on core i
" isa " << procs[i]->isa_string << ";\n"
- " addr 0x" << cpu_addr << ";\n"
" timecmp 0x" << (rtc_addr + 8*(1+i)) << ";\n"
" };\n"
" };\n";
- bus.add_device(cpu_addr, procs[i]);
- cpu_addr += csr_size;
}
s << "};\n";
- std::string str = s.str();
- std::vector<char> vec(str.begin(), str.end());
- vec.push_back(0);
- assert(vec.size() <= csr_size);
- config_string.reset(new rom_device_t(vec));
- bus.add_device(config_string_addr, config_string.get());
+ config_string = s.str();
+ rom.insert(rom.end(), config_string.begin(), config_string.end());
+ rom.push_back(0);
+ boot_rom.reset(new rom_device_t(rom));
+ bus.add_device(DEFAULT_RSTVEC, boot_rom.get());
}
#include <string>
#include <memory>
#include "processor.h"
-#include "mmu.h"
#include "devices.h"
class htif_isasim_t;
+class mmu_t;
// this class encapsulates the processors and memory in a RISC-V machine.
class sim_t
void set_histogram(bool value);
void set_procs_debug(bool value);
htif_isasim_t* get_htif() { return htif.get(); }
- const char* get_config_string() { return &config_string->contents()[0]; }
+ const char* get_config_string() { return config_string.c_str(); }
// returns the number of processors in this simulator
size_t num_cores() { return procs.size(); }
size_t memsz; // memory size in bytes
mmu_t* debug_mmu; // debug port into main memory
std::vector<processor_t*> procs;
- std::unique_ptr<rom_device_t> config_string;
+ std::string config_string;
+ std::unique_ptr<rom_device_t> boot_rom;
std::unique_ptr<rtc_t> rtc;
reg_t config_string_addr;
bus_t bus;
bool histogram_enabled; // provide a histogram of PCs
// memory-mapped I/O routines
+ bool addr_is_mem(reg_t addr) {
+ return addr >= MEM_BASE && addr < MEM_BASE + memsz;
+ }
+ char* addr_to_mem(reg_t addr) { return mem + addr - MEM_BASE; }
+ reg_t mem_to_addr(char* x) { return x - mem + MEM_BASE; }
bool mmio_load(reg_t addr, size_t len, uint8_t* bytes);
bool mmio_store(reg_t addr, size_t len, const uint8_t* bytes);
void make_config_string();
// See LICENSE for license details.
#include "sim.h"
+#include "mmu.h"
#include "htif.h"
#include "cachesim.h"
#include "extension.h"