From 357acc5d82a2b04cf846d69826ec89671e78e759 Mon Sep 17 00:00:00 2001 From: Alejandro Soto Date: Thu, 8 Dec 2022 19:20:58 -0600 Subject: Support gdb interrupts --- sim/gdbstub.py | 52 ++++++++++++++----- sim/sim.py | 111 ++++++++++++++++++++++++++-------------- tb/top/conspiracion.cpp | 133 ++++++++++++++++++++++++++---------------------- 3 files changed, 184 insertions(+), 112 deletions(-) diff --git a/sim/gdbstub.py b/sim/gdbstub.py index 8d20f39..914f717 100644 --- a/sim/gdbstub.py +++ b/sim/gdbstub.py @@ -3,29 +3,53 @@ import sys, socket cycles = None enable_tty = True start_halted = True +sock, client = None, None def init(): - global client + global sock sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('127.0.0.1', 1234)) sock.listen() - print('Listening for gdb on', sock.getsockname(), file=sys.stderr) - client, peer = sock.accept() - sock.close() - print('Accepted connection from', peer, file=sys.stderr) +def halt(): + return yield_to_gdb() + +def fatal(): + global stop_reason + stop_reason = 'fatal' + yield_to_gdb() buffer = b'' -haltFromStop = False +stop_reason = None -def halt(): - global buffer, haltFromStop +def yield_to_gdb(): + global client, buffer, stop_reason + + if not client: + print('Listening for gdb on', sock.getsockname(), file=sys.stderr) + + client, peer = sock.accept() + print('Accepted connection from', peer, file=sys.stderr) + + register_interrupt(client) + stop_reason = 'reset' + + dead = stop_reason == 'fatal' + sendStop = True + + if stop_reason == 'break': + stop_reply = b'S05' + elif stop_reason == 'reset': + stop_reply = b'S05' + stop_reason = 'break' + sendStop = False + elif stop_reason == 'fatal': + stop_reply = b'S0a' - if haltFromStop: - reply(b'S05') + if sendStop: + reply(stop_reply) - haltFromStop = True while True: data = client.recv(4096) if not data: @@ -53,8 +77,8 @@ def halt(): client.send(b'+') if data == b'?': - out = b'S05' - elif data == b'c': + out = stop_reply + elif data == b'c' and not dead: return 'continue' elif data == b'D': out = b'OK' @@ -75,7 +99,7 @@ def halt(): elif data[0] == b'p'[0]: reg = gdb_reg(int(data[1:], 16)) out = hexout(read_reg(reg) if reg is not None else None) - elif data == b's': + elif data == b's' and not dead: return 'step' else: out = b'' diff --git a/sim/sim.py b/sim/sim.py index 189ecfc..491ac43 100755 --- a/sim/sim.py +++ b/sim/sim.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -import importlib.util, os, pathlib, random, socket, subprocess, sys +import importlib.util, os, pathlib, random, selectors, signal, socket, subprocess, sys module_path, verilated, image = sys.argv[1:] test_name = pathlib.Path(module_path).stem @@ -258,17 +258,26 @@ if test_name in os.getenv('SIM_SKIP', '').split(','): exit(success=True) +sel = selectors.DefaultSelector() +def interrupt(): + if not halted: + process.send_signal(signal.SIGUSR1) + +def register_interrupt(source): + sel.register(source, selectors.EVENT_READ, interrupt) + spec = importlib.util.spec_from_file_location('sim', module_path) module = importlib.util.module_from_spec(spec) prelude = { - 'read_reg': read_reg, - 'read_mem': read_mem, - 'write_mem': write_mem, - 'assert_reg': assert_reg, - 'assert_mem': assert_mem, - 'init_reg': init_reg, - 'split_dword': split_dword, + 'read_reg': read_reg, + 'read_mem': read_mem, + 'write_mem': write_mem, + 'assert_reg': assert_reg, + 'assert_mem': assert_mem, + 'init_reg': init_reg, + 'split_dword': split_dword, + 'register_interrupt': register_interrupt, } prelude.update({k: v for k, v in all_regs}) @@ -308,8 +317,8 @@ for addr, filename in module_get('loads', {}).items(): if module_get('start_halted', False): exec_args.append('--start-halted') -sim_end, target_end = socket.socketpair() -sim_end = sim_end.makefile('rw') +sim_end_sock, target_end = socket.socketpair() +sim_end = sim_end_sock.makefile('rw') target_fd = target_end.fileno() exec_args.extend(['--control-fd', str(target_fd)]) @@ -327,36 +336,64 @@ target_end.close() in_regs = False halt = module_get('halt') -while True: - for line in sim_end: - line = line.strip() - if line == '=== halted ===': - break - if line == '=== dump-regs ===': - in_regs = True - elif line == '=== end-regs ===': - in_regs = False - elif line == '=== dump-mem ===': - recv_mem_dump() - elif not line: - continue - elif in_regs: - value, reg = line.split() - regs[reg] = int(value, 16) - else: - while_running() - print(f'{COLOR_BLUE}{line}{COLOR_RESET}') - else: +done = False +halted = False +faulted = False + +def read_ctrl(): + global done, halted, faulted, in_regs + + while True: + try: + sim_end_sock.setblocking(False) + line = next(sim_end) + except StopIteration: + done = True + return + finally: + sim_end_sock.setblocking(True) + + if line := line.strip(): + if line == '=== halted ===': + halted = True + break + elif line == '=== fault ===': + faulted = True + break + elif line == '=== dump-regs ===': + in_regs = True + elif line == '=== end-regs ===': + in_regs = False + elif line == '=== dump-mem ===': + recv_mem_dump() + elif in_regs: + value, reg = line.split() + regs[reg] = int(value, 16) + else: + while_running() + print(f'{COLOR_BLUE}{line}{COLOR_RESET}') + +sel.register(sim_end_sock, selectors.EVENT_READ, read_ctrl) +while not done: + events = sel.select() + for key, _ in events: + (key.data)() + + if faulted: + if fatal := module_get('fatal'): + fatal() + break + elif halted: + mode = None + if halt: + mode = halt() - mode = None - halted = True - if halt: - mode = halt() + print('step' if mode == 'step' else 'continue', file=sim_end, flush=True) + if not halt: + break - print('step' if mode == 'step' else 'continue', file=sim_end, flush=True) - if not halt: - break + halted = False process.wait(timeout=1) if process.returncode != 0: diff --git a/tb/top/conspiracion.cpp b/tb/top/conspiracion.cpp index f222e54..099baf3 100644 --- a/tb/top/conspiracion.cpp +++ b/tb/top/conspiracion.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -33,6 +34,8 @@ namespace { + volatile sig_atomic_t async_halt = 0; + constexpr const char *gp_regs[30] = { [0] = "r0", @@ -157,6 +160,11 @@ namespace return stream; } + + void async_halt_handler(int) + { + async_halt = 1; + } } int main(int argc, char **argv) @@ -381,7 +389,7 @@ int main(int argc, char **argv) tick(); tick(); - if(top.breakpoint) + if(top.breakpoint || (!(time & ((1 << 8) - 1)) && async_halt)) [[unlikely]] { top.halt = 1; } @@ -446,91 +454,94 @@ int main(int argc, char **argv) std::fputs("=== end-mem ===\n", ctrl); }; + std::signal(SIGUSR1, async_halt_handler); + unsigned i = 0; // Abuse unsigned overflow (cycles is UINT_MAX by default) while(!failed && i + 1 <= *cycles) { - for(; i + 1 <= *cycles; ++i) + do { cycle(); if(failed || top.cpu_halted) [[unlikely]] { - break; + goto halt_or_fail; } - } + } while(++i + 1 <= *cycles); - if(top.cpu_halted) - { - top.step = 0; - top.halt = 0; + break; - do_reg_dump(); - std::fputs("=== halted ===\n", ctrl); +halt_or_fail: + top.step = 0; + top.halt = 0; - char *line = nullptr; - std::size_t buf_size = 0; + do_reg_dump(); + std::fprintf(ctrl, "=== %s ===\n", failed ? "fault" : "halted"); + + char *line = nullptr; + std::size_t buf_size = 0; - while(true) + while(true) + { + ssize_t read = getline(&line, &buf_size, ctrl); + if(read == -1) { - ssize_t read = getline(&line, &buf_size, ctrl); - if(read == -1) + if(!std::feof(ctrl)) { - if(!std::feof(ctrl)) - { - std::perror("getline()"); - failed = true; - } - - break; + std::perror("getline()"); + failed = true; } - if(read > 0 && line[read - 1] == '\n') - { - line[read - 1] = '\0'; - } + break; + } - const char *cmd = std::strtok(line, " "); - if(!std::strcmp(cmd, "continue")) - { - break; - } else if(!std::strcmp(cmd, "step")) - { - top.step = 1; - break; - } else if(!std::strcmp(cmd, "dump-mem")) - { - mem_region dump = {}; - std::sscanf(std::strtok(nullptr, " "), "%zu", &dump.start); - std::sscanf(std::strtok(nullptr, " "), "%zu", &dump.length); - do_mem_dump(&dump, 1); - } else if(!std::strcmp(cmd, "patch-mem")) - { - std::uint32_t addr; - std::sscanf(std::strtok(nullptr, " "), "%u", &addr); + if(read > 0 && line[read - 1] == '\n') + { + line[read - 1] = '\0'; + } + + const char *cmd = std::strtok(line, " "); + if(!std::strcmp(cmd, "continue")) + { + break; + } else if(!std::strcmp(cmd, "step")) + { + top.step = 1; + break; + } else if(!std::strcmp(cmd, "dump-mem")) + { + mem_region dump = {}; + std::sscanf(std::strtok(nullptr, " "), "%zu", &dump.start); + std::sscanf(std::strtok(nullptr, " "), "%zu", &dump.length); + do_mem_dump(&dump, 1); + } else if(!std::strcmp(cmd, "patch-mem")) + { + std::uint32_t addr; + std::sscanf(std::strtok(nullptr, " "), "%u", &addr); - const char *data = std::strtok(nullptr, " "); - std::size_t length = std::strlen(data); + const char *data = std::strtok(nullptr, " "); + std::size_t length = std::strlen(data); - while(data && length >= 8) - { - std::uint32_t word; - std::sscanf(data, "%08x", &word); + while(data && length >= 8) + { + std::uint32_t word; + std::sscanf(data, "%08x", &word); - data += 8; - length -= 8; + data += 8; + length -= 8; - word = (word & 0xff) << 24 - | ((word >> 8) & 0xff) << 16 - | ((word >> 16) & 0xff) << 8 - | ((word >> 24) & 0xff); + word = (word & 0xff) << 24 + | ((word >> 8) & 0xff) << 16 + | ((word >> 16) & 0xff) << 8 + | ((word >> 24) & 0xff); - avl.patch(addr++, word); - } + avl.patch(addr++, word); } } - - std::free(line); } + + std::free(line); + async_halt = 0; } if(!no_tty) @@ -543,7 +554,7 @@ int main(int argc, char **argv) trace.close(); } - if(dump_regs || failed) + if(dump_regs) { do_reg_dump(); } -- cgit v1.2.3