--- /dev/null
--- /dev/null
++#include <arpa/inet.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <stdlib.h>
++#include <string.h>
++#include <unistd.h>
++
++#include <algorithm>
++#include <cassert>
++#include <cstdio>
++
++#include "remote_bitbang.h"
++
++#if 1
++# define D(x) x
++#else
++# define D(x)
++#endif
++
++/////////// remote_bitbang_t
++
++remote_bitbang_t::remote_bitbang_t(uint16_t port, jtag_dtm_t *tap) :
++ tap(tap),
++ socket_fd(0),
++ client_fd(0),
++ recv_start(0),
++ recv_end(0)
++{
++ socket_fd = socket(AF_INET, SOCK_STREAM, 0);
++ if (socket_fd == -1) {
++ fprintf(stderr, "remote_bitbang failed to make socket: %s (%d)\n",
++ strerror(errno), errno);
++ abort();
++ }
++
++ fcntl(socket_fd, F_SETFL, O_NONBLOCK);
++ int reuseaddr = 1;
++ if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
++ sizeof(int)) == -1) {
++ fprintf(stderr, "remote_bitbang failed setsockopt: %s (%d)\n",
++ strerror(errno), errno);
++ abort();
++ }
++
++ struct sockaddr_in addr;
++ memset(&addr, 0, sizeof(addr));
++ addr.sin_family = AF_INET;
++ addr.sin_addr.s_addr = INADDR_ANY;
++ addr.sin_port = htons(port);
++
++ if (bind(socket_fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
++ fprintf(stderr, "remote_bitbang failed to bind socket: %s (%d)\n",
++ strerror(errno), errno);
++ abort();
++ }
++
++ if (listen(socket_fd, 1) == -1) {
++ fprintf(stderr, "remote_bitbang failed to listen on socket: %s (%d)\n",
++ strerror(errno), errno);
++ abort();
++ }
++
++ socklen_t addrlen = sizeof(addr);
++ if (getsockname(socket_fd, (struct sockaddr *) &addr, &addrlen) == -1) {
++ fprintf(stderr, "remote_bitbang getsockname failed: %s (%d)\n",
++ strerror(errno), errno);
++ abort();
++ }
++
++ printf("Listening for remote bitbang connection on port %d.\n",
++ ntohs(addr.sin_port));
++ fflush(stdout);
++}
++
++void remote_bitbang_t::accept()
++{
++ client_fd = ::accept(socket_fd, NULL, NULL);
++ if (client_fd == -1) {
++ if (errno == EAGAIN) {
++ // No client waiting to connect right now.
++ } else {
++ fprintf(stderr, "failed to accept on socket: %s (%d)\n", strerror(errno),
++ errno);
++ abort();
++ }
++ } else {
++ fcntl(client_fd, F_SETFL, O_NONBLOCK);
++ }
++}
++
++void remote_bitbang_t::tick()
++{
++ if (client_fd > 0) {
++ execute_commands();
++ } else {
++ this->accept();
++ }
++}
++
++void remote_bitbang_t::execute_commands()
++{
++ static char send_buf[buf_size];
++ unsigned total_processed = 0;
++ bool quit = false;
++ bool in_rti = tap->state() == RUN_TEST_IDLE;
++ bool entered_rti = false;
++ while (1) {
++ if (recv_start < recv_end) {
++ unsigned send_offset = 0;
++ while (recv_start < recv_end) {
++ uint8_t command = recv_buf[recv_start];
++
++ switch (command) {
++ case 'B': /* fprintf(stderr, "*BLINK*\n"); */ break;
++ case 'b': /* fprintf(stderr, "_______\n"); */ break;
++ case 'r': tap->reset(); break;
++ case '0': tap->set_pins(0, 0, 0); break;
++ case '1': tap->set_pins(0, 0, 1); break;
++ case '2': tap->set_pins(0, 1, 0); break;
++ case '3': tap->set_pins(0, 1, 1); break;
++ case '4': tap->set_pins(1, 0, 0); break;
++ case '5': tap->set_pins(1, 0, 1); break;
++ case '6': tap->set_pins(1, 1, 0); break;
++ case '7': tap->set_pins(1, 1, 1); break;
++ case 'R': send_buf[send_offset++] = tap->tdo() ? '1' : '0'; break;
++ case 'Q': quit = true; break;
++ default:
++ fprintf(stderr, "remote_bitbang got unsupported command '%c'\n",
++ command);
++ }
++ recv_start++;
++ total_processed++;
++ if (!in_rti && tap->state() == RUN_TEST_IDLE) {
++ entered_rti = true;
++ break;
++ }
++ in_rti = false;
++ }
++ unsigned sent = 0;
++ while (sent < send_offset) {
++ ssize_t bytes = write(client_fd, send_buf + sent, send_offset);
++ if (bytes == -1) {
++ fprintf(stderr, "failed to write to socket: %s (%d)\n", strerror(errno), errno);
++ abort();
++ }
++ sent += bytes;
++ }
++ }
++
++ if (total_processed > buf_size || quit || entered_rti) {
++ // Don't go forever, because that could starve the main simulation.
++ break;
++ }
++
++ recv_start = 0;
++ recv_end = read(client_fd, recv_buf, buf_size);
++
++ if (recv_end == -1) {
++ if (errno == EAGAIN) {
++ // We'll try again the next call.
++ } else {
++ fprintf(stderr, "remote_bitbang failed to read on socket: %s (%d)\n",
++ strerror(errno), errno);
++ abort();
++ }
++ }
++ if (recv_end == 0 || quit) {
++ // The remote disconnected.
++ close(client_fd);
++ client_fd = 0;
++ break;
++ }
++ }
++}
--- /dev/null
--- /dev/null
++#ifndef REMOTE_BITBANG_H
++#define REMOTE_BITBANG_H
++
++#include <stdint.h>
++
++#include "jtag_dtm.h"
++
++class remote_bitbang_t
++{
++public:
++ // Create a new server, listening for connections from localhost on the given
++ // port.
++ remote_bitbang_t(uint16_t port, jtag_dtm_t *tap);
++
++ // Do a bit of work.
++ void tick();
++
++private:
++ jtag_dtm_t *tap;
++
++ int socket_fd;
++ int client_fd;
++
++ static const ssize_t buf_size = 64 * 1024;
++ char recv_buf[buf_size];
++ ssize_t recv_start, recv_end;
++
++ // Check for a client connecting, and accept if there is one.
++ void accept();
++ // Execute any commands the client has for us.
++ void execute_commands();
++};
++
++#endif
# error spike requires a two''s-complement c++ implementation
#endif
+ #ifdef WORDS_BIGENDIAN
+ # error spike requires a little-endian host
+ #endif
+
#include <cstdint>
#include <string.h>
#include <strings.h>
#include "encoding.h"
#include "config.h"
#include "common.h"
+ #include "softfloat_types.h"
+ #include "specialize.h"
#include <cinttypes>
typedef int64_t sreg_t;
typedef uint64_t reg_t;
- typedef uint64_t freg_t;
const int NXPR = 32;
const int NFPR = 32;
#ifndef RISCV_ENABLE_COMMITLOG
# define WRITE_REG(reg, value) STATE.XPR.write(reg, value)
- # define WRITE_FREG(reg, value) DO_WRITE_FREG(reg, value)
+ # define WRITE_FREG(reg, value) DO_WRITE_FREG(reg, freg(value))
#else
# define WRITE_REG(reg, value) ({ \
reg_t wdata = (value); /* value may have side effects */ \
STATE.XPR.write(reg, wdata); \
})
# define WRITE_FREG(reg, value) ({ \
- freg_t wdata = (value); /* value may have side effects */ \
+ freg_t wdata = freg(value); /* value may have side effects */ \
STATE.log_reg_write = (commit_log_reg_t){((reg) << 1) | 1, wdata}; \
DO_WRITE_FREG(reg, wdata); \
})
#define JUMP_TARGET (pc + insn.uj_imm())
#define RM ({ int rm = insn.rm(); \
if(rm == 7) rm = STATE.frm; \
- if(rm > 4) throw trap_illegal_instruction(); \
+ if(rm > 4) throw trap_illegal_instruction(0); \
rm; })
#define get_field(reg, mask) (((reg) & (decltype(reg))(mask)) / ((mask) & ~((mask) << 1)))
#define set_field(reg, mask, val) (((reg) & ~(decltype(reg))(mask)) | (((decltype(reg))(val) * ((mask) & ~((mask) << 1))) & (decltype(reg))(mask)))
- #define require(x) if (unlikely(!(x))) throw trap_illegal_instruction()
+ #define require(x) if (unlikely(!(x))) throw trap_illegal_instruction(0)
#define require_privilege(p) require(STATE.prv >= (p))
#define require_rv64 require(xlen == 64)
#define require_rv32 require(xlen == 32)
} while(0)
#define set_pc_and_serialize(x) \
- do { set_pc(x); /* check alignment */ \
+ do { reg_t __npc = (x); \
+ set_pc(__npc); /* check alignment */ \
npc = PC_SERIALIZE_AFTER; \
- STATE.pc = (x); \
+ STATE.pc = __npc; \
} while(0)
/* Sentinel PC values to serialize simulator pipeline */
#define invalid_pc(pc) ((pc) & 1)
/* Convenience wrappers to simplify softfloat code sequences */
- #define f32(x) ((float32_t){(uint32_t)x})
- #define f64(x) ((float64_t){(uint64_t)x})
+ #define isBoxedF32(r) (((r) & 0xffffffff00000000) == 0xffffffff00000000)
+ #define unboxF32(r) (isBoxedF32(r) ? (r) : defaultNaNF32UI)
+ #define unboxF64(r) (r)
+ struct freg_t { uint64_t v; };
+ inline float32_t f32(uint32_t v) { return { v }; }
+ inline float64_t f64(uint64_t v) { return { v }; }
+ inline float32_t f32(freg_t r) { return f32(unboxF32(r.v)); }
+ inline float64_t f64(freg_t r) { return f64(unboxF64(r.v)); }
+ inline freg_t freg(float32_t f) { return { ((decltype(freg_t::v))-1 << 32) | f.v }; }
+ inline freg_t freg(float64_t f) { return { f.v }; }
+ inline freg_t freg(freg_t f) { return f; }
+ #define F64_SIGN ((decltype(freg_t::v))1 << 63)
+ #define F32_SIGN ((decltype(freg_t::v))1 << 31)
+ #define fsgnj32(a, b, n, x) \
+ f32((f32(a).v & ~F32_SIGN) | ((((x) ? f32(a).v : (n) ? F32_SIGN : 0) ^ f32(b).v) & F32_SIGN))
+ #define fsgnj64(a, b, n, x) \
+ f64((f64(a).v & ~F64_SIGN) | ((((x) ? f64(a).v : (n) ? F64_SIGN : 0) ^ f64(b).v) & F64_SIGN))
#define validate_csr(which, write) ({ \
if (!STATE.serialized) return PC_SERIALIZE_BEFORE; \
unsigned csr_priv = get_field((which), 0x300); \
unsigned csr_read_only = get_field((which), 0xC00) == 3; \
if (((write) && csr_read_only) || STATE.prv < csr_priv) \
- throw trap_illegal_instruction(); \
+ throw trap_illegal_instruction(0); \
(which); })
-#define DEBUG_START 0x100
-#define DEBUG_ROM_START 0x800
-#define DEBUG_ROM_RESUME (DEBUG_ROM_START + 4)
-#define DEBUG_ROM_EXCEPTION (DEBUG_ROM_START + 8)
-#define DEBUG_ROM_END (DEBUG_ROM_START + debug_rom_raw_len)
-#define DEBUG_RAM_START 0x400
+#define DEBUG_START 0x20000
+#define DEBUG_ROM_ENTRY DEBUG_START
+#define DEBUG_ROM_ENTRY_SIZE (1024 * 4)
+#define DEBUG_ROM_CODE (DEBUG_ROM_ENTRY + DEBUG_ROM_ENTRY_SIZE)
+#define DEBUG_ROM_CODE_SIZE 256
+#define DEBUG_ROM_EXCEPTION (DEBUG_ROM_CODE + DEBUG_ROM_CODE_SIZE)
+#define DEBUG_ROM_EXCEPTION_SIZE 4
+#define DEBUG_RAM_START (DEBUG_ROM_EXCEPTION + DEBUG_ROM_EXCEPTION_SIZE)
#define DEBUG_RAM_SIZE 64
#define DEBUG_RAM_END (DEBUG_RAM_START + DEBUG_RAM_SIZE)
-#define DEBUG_END 0xfff
-#define DEBUG_CLEARDEBINT 0x100
-#define DEBUG_SETHALTNOT 0x10c
-#define DEBUG_SIZE (DEBUG_END - DEBUG_START + 1)
+#define DEBUG_END DEBUG_RAM_END
+
+#define DEBUG_EXCHANGE 0x400
+#define DEBUG_EXCHANGE_SIZE 0x20
#endif
void processor_t::step(size_t n)
{
if (state.dcsr.cause == DCSR_CAUSE_NONE) {
- // TODO: get_interrupt() isn't super fast. Does that matter?
- if (sim->debug_module.get_interrupt(id)) {
+ if (halt_request) {
enter_debug_mode(DCSR_CAUSE_DEBUGINT);
} else if (state.dcsr.halt) {
enter_debug_mode(DCSR_CAUSE_HALT);
}
- } else {
- // In Debug Mode, just do 11 steps at a time. Otherwise we're going to be
- // spinning the rest of the time anyway.
- n = std::min(n, (size_t) 11);
}
while (n > 0) {
if (unlikely(invalid_pc(pc))) { \
switch (pc) { \
case PC_SERIALIZE_BEFORE: state.serialized = true; break; \
- case PC_SERIALIZE_AFTER: instret++; break; \
+ case PC_SERIALIZE_AFTER: n = ++instret; break; \
default: abort(); \
} \
pc = state.pc; \
try
{
- take_interrupt();
+ take_pending_interrupt();
if (unlikely(slow_path()))
{
// enter_debug_mode changed state.pc, so we can't just continue.
break;
}
+
+ if (unlikely(state.pc >= DEBUG_ROM_ENTRY &&
+ state.pc < DEBUG_ROM_ENTRY + DEBUG_ROM_ENTRY_SIZE)) {
+ // We're spinning waiting for the debugger to tell us something.
+ // Let's go talk to the debugger.
+ return;
+ }
}
}
else while (instret < n)
#include "sim.h"
#include "mmu.h"
#include "disasm.h"
-#include "gdbserver.h"
#include <cinttypes>
#include <cmath>
#include <cstdlib>
processor_t::processor_t(const char* isa, sim_t* sim, uint32_t id,
bool halt_on_reset)
- : debug(false), sim(sim), ext(NULL), id(id), halt_on_reset(halt_on_reset)
+ : debug(false), halt_request(false), sim(sim), ext(NULL), id(id),
+ halt_on_reset(halt_on_reset)
{
parse_isa_string(isa);
register_base_instructions();
memset(this, 0, sizeof(*this));
prv = PRV_M;
pc = DEFAULT_RSTVEC;
- mtvec = DEFAULT_MTVEC;
load_reservation = -1;
tselect = 0;
for (unsigned int i = 0; i < num_triggers; i++)
ext->reset(); // reset the extension
}
- void processor_t::raise_interrupt(reg_t which)
- {
- throw trap_t(((reg_t)1 << (max_xlen-1)) | which);
- }
-
// Count number of contiguous 0 bits starting from the LSB.
static int ctz(reg_t val)
{
return res;
}
- void processor_t::take_interrupt()
+ void processor_t::take_interrupt(reg_t pending_interrupts)
{
- reg_t pending_interrupts = state.mip & state.mie;
-
reg_t mie = get_field(state.mstatus, MSTATUS_MIE);
reg_t m_enabled = state.prv < PRV_M || (state.prv == PRV_M && mie);
reg_t enabled_interrupts = pending_interrupts & ~state.mideleg & -m_enabled;
reg_t sie = get_field(state.mstatus, MSTATUS_SIE);
reg_t s_enabled = state.prv < PRV_S || (state.prv == PRV_S && sie);
- enabled_interrupts |= pending_interrupts & state.mideleg & -s_enabled;
+ if (enabled_interrupts == 0)
+ enabled_interrupts = pending_interrupts & state.mideleg & -s_enabled;
if (enabled_interrupts)
- raise_interrupt(ctz(enabled_interrupts));
+ throw trap_t(((reg_t)1 << (max_xlen-1)) | ctz(enabled_interrupts));
}
void processor_t::set_privilege(reg_t prv)
state.dcsr.prv = state.prv;
set_privilege(PRV_M);
state.dpc = state.pc;
- state.pc = DEBUG_ROM_START;
+ state.pc = debug_rom_entry();
}
void processor_t::take_trap(trap_t& t, reg_t epc)
t.get_badaddr());
}
+ if (state.dcsr.cause) {
+ if (t.cause() == CAUSE_BREAKPOINT) {
+ state.pc = debug_rom_entry();
+ } else {
+ state.pc = DEBUG_ROM_EXCEPTION;
+ }
+ return;
+ }
+
if (t.cause() == CAUSE_BREAKPOINT && (
(state.prv == PRV_M && state.dcsr.ebreakm) ||
(state.prv == PRV_H && state.dcsr.ebreakh) ||
return;
}
- if (state.dcsr.cause) {
- state.pc = DEBUG_ROM_EXCEPTION;
- return;
- }
-
// by default, trap to M-mode, unless delegated to S-mode
reg_t bit = t.cause();
reg_t deleg = state.medeleg;
- if (bit & ((reg_t)1 << (max_xlen-1)))
+ bool interrupt = (bit & ((reg_t)1 << (max_xlen-1))) != 0;
+ if (interrupt)
deleg = state.mideleg, bit &= ~((reg_t)1 << (max_xlen-1));
if (state.prv <= PRV_S && bit < max_xlen && ((deleg >> bit) & 1)) {
// handle the trap in S-mode
state.sbadaddr = t.get_badaddr();
reg_t s = state.mstatus;
- s = set_field(s, MSTATUS_SPIE, get_field(s, MSTATUS_UIE << state.prv));
+ s = set_field(s, MSTATUS_SPIE, get_field(s, MSTATUS_SIE));
s = set_field(s, MSTATUS_SPP, state.prv);
s = set_field(s, MSTATUS_SIE, 0);
set_csr(CSR_MSTATUS, s);
set_privilege(PRV_S);
} else {
- state.pc = state.mtvec;
+ reg_t vector = (state.mtvec & 1) && interrupt ? 4*bit : 0;
+ state.pc = (state.mtvec & ~(reg_t)1) + vector;
state.mepc = epc;
state.mcause = t.cause();
if (t.has_badaddr())
state.mbadaddr = t.get_badaddr();
reg_t s = state.mstatus;
- s = set_field(s, MSTATUS_MPIE, get_field(s, MSTATUS_UIE << state.prv));
+ s = set_field(s, MSTATUS_MPIE, get_field(s, MSTATUS_MIE));
s = set_field(s, MSTATUS_MPP, state.prv);
s = set_field(s, MSTATUS_MIE, 0);
set_csr(CSR_MSTATUS, s);
void processor_t::disasm(insn_t insn)
{
+ static uint64_t last_pc = 1, last_bits;
+ static uint64_t executions = 1;
+
uint64_t bits = insn.bits() & ((1ULL << (8 * insn_length(insn.bits()))) - 1);
- fprintf(stderr, "core %3d: 0x%016" PRIx64 " (0x%08" PRIx64 ") %s\n",
- id, state.pc, bits, disassembler->disassemble(insn).c_str());
+ if (last_pc != state.pc || last_bits != bits) {
+ if (executions != 1) {
+ fprintf(stderr, "core %3d: Executed %" PRIx64 " times\n", id, executions);
+ }
+
+ fprintf(stderr, "core %3d: 0x%016" PRIx64 " (0x%08" PRIx64 ") %s\n",
+ id, state.pc, bits, disassembler->disassemble(insn).c_str());
+ last_pc = state.pc;
+ last_bits = bits;
+ executions = 1;
+ } else {
+ executions++;
+ }
}
- static bool validate_vm(int max_xlen, reg_t vm)
- {
- if (max_xlen == 64 && (vm == VM_SV39 || vm == VM_SV48))
- return true;
- if (max_xlen == 32 && vm == VM_SV32)
- return true;
- return vm == VM_MBARE;
- }
-
int processor_t::paddr_bits()
{
assert(xlen == max_xlen);
break;
case CSR_MSTATUS: {
if ((val ^ state.mstatus) &
- (MSTATUS_VM | MSTATUS_MPP | MSTATUS_MPRV | MSTATUS_PUM | MSTATUS_MXR))
+ (MSTATUS_MPP | MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_MXR))
mmu->flush_tlb();
reg_t mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE
- | MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_PUM
- | MSTATUS_MPP | MSTATUS_MXR | (ext ? MSTATUS_XS : 0);
-
- if (validate_vm(max_xlen, get_field(val, MSTATUS_VM)))
- mask |= MSTATUS_VM;
+ | MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM
+ | MSTATUS_MPP | MSTATUS_MXR | MSTATUS_TW | MSTATUS_TVM
+ | MSTATUS_TSR | (ext ? MSTATUS_XS : 0);
state.mstatus = (state.mstatus & ~mask) | (val & mask);
case CSR_MCYCLEH:
state.minstret = (val << 32) | (state.minstret << 32 >> 32);
break;
- case CSR_MUCOUNTEREN:
- state.mucounteren = val;
+ case CSR_SCOUNTEREN:
+ state.scounteren = val;
break;
- case CSR_MSCOUNTEREN:
- state.mscounteren = val;
+ case CSR_MCOUNTEREN:
+ state.mcounteren = val;
break;
case CSR_SSTATUS: {
reg_t mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_SPP | SSTATUS_FS
- | SSTATUS_XS | SSTATUS_PUM;
+ | SSTATUS_XS | SSTATUS_SUM | SSTATUS_MXR;
return set_csr(CSR_MSTATUS, (state.mstatus & ~mask) | (val & mask));
}
case CSR_SIP: {
return set_csr(CSR_MIE,
(state.mie & ~state.mideleg) | (val & state.mideleg));
case CSR_SPTBR: {
- // upper bits of sptbr are the ASID; we only support ASID = 0
- state.sptbr = val & (((reg_t)1 << (paddr_bits() - PGSHIFT)) - 1);
+ mmu->flush_tlb();
+ if (max_xlen == 32)
+ state.sptbr = val & (SPTBR32_PPN | SPTBR32_MODE);
+ if (max_xlen == 64 && (get_field(val, SPTBR64_MODE) == SPTBR_MODE_OFF ||
+ get_field(val, SPTBR64_MODE) == SPTBR_MODE_SV39 ||
+ get_field(val, SPTBR64_MODE) == SPTBR_MODE_SV48))
+ state.sptbr = val & (SPTBR64_PPN | SPTBR64_MODE);
break;
}
case CSR_SEPC: state.sepc = val; break;
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_MTVEC: state.mtvec = val & ~(reg_t)2; break;
case CSR_MSCRATCH: state.mscratch = val; break;
case CSR_MCAUSE: state.mcause = val; break;
case CSR_MBADADDR: state.mbadaddr = val; break;
reg_t processor_t::get_csr(int which)
{
- reg_t ctr_en = state.prv == PRV_U ? state.mucounteren :
- state.prv == PRV_S ? state.mscounteren : -1U;
+ uint32_t ctr_en = -1;
+ if (state.prv < PRV_M)
+ ctr_en &= state.mcounteren;
+ if (state.prv < PRV_S)
+ ctr_en &= state.scounteren;
bool ctr_ok = (ctr_en >> (which & 31)) & 1;
if (ctr_ok) {
}
if (which >= CSR_MHPMCOUNTER3 && which <= CSR_MHPMCOUNTER31)
return 0;
- if (xlen == 32 && which >= CSR_MHPMCOUNTER3 && which <= CSR_MHPMCOUNTER31)
+ if (xlen == 32 && which >= CSR_MHPMCOUNTER3H && which <= CSR_MHPMCOUNTER31H)
return 0;
if (which >= CSR_MHPMEVENT3 && which <= CSR_MHPMEVENT31)
return 0;
if (xlen == 32)
return state.minstret >> 32;
break;
- case CSR_MUCOUNTEREN: return state.mucounteren;
- case CSR_MSCOUNTEREN: return state.mscounteren;
+ case CSR_SCOUNTEREN: return state.scounteren;
+ case CSR_MCOUNTEREN: return state.mcounteren;
case CSR_SSTATUS: {
reg_t mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_SPP | SSTATUS_FS
- | SSTATUS_XS | SSTATUS_PUM;
+ | SSTATUS_XS | SSTATUS_SUM;
reg_t sstatus = state.mstatus & mask;
if ((sstatus & SSTATUS_FS) == SSTATUS_FS ||
(sstatus & SSTATUS_XS) == SSTATUS_XS)
if (max_xlen > xlen)
return state.scause | ((state.scause >> (max_xlen-1)) << (xlen-1));
return state.scause;
- case CSR_SPTBR: return state.sptbr;
+ case CSR_SPTBR:
+ if (get_field(state.mstatus, MSTATUS_TVM))
+ require_privilege(PRV_M);
+ return state.sptbr;
case CSR_SSCRATCH: return state.sscratch;
case CSR_MSTATUS: return state.mstatus;
case CSR_MIP: return state.mip;
{
uint32_t v = 0;
v = set_field(v, DCSR_XDEBUGVER, 1);
- v = set_field(v, DCSR_NDRESET, 0);
- v = set_field(v, DCSR_FULLRESET, 0);
- v = set_field(v, DCSR_PRV, state.dcsr.prv);
- v = set_field(v, DCSR_STEP, state.dcsr.step);
- v = set_field(v, DCSR_DEBUGINT, sim->debug_module.get_interrupt(id));
- v = set_field(v, DCSR_STOPCYCLE, 0);
- v = set_field(v, DCSR_STOPTIME, 0);
v = set_field(v, DCSR_EBREAKM, state.dcsr.ebreakm);
v = set_field(v, DCSR_EBREAKH, state.dcsr.ebreakh);
v = set_field(v, DCSR_EBREAKS, state.dcsr.ebreaks);
v = set_field(v, DCSR_EBREAKU, state.dcsr.ebreaku);
- v = set_field(v, DCSR_HALT, state.dcsr.halt);
+ v = set_field(v, DCSR_STOPCYCLE, 0);
+ v = set_field(v, DCSR_STOPTIME, 0);
v = set_field(v, DCSR_CAUSE, state.dcsr.cause);
+ v = set_field(v, DCSR_STEP, state.dcsr.step);
+ v = set_field(v, DCSR_PRV, state.dcsr.prv);
return v;
}
case CSR_DPC:
case CSR_DSCRATCH:
return state.dscratch;
}
- throw trap_illegal_instruction();
+ throw trap_illegal_instruction(0);
}
reg_t illegal_instruction(processor_t* p, insn_t insn, reg_t pc)
{
- throw trap_illegal_instruction();
+ throw trap_illegal_instruction(0);
}
insn_func_t processor_t::decode_insn(insn_t insn)
bool processor_t::load(reg_t addr, size_t len, uint8_t* bytes)
{
+ switch (addr)
+ {
+ case 0:
+ if (len <= 4) {
+ memset(bytes, 0, len);
+ bytes[0] = get_field(state.mip, MIP_MSIP);
+ return true;
+ }
+ break;
+ }
+
return false;
}
switch (addr)
{
case 0:
- state.mip &= ~MIP_MSIP;
- if (bytes[0] & 1)
- state.mip |= MIP_MSIP;
- return true;
-
- default:
- return false;
+ if (len <= 4) {
+ state.mip = set_field(state.mip, MIP_MSIP, bytes[0]);
+ return true;
+ }
+ break;
}
+
+ return false;
}
void processor_t::trigger_updated()
reg_t mip;
reg_t medeleg;
reg_t mideleg;
- uint32_t mucounteren;
- uint32_t mscounteren;
+ uint32_t mcounteren;
+ uint32_t scounteren;
reg_t sepc;
reg_t sbadaddr;
reg_t sscratch;
void reset();
void step(size_t n); // run for n cycles
void set_csr(int which, reg_t val);
- void raise_interrupt(reg_t which);
reg_t get_csr(int which);
mmu_t* get_mmu() { return mmu; }
state_t* get_state() { return &state; }
bool debug;
// When true, take the slow simulation path.
bool slow_path();
+ bool halted() { return state.dcsr.cause ? true : false; }
+ bool halt_request;
+ // The unique debug rom address that this hart jumps to when entering debug
+ // mode. Rely on the fact that spike hart IDs start at 0 and are consecutive.
+ uint32_t debug_rom_entry() {
+ return DEBUG_ROM_ENTRY + 4 * id;
+ }
// Return the index of a trigger that matched, or -1.
inline int trigger_match(trigger_operation_t operation, reg_t address, reg_t data)
static const size_t OPCODE_CACHE_SIZE = 8191;
insn_desc_t opcode_cache[OPCODE_CACHE_SIZE];
- void check_timer();
- void take_interrupt(); // take a trap if any interrupts are pending
+ void take_pending_interrupt() { take_interrupt(state.mip & state.mie); }
+ void take_interrupt(reg_t mask); // take first enabled interrupt in mask
void take_trap(trap_t& t, reg_t epc); // take an exception
void disasm(insn_t insn); // disassemble and print an instruction
int paddr_bits();
friend class sim_t;
friend class mmu_t;
- friend class rtc_t;
+ friend class clint_t;
friend class extension_t;
void parse_isa_string(const char* isa);
rocc.h \
insn_template.h \
mulhi.h \
- gdbserver.h \
debug_module.h \
+ remote_bitbang.h \
+ jtag_dtm.h \
riscv_precompiled_hdrs = \
insn_template.h \
regnames.cc \
devices.cc \
rom.cc \
+ rtc.cc \
+ clint.cc \
+ gdbserver.cc \
debug_module.cc \
+ remote_bitbang.cc \
+ jtag_dtm.cc \
$(riscv_gen_srcs) \
riscv_test_srcs =
sc_d \
sc_w \
sd \
- sfence_vm \
+ sfence_vma \
sh \
sll \
slli \
#include "sim.h"
#include "mmu.h"
-#include "gdbserver.h"
+#include "remote_bitbang.h"
#include <map>
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <cassert>
#include <signal.h>
+ #include <unistd.h>
+ #include <sys/wait.h>
+ #include <sys/types.h>
volatile bool ctrlc_pressed = false;
static void handle_signal(int sig)
sim_t::sim_t(const char* isa, size_t nprocs, size_t mem_mb, bool halted,
const std::vector<std::string>& args)
- : htif_t(args), procs(std::max(nprocs, size_t(1))),
- current_step(0), current_proc(0), debug(false), gdbserver(NULL)
+ : htif_t(args), debug_module(this), procs(std::max(nprocs, size_t(1))),
+ current_step(0), current_proc(0), debug(false), remote_bitbang(NULL)
{
signal(SIGINT, &handle_signal);
// allocate target machine's memory, shrinking it as necessary
size_t memsz0 = (size_t)mem_mb << 20;
size_t quantum = 1L << 20;
if (memsz0 == 0)
- memsz0 = (size_t)((sizeof(size_t) == 8 ? 4096 : 2048) - 256) << 20;
+ memsz0 = (size_t)2048 << 20;
memsz = memsz0;
while ((mem = (char*)calloc(1, memsz)) == NULL)
fprintf(stderr, "warning: only got %zu bytes of target mem (wanted %zu)\n",
memsz, memsz0);
- bus.add_device(DEBUG_START, &debug_module);
+ debug_module.add_device(&bus);
debug_mmu = new mmu_t(this, NULL);
procs[i] = new processor_t(isa, this, i, halted);
}
- rtc.reset(new rtc_t(procs));
- make_config_string();
+ clint.reset(new clint_t(procs));
+ bus.add_device(CLINT_BASE, clint.get());
+
+ make_dtb();
}
sim_t::~sim_t()
interactive();
else
step(INTERLEAVE);
- if (gdbserver) {
- gdbserver->handle();
+ if (remote_bitbang) {
+ remote_bitbang->tick();
}
}
}
procs[current_proc]->yield_load_reservation();
if (++current_proc == procs.size()) {
current_proc = 0;
- rtc->increment(INTERLEAVE / INSNS_PER_RTC_TICK);
+ clint->increment(INTERLEAVE / INSNS_PER_RTC_TICK);
}
host->switch_to();
return bus.store(addr, len, bytes);
}
- void sim_t::make_config_string()
+ static std::string dts_compile(const std::string& dts)
{
- reg_t rtc_addr = EXT_IO_BASE;
- bus.add_device(rtc_addr, rtc.get());
+ // Convert the DTS to DTB
+ int dts_pipe[2];
+ pid_t dts_pid;
- const int align = 0x1000;
- reg_t cpu_addr = rtc_addr + ((rtc->size() - 1) / align + 1) * align;
- reg_t cpu_size = align;
-
- uint32_t reset_vec[8] = {
- 0x297 + DRAM_BASE - DEFAULT_RSTVEC, // reset vector
- 0x00028067, // jump straight to DRAM_BASE
- 0x00000000, // reserved
- 0, // config string pointer
- 0, 0, 0, 0 // trap vector
+ if (pipe(dts_pipe) != 0 || (dts_pid = fork()) < 0) {
+ std::cerr << "Failed to fork dts child: " << strerror(errno) << std::endl;
+ exit(1);
+ }
+
+ // Child process to output dts
+ if (dts_pid == 0) {
+ close(dts_pipe[0]);
+ int step, len = dts.length();
+ const char *buf = dts.c_str();
+ for (int done = 0; done < len; done += step) {
+ step = write(dts_pipe[1], buf+done, len-done);
+ if (step == -1) {
+ std::cerr << "Failed to write dts: " << strerror(errno) << std::endl;
+ exit(1);
+ }
+ }
+ close(dts_pipe[1]);
+ exit(0);
+ }
+
+ pid_t dtb_pid;
+ int dtb_pipe[2];
+ if (pipe(dtb_pipe) != 0 || (dtb_pid = fork()) < 0) {
+ std::cerr << "Failed to fork dtb child: " << strerror(errno) << std::endl;
+ exit(1);
+ }
+
+ // Child process to output dtb
+ if (dtb_pid == 0) {
+ dup2(dts_pipe[0], 0);
+ dup2(dtb_pipe[1], 1);
+ close(dts_pipe[0]);
+ close(dts_pipe[1]);
+ close(dtb_pipe[0]);
+ close(dtb_pipe[1]);
+ execl(DTC, DTC, "-O", "dtb", 0);
+ std::cerr << "Failed to run " DTC ": " << strerror(errno) << std::endl;
+ exit(1);
+ }
+
+ close(dts_pipe[1]);
+ close(dts_pipe[0]);
+ close(dtb_pipe[1]);
+
+ // Read-out dtb
+ std::stringstream dtb;
+
+ int got;
+ char buf[4096];
+ while ((got = read(dtb_pipe[0], buf, sizeof(buf))) > 0) {
+ dtb.write(buf, got);
+ }
+ if (got == -1) {
+ std::cerr << "Failed to read dtb: " << strerror(errno) << std::endl;
+ exit(1);
+ }
+ close(dtb_pipe[0]);
+
+ // Reap children
+ int status;
+ waitpid(dts_pid, &status, 0);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ std::cerr << "Child dts process failed" << std::endl;
+ exit(1);
+ }
+ waitpid(dtb_pid, &status, 0);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ std::cerr << "Child dtb process failed" << std::endl;
+ exit(1);
+ }
+
+ return dtb.str();
+ }
+
+ 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)
};
- reset_vec[3] = DEFAULT_RSTVEC + sizeof(reset_vec); // config string pointer
+ 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));
std::stringstream s;
- s << std::hex <<
- "platform {\n"
- " vendor ucb;\n"
- " arch spike;\n"
- "};\n"
- "rtc {\n"
- " addr 0x" << rtc_addr << ";\n"
- "};\n"
- "ram {\n"
- " 0 {\n"
- " addr 0x" << DRAM_BASE << ";\n"
- " size 0x" << memsz << ";\n"
- " };\n"
- "};\n"
- "core {\n";
+ s << std::dec <<
+ "/dts-v1/;\n"
+ "\n"
+ "/ {\n"
+ " #address-cells = <2>;\n"
+ " #size-cells = <2>;\n"
+ " compatible = \"ucbbar,spike-bare-dev\";\n"
+ " model = \"ucbbar,spike-bare\";\n"
+ " cpus {\n"
+ " #address-cells = <1>;\n"
+ " #size-cells = <0>;\n"
+ " timebase-frequency = <" << (CPU_HZ/INSNS_PER_RTC_TICK) << ">;\n";
for (size_t i = 0; i < procs.size(); i++) {
- s <<
- " " << i << " {\n"
- " " << "0 {\n" << // hart 0 on core i
- " isa " << procs[i]->isa_string << ";\n"
- " timecmp 0x" << (rtc_addr + 8*(1+i)) << ";\n"
- " ipi 0x" << cpu_addr << ";\n"
- " };\n"
- " };\n";
- bus.add_device(cpu_addr, procs[i]);
- cpu_addr += cpu_size;
+ s << " CPU" << i << ": cpu@" << i << " {\n"
+ " device_type = \"cpu\";\n"
+ " reg = <" << i << ">;\n"
+ " status = \"okay\";\n"
+ " compatible = \"riscv\";\n"
+ " riscv,isa = \"" << procs[i]->isa_string << "\";\n"
+ " mmu-type = \"riscv," << (procs[i]->max_xlen <= 32 ? "sv32" : "sv48") << "\";\n"
+ " clock-frequency = <" << CPU_HZ << ">;\n"
+ " CPU" << i << "_intc: interrupt-controller {\n"
+ " #interrupt-cells = <1>;\n"
+ " interrupt-controller;\n"
+ " compatible = \"riscv,cpu-intc\";\n"
+ " };\n"
+ " };\n";
}
- s << "};\n";
-
- config_string = s.str();
- rom.insert(rom.end(), config_string.begin(), config_string.end());
- rom.resize((rom.size() / align + 1) * align);
+ reg_t membs = DRAM_BASE;
+ s << std::hex <<
+ " };\n"
+ " memory@" << DRAM_BASE << " {\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"
+ " #address-cells = <2>;\n"
+ " #size-cells = <2>;\n"
+ " compatible = \"ucbbar,spike-bare-soc\", \"simple-bus\";\n"
+ " ranges;\n"
+ " clint@" << CLINT_BASE << " {\n"
+ " compatible = \"riscv,clint0\";\n"
+ " interrupts-extended = <" << std::dec;
+ for (size_t i = 0; i < procs.size(); i++)
+ s << "&CPU" << i << "_intc 3 &CPU" << i << "_intc 7 ";
+ reg_t clintbs = CLINT_BASE;
+ reg_t clintsz = CLINT_SIZE;
+ s << std::hex << ">;\n"
+ " reg = <0x" << (clintbs >> 32) << " 0x" << (clintbs & (uint32_t)-1) <<
+ " 0x" << (clintsz >> 32) << " 0x" << (clintsz & (uint32_t)-1) << ">;\n"
+ " };\n"
+ " };\n"
+ "};\n";
+
+ dts = s.str();
+ std::string dtb = dts_compile(dts);
+
+ rom.insert(rom.end(), dtb.begin(), dtb.end());
+ const int align = 0x1000;
+ rom.resize((rom.size() + align - 1) / align * align);
boot_rom.reset(new rom_device_t(rom));
bus.add_device(DEFAULT_RSTVEC, boot_rom.get());
#include <memory>
class mmu_t;
-class gdbserver_t;
+class remote_bitbang_t;
// this class encapsulates the processors and memory in a RISC-V machine.
class sim_t : public htif_t
void set_log(bool value);
void set_histogram(bool value);
void set_procs_debug(bool value);
- void set_gdbserver(gdbserver_t* gdbserver) { this->gdbserver = gdbserver; }
+ void set_remote_bitbang(remote_bitbang_t* remote_bitbang) {
+ this->remote_bitbang = remote_bitbang;
+ }
- const char* get_config_string() { return config_string.c_str(); }
+ const char* get_dts() { 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
mmu_t* debug_mmu; // debug port into main memory
std::vector<processor_t*> procs;
- std::string config_string;
+ std::string dts;
std::unique_ptr<rom_device_t> boot_rom;
- std::unique_ptr<rtc_t> rtc;
+ std::unique_ptr<clint_t> clint;
bus_t bus;
- debug_module_t debug_module;
processor_t* get_core(const std::string& i);
void step(size_t n); // step through simulation
static const size_t INTERLEAVE = 5000;
static const size_t INSNS_PER_RTC_TICK = 100; // 10 MHz clock for 1 BIPS core
+ static const size_t CPU_HZ = 1000000000; // 1GHz CPU
size_t current_step;
size_t current_proc;
bool debug;
bool log;
bool histogram_enabled; // provide a histogram of PCs
- gdbserver_t* gdbserver;
+ remote_bitbang_t* remote_bitbang;
// memory-mapped I/O routines
bool addr_is_mem(reg_t addr) {
reg_t mem_to_addr(char* x) { return x - mem + DRAM_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();
+ void make_dtb();
// presents a prompt for introspection into the simulation
void interactive();
void interactive_run_noisy(const std::string& cmd, const std::vector<std::string>& args);
void interactive_run_silent(const std::string& cmd, const std::vector<std::string>& args);
void interactive_reg(const std::string& cmd, const std::vector<std::string>& args);
+ void interactive_freg(const std::string& cmd, const std::vector<std::string>& args);
void interactive_fregs(const std::string& cmd, const std::vector<std::string>& args);
void interactive_fregd(const std::string& cmd, const std::vector<std::string>& args);
void interactive_pc(const std::string& cmd, const std::vector<std::string>& args);
void interactive_str(const std::string& cmd, const std::vector<std::string>& args);
void interactive_until(const std::string& cmd, const std::vector<std::string>& args);
reg_t get_reg(const std::vector<std::string>& args);
- reg_t get_freg(const std::vector<std::string>& args);
+ freg_t get_freg(const std::vector<std::string>& args);
reg_t get_mem(const std::vector<std::string>& args);
reg_t get_pc(const std::vector<std::string>& args);
friend class processor_t;
friend class mmu_t;
- friend class gdbserver_t;
// htif
friend void sim_thread_main(void*);
#include "sim.h"
#include "mmu.h"
-#include "gdbserver.h"
+#include "remote_bitbang.h"
#include "cachesim.h"
#include "extension.h"
#include <dlfcn.h>
fprintf(stderr, " --l2=<S>:<W>:<B> B both powers of 2).\n");
fprintf(stderr, " --extension=<name> Specify RoCC Extension\n");
fprintf(stderr, " --extlib=<name> Shared library to load\n");
- fprintf(stderr, " --gdb-port=<port> Listen on <port> for gdb to connect\n");
+ fprintf(stderr, " --rbb-port=<port> Listen on <port> for remote bitbang connection\n");
- fprintf(stderr, " --dump-config-string Print platform configuration string and exit\n");
+ fprintf(stderr, " --dump-dts Print device tree string and exit\n");
exit(1);
}
bool halted = false;
bool histogram = false;
bool log = false;
- bool dump_config_string = false;
+ bool dump_dts = false;
size_t nprocs = 1;
size_t mem_mb = 0;
std::unique_ptr<icache_sim_t> ic;
std::unique_ptr<cache_sim_t> l2;
std::function<extension_t*()> extension;
const char* isa = DEFAULT_ISA;
- uint16_t gdb_port = 0;
+ uint16_t rbb_port = 0;
+ bool use_rbb = false;
option_parser_t parser;
parser.help(&help);
parser.option('m', 0, 1, [&](const char* s){mem_mb = atoi(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, "gdb-port", 1, [&](const char* s){gdb_port = atoi(s);});
+ parser.option(0, "rbb-port", 1, [&](const char* s){use_rbb = true; rbb_port = atoi(s);});
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$"));});
parser.option(0, "isa", 1, [&](const char* s){isa = s;});
parser.option(0, "extension", 1, [&](const char* s){extension = find_extension(s);});
- parser.option(0, "dump-config-string", 0, [&](const char *s){dump_config_string = true;});
+ parser.option(0, "dump-dts", 0, [&](const char *s){dump_dts = true;});
parser.option(0, "extlib", 1, [&](const char *s){
void *lib = dlopen(s, RTLD_NOW | RTLD_GLOBAL);
if (lib == NULL) {
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<gdbserver_t> gdbserver;
- if (gdb_port) {
- gdbserver = std::unique_ptr<gdbserver_t>(new gdbserver_t(gdb_port, &s));
- s.set_gdbserver(&(*gdbserver));
+ std::unique_ptr<jtag_dtm_t> jtag_dtm(new jtag_dtm_t(&s.debug_module));
+ std::unique_ptr<remote_bitbang_t> remote_bitbang((remote_bitbang_t *) NULL);
+ if (use_rbb) {
+ remote_bitbang.reset(new remote_bitbang_t(rbb_port, &(*jtag_dtm)));
+ s.set_remote_bitbang(&(*remote_bitbang));
}
- if (dump_config_string) {
- printf("%s", s.get_config_string());
+ if (dump_dts) {
+ printf("%s", s.get_dts());
return 0;
}