diff options
Diffstat (limited to '')
| -rw-r--r-- | sim/gdbstub.py | 134 | ||||
| -rw-r--r-- | sim/link.ld (renamed from tb/sim/link.ld) | 0 | ||||
| -rwxr-xr-x | sim/sim.py (renamed from tb/sim/sim.py) | 99 | ||||
| -rw-r--r-- | sim/start.S (renamed from tb/sim/start.S) | 0 |
4 files changed, 212 insertions, 21 deletions
diff --git a/sim/gdbstub.py b/sim/gdbstub.py new file mode 100644 index 0000000..b262971 --- /dev/null +++ b/sim/gdbstub.py @@ -0,0 +1,134 @@ +import sys, socket + +start_halted = True + +def init(): + global client + + 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) + +buffer = b'' +haltFromStop = False + +def halt(): + global buffer, haltFromStop + + if haltFromStop: + reply(b'S05') + haltFromStop = False + + while True: + data = client.recv(4096) + if not data: + break + + buffer = buffer + data if buffer else data + + try: + start = buffer.index(b'$') + marker = buffer.index(b'#', start + 1) + except ValueError: + continue + + if marker + 2 >= len(buffer): + continue + + data = buffer[start + 1:marker] + cksum = int(buffer[marker + 1:marker + 3], 16) + + if cksum != (sum(data) & 0xff): + raise Exception(f'bad packet checksum: {buffer[start:marker + 3]}') + + buffer = buffer[marker + 3:] + + client.send(b'+') + + if data[0] == b'?'[0]: + out = b'S05' + elif data[0] == b'c'[0]: + assert not data[1:] #TODO + break + elif data[0] == b'D'[0]: + out = b'OK' + elif data[0] == b'g'[0]: + out = hexout(read_reg(gdb_reg(r)) for r in range(16)) + elif data[0] == b'm'[0]: + addr, length = (int(x, 16) for x in data[1:].split(b',')) + out = hexout(read_mem(addr, length)) + elif data[0] == b'M'[0]: + addrlen, data = data[1:].split(b':') + addr, length = (int(x, 16) for x in addrlen.split(b',')) + + data = bytes.fromhex(str(data, 'ascii')) + assert len(data) == length + + write_mem(addr, data) + out = b'OK' + 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) + else: + print('unhandled packet:', data) + out = b'' + + reply(out) + + haltFromStop = True + +def reply(out): + client.send(b'$' + out + b'#' + hexout(sum(out) & 0xff, 1)) + +def gdb_reg(n): + if 0 <= n < 8: + return (r0, r1, r2, r3, r4, r5, r6, r7)[n] + + if n == 15: + return pc + + if n == 0x19: + return cpsr + + mode = read_reg(cpsr) & 0b11111 + if 8 <= n < 13: + if mode == 0b10001: + regs = (r8_fiq, r9_fiq, r10_fiq, r11_fiq, r12_fiq) + else: + regs = (r8_fiq, r9_fiq, r10_fiq, r11_fiq, r12_fiq) + + return regs[n - 8] + + if 13 <= n < 15: + if mode == 0b10011: + regs = (r13_svc, r14_svc) + if mode == 0b10111: + regs = (r13_abt, r14_abt) + if mode == 0b11011: + regs = (r13_und, r14_und) + if mode == 0b10010: + regs = (r13_irq, r14_irq) + if mode == 0b10001: + regs = (r13_fiq, r14_fiq) + else: + regs = (r13_usr, r14_usr) + + return regs[n - 13] + + return None + +def hexout(data, size=4): + if data is None: + return b'' + elif type(data) is bytes: + return data.hex().encode('ascii') + elif type(data) is int: + data = [data] + + return b''.join(hex(d)[2:].zfill(2 * size)[:2 * size].encode('ascii') for d in data) + diff --git a/tb/sim/link.ld b/sim/link.ld index d26cb2a..d26cb2a 100644 --- a/tb/sim/link.ld +++ b/sim/link.ld diff --git a/tb/sim/sim.py b/sim/sim.py index 91fc348..16ceb54 100755 --- a/tb/sim/sim.py +++ b/sim/sim.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -import importlib.util, os, pathlib, random, subprocess, sys +import importlib.util, os, pathlib, random, socket, subprocess, sys module_path, verilated, image = sys.argv[1:] test_name = pathlib.Path(module_path).stem @@ -74,10 +74,28 @@ regs = {} read_reg = lambda r: regs.setdefault(r, 0) dumped = [] +halted = False + +def recv_mem_dump(): + dumped.clear() + for line in sim_end: + line = line.strip() + if line == '=== dump-mem ===' or not line: + continue + elif line == '=== end-mem ===': + break + + base, data = line.split() + dumped.append((int(base, 16) << 2, bytes.fromhex(data))) + def read_mem(base, length): fragments = [] i = 0 + if halted and length > 0: + print('dump-mem', base >> 2, (length + base - (base & ~0b11) + 0b11) >> 2, file=sim_end, flush=True) + recv_mem_dump() + while length > 0: assert i < len(dumped), f'memory at 0x{base:08x} not dumped' start, data = dumped[i] @@ -96,6 +114,19 @@ def read_mem(base, length): return b''.join(fragments) +def write_mem(base, data): + assert halted + + if not data: + return + + prefix = read_mem(base & ~0b11, base & 0b11) + suffix = read_mem(base + len(data), (4 - ((base + len(data)) & 0b11)) & 0b11) + print('patch-mem ', base >> 2, ' ', prefix.hex(), data.hex(), suffix.hex(), sep='', file=sim_end, flush=True) + + #TODO: Invalidate written addresses only + dumped.clear() + def hexdump(base, memory): lines = [] offset = 0 @@ -233,6 +264,7 @@ 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, @@ -267,6 +299,15 @@ for addr, const in module_get('consts', {}).items(): for addr, filename in module_get('loads', {}).items(): exec_args.extend(['--load', f'{addr},{filename}']) +if module_get('start_halted', False): + exec_args.append('--start-halted') + +sim_end, target_end = socket.socketpair() +sim_end = sim_end.makefile('rw') +target_fd = target_end.fileno() + +exec_args.extend(['--control-fd', str(target_fd)]) + init_regs = None exec_args.append(image) @@ -274,29 +315,45 @@ exec_args.append(f'+verilator+seed+{seed}') if not os.getenv('SIM_PULLX', 0): exec_args.append('+verilator+rand+reset+2') -output = subprocess.run(exec_args, stdout=subprocess.PIPE, text=True) -if output.returncode != 0: - exit(success=False) +process = subprocess.Popen(exec_args, pass_fds=(target_fd,)) +target_end.close() in_regs = False -in_mem = False - -for line in output.stdout.split('\n'): - if line == '=== dump-regs ===': - in_regs = True - elif line == '=== dump-mem ===': - in_mem = True - elif not line: - continue - elif in_mem: - base, data = line.split() - dumped.append((int(base, 16) << 2, bytes.fromhex(data))) - elif in_regs: - value, reg = line.split() - regs[reg] = int(value, 16) +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: - while_running() - print(f'{COLOR_BLUE}{line}{COLOR_RESET}') + break + + halted = True + if halt: + halt() + + print('continue', file=sim_end, flush=True) + if not halt: + break + +process.wait(timeout=1) +if process.returncode != 0: + exit(success=False) if final := module_get('final'): final() diff --git a/tb/sim/start.S b/sim/start.S index 7639513..7639513 100644 --- a/tb/sim/start.S +++ b/sim/start.S |
