#include "sim.h"
#include "htif.h"
#include <sys/mman.h>
+#include <termios.h>
#include <map>
#include <iostream>
#include <climits>
#include <string>
#include <vector>
-static std::string readline()
+static std::string readline(int fd)
{
+ struct termios tios;
+ bool noncanonical = tcgetattr(fd, &tios) == 0 && (tios.c_lflag & ICANON) == 0;
+
std::string s;
- while (1)
+ for (char ch; read(fd, &ch, 1) == 1; )
{
- char ch;
- assert(read(1, &ch, 1) == 1);
-
if (ch == '\x7f')
{
if (s.empty())
continue;
s.erase(s.end()-1);
+
+ if (noncanonical && write(fd, "\b \b", 3) != 3)
+ ; // shut up gcc
}
+ else if (noncanonical && write(fd, &ch, 1) != 1)
+ ; // shut up gcc
- assert(write(1, &ch, 1) == 1);
if (ch == '\n')
- return s;
+ break;
if (ch != '\x7f')
s += ch;
}
+ return s;
}
void sim_t::interactive()
{
- std::cout << ": " << std::flush;
- std::string s = readline();
-
- std::stringstream ss(s);
- std::string cmd, tmp;
- std::vector<std::string> args;
- if (!(ss >> cmd))
+ while (true)
{
- interactive_run_noisy(std::string("r"), std::vector<std::string>(1,"1"));
- return;
- }
- while (ss >> tmp)
- args.push_back(tmp);
-
- typedef void (sim_t::*interactive_func)(const std::string&, const std::vector<std::string>&);
- std::map<std::string,interactive_func> funcs;
-
- funcs["r"] = &sim_t::interactive_run_noisy;
- funcs["rs"] = &sim_t::interactive_run_silent;
- funcs["reg"] = &sim_t::interactive_reg;
- funcs["fregs"] = &sim_t::interactive_fregs;
- funcs["fregd"] = &sim_t::interactive_fregd;
- funcs["mem"] = &sim_t::interactive_mem;
- funcs["str"] = &sim_t::interactive_str;
- funcs["until"] = &sim_t::interactive_until;
- funcs["while"] = &sim_t::interactive_until;
- funcs["q"] = &sim_t::interactive_quit;
-
- try
- {
- if(funcs.count(cmd))
- (this->*funcs[cmd])(cmd, args);
+ std::cerr << ": " << std::flush;
+ std::string s = readline(2);
+
+ std::stringstream ss(s);
+ std::string cmd, tmp;
+ std::vector<std::string> args;
+
+ if (!(ss >> cmd))
+ {
+ step(1, true);
+ continue;
+ }
+
+ while (ss >> tmp)
+ args.push_back(tmp);
+
+ typedef void (sim_t::*interactive_func)(const std::string&, const std::vector<std::string>&);
+ std::map<std::string,interactive_func> funcs;
+
+ funcs["r"] = &sim_t::interactive_run_noisy;
+ funcs["rs"] = &sim_t::interactive_run_silent;
+ funcs["reg"] = &sim_t::interactive_reg;
+ funcs["fregs"] = &sim_t::interactive_fregs;
+ funcs["fregd"] = &sim_t::interactive_fregd;
+ funcs["mem"] = &sim_t::interactive_mem;
+ funcs["str"] = &sim_t::interactive_str;
+ funcs["until"] = &sim_t::interactive_until;
+ funcs["while"] = &sim_t::interactive_until;
+ funcs["q"] = &sim_t::interactive_quit;
+
+ try
+ {
+ if(funcs.count(cmd))
+ (this->*funcs[cmd])(cmd, args);
+ }
+ catch(trap_t t) {}
}
- catch(trap_t t) {}
+ ctrlc_pressed = false;
}
void sim_t::interactive_run_noisy(const std::string& cmd, const std::vector<std::string>& args)
void sim_t::interactive_run(const std::string& cmd, const std::vector<std::string>& args, bool noisy)
{
size_t steps = args.size() ? atoll(args[0].c_str()) : -1;
- step(steps, noisy);
+ ctrlc_pressed = false;
+ for (size_t i = 0; i < steps && !ctrlc_pressed; i++)
+ step(1, noisy);
}
void sim_t::interactive_quit(const std::string& cmd, const std::vector<std::string>& args)
{
- stop();
+ exit(0);
}
reg_t sim_t::get_pc(const std::vector<std::string>& args)
void sim_t::interactive_until(const std::string& cmd, const std::vector<std::string>& args)
{
+ bool cmd_until = cmd == "until";
+
if(args.size() < 3)
return;
- std::string scmd = args[0];
reg_t val = strtol(args[args.size()-1].c_str(),NULL,16);
if(val == LONG_MAX)
val = strtoul(args[args.size()-1].c_str(),NULL,16);
std::vector<std::string> args2;
args2 = std::vector<std::string>(args.begin()+1,args.end()-1);
+ auto func = args[0] == "reg" ? &sim_t::get_reg :
+ args[0] == "pc" ? &sim_t::get_pc :
+ args[0] == "mem" ? &sim_t::get_mem :
+ NULL;
+
+ if (func == NULL)
+ return;
+
+ ctrlc_pressed = false;
+
while (1)
{
- reg_t current;
- if(scmd == "reg")
- current = get_reg(args2);
- else if(scmd == "pc")
- current = get_pc(args2);
- else if(scmd == "mem")
- current = get_mem(args2);
- else
- return;
-
- if(cmd == "until" && current == val)
+ reg_t current = (this->*func)(args2);
+
+ if (cmd_until == (current == val))
break;
- if(cmd == "while" && current != val)
+ if (ctrlc_pressed)
break;
step(1, false);
if(noisy)
{
if ((sreg_t)t < 0)
- printf("core %3d: interrupt %d, epc 0x%016" PRIx64 "\n",
- id, uint8_t(t), pc);
+ fprintf(stderr, "core %3d: interrupt %d, epc 0x%016" PRIx64 "\n",
+ id, uint8_t(t), pc);
else
- printf("core %3d: trap %s, epc 0x%016" PRIx64 "\n",
- id, trap_name(trap_t(t)), pc);
+ fprintf(stderr, "core %3d: trap %s, epc 0x%016" PRIx64 "\n",
+ id, trap_name(trap_t(t)), pc);
}
// switch to supervisor, set previous supervisor bit, disable traps
{
// the disassembler is stateless, so we share it
static disassembler disasm;
- printf("core %3d: 0x%016" PRIx64 " (0x%08" PRIx32 ") %s\n",
- id, pc, insn.bits, disasm.disassemble(insn).c_str());
+ fprintf(stderr, "core %3d: 0x%016" PRIx64 " (0x%08" PRIx32 ") %s\n",
+ id, pc, insn.bits, disasm.disassemble(insn).c_str());
}
void processor_t::set_pcr(int which, reg_t val)
riscv_install_prog_srcs = \
spike.cc \
+ xspike.cc \
+ termios-xspike.cc \
riscv_hdrs = \
htif.h \
#include <climits>
#include <cstdlib>
#include <cassert>
+#include <signal.h>
+
+volatile bool ctrlc_pressed = false;
+static void handle_signal(int sig)
+{
+ if (ctrlc_pressed)
+ exit(-1);
+ ctrlc_pressed = true;
+ 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)
{
+ 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;
{
while (!htif->done())
{
- if (debug)
+ if (debug || ctrlc_pressed)
interactive();
else
step(INTERLEAVE, false);
friend class htif_isasim_t;
};
+extern volatile bool ctrlc_pressed;
+
#endif
fprintf(stderr, " -m <n> Provide <n> MB of target memory\n");
fprintf(stderr, " -d Interactive debug mode\n");
fprintf(stderr, " -h Print this help message\n");
- fprintf(stderr, " -h Print this help message\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");
option_parser_t parser;
parser.help(&help);
+ parser.option('h', 0, 0, [&](const char* s){help();});
parser.option('d', 0, 0, [&](const char* s){debug = true;});
parser.option('p', 0, 1, [&](const char* s){nprocs = atoi(s);});
parser.option('m', 0, 1, [&](const char* s){mem_mb = atoi(s);});
--- /dev/null
+// See LICENSE for license details.
+
+// termios-xspike sets up a canonical terminal and blocks forever.
+// It allows us to send Ctrl-C etc. to the target machine.
+
+#include <unistd.h>
+#include <termios.h>
+#include <signal.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+int main()
+{
+ struct termios old_tios;
+ if (tcgetattr(0, &old_tios) < 0)
+ return -1;
+
+ signal(SIGTERM, [](int) { });
+
+ struct termios new_tios = old_tios;
+ new_tios.c_lflag &= ~(ICANON | ECHO | ISIG);
+ if (tcsetattr(0, TCSANOW, &new_tios) < 0)
+ return -1;
+
+ pause();
+
+ return tcsetattr(0, TCSANOW, &old_tios);
+}
--- /dev/null
+// See LICENSE for license details.
+
+// xspike forks an xterm for spike's target machine console,
+// preserving the current terminal for debugging.
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <cstdio>
+#include <climits>
+#include <cstring>
+#include <stdexcept>
+
+static pid_t fork_spike(int tty_fd, int argc, char** argv);
+static pid_t fork_xterm(int* tty_fd);
+
+int main(int argc, char** argv)
+{
+ int tty_fd, wait_status, ret = -1;
+ pid_t xterm, spike;
+
+ static bool signal_exit = false;
+ auto handle_signal = [](int) { signal_exit = true; };
+ signal(SIGINT, handle_signal);
+
+ if ((xterm = fork_xterm(&tty_fd)) < 0)
+ {
+ fprintf(stderr, "could not open xterm\n");
+ goto out;
+ }
+
+ if ((spike = fork_spike(tty_fd, argc, argv)) < 0)
+ {
+ fprintf(stderr, "could not open spike\n");
+ goto close_xterm;
+ }
+
+ while (waitpid(spike, &wait_status, 0) < 0 && !signal_exit)
+ ;
+ ret = !signal_exit && WIFEXITED(wait_status) ? WEXITSTATUS(wait_status) : -1;
+
+close_xterm:
+ kill(-xterm, SIGTERM);
+out:
+ return ret;
+}
+
+static pid_t fork_spike(int tty_fd, int argc, char** argv)
+{
+ pid_t pid = fork();
+ if (pid < 0)
+ return -1;
+
+ if (pid == 0)
+ {
+ if (dup2(tty_fd, STDIN_FILENO) < 0 || dup2(tty_fd, STDOUT_FILENO) < 0)
+ return -1;
+ execvp("spike", argv);
+ return -1;
+ }
+
+ return pid;
+}
+
+static pid_t fork_xterm(int* tty_fd)
+{
+ static const char cmd[] = "3>&1 xterm -title xspike -e sh -c 'tty 1>&3; termios-xspike'";
+
+ int fds[2];
+ if (pipe(fds) < 0)
+ return -1;
+
+ pid_t pid = fork();
+ if (pid < 0)
+ return -1;
+
+ if (pid == 0)
+ {
+ setpgid(0, 0);
+ if (dup2(fds[1], STDOUT_FILENO) < 0)
+ return -1;
+ execl("/bin/sh", "sh", "-c", cmd, NULL);
+ return -1;
+ }
+
+ char tty[NAME_MAX];
+ ssize_t ttylen = read(fds[0], tty, sizeof(tty));
+ if (ttylen <= 1 || tty[ttylen-1] != '\n')
+ return -1;
+ tty[ttylen-1] = '\0';
+ if ((*tty_fd = open(tty, O_RDWR)) < 0)
+ return -1;
+
+ return pid;
+}