return false;
return it->second->store(addr - -it->first, len, bytes);
}
+
+std::pair<reg_t, abstract_device_t*> bus_t::find_device(reg_t addr)
+{
+ auto it = devices.lower_bound(-addr);
+ if (it == devices.end() || addr < -it->first)
+ return std::make_pair((reg_t)0, (abstract_device_t*)NULL);
+ return std::make_pair(-it->first, it->second);
+}
bool store(reg_t addr, size_t len, const uint8_t* bytes);
void add_device(reg_t addr, abstract_device_t* dev);
+ std::pair<reg_t, abstract_device_t*> find_device(reg_t addr);
+
private:
std::map<reg_t, abstract_device_t*> devices;
};
std::vector<char> data;
};
+class mem_t : public abstract_device_t {
+ public:
+ mem_t(size_t size) : len(size) {
+ data = (char*)calloc(1, size);
+ if (!data)
+ throw std::runtime_error("couldn't allocate " + std::to_string(size) + " bytes of target memory");
+ }
+ mem_t(const mem_t& that) = delete;
+ ~mem_t() { free(data); }
+
+ bool load(reg_t addr, size_t len, uint8_t* bytes) { return false; }
+ bool store(reg_t addr, size_t len, const uint8_t* bytes) { return false; }
+ char* contents() { return data; }
+ size_t size() { return len; }
+
+ private:
+ char* data;
+ size_t len;
+};
+
class clint_t : public abstract_device_t {
public:
clint_t(std::vector<processor_t*>&);
#define MSTATUS_TW 0x00200000
#define MSTATUS_TSR 0x00400000
#define MSTATUS32_SD 0x80000000
+#define MSTATUS_UXL 0x0000000300000000
+#define MSTATUS_SXL 0x0000000C00000000
#define MSTATUS64_SD 0x8000000000000000
#define SSTATUS_UIE 0x00000001
#define SSTATUS_SUM 0x00040000
#define SSTATUS_MXR 0x00080000
#define SSTATUS32_SD 0x80000000
+#define SSTATUS_UXL 0x0000000300000000
#define SSTATUS64_SD 0x8000000000000000
#define DCSR_XDEBUGVER (3U<<30)
#define MASK_URET 0xffffffff
#define MATCH_SRET 0x10200073
#define MASK_SRET 0xffffffff
-#define MATCH_HRET 0x20200073
-#define MASK_HRET 0xffffffff
#define MATCH_MRET 0x30200073
#define MASK_MRET 0xffffffff
#define MATCH_DRET 0x7b200073
#define MASK_FCVT_L_S 0xfff0007f
#define MATCH_FCVT_LU_S 0xc0300053
#define MASK_FCVT_LU_S 0xfff0007f
-#define MATCH_FMV_X_S 0xe0000053
-#define MASK_FMV_X_S 0xfff0707f
+#define MATCH_FMV_X_W 0xe0000053
+#define MASK_FMV_X_W 0xfff0707f
#define MATCH_FCLASS_S 0xe0001053
#define MASK_FCLASS_S 0xfff0707f
#define MATCH_FCVT_W_D 0xc2000053
#define MASK_FCVT_S_L 0xfff0007f
#define MATCH_FCVT_S_LU 0xd0300053
#define MASK_FCVT_S_LU 0xfff0007f
-#define MATCH_FMV_S_X 0xf0000053
-#define MASK_FMV_S_X 0xfff0707f
+#define MATCH_FMV_W_X 0xf0000053
+#define MASK_FMV_W_X 0xfff0707f
#define MATCH_FCVT_D_W 0xd2000053
#define MASK_FCVT_D_W 0xfff0007f
#define MATCH_FCVT_D_WU 0xd2100053
DECLARE_INSN(ebreak, MATCH_EBREAK, MASK_EBREAK)
DECLARE_INSN(uret, MATCH_URET, MASK_URET)
DECLARE_INSN(sret, MATCH_SRET, MASK_SRET)
-DECLARE_INSN(hret, MATCH_HRET, MASK_HRET)
DECLARE_INSN(mret, MATCH_MRET, MASK_MRET)
DECLARE_INSN(dret, MATCH_DRET, MASK_DRET)
DECLARE_INSN(sfence_vma, MATCH_SFENCE_VMA, MASK_SFENCE_VMA)
DECLARE_INSN(fcvt_wu_s, MATCH_FCVT_WU_S, MASK_FCVT_WU_S)
DECLARE_INSN(fcvt_l_s, MATCH_FCVT_L_S, MASK_FCVT_L_S)
DECLARE_INSN(fcvt_lu_s, MATCH_FCVT_LU_S, MASK_FCVT_LU_S)
-DECLARE_INSN(fmv_x_s, MATCH_FMV_X_S, MASK_FMV_X_S)
+DECLARE_INSN(fmv_x_w, MATCH_FMV_X_W, MASK_FMV_X_W)
DECLARE_INSN(fclass_s, MATCH_FCLASS_S, MASK_FCLASS_S)
DECLARE_INSN(fcvt_w_d, MATCH_FCVT_W_D, MASK_FCVT_W_D)
DECLARE_INSN(fcvt_wu_d, MATCH_FCVT_WU_D, MASK_FCVT_WU_D)
DECLARE_INSN(fcvt_s_wu, MATCH_FCVT_S_WU, MASK_FCVT_S_WU)
DECLARE_INSN(fcvt_s_l, MATCH_FCVT_S_L, MASK_FCVT_S_L)
DECLARE_INSN(fcvt_s_lu, MATCH_FCVT_S_LU, MASK_FCVT_S_LU)
-DECLARE_INSN(fmv_s_x, MATCH_FMV_S_X, MASK_FMV_S_X)
+DECLARE_INSN(fmv_w_x, MATCH_FMV_W_X, MASK_FMV_W_X)
DECLARE_INSN(fcvt_d_w, MATCH_FCVT_D_W, MASK_FCVT_D_W)
DECLARE_INSN(fcvt_d_wu, MATCH_FCVT_D_WU, MASK_FCVT_D_WU)
DECLARE_INSN(fcvt_d_l, MATCH_FCVT_D_L, MASK_FCVT_D_L)
require_extension('C');
-require(insn.rvc_rd() != 0);
WRITE_RD(insn.rvc_imm());
require(insn.rvc_addi16sp_imm() != 0);
WRITE_REG(X_SP, sext_xlen(RVC_SP + insn.rvc_addi16sp_imm()));
} else {
- require(insn.rvc_rd() != 0);
+ require(insn.rvc_imm() != 0);
WRITE_RD(insn.rvc_imm() << 12);
}
+++ /dev/null
-require_extension('F');
-require_fp;
-WRITE_FRD(f32(RS1));
--- /dev/null
+require_extension('F');
+require_fp;
+WRITE_FRD(f32(RS1));
+++ /dev/null
-require_extension('F');
-require_fp;
-WRITE_RD(sext32(FRS1.v));
--- /dev/null
+require_extension('F');
+require_fp;
+WRITE_RD(sext32(FRS1.v));
return walk(addr, type, mode) | (addr & (PGSIZE-1));
}
-const uint16_t* mmu_t::fetch_slow_path(reg_t vaddr)
+tlb_entry_t mmu_t::fetch_slow_path(reg_t vaddr)
{
reg_t paddr = translate(vaddr, FETCH);
- if (sim->addr_is_mem(paddr)) {
- refill_tlb(vaddr, paddr, FETCH);
- return (const uint16_t*)sim->addr_to_mem(paddr);
+ if (auto host_addr = sim->addr_to_mem(paddr)) {
+ return refill_tlb(vaddr, paddr, host_addr, FETCH);
} else {
if (!sim->mmio_load(paddr, sizeof fetch_temp, (uint8_t*)&fetch_temp))
throw trap_instruction_access_fault(vaddr);
- return &fetch_temp;
+ tlb_entry_t entry = {(char*)&fetch_temp - vaddr, paddr - vaddr};
+ return entry;
}
}
{
reg_t paddr = translate(addr, LOAD);
- if (sim->addr_is_mem(paddr)) {
- memcpy(bytes, sim->addr_to_mem(paddr), len);
+ if (auto host_addr = sim->addr_to_mem(paddr)) {
+ memcpy(bytes, host_addr, len);
if (tracer.interested_in_range(paddr, paddr + PGSIZE, LOAD))
tracer.trace(paddr, len, LOAD);
else
- refill_tlb(addr, paddr, LOAD);
+ refill_tlb(addr, paddr, host_addr, LOAD);
} else if (!sim->mmio_load(paddr, len, bytes)) {
throw trap_load_access_fault(addr);
}
throw *matched_trigger;
}
- if (sim->addr_is_mem(paddr)) {
- memcpy(sim->addr_to_mem(paddr), bytes, len);
+ if (auto host_addr = sim->addr_to_mem(paddr)) {
+ memcpy(host_addr, bytes, len);
if (tracer.interested_in_range(paddr, paddr + PGSIZE, STORE))
tracer.trace(paddr, len, STORE);
else
- refill_tlb(addr, paddr, STORE);
+ refill_tlb(addr, paddr, host_addr, STORE);
} else if (!sim->mmio_store(paddr, len, bytes)) {
throw trap_store_access_fault(addr);
}
}
-void mmu_t::refill_tlb(reg_t vaddr, reg_t paddr, access_type type)
+tlb_entry_t mmu_t::refill_tlb(reg_t vaddr, reg_t paddr, char* host_addr, access_type type)
{
reg_t idx = (vaddr >> PGSHIFT) % TLB_ENTRIES;
reg_t expected_tag = vaddr >> PGSHIFT;
else if (type == STORE) tlb_store_tag[idx] = expected_tag;
else tlb_load_tag[idx] = expected_tag;
- tlb_data[idx] = sim->addr_to_mem(paddr) - vaddr;
+ tlb_entry_t entry = {host_addr - vaddr, paddr - vaddr};
+ tlb_data[idx] = entry;
+ return entry;
}
reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode)
reg_t idx = (addr >> (PGSHIFT + ptshift)) & ((1 << vm.idxbits) - 1);
// check that physical address of PTE is legal
- reg_t pte_addr = base + idx * vm.ptesize;
- if (!sim->addr_is_mem(pte_addr))
+ auto ppte = sim->addr_to_mem(base + idx * vm.ptesize);
+ if (!ppte)
throw trap_load_access_fault(addr);
- void* ppte = sim->addr_to_mem(pte_addr);
reg_t pte = vm.ptesize == 4 ? *(uint32_t*)ppte : *(uint64_t*)ppte;
reg_t ppn = pte >> PTE_PPN_SHIFT;
type == LOAD ? !(pte & PTE_R) && !(mxr && (pte & PTE_X)) :
!((pte & PTE_R) && (pte & PTE_W))) {
break;
+ } else if ((ppn & ((reg_t(1) << ptshift) - 1)) != 0) {
+ break;
} else {
reg_t ad = PTE_A | ((type == STORE) * PTE_D);
#ifdef RISCV_ENABLE_DIRTY
insn_fetch_t data;
};
+struct tlb_entry_t {
+ char* host_offset;
+ reg_t target_offset;
+};
+
class trigger_matched_t
{
public:
return misaligned_load(addr, sizeof(type##_t)); \
reg_t vpn = addr >> PGSHIFT; \
if (likely(tlb_load_tag[vpn % TLB_ENTRIES] == vpn)) \
- return *(type##_t*)(tlb_data[vpn % TLB_ENTRIES] + addr); \
+ return *(type##_t*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr); \
if (unlikely(tlb_load_tag[vpn % TLB_ENTRIES] == (vpn | TLB_CHECK_TRIGGERS))) { \
- type##_t data = *(type##_t*)(tlb_data[vpn % TLB_ENTRIES] + addr); \
+ type##_t data = *(type##_t*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr); \
if (!matched_trigger) { \
matched_trigger = trigger_exception(OPERATION_LOAD, addr, data); \
if (matched_trigger) \
return misaligned_store(addr, val, sizeof(type##_t)); \
reg_t vpn = addr >> PGSHIFT; \
if (likely(tlb_store_tag[vpn % TLB_ENTRIES] == vpn)) \
- *(type##_t*)(tlb_data[vpn % TLB_ENTRIES] + addr) = val; \
+ *(type##_t*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr) = val; \
else if (unlikely(tlb_store_tag[vpn % TLB_ENTRIES] == (vpn | TLB_CHECK_TRIGGERS))) { \
if (!matched_trigger) { \
matched_trigger = trigger_exception(OPERATION_STORE, addr, val); \
if (matched_trigger) \
throw *matched_trigger; \
} \
- *(type##_t*)(tlb_data[vpn % TLB_ENTRIES] + addr) = val; \
+ *(type##_t*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr) = val; \
} \
else \
store_slow_path(addr, sizeof(type##_t), (const uint8_t*)&val); \
inline icache_entry_t* refill_icache(reg_t addr, icache_entry_t* entry)
{
- const uint16_t* iaddr = translate_insn_addr(addr);
- insn_bits_t insn = *iaddr;
+ auto tlb_entry = translate_insn_addr(addr);
+ insn_bits_t insn = *(uint16_t*)(tlb_entry.host_offset + addr);
int length = insn_length(insn);
if (likely(length == 4)) {
- insn |= (insn_bits_t)*(const int16_t*)translate_insn_addr(addr + 2) << 16;
+ insn |= (insn_bits_t)*(const int16_t*)translate_insn_addr_to_host(addr + 2) << 16;
} else if (length == 2) {
insn = (int16_t)insn;
} else if (length == 6) {
- insn |= (insn_bits_t)*(const int16_t*)translate_insn_addr(addr + 4) << 32;
- insn |= (insn_bits_t)*(const uint16_t*)translate_insn_addr(addr + 2) << 16;
+ insn |= (insn_bits_t)*(const int16_t*)translate_insn_addr_to_host(addr + 4) << 32;
+ insn |= (insn_bits_t)*(const uint16_t*)translate_insn_addr_to_host(addr + 2) << 16;
} else {
static_assert(sizeof(insn_bits_t) == 8, "insn_bits_t must be uint64_t");
- insn |= (insn_bits_t)*(const int16_t*)translate_insn_addr(addr + 6) << 48;
- insn |= (insn_bits_t)*(const uint16_t*)translate_insn_addr(addr + 4) << 32;
- insn |= (insn_bits_t)*(const uint16_t*)translate_insn_addr(addr + 2) << 16;
+ insn |= (insn_bits_t)*(const int16_t*)translate_insn_addr_to_host(addr + 6) << 48;
+ insn |= (insn_bits_t)*(const uint16_t*)translate_insn_addr_to_host(addr + 4) << 32;
+ insn |= (insn_bits_t)*(const uint16_t*)translate_insn_addr_to_host(addr + 2) << 16;
}
insn_fetch_t fetch = {proc->decode_insn(insn), insn};
entry->tag = addr;
entry->data = fetch;
- reg_t paddr = sim->mem_to_addr((char*)iaddr);
+ reg_t paddr = tlb_entry.target_offset + addr;;
if (tracer.interested_in_range(paddr, paddr + 1, FETCH)) {
entry->tag = -1;
tracer.trace(paddr, length, FETCH);
// If a TLB tag has TLB_CHECK_TRIGGERS set, then the MMU must check for a
// trigger match before completing an access.
static const reg_t TLB_CHECK_TRIGGERS = reg_t(1) << 63;
- char* tlb_data[TLB_ENTRIES];
+ tlb_entry_t tlb_data[TLB_ENTRIES];
reg_t tlb_insn_tag[TLB_ENTRIES];
reg_t tlb_load_tag[TLB_ENTRIES];
reg_t tlb_store_tag[TLB_ENTRIES];
// finish translation on a TLB miss and update the TLB
- void refill_tlb(reg_t vaddr, reg_t paddr, access_type type);
+ tlb_entry_t refill_tlb(reg_t vaddr, reg_t paddr, char* host_addr, access_type type);
const char* fill_from_mmio(reg_t vaddr, reg_t paddr);
// perform a page table walk for a given VA; set referenced/dirty bits
reg_t walk(reg_t addr, access_type type, reg_t prv);
// handle uncommon cases: TLB misses, page faults, MMIO
- const uint16_t* fetch_slow_path(reg_t addr);
+ tlb_entry_t fetch_slow_path(reg_t addr);
void load_slow_path(reg_t addr, reg_t len, uint8_t* bytes);
void store_slow_path(reg_t addr, reg_t len, const uint8_t* bytes);
reg_t translate(reg_t addr, access_type type);
// ITLB lookup
- inline const uint16_t* translate_insn_addr(reg_t addr) {
+ inline tlb_entry_t translate_insn_addr(reg_t addr) {
reg_t vpn = addr >> PGSHIFT;
if (likely(tlb_insn_tag[vpn % TLB_ENTRIES] == vpn))
- return (uint16_t*)(tlb_data[vpn % TLB_ENTRIES] + addr);
+ return tlb_data[vpn % TLB_ENTRIES];
if (unlikely(tlb_insn_tag[vpn % TLB_ENTRIES] == (vpn | TLB_CHECK_TRIGGERS))) {
- uint16_t* ptr = (uint16_t*)(tlb_data[vpn % TLB_ENTRIES] + addr);
+ uint16_t* ptr = (uint16_t*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr);
int match = proc->trigger_match(OPERATION_EXECUTE, addr, *ptr);
if (match >= 0)
throw trigger_matched_t(match, OPERATION_EXECUTE, addr, *ptr);
- return ptr;
+ return tlb_data[vpn % TLB_ENTRIES];
}
return fetch_slow_path(addr);
}
+ inline const uint16_t* translate_insn_addr_to_host(reg_t addr) {
+ return (uint16_t*)(translate_insn_addr(addr).host_offset + addr);
+ }
+
inline trigger_matched_t *trigger_exception(trigger_operation_t operation,
reg_t address, reg_t data)
{
throw trap_t(((reg_t)1 << (max_xlen-1)) | ctz(enabled_interrupts));
}
+static int xlen_to_uxl(int xlen)
+{
+ if (xlen == 32)
+ return 1;
+ if (xlen == 64)
+ return 2;
+ abort();
+}
+
void processor_t::set_privilege(reg_t prv)
{
assert(prv <= PRV_M);
reg_t mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE
| MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM
| MSTATUS_MPP | MSTATUS_MXR | MSTATUS_TW | MSTATUS_TVM
- | MSTATUS_TSR | (ext ? MSTATUS_XS : 0);
+ | MSTATUS_TSR | MSTATUS_UXL | MSTATUS_SXL |
+ (ext ? MSTATUS_XS : 0);
state.mstatus = (state.mstatus & ~mask) | (val & mask);
else
state.mstatus = set_field(state.mstatus, MSTATUS64_SD, dirty);
- // spike supports the notion of xlen < max_xlen, but current priv spec
- // doesn't provide a mechanism to run RV32 software on an RV64 machine
+ state.mstatus = set_field(state.mstatus, MSTATUS_UXL, xlen_to_uxl(max_xlen));
+ state.mstatus = set_field(state.mstatus, MSTATUS_SXL, xlen_to_uxl(max_xlen));
+ // U-XLEN == S-XLEN == M-XLEN
xlen = max_xlen;
break;
}
case CSR_MCOUNTEREN: return state.mcounteren;
case CSR_SSTATUS: {
reg_t mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_SPP | SSTATUS_FS
- | SSTATUS_XS | SSTATUS_SUM;
+ | SSTATUS_XS | SSTATUS_SUM | SSTATUS_UXL;
reg_t sstatus = state.mstatus & mask;
if ((sstatus & SSTATUS_FS) == SSTATUS_FS ||
(sstatus & SSTATUS_XS) == SSTATUS_XS)
fmul_d \
fmul_s \
fmv_d_x \
- fmv_s_x \
+ fmv_w_x \
fmv_x_d \
- fmv_x_s \
+ fmv_x_w \
fnmadd_d \
fnmadd_s \
fnmsub_d \
signal(sig, &handle_signal);
}
-sim_t::sim_t(const char* isa, size_t nprocs, size_t mem_mb, bool halted,
+sim_t::sim_t(const char* isa, size_t nprocs, bool halted, reg_t start_pc,
+ std::vector<std::pair<reg_t, mem_t*>> mems,
const std::vector<std::string>& args)
- : htif_t(args), debug_module(this), procs(std::max(nprocs, size_t(1))),
+ : htif_t(args), debug_module(this), mems(mems), procs(std::max(nprocs, size_t(1))),
+ start_pc(start_pc),
current_step(0), current_proc(0), debug(false), remote_bitbang(NULL)
{
signal(SIGINT, &handle_signal);
- // allocate target machine's memory, shrinking it as necessary
- // until the allocation succeeds
- size_t memsz0 = (size_t)mem_mb << 20;
- size_t quantum = 1L << 20;
- if (memsz0 == 0)
- memsz0 = (size_t)2048 << 20;
- memsz = memsz0;
- while ((mem = (char*)calloc(1, memsz)) == NULL)
- memsz = (size_t)(memsz*0.9)/quantum*quantum;
-
- if (memsz != memsz0)
- fprintf(stderr, "warning: only got %zu bytes of target mem (wanted %zu)\n",
- memsz, memsz0);
+ for (auto& x : mems)
+ bus.add_device(x.first, x.second);
debug_module.add_device(&bus);
clint.reset(new clint_t(procs));
bus.add_device(CLINT_BASE, clint.get());
-
- make_dtb();
}
sim_t::~sim_t()
for (size_t i = 0; i < procs.size(); i++)
delete procs[i];
delete debug_mmu;
- free(mem);
}
void sim_thread_main(void* arg)
void sim_t::make_dtb()
{
- uint32_t reset_vec[] = {
- 0x297 + DRAM_BASE - DEFAULT_RSTVEC, // auipc t0, DRAM_BASE
- 0x597, // auipc a1, 0
- 0x58593, // addi a1, a1, 0
- 0xf1402573, // csrr a0,mhartid
- 0x00028067 // jalr zero, t0, 0 (jump straight to DRAM_BASE)
+ const int reset_vec_size = 8;
+
+ start_pc = start_pc == reg_t(-1) ? get_entry_point() : start_pc;
+ reg_t pc_delta = start_pc - DEFAULT_RSTVEC;
+ reg_t pc_delta_hi = (pc_delta + 0x800U) & ~reg_t(0xfffU);
+ reg_t pc_delta_lo = pc_delta - pc_delta_hi;
+ if ((pc_delta_hi >> 31) != 0 && (pc_delta_hi >> 31) != reg_t(-1) >> 31) {
+ fprintf(stderr, "initial pc %" PRIx64 " out of range\n", pc_delta);
+ abort();
+ }
+
+ uint32_t reset_vec[reset_vec_size] = {
+ 0x297 + uint32_t(pc_delta_hi), // auipc t0, &pc
+ 0x597, // auipc a1, &dtb
+ 0x58593 + ((reset_vec_size - 1) * 4 << 20), // addi a1, a1, &dtb
+ 0xf1402573, // csrr a0, mhartid
+ 0x28067 + uint32_t(pc_delta_lo << 20) // jalr zero, t0, &pc
};
- reset_vec[2] += (sizeof(reset_vec) - 4) << 20; // addi a1, a1, sizeof(reset_vec) - 4 = DTB start
std::vector<char> rom((char*)reset_vec, (char*)reset_vec + sizeof(reset_vec));
" };\n"
" };\n";
}
- reg_t membs = DRAM_BASE;
- s << std::hex <<
- " };\n"
- " memory@" << DRAM_BASE << " {\n"
+ s << " };\n";
+ for (auto& m : mems) {
+ s << std::hex <<
+ " memory@" << m.first << " {\n"
" device_type = \"memory\";\n"
- " reg = <0x" << (membs >> 32) << " 0x" << (membs & (uint32_t)-1) <<
- " 0x" << (memsz >> 32) << " 0x" << (memsz & (uint32_t)-1) << ">;\n"
- " };\n"
- " soc {\n"
+ " reg = <0x" << (m.first >> 32) << " 0x" << (m.first & (uint32_t)-1) <<
+ " 0x" << (m.second->size() >> 32) << " 0x" << (m.second->size() & (uint32_t)-1) << ">;\n"
+ " };\n";
+ }
+ s << " soc {\n"
" #address-cells = <2>;\n"
" #size-cells = <2>;\n"
" compatible = \"ucbbar,spike-bare-soc\", \"simple-bus\";\n"
bus.add_device(DEFAULT_RSTVEC, boot_rom.get());
}
+char* sim_t::addr_to_mem(reg_t addr) {
+ auto desc = bus.find_device(addr);
+ if (auto mem = dynamic_cast<mem_t*>(desc.second))
+ if (addr - desc.first < mem->size())
+ return mem->contents() + (addr - desc.first);
+ return NULL;
+}
+
// htif
+void sim_t::reset()
+{
+ make_dtb();
+}
+
void sim_t::idle()
{
target.switch_to();
class sim_t : public htif_t
{
public:
- sim_t(const char* isa, size_t _nprocs, size_t mem_mb, bool halted,
+ sim_t(const char* isa, size_t _nprocs, bool halted, reg_t start_pc,
+ std::vector<std::pair<reg_t, mem_t*>> mems,
const std::vector<std::string>& args);
~sim_t();
void set_remote_bitbang(remote_bitbang_t* remote_bitbang) {
this->remote_bitbang = remote_bitbang;
}
- const char* get_dts() { return dts.c_str(); }
+ const char* get_dts() { if (dts.empty()) reset(); return dts.c_str(); }
processor_t* get_core(size_t i) { return procs.at(i); }
unsigned nprocs() const { return procs.size(); }
debug_module_t debug_module;
private:
- char* mem; // main memory
- size_t memsz; // memory size in bytes
+ std::vector<std::pair<reg_t, mem_t*>> mems;
mmu_t* debug_mmu; // debug port into main memory
std::vector<processor_t*> procs;
+ reg_t start_pc;
std::string dts;
std::unique_ptr<rom_device_t> boot_rom;
std::unique_ptr<clint_t> clint;
remote_bitbang_t* remote_bitbang;
// memory-mapped I/O routines
- bool addr_is_mem(reg_t addr) {
- return addr >= DRAM_BASE && addr < DRAM_BASE + memsz;
- }
- char* addr_to_mem(reg_t addr) { return mem + addr - DRAM_BASE; }
- reg_t mem_to_addr(char* x) { return x - mem + DRAM_BASE; }
+ char* addr_to_mem(reg_t addr);
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_dtb();
context_t* host;
context_t target;
- void reset() { }
+ void reset();
void idle();
void read_chunk(addr_t taddr, size_t len, void* dst);
void write_chunk(addr_t taddr, size_t len, const void* src);
DEFINE_NOARG(ebreak);
DEFINE_NOARG(uret);
DEFINE_NOARG(sret);
- DEFINE_NOARG(hret);
DEFINE_NOARG(mret);
DEFINE_NOARG(fence);
DEFINE_NOARG(fence_i);
DEFINE_XFTYPE(fcvt_s_w);
DEFINE_XFTYPE(fcvt_s_wu);
DEFINE_XFTYPE(fcvt_s_wu);
- DEFINE_XFTYPE(fmv_s_x);
+ DEFINE_XFTYPE(fmv_w_x);
DEFINE_FXTYPE(fcvt_l_s);
DEFINE_FXTYPE(fcvt_lu_s);
DEFINE_FXTYPE(fcvt_w_s);
DEFINE_FXTYPE(fcvt_wu_s);
DEFINE_FXTYPE(fclass_s);
- DEFINE_FXTYPE(fmv_x_s);
+ DEFINE_FXTYPE(fmv_x_w);
DEFINE_FXTYPE(feq_s);
DEFINE_FXTYPE(flt_s);
DEFINE_FXTYPE(fle_s);
fprintf(stderr, "usage: spike [host options] <target program> [target options]\n");
fprintf(stderr, "Host Options:\n");
fprintf(stderr, " -p<n> Simulate <n> processors [default 1]\n");
- fprintf(stderr, " -m<n> Provide <n> MiB of target memory [default 4096]\n");
+ fprintf(stderr, " -m<n> Provide <n> MiB of target memory [default 2048]\n");
+ fprintf(stderr, " -m<a:m,b:n,...> Provide memory regions of size m and n bytes\n");
+ fprintf(stderr, " at base addresses a and b (with 4 KiB alignment)\n");
fprintf(stderr, " -d Interactive debug mode\n");
fprintf(stderr, " -g Track histogram of PCs\n");
fprintf(stderr, " -l Generate a log of execution\n");
fprintf(stderr, " -h Print this help message\n");
fprintf(stderr, " -H Start halted, allowing a debugger to connect\n");
fprintf(stderr, " --isa=<name> RISC-V ISA string [default %s]\n", DEFAULT_ISA);
+ fprintf(stderr, " --pc=<address> Override ELF entry point\n");
fprintf(stderr, " --ic=<S>:<W>:<B> Instantiate a cache model with S sets,\n");
fprintf(stderr, " --dc=<S>:<W>:<B> W ways, and B-byte blocks (with S and\n");
fprintf(stderr, " --l2=<S>:<W>:<B> B both powers of 2).\n");
exit(1);
}
+static std::vector<std::pair<reg_t, mem_t*>> make_mems(const char* arg)
+{
+ // handle legacy mem argument
+ char* p;
+ auto mb = strtoull(arg, &p, 0);
+ if (*p == 0) {
+ reg_t size = reg_t(mb) << 20;
+ return std::vector<std::pair<reg_t, mem_t*>>(1, std::make_pair(reg_t(DRAM_BASE), new mem_t(size)));
+ }
+
+ // handle base/size tuples
+ std::vector<std::pair<reg_t, mem_t*>> res;
+ while (true) {
+ auto base = strtoull(arg, &p, 0);
+ if (!*p || *p != ':')
+ help();
+ auto size = strtoull(p + 1, &p, 0);
+ if ((size | base) % PGSIZE != 0)
+ help();
+ res.push_back(std::make_pair(reg_t(base), new mem_t(size)));
+ if (!*p)
+ break;
+ if (*p != ',')
+ help();
+ arg = p + 1;
+ }
+ return res;
+}
+
int main(int argc, char** argv)
{
bool debug = false;
bool log = false;
bool dump_dts = false;
size_t nprocs = 1;
- size_t mem_mb = 0;
+ reg_t start_pc = reg_t(-1);
+ std::vector<std::pair<reg_t, mem_t*>> mems;
std::unique_ptr<icache_sim_t> ic;
std::unique_ptr<dcache_sim_t> dc;
std::unique_ptr<cache_sim_t> l2;
parser.option('g', 0, 0, [&](const char* s){histogram = true;});
parser.option('l', 0, 0, [&](const char* s){log = true;});
parser.option('p', 0, 1, [&](const char* s){nprocs = atoi(s);});
- parser.option('m', 0, 1, [&](const char* s){mem_mb = atoi(s);});
+ parser.option('m', 0, 1, [&](const char* s){mems = make_mems(s);});
// I wanted to use --halted, but for some reason that doesn't work.
parser.option('H', 0, 0, [&](const char* s){halted = true;});
parser.option(0, "rbb-port", 1, [&](const char* s){use_rbb = true; rbb_port = atoi(s);});
+ parser.option(0, "pc", 1, [&](const char* s){start_pc = strtoull(s, 0, 0);});
parser.option(0, "ic", 1, [&](const char* s){ic.reset(new icache_sim_t(s));});
parser.option(0, "dc", 1, [&](const char* s){dc.reset(new dcache_sim_t(s));});
parser.option(0, "l2", 1, [&](const char* s){l2.reset(cache_sim_t::construct(s, "L2$"));});
auto argv1 = parser.parse(argv);
std::vector<std::string> htif_args(argv1, (const char*const*)argv + argc);
- sim_t s(isa, nprocs, mem_mb, halted, htif_args);
- std::unique_ptr<jtag_dtm_t> jtag_dtm(new jtag_dtm_t(&s.debug_module));
+ if (mems.empty())
+ mems = make_mems("2048");
+
+ sim_t s(isa, nprocs, halted, start_pc, mems, htif_args);
std::unique_ptr<remote_bitbang_t> remote_bitbang((remote_bitbang_t *) NULL);
+ std::unique_ptr<jtag_dtm_t> jtag_dtm(new jtag_dtm_t(&s.debug_module));
if (use_rbb) {
remote_bitbang.reset(new remote_bitbang_t(rbb_port, &(*jtag_dtm)));
s.set_remote_bitbang(&(*remote_bitbang));