CFLAGS="-Wall -O2 -Wno-unused"
-CXXFLAGS="-Wall -O2 -std=c++0x -Wno-pmf-conversions"
+CXXFLAGS="-Wall -O2 -std=c++0x"
#-------------------------------------------------------------------------
#-------------------------------------------------------------------------
AC_SUBST([CFLAGS], ["-Wall -O2 -Wno-unused"])
-AC_SUBST([CXXFLAGS],["-Wall -O2 -std=c++0x -Wno-pmf-conversions"])
+AC_SUBST([CXXFLAGS],["-Wall -O2 -std=c++0x"])
#-------------------------------------------------------------------------
# MCPPBS subproject list
};
// helpful macros, etc
-#define RS1 XPR[insn.rtype.rs1]
-#define RS2 XPR[insn.rtype.rs2]
-#define RD XPR.write_port(insn.rtype.rd)
-#define RA XPR.write_port(1)
-#define FRS1 FPR[insn.ftype.rs1]
-#define FRS2 FPR[insn.ftype.rs2]
-#define FRS3 FPR[insn.ftype.rs3]
-#define FRD FPR.write_port(insn.ftype.rd)
+#define MMU (*p->get_mmu())
+#define RS1 p->get_state()->XPR[insn.rtype.rs1]
+#define RS2 p->get_state()->XPR[insn.rtype.rs2]
+#define RD p->get_state()->XPR.write_port(insn.rtype.rd)
+#define RA p->get_state()->XPR.write_port(1)
+#define FRS1 p->get_state()->FPR[insn.ftype.rs1]
+#define FRS2 p->get_state()->FPR[insn.ftype.rs2]
+#define FRS3 p->get_state()->FPR[insn.ftype.rs3]
+#define FRD p->get_state()->FPR.write_port(insn.ftype.rd)
#define BIGIMM insn.ltype.bigimm
#define SIMM insn.itype.imm12
#define BIMM ((signed)insn.btype.immlo | (insn.btype.immhi << IMMLO_BITS))
#define ITYPE_EADDR sext_xprlen(RS1 + SIMM)
#define BTYPE_EADDR sext_xprlen(RS1 + BIMM)
#define RM ({ int rm = insn.ftype.rm; \
- if(rm == 7) rm = (fsr & FSR_RD) >> FSR_RD_SHIFT; \
- if(rm > 4) throw trap_illegal_instruction; \
+ if(rm == 7) rm = (p->get_state()->fsr & FSR_RD) >> FSR_RD_SHIFT; \
+ if(rm > 4) throw trap_illegal_instruction(); \
rm; })
#define xpr64 (xprlen == 64)
-#define require_supervisor if(unlikely(!(sr & SR_S))) throw trap_privileged_instruction
-#define require_xpr64 if(unlikely(!xpr64)) throw trap_illegal_instruction
-#define require_xpr32 if(unlikely(xpr64)) throw trap_illegal_instruction
+#define require_supervisor if(unlikely(!(p->get_state()->sr & SR_S))) throw trap_privileged_instruction()
+#define require_xpr64 if(unlikely(!xpr64)) throw trap_illegal_instruction()
+#define require_xpr32 if(unlikely(xpr64)) throw trap_illegal_instruction()
#ifndef RISCV_ENABLE_FPU
-# define require_fp throw trap_illegal_instruction
+# define require_fp throw trap_illegal_instruction()
#else
-# define require_fp if(unlikely(!(sr & SR_EF))) throw trap_fp_disabled
+# define require_fp if(unlikely(!(p->get_state()->sr & SR_EF))) throw trap_fp_disabled()
#endif
#define cmp_trunc(reg) (reg_t(reg) << (64-xprlen))
-#define set_fp_exceptions ({ set_fsr(fsr | \
+#define set_fp_exceptions ({ p->set_fsr(p->get_state()->fsr | \
(softfloat_exceptionFlags << FSR_AEXC_SHIFT)); \
softfloat_exceptionFlags = 0; })
#define set_pc(x) \
do { if ((x) & 3 /* For now... */) \
- throw trap_instruction_address_misaligned; \
+ throw trap_instruction_address_misaligned(); \
npc = (x); \
} while(0)
uint64_t buf[hdr.data_size];
for (size_t i = 0; i < hdr.data_size; i++)
- buf[i] = sim->mmu->load_uint64((hdr.addr+i)*HTIF_DATA_ALIGN);
+ buf[i] = sim->debug_mmu->load_uint64((hdr.addr+i)*HTIF_DATA_ALIGN);
send(buf, hdr.data_size * sizeof(buf[0]));
break;
}
{
const uint64_t* buf = (const uint64_t*)p.get_payload();
for (size_t i = 0; i < hdr.data_size; i++)
- sim->mmu->store_uint64((hdr.addr+i)*HTIF_DATA_ALIGN, buf[i]);
+ sim->debug_mmu->store_uint64((hdr.addr+i)*HTIF_DATA_ALIGN, buf[i]);
packet_header_t ack(HTIF_CMD_ACK, seqno, 0, 0);
send(&ack, sizeof(ack));
send(&old_val, sizeof(old_val));
if (regno == PCR_TOHOST)
- sim->procs[coreid]->tohost = 0;
+ sim->procs[coreid]->state.tohost = 0;
if (hdr.cmd == HTIF_CMD_WRITE_CONTROL_REG)
{
#include "internals.h" // ditto
#include <assert.h>
-reg_t processor_t::rv32_NAME(insn_t insn, reg_t pc)
+reg_t rv32_NAME(processor_t* p, insn_t insn, reg_t pc)
{
int xprlen = 32;
reg_t npc = sext_xprlen(pc + insn_length(OPCODE));
return npc;
}
-reg_t processor_t::rv64_NAME(insn_t insn, reg_t pc)
+reg_t rv64_NAME(processor_t* p, insn_t insn, reg_t pc)
{
int xprlen = 64;
reg_t npc = sext_xprlen(pc + insn_length(OPCODE));
require_xpr64;
-reg_t v = mmu.load_uint64(RS1);
-mmu.store_uint64(RS1, RS2 + v);
+reg_t v = MMU.load_uint64(RS1);
+MMU.store_uint64(RS1, RS2 + v);
RD = v;
-reg_t v = mmu.load_int32(RS1);
-mmu.store_uint32(RS1, RS2 + v);
+reg_t v = MMU.load_int32(RS1);
+MMU.store_uint32(RS1, RS2 + v);
RD = v;
require_xpr64;
-reg_t v = mmu.load_uint64(RS1);
-mmu.store_uint64(RS1, RS2 & v);
+reg_t v = MMU.load_uint64(RS1);
+MMU.store_uint64(RS1, RS2 & v);
RD = v;
-reg_t v = mmu.load_int32(RS1);
-mmu.store_uint32(RS1, RS2 & v);
+reg_t v = MMU.load_int32(RS1);
+MMU.store_uint32(RS1, RS2 & v);
RD = v;
require_xpr64;
-sreg_t v = mmu.load_int64(RS1);
-mmu.store_uint64(RS1, std::max(sreg_t(RS2),v));
+sreg_t v = MMU.load_int64(RS1);
+MMU.store_uint64(RS1, std::max(sreg_t(RS2),v));
RD = v;
-int32_t v = mmu.load_int32(RS1);
-mmu.store_uint32(RS1, std::max(int32_t(RS2),v));
+int32_t v = MMU.load_int32(RS1);
+MMU.store_uint32(RS1, std::max(int32_t(RS2),v));
RD = v;
require_xpr64;
-reg_t v = mmu.load_uint64(RS1);
-mmu.store_uint64(RS1, std::max(RS2,v));
+reg_t v = MMU.load_uint64(RS1);
+MMU.store_uint64(RS1, std::max(RS2,v));
RD = v;
-uint32_t v = mmu.load_int32(RS1);
-mmu.store_uint32(RS1, std::max(uint32_t(RS2),v));
+uint32_t v = MMU.load_int32(RS1);
+MMU.store_uint32(RS1, std::max(uint32_t(RS2),v));
RD = (int32_t)v;
require_xpr64;
-sreg_t v = mmu.load_int64(RS1);
-mmu.store_uint64(RS1, std::min(sreg_t(RS2),v));
+sreg_t v = MMU.load_int64(RS1);
+MMU.store_uint64(RS1, std::min(sreg_t(RS2),v));
RD = v;
-int32_t v = mmu.load_int32(RS1);
-mmu.store_uint32(RS1, std::min(int32_t(RS2),v));
+int32_t v = MMU.load_int32(RS1);
+MMU.store_uint32(RS1, std::min(int32_t(RS2),v));
RD = v;
require_xpr64;
-reg_t v = mmu.load_uint64(RS1);
-mmu.store_uint64(RS1, std::min(RS2,v));
+reg_t v = MMU.load_uint64(RS1);
+MMU.store_uint64(RS1, std::min(RS2,v));
RD = v;
-uint32_t v = mmu.load_int32(RS1);
-mmu.store_uint32(RS1, std::min(uint32_t(RS2),v));
+uint32_t v = MMU.load_int32(RS1);
+MMU.store_uint32(RS1, std::min(uint32_t(RS2),v));
RD = (int32_t)v;
require_xpr64;
-reg_t v = mmu.load_uint64(RS1);
-mmu.store_uint64(RS1, RS2 | v);
+reg_t v = MMU.load_uint64(RS1);
+MMU.store_uint64(RS1, RS2 | v);
RD = v;
-reg_t v = mmu.load_int32(RS1);
-mmu.store_uint32(RS1, RS2 | v);
+reg_t v = MMU.load_int32(RS1);
+MMU.store_uint32(RS1, RS2 | v);
RD = v;
require_xpr64;
-reg_t v = mmu.load_uint64(RS1);
-mmu.store_uint64(RS1, RS2);
+reg_t v = MMU.load_uint64(RS1);
+MMU.store_uint64(RS1, RS2);
RD = v;
-reg_t v = mmu.load_int32(RS1);
-mmu.store_uint32(RS1, RS2);
+reg_t v = MMU.load_int32(RS1);
+MMU.store_uint32(RS1, RS2);
RD = v;
-throw trap_breakpoint;
+throw trap_breakpoint();
require_supervisor;
-reg_t temp = get_pcr(insn.rtype.rs1);
-set_pcr(insn.rtype.rs1, temp & ~SIMM);
-RD = temp;
+RD = p->set_pcr(insn.rtype.rs1, p->get_pcr(insn.rtype.rs1) & ~SIMM);
require_supervisor;
-set_pcr(PCR_SR, ((sr & ~(SR_S | SR_EI)) |
- ((sr & SR_PS) ? SR_S : 0)) |
- ((sr & SR_PEI) ? SR_EI : 0));
-set_pc(epc);
+p->set_pcr(PCR_SR, ((p->get_state()->sr & ~(SR_S | SR_EI)) |
+ ((p->get_state()->sr & SR_PS) ? SR_S : 0)) |
+ ((p->get_state()->sr & SR_PEI) ? SR_EI : 0));
+set_pc(p->get_state()->epc);
-mmu.flush_icache();
+MMU.flush_icache();
require_fp;
-FRD = mmu.load_int64(ITYPE_EADDR);
+FRD = MMU.load_int64(ITYPE_EADDR);
require_fp;
-FRD = mmu.load_int32(ITYPE_EADDR);
+FRD = MMU.load_int32(ITYPE_EADDR);
require_fp;
-RD = fsr;
+RD = p->get_fsr();
require_fp;
-mmu.store_uint64(BTYPE_EADDR, FRS2);
+MMU.store_uint64(BTYPE_EADDR, FRS2);
require_fp;
-uint32_t tmp = fsr;
-set_fsr(RS1);
-RD = tmp;
+RD = p->set_fsr(RS1);
require_fp;
-mmu.store_uint32(BTYPE_EADDR, FRS2);
+MMU.store_uint32(BTYPE_EADDR, FRS2);
-RD = mmu.load_int8(ITYPE_EADDR);
+RD = MMU.load_int8(ITYPE_EADDR);
-RD = mmu.load_uint8(ITYPE_EADDR);
+RD = MMU.load_uint8(ITYPE_EADDR);
require_xpr64;
-RD = mmu.load_int64(ITYPE_EADDR);
+RD = MMU.load_int64(ITYPE_EADDR);
-RD = mmu.load_int16(ITYPE_EADDR);
+RD = MMU.load_int16(ITYPE_EADDR);
-RD = mmu.load_uint16(ITYPE_EADDR);
+RD = MMU.load_uint16(ITYPE_EADDR);
require_xpr64;
-RD = mmu.load_reserved_int64(RS1);
+p->get_state()->load_reservation = RS1;
+RD = MMU.load_int64(RS1);
-RD = mmu.load_reserved_int32(RS1);
+p->get_state()->load_reservation = RS1;
+RD = MMU.load_int32(RS1);
-RD = mmu.load_int32(ITYPE_EADDR);
+RD = MMU.load_int32(ITYPE_EADDR);
require_xpr64;
-RD = mmu.load_uint32(ITYPE_EADDR);
+RD = MMU.load_uint32(ITYPE_EADDR);
require_supervisor;
-RD = get_pcr(insn.rtype.rs1);
+RD = p->get_pcr(insn.rtype.rs1);
require_supervisor;
-reg_t val = get_pcr(insn.rtype.rs1);
-set_pcr(insn.rtype.rs1, RS2);
-RD = val;
+RD = p->set_pcr(insn.rtype.rs1, RS2);
-RD = cycle;
+RD = sext_xprlen(p->get_state()->cycle);
-RD = cycle;
+#include "insns/rdcycle.h"
-RD = cycle;
+#include "insns/rdcycle.h"
-mmu.store_uint8(BTYPE_EADDR, RS2);
+MMU.store_uint8(BTYPE_EADDR, RS2);
require_xpr64;
-RD = mmu.store_conditional_uint64(RS1, RS2);
+if (RS1 == p->get_state()->load_reservation)
+{
+ MMU.store_uint64(RS1, RS2);
+ RD = 0;
+}
+else
+ RD = 1;
-RD = mmu.store_conditional_uint32(RS1, RS2);
+if (RS1 == p->get_state()->load_reservation)
+{
+ MMU.store_uint32(RS1, RS2);
+ RD = 0;
+}
+else
+ RD = 1;
require_xpr64;
-mmu.store_uint64(BTYPE_EADDR, RS2);
+MMU.store_uint64(BTYPE_EADDR, RS2);
require_supervisor;
-reg_t temp = get_pcr(insn.rtype.rs1);
-set_pcr(insn.rtype.rs1, temp | SIMM);
-RD = temp;
+RD = p->set_pcr(insn.rtype.rs1, p->get_pcr(insn.rtype.rs1) | SIMM);
-mmu.store_uint16(BTYPE_EADDR, RS2);
+MMU.store_uint16(BTYPE_EADDR, RS2);
else
{
if(SHAMT & 0x20)
- throw trap_illegal_instruction;
+ throw trap_illegal_instruction();
RD = sext32(RS1 << SHAMT);
}
else
{
if(SHAMT & 0x20)
- throw trap_illegal_instruction;
+ throw trap_illegal_instruction();
RD = sext32(int32_t(RS1) >> SHAMT);
}
else
{
if(SHAMT & 0x20)
- throw trap_illegal_instruction;
+ throw trap_illegal_instruction();
RD = sext32((uint32_t)RS1 >> SHAMT);
}
-mmu.store_uint32(BTYPE_EADDR, RS2);
+MMU.store_uint32(BTYPE_EADDR, RS2);
-throw trap_syscall;
+throw trap_syscall();
reg_t sim_t::get_pc(const std::vector<std::string>& args)
{
if(args.size() != 1)
- throw trap_illegal_instruction;
+ throw trap_illegal_instruction();
int p = atoi(args[0].c_str());
if(p >= (int)num_cores())
- throw trap_illegal_instruction;
+ throw trap_illegal_instruction();
- return procs[p]->pc;
+ return procs[p]->state.pc;
}
reg_t sim_t::get_reg(const std::vector<std::string>& args)
{
if(args.size() != 2)
- throw trap_illegal_instruction;
+ throw trap_illegal_instruction();
int p = atoi(args[0].c_str());
int r = atoi(args[1].c_str());
if(p >= (int)num_cores() || r >= NXPR)
- throw trap_illegal_instruction;
+ throw trap_illegal_instruction();
- return procs[p]->XPR[r];
+ return procs[p]->state.XPR[r];
}
reg_t sim_t::get_freg(const std::vector<std::string>& args)
{
if(args.size() != 2)
- throw trap_illegal_instruction;
+ throw trap_illegal_instruction();
int p = atoi(args[0].c_str());
int r = atoi(args[1].c_str());
if(p >= (int)num_cores() || r >= NFPR)
- throw trap_illegal_instruction;
+ throw trap_illegal_instruction();
- return procs[p]->FPR[r];
+ return procs[p]->state.FPR[r];
}
void sim_t::interactive_reg(const std::string& cmd, const std::vector<std::string>& args)
reg_t sim_t::get_mem(const std::vector<std::string>& args)
{
if(args.size() != 1 && args.size() != 2)
- throw trap_illegal_instruction;
+ throw trap_illegal_instruction();
std::string addr_str = args[0];
+ mmu_t* mmu = debug_mmu;
if(args.size() == 2)
{
int p = atoi(args[0].c_str());
if(p >= (int)num_cores())
- throw trap_illegal_instruction;
- mmu->set_ptbr(procs[p]->mmu.get_ptbr());
+ throw trap_illegal_instruction();
+ mmu = procs[p]->get_mmu();
addr_str = args[1];
}
void sim_t::interactive_str(const std::string& cmd, const std::vector<std::string>& args)
{
if(args.size() != 1)
- throw trap_illegal_instruction;
+ throw trap_illegal_instruction();
reg_t addr = strtol(args[0].c_str(),NULL,16);
char ch;
- while((ch = mmu->load_uint8(addr++)))
+ while((ch = debug_mmu->load_uint8(addr++)))
putchar(ch);
putchar('\n');
#include "processor.h"
mmu_t::mmu_t(char* _mem, size_t _memsz)
- : mem(_mem), memsz(_memsz), badvaddr(0),
- ptbr(0), proc(NULL)
+ : mem(_mem), memsz(_memsz), proc(NULL)
{
flush_tlb();
}
memset(tlb_store_tag, -1, sizeof(tlb_store_tag));
flush_icache();
- yield_load_reservation();
}
reg_t mmu_t::refill_tlb(reg_t addr, reg_t bytes, bool store, bool fetch)
reg_t pte = walk(addr);
reg_t pte_perm = pte & PTE_PERM;
- if (proc == NULL || (proc->sr & SR_S))
+ if (proc == NULL || (proc->state.sr & SR_S))
pte_perm = (pte_perm/(PTE_SX/PTE_UX)) & PTE_PERM;
pte_perm |= pte & PTE_V;
if(unlikely((pte_perm & perm) != perm))
{
if (fetch)
- throw trap_instruction_access_fault;
+ throw trap_instruction_access_fault();
- badvaddr = addr;
- throw store ? trap_store_access_fault : trap_load_access_fault;
+ if (store)
+ throw trap_store_access_fault(addr);
+ throw trap_load_access_fault(addr);
}
reg_t pgoff = addr & (PGSIZE-1);
int shift = 8*sizeof(reg_t) - VA_BITS;
if (((sreg_t)addr << shift >> shift) != (sreg_t)addr)
;
- else if (proc == NULL || !(proc->sr & SR_VM))
+ else if (proc == NULL || !(proc->state.sr & SR_VM))
{
if(addr < memsz)
pte = PTE_V | PTE_PERM | ((addr >> PGSHIFT) << PGSHIFT);
}
else
{
- reg_t base = ptbr;
+ reg_t base = proc->get_state()->ptbr;
reg_t ptd;
int ptshift = (LEVELS-1)*PTIDXBITS;
#define load_func(type) \
type##_t load_##type(reg_t addr) { \
if(unlikely(addr % sizeof(type##_t))) \
- { \
- badvaddr = addr; \
- throw trap_load_address_misaligned; \
- } \
+ throw trap_load_address_misaligned(addr); \
reg_t paddr = translate(addr, sizeof(type##_t), false, false); \
return *(type##_t*)(mem + paddr); \
- } \
- type##_t load_reserved_##type(reg_t addr) { \
- load_reservation = addr; \
- return load_##type(addr); \
}
// load value from memory at aligned address; zero extend to register width
#define store_func(type) \
void store_##type(reg_t addr, type##_t val) { \
if(unlikely(addr % sizeof(type##_t))) \
- { \
- badvaddr = addr; \
- throw trap_store_address_misaligned; \
- } \
+ throw trap_store_address_misaligned(addr); \
reg_t paddr = translate(addr, sizeof(type##_t), true, false); \
*(type##_t*)(mem + paddr) = val; \
- } \
- reg_t store_conditional_##type(reg_t addr, type##_t val) { \
- if (addr == load_reservation) { \
- store_##type(addr, val); \
- return 0; \
- } else return 1; \
}
// store value to memory at aligned address
return icache_data[idx];
}
- reg_t get_badvaddr() { return badvaddr; }
- reg_t get_ptbr() { return ptbr; }
- void set_ptbr(reg_t addr) { ptbr = addr & ~(PGSIZE-1); }
void set_processor(processor_t* p) { proc = p; flush_tlb(); }
void flush_tlb();
void flush_icache();
- void yield_load_reservation() { load_reservation = -1; }
void register_memtracer(memtracer_t*);
private:
char* mem;
size_t memsz;
- reg_t load_reservation;
- reg_t badvaddr;
- reg_t ptbr;
processor_t* proc;
memtracer_list_t tracer;
mmu.set_processor(this);
#define DECLARE_INSN(name, match, mask) \
- register_insn(match, mask, (insn_func_t)&processor_t::rv32_##name, (insn_func_t)&processor_t::rv64_##name);
+ extern reg_t rv32_##name(processor_t*, insn_t, reg_t); \
+ extern reg_t rv64_##name(processor_t*, insn_t, reg_t); \
+ register_insn(match, mask, rv32_##name, rv64_##name);
#include "opcodes.h"
#undef DECLARE_INSN
}
{
}
-void processor_t::reset(bool value)
+void state_t::reset()
{
- if (run == !value)
- return;
- run = !value;
-
// the ISA guarantees on boot that the PC is 0x2000 and the the processor
// is in supervisor mode, and in 64-bit mode, if supported, with traps
// and virtual memory disabled.
- sr = 0;
- set_pcr(PCR_SR, SR_S | SR_S64 | SR_IM);
+ sr = SR_S;
+#ifdef RISCV_ENABLE_64BIT
+ sr |= SR_S64;
+#endif
pc = 0x2000;
// the following state is undefined upon boot-up,
count = 0;
compare = 0;
cycle = 0;
- set_fsr(0);
+ fsr = 0;
+
+ load_reservation = -1;
+}
+
+void processor_t::reset(bool value)
+{
+ if (run == !value)
+ return;
+ run = !value;
+
+ state.reset();
}
-void processor_t::set_fsr(uint32_t val)
+uint32_t processor_t::set_fsr(uint32_t val)
{
- fsr = val & ~FSR_ZERO; // clear FSR bits that read as zero
+ uint32_t old_fsr = state.fsr;
+ state.fsr = val & ~FSR_ZERO; // clear FSR bits that read as zero
+ return old_fsr;
}
void processor_t::take_interrupt()
{
- uint32_t interrupts = (sr & SR_IP) >> SR_IP_SHIFT;
- interrupts &= (sr & SR_IM) >> SR_IM_SHIFT;
+ uint32_t interrupts = (state.sr & SR_IP) >> SR_IP_SHIFT;
+ interrupts &= (state.sr & SR_IM) >> SR_IM_SHIFT;
- if(interrupts && (sr & SR_EI))
- for(int i = 0; ; i++, interrupts >>= 1)
- if(interrupts & 1)
- throw interrupt_t(i);
+ if (interrupts && (state.sr & SR_EI))
+ for (int i = 0; ; i++, interrupts >>= 1)
+ if (interrupts & 1)
+ throw trap_t((1ULL << ((state.sr & SR_S64) ? 63 : 31)) + i);
}
void processor_t::step(size_t n, bool noisy)
return;
size_t i = 0;
+ reg_t npc = state.pc;
+ mmu_t& _mmu = mmu;
+
try
{
take_interrupt();
- mmu_t& _mmu = mmu;
- reg_t npc = pc;
-
// execute_insn fetches and executes one instruction
#define execute_insn(noisy) \
do { \
mmu_t::insn_fetch_t fetch = _mmu.load_insn(npc); \
if(noisy) disasm(fetch.insn, npc); \
npc = fetch.func(this, fetch.insn, npc); \
- pc = npc; \
} while(0)
if(noisy) for( ; i < n; i++) // print out instructions as we go
for( ; i < n; i++)
execute_insn(false);
}
+
+ state.pc = npc;
}
- catch(trap_t t)
- {
- // an exception occurred in the target processor
- take_trap(t,noisy);
- }
- catch(interrupt_t t)
+ catch(trap_t& t)
{
- take_trap((1ULL << ((sr & SR_S64) ? 63 : 31)) + t.i, noisy);
+ take_trap(npc, t, noisy);
}
- cycle += i;
+ state.cycle += i;
// update timer and possibly register a timer interrupt
- uint32_t old_count = count;
- count += i;
- if(old_count < compare && uint64_t(old_count) + i >= compare)
+ uint32_t old_count = state.count;
+ state.count += i;
+ if(old_count < state.compare && uint64_t(old_count) + i >= state.compare)
set_interrupt(IRQ_TIMER, true);
}
-void processor_t::take_trap(reg_t t, bool noisy)
+void processor_t::take_trap(reg_t pc, trap_t& t, bool noisy)
{
if(noisy)
{
- if ((sreg_t)t < 0)
+ if ((sreg_t)t.cause() < 0)
fprintf(stderr, "core %3d: interrupt %d, epc 0x%016" PRIx64 "\n",
- id, uint8_t(t), pc);
+ id, uint8_t(t.cause()), pc);
else
fprintf(stderr, "core %3d: trap %s, epc 0x%016" PRIx64 "\n",
- id, trap_name(trap_t(t)), pc);
+ id, t.name(), pc);
}
- // switch to supervisor, set previous supervisor bit, disable traps
- set_pcr(PCR_SR, (((sr & ~SR_EI) | SR_S) & ~SR_PS & ~SR_PEI) |
- ((sr & SR_S) ? SR_PS : 0) |
- ((sr & SR_EI) ? SR_PEI : 0));
- cause = t;
- epc = pc;
- pc = evec;
- badvaddr = mmu.get_badvaddr();
+ // switch to supervisor, set previous supervisor bit, disable interrupts
+ set_pcr(PCR_SR, (((state.sr & ~SR_EI) | SR_S) & ~SR_PS & ~SR_PEI) |
+ ((state.sr & SR_S) ? SR_PS : 0) |
+ ((state.sr & SR_EI) ? SR_PEI : 0));
+ yield_load_reservation();
+ state.cause = t.cause();
+ state.epc = pc;
+ state.pc = state.evec;
+
+ t.side_effects(&state); // might set badvaddr etc.
}
void processor_t::deliver_ipi()
// the disassembler is stateless, so we share it
static disassembler disasm;
fprintf(stderr, "core %3d: 0x%016" PRIx64 " (0x%08" PRIxFAST32 ") %s\n",
- id, pc, insn.bits, disasm.disassemble(insn).c_str());
+ id, state.pc, insn.bits, disasm.disassemble(insn).c_str());
}
-void processor_t::set_pcr(int which, reg_t val)
+reg_t processor_t::set_pcr(int which, reg_t val)
{
+ reg_t old_pcr = get_pcr(which);
+
switch (which)
{
case PCR_SR:
- sr = (val & ~SR_IP) | (sr & SR_IP);
+ state.sr = (val & ~SR_IP) | (state.sr & SR_IP);
#ifndef RISCV_ENABLE_64BIT
- sr &= ~(SR_S64 | SR_U64);
+ state.sr &= ~(SR_S64 | SR_U64);
#endif
#ifndef RISCV_ENABLE_FPU
- sr &= ~SR_EF;
+ state.sr &= ~SR_EF;
#endif
#ifndef RISCV_ENABLE_VEC
- sr &= ~SR_EV;
+ state.sr &= ~SR_EV;
#endif
- sr &= ~SR_ZERO;
+ state.sr &= ~SR_ZERO;
mmu.flush_tlb();
break;
case PCR_EPC:
- epc = val;
+ state.epc = val;
break;
case PCR_EVEC:
- evec = val;
+ state.evec = val;
break;
case PCR_COUNT:
- count = val;
+ state.count = val;
break;
case PCR_COMPARE:
set_interrupt(IRQ_TIMER, false);
- compare = val;
+ state.compare = val;
break;
case PCR_PTBR:
- mmu.set_ptbr(val);
+ state.ptbr = val & ~(PGSIZE-1);
break;
case PCR_SEND_IPI:
sim.send_ipi(val);
set_interrupt(IRQ_IPI, val & 1);
break;
case PCR_K0:
- pcr_k0 = val;
+ state.pcr_k0 = val;
break;
case PCR_K1:
- pcr_k1 = val;
+ state.pcr_k1 = val;
break;
case PCR_TOHOST:
- if (tohost == 0)
- tohost = val;
+ if (state.tohost == 0)
+ state.tohost = val;
break;
case PCR_FROMHOST:
set_interrupt(IRQ_HOST, val != 0);
- fromhost = val;
+ state.fromhost = val;
break;
}
+
+ return old_pcr;
}
reg_t processor_t::get_pcr(int which)
switch (which)
{
case PCR_SR:
- return sr;
+ return state.sr;
case PCR_EPC:
- return epc;
+ return state.epc;
case PCR_BADVADDR:
- return badvaddr;
+ return state.badvaddr;
case PCR_EVEC:
- return evec;
+ return state.evec;
case PCR_COUNT:
- return count;
+ return state.count;
case PCR_COMPARE:
- return compare;
+ return state.compare;
case PCR_CAUSE:
- return cause;
+ return state.cause;
case PCR_PTBR:
- return mmu.get_ptbr();
+ return state.ptbr;
case PCR_ASID:
return 0;
case PCR_FATC:
mmu.flush_tlb();
+ return 0;
case PCR_HARTID:
return id;
case PCR_IMPL:
return 1;
case PCR_K0:
- return pcr_k0;
+ return state.pcr_k0;
case PCR_K1:
- return pcr_k1;
+ return state.pcr_k1;
case PCR_TOHOST:
- return tohost;
+ return state.tohost;
case PCR_FROMHOST:
- return fromhost;
+ return state.fromhost;
}
return -1;
}
{
uint32_t mask = (1 << (which + SR_IP_SHIFT)) & SR_IP;
if (on)
- sr |= mask;
+ state.sr |= mask;
else
- sr &= ~mask;
+ state.sr &= ~mask;
+}
+
+static reg_t illegal_instruction(processor_t* p, insn_t insn, reg_t pc)
+{
+ throw trap_illegal_instruction();
}
insn_func_t processor_t::decode_insn(insn_t insn)
{
- bool rv64 = (sr & SR_S) ? (sr & SR_S64) : (sr & SR_U64);
+ bool rv64 = (state.sr & SR_S) ? (state.sr & SR_S64) : (state.sr & SR_U64);
auto key = insn.bits & ((1L << opcode_bits)-1);
- auto it = opcode_map.find(key);
for (auto it = opcode_map.find(key); it != opcode_map.end() && it->first == key; ++it)
if ((insn.bits & it->second.mask) == it->second.match)
return rv64 ? it->second.rv64 : it->second.rv32;
- return &processor_t::illegal_instruction;
-}
-
-reg_t processor_t::illegal_instruction(insn_t insn, reg_t pc)
-{
- throw trap_illegal_instruction;
+ return &illegal_instruction;
}
void processor_t::register_insn(uint32_t match, uint32_t mask, insn_func_t rv32, insn_func_t rv64)
#include "decode.h"
#include <cstring>
-#include "trap.h"
#include "config.h"
#include <map>
class mmu_t;
typedef reg_t (*insn_func_t)(processor_t*, insn_t, reg_t);
class sim_t;
+class trap_t;
-// this class represents one processor in a RISC-V machine.
-class processor_t
+// architectural state of a RISC-V hart
+struct state_t
{
-public:
- processor_t(sim_t* _sim, mmu_t* _mmu, uint32_t _id);
- ~processor_t();
-
- void reset(bool value);
- void step(size_t n, bool noisy); // run for n cycles
- void deliver_ipi(); // register an interprocessor interrupt
- bool running() { return run; }
- void set_pcr(int which, reg_t val);
- void set_interrupt(int which, bool on);
- reg_t get_pcr(int which);
- mmu_t* get_mmu() { return &mmu; }
-
- void register_insn(uint32_t match, uint32_t mask, insn_func_t rv32, insn_func_t rv64);
+ void reset();
-private:
- sim_t& sim;
- mmu_t& mmu; // main memory is always accessed via the mmu
-
- // user-visible architected state
+ // user-visible state
reg_t pc;
regfile_t<reg_t, NXPR, true> XPR;
regfile_t<freg_t, NFPR, false> FPR;
reg_t epc;
reg_t badvaddr;
reg_t evec;
+ reg_t ptbr;
reg_t pcr_k0;
reg_t pcr_k1;
reg_t cause;
reg_t tohost;
reg_t fromhost;
- uint32_t id;
uint32_t sr; // only modify the status register using set_pcr()
uint32_t fsr;
uint32_t count;
uint32_t compare;
+ reg_t load_reservation;
+};
+
+// this class represents one processor in a RISC-V machine.
+class processor_t
+{
+public:
+ processor_t(sim_t* _sim, mmu_t* _mmu, uint32_t _id);
+ ~processor_t();
+
+ void reset(bool value);
+ void step(size_t n, bool noisy); // run for n cycles
+ void deliver_ipi(); // register an interprocessor interrupt
+ bool running() { return run; }
+ reg_t set_pcr(int which, reg_t val);
+ uint32_t set_fsr(uint32_t val); // set the floating-point status register
+ void set_interrupt(int which, bool on);
+ reg_t get_pcr(int which);
+ uint32_t get_fsr() { return state.fsr; }
+ mmu_t* get_mmu() { return &mmu; }
+ state_t* get_state() { return &state; }
+ void yield_load_reservation() { state.load_reservation = (reg_t)-1; }
+
+ void register_insn(uint32_t match, uint32_t mask, insn_func_t rv32, insn_func_t rv64);
+
+private:
+ sim_t& sim;
+ mmu_t& mmu; // main memory is always accessed via the mmu
+ state_t state;
+ uint32_t id;
bool run; // !reset
struct opcode_map_entry_t
std::multimap<uint32_t, opcode_map_entry_t> opcode_map;
void take_interrupt(); // take a trap if any interrupts are pending
- void set_fsr(uint32_t val); // set the floating-point status register
- void take_trap(reg_t t, bool noisy); // take an exception
+ void take_trap(reg_t pc, trap_t& t, bool noisy); // take an exception
void disasm(insn_t insn, reg_t pc); // disassemble and print an instruction
friend class sim_t;
friend class mmu_t;
friend class htif_isasim_t;
- #define DECLARE_INSN(name, match, mask) \
- reg_t rv32_ ## name(insn_t insn, reg_t pc); \
- reg_t rv64_ ## name(insn_t insn, reg_t pc);
- #include "opcodes.h"
- #undef DECLARE_INSN
-
insn_func_t decode_insn(insn_t insn);
- reg_t illegal_instruction(insn_t insn, reg_t pc);
};
#endif
signal(sig, &handle_signal);
}
-sim_t::sim_t(size_t _nprocs, size_t mem_mb, const std::vector<std::string>& args)
- : htif(new htif_isasim_t(this, args)),
- procs(_nprocs), current_step(0), current_proc(0), debug(false)
+sim_t::sim_t(size_t nprocs, size_t mem_mb, const std::vector<std::string>& args)
+ : htif(new htif_isasim_t(this, args)), procs(std::max(nprocs, size_t(1))),
+ current_step(0), current_proc(0), debug(false)
{
signal(SIGINT, &handle_signal);
// allocate target machine's memory, shrinking it as necessary
fprintf(stderr, "warning: only got %lu bytes of target mem (wanted %lu)\n",
(unsigned long)memsz, (unsigned long)memsz0);
- mmu = new mmu_t(mem, memsz);
+ debug_mmu = new mmu_t(mem, memsz);
- if (_nprocs == 0)
- _nprocs = 1;
- for (size_t i = 0; i < _nprocs; i++)
+ for (size_t i = 0; i < procs.size(); i++)
procs[i] = new processor_t(this, new mmu_t(mem, memsz), i);
}
delete procs[i];
delete pmmu;
}
- delete mmu;
+ delete debug_mmu;
free(mem);
}
if (current_step == INTERLEAVE)
{
current_step = 0;
- procs[current_proc]->mmu.yield_load_reservation();
+ procs[current_proc]->yield_load_reservation();
if (++current_proc == procs.size())
current_proc = 0;
}
void sim_t::stop()
{
- procs[0]->tohost = 1;
+ procs[0]->state.tohost = 1;
while (!htif->done())
htif->tick();
}
std::auto_ptr<htif_isasim_t> htif;
char* mem; // main memory
size_t memsz; // memory size in bytes
- mmu_t* mmu; // debug port into main memory
+ mmu_t* debug_mmu; // debug port into main memory
std::vector<processor_t*> procs;
void step(size_t n, bool noisy); // step through simulation
-// See LICENSE for license details.
-
#include "trap.h"
+#include "processor.h"
+#include <cstdio>
-const char* trap_name(trap_t t)
+const char* trap_t::name()
{
- #define DECLARE_TRAP(x) "trap_"#x
- static const char* names[] = { TRAP_LIST };
- #undef DECLARE_TRAP
+ const char* fmt = uint8_t(which) == which ? "trap #%u" : "interrupt #%u";
+ sprintf(_name, fmt, uint8_t(which));
+ return _name;
+}
- return (unsigned)t >= sizeof(names)/sizeof(names[0]) ? "unknown" : names[t];
+void mem_trap_t::side_effects(state_t* state)
+{
+ state->badvaddr = badvaddr;
}
#ifndef _RISCV_TRAP_H
#define _RISCV_TRAP_H
-#define TRAP_LIST \
- DECLARE_TRAP(instruction_address_misaligned), \
- DECLARE_TRAP(instruction_access_fault), \
- DECLARE_TRAP(illegal_instruction), \
- DECLARE_TRAP(privileged_instruction), \
- DECLARE_TRAP(fp_disabled), \
- DECLARE_TRAP(reserved0), \
- DECLARE_TRAP(syscall), \
- DECLARE_TRAP(breakpoint), \
- DECLARE_TRAP(load_address_misaligned), \
- DECLARE_TRAP(store_address_misaligned), \
- DECLARE_TRAP(load_access_fault), \
- DECLARE_TRAP(store_access_fault), \
- DECLARE_TRAP(vector_disabled), \
- DECLARE_TRAP(vector_bank), \
- DECLARE_TRAP(vector_illegal_instruction), \
- DECLARE_TRAP(reserved1), \
-
-#define DECLARE_TRAP(x) trap_##x
-enum trap_t
+#include "decode.h"
+
+class state_t;
+
+class trap_t
+{
+ public:
+ trap_t(reg_t which) : which(which) {}
+ virtual const char* name();
+ virtual void side_effects(state_t* state) {}
+ reg_t cause() { return which; }
+ private:
+ char _name[16];
+ reg_t which;
+};
+
+class mem_trap_t : public trap_t
{
- TRAP_LIST
- NUM_TRAPS
+ public:
+ mem_trap_t(reg_t which, reg_t badvaddr)
+ : trap_t(which), badvaddr(badvaddr) {}
+ void side_effects(state_t* state);
+ private:
+ reg_t badvaddr;
+};
+
+#define DECLARE_TRAP(n, x) class trap_##x : public trap_t { \
+ public: \
+ trap_##x() : trap_t(n) {} \
+ const char* name() { return "trap_"#x; } \
};
-#undef DECLARE_TRAP
-struct interrupt_t { interrupt_t(int which) : i(which) {} int i; };
-struct halt_t {}; // thrown to stop the processor from running
+#define DECLARE_MEM_TRAP(n, x) class trap_##x : public mem_trap_t { \
+ public: \
+ trap_##x(reg_t badvaddr) : mem_trap_t(n, badvaddr) {} \
+ const char* name() { return "trap_"#x; } \
+};
-extern "C" const char* trap_name(trap_t t);
+DECLARE_TRAP(0, instruction_address_misaligned)
+DECLARE_TRAP(1, instruction_access_fault)
+DECLARE_TRAP(2, illegal_instruction)
+DECLARE_TRAP(3, privileged_instruction)
+DECLARE_TRAP(4, fp_disabled)
+DECLARE_TRAP(5, reserved0)
+DECLARE_TRAP(6, syscall)
+DECLARE_TRAP(7, breakpoint)
+DECLARE_MEM_TRAP(8, load_address_misaligned)
+DECLARE_MEM_TRAP(9, store_address_misaligned)
+DECLARE_MEM_TRAP(10, load_access_fault)
+DECLARE_MEM_TRAP(11, store_access_fault)
+DECLARE_TRAP(12, vector_disabled)
+DECLARE_TRAP(13, vector_bank)
+DECLARE_TRAP(14, vector_illegal_instruction)
#endif