summaryrefslogtreecommitdiff
path: root/sim
diff options
context:
space:
mode:
authorAlejandro Soto <alejandro@34project.org>2022-12-12 12:48:38 -0600
committerAlejandro Soto <alejandro@34project.org>2022-12-16 16:29:10 -0600
commitbb38130bb8e2b8ec17cb6347173af435c5ceef18 (patch)
tree36584332d684a35d3ce512d529e3ba68c8c896fa /sim
parentc67d424c6d130f810c251576fd55389d1385edb5 (diff)
Implement gdbstub monitor
Diffstat (limited to 'sim')
-rw-r--r--sim/gdbstub.py47
-rwxr-xr-xsim/sim.py67
2 files changed, 83 insertions, 31 deletions
diff --git a/sim/gdbstub.py b/sim/gdbstub.py
index 3833da7..63bef9b 100644
--- a/sim/gdbstub.py
+++ b/sim/gdbstub.py
@@ -1,4 +1,4 @@
-import sys, socket
+import sys, socket, traceback
cycles = None
enable_tty = True
@@ -20,6 +20,15 @@ def fatal():
stop_reason = 'fatal'
yield_to_gdb()
+def do_output(text):
+ if text is None:
+ return not is_halted()
+ elif not client:
+ return False
+
+ reply(b'O' + hexout(text.encode('ascii')))
+ return True
+
buffer = b''
stop_reason = None
@@ -27,11 +36,7 @@ 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'
@@ -77,19 +82,19 @@ def yield_to_gdb():
client.send(b'+')
if data == b'?':
- out = stop_reply
+ replyout = stop_reply
elif data == b'c' and not dead:
return 'continue'
elif data == b'D':
- out = b'OK'
+ replyout = b'OK'
elif data == b'g':
- out = hexout(read_reg(gdb_reg(r)) for r in range(16))
+ replyout = hexout(read_reg(gdb_reg(r)) for r in range(16))
elif data[0] == b'G':
for reg, value in enumerate(hexin(data[1:])):
write_reg(reg, value)
elif data[0] == b'm'[0]:
addr, length = (int(x, 16) for x in data[1:].split(b','))
- out = hexout(read_mem(addr, length, may_fail = True))
+ replyout = hexout(read_mem(addr, length, may_fail = True))
elif data[0] == b'M'[0]:
addrlen, data = data[1:].split(b':')
addr, length = (int(x, 16) for x in addrlen.split(b','))
@@ -98,22 +103,34 @@ def yield_to_gdb():
assert len(data) == length
write_mem(addr, data)
- out = b'OK'
+ replyout = 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)
+ replyout = hexout(read_reg(reg) if reg is not None else None)
elif data[0] == b'P'[0]:
reg, value = data[1:].split(b'=')
write_reg(gdb_reg(int(reg, 16)), hexin(value, single=True))
elif data == b's' and not dead:
return 'step'
+ elif data.startswith(b'qRcmd,'):
+ try:
+ result = eval(bytes.fromhex(str(data.removeprefix(b'qRcmd,'), 'ascii')))
+ except Exception:
+ result = None
+ out(traceback.format_exc())
+
+ if result is not None:
+ out(repr(result))
+
+ flush_out()
+ replyout = b'OK'
else:
- out = b''
+ replyout = b''
- reply(out)
+ reply(replyout)
-def reply(out):
- client.send(b'$' + out + b'#' + hexout(sum(out) & 0xff, size=1))
+def reply(replyout):
+ client.send(b'$' + replyout + b'#' + hexout(sum(replyout) & 0xff, size=1))
def gdb_reg(n):
if 0 <= n < 8:
diff --git a/sim/sim.py b/sim/sim.py
index 6ef0ed0..dceabb4 100755
--- a/sim/sim.py
+++ b/sim/sim.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-import importlib.util, os, pathlib, random, selectors, signal, socket, subprocess, sys
+import importlib.util, io, os, pathlib, random, selectors, signal, socket, subprocess, sys
module_path, verilated, image = sys.argv[1:]
test_name = pathlib.Path(module_path).stem
@@ -77,6 +77,34 @@ all_regs = [
regs = {}
read_reg = lambda r: regs.setdefault(r, 0)
+output_buffer = None
+def out(*args, **kwargs):
+ global output_buffer
+
+ if do_output:
+ if output_buffer is None:
+ output_buffer = io.StringIO()
+
+ print(*args, **kwargs, file=output_buffer)
+ if do_output(None):
+ flush_out()
+ else:
+ print(*args, **kwargs, file=sys.stderr)
+
+def flush_out():
+ global do_output, output_buffer
+ if output_buffer and output_buffer:
+ text = output_buffer.getvalue()
+ output_buffer.close()
+ output_buffer = None
+
+ try:
+ if do_output(text):
+ return
+ except:
+ do_output = None
+ print(text, file=sys.stderr)
+
def write_reg(reg, value):
assert halted
@@ -102,7 +130,7 @@ def recv_mem_dump():
dumped.append((int(base, 16) << 2, bytes.fromhex(data)))
except ValueError:
while_running()
- print(f'{COLOR_BLUE}{line}{COLOR_RESET}')
+ out(f'{COLOR_BLUE}{line}{COLOR_RESET}')
def read_mem(base, length, *, may_fail = False):
fragments = []
@@ -180,12 +208,12 @@ def exit(*, success):
if not success:
while_running()
if exec_args:
- print('cmdline:', subprocess.list2cmdline(exec_args), file=sys.stderr)
+ out('cmdline:', subprocess.list2cmdline(exec_args))
status, color = ('passed', COLOR_GREEN) if success else (f'failed (seed: {seed})', COLOR_RED)
- print( \
+ out( \
f'{color}Test \'{COLOR_YELLOW}{test_name}{COLOR_RESET}{color}\' ' +
- f'{status}{COLOR_RESET}', file=sys.stderr)
+ f'{status}{COLOR_RESET}')
sys.exit(0 if success else 1)
@@ -195,24 +223,24 @@ def dump_regs():
for reg, value in sorted(regs.items(), key=lambda item: order[item[0]]):
if next_col > 0:
- print(' ', end='', file=sys.stderr)
+ out(' ', end='')
- print(f'{reg:<8} = 0x{value:08x}', end='', file=sys.stderr)
+ out(f'{reg:<8} = 0x{value:08x}', end='')
if next_col == 3:
- print(file=sys.stderr)
+ out()
next_col = 0
else:
next_col += 1
if next_col != 0:
- print(file=sys.stderr)
+ out()
printed_while_running = False
def while_running():
global printed_while_running
if not printed_while_running:
- print(
+ out(
f'{COLOR_BLUE}While running test \'{COLOR_YELLOW}{test_name}' + \
f'{COLOR_RESET}{COLOR_BLUE}\'{COLOR_RESET}')
@@ -221,7 +249,7 @@ def while_running():
def test_assert(condition, message):
if not condition:
while_running()
- print(f'{COLOR_RED}{message()}{COLOR_RESET}', file=sys.stderr)
+ out(f'{COLOR_RED}{message()}{COLOR_RESET}')
if regs:
dump_regs()
@@ -271,9 +299,9 @@ def init_reg(r, value):
init_regs[r] = unsigned(value)
if test_name in os.getenv('SIM_SKIP', '').split(','):
- print( \
+ out( \
f'{COLOR_BLUE}Test \'{COLOR_YELLOW}{test_name}{COLOR_RESET}' +
- f'{COLOR_BLUE}\' skipped{COLOR_RESET}', file=sys.stderr)
+ f'{COLOR_BLUE}\' skipped{COLOR_RESET}')
exit(success=True)
@@ -289,6 +317,9 @@ spec = importlib.util.spec_from_file_location('sim', module_path)
module = importlib.util.module_from_spec(spec)
prelude = {
+ 'out': out,
+ 'flush_out': flush_out,
+ 'is_halted': lambda: halted,
'read_reg': read_reg,
'write_reg': write_reg,
'read_mem': read_mem,
@@ -296,6 +327,7 @@ prelude = {
'assert_reg': assert_reg,
'assert_mem': assert_mem,
'init_reg': init_reg,
+ 'dump_regs': dump_regs,
'split_dword': split_dword,
'register_interrupt': register_interrupt,
}
@@ -305,6 +337,7 @@ module.__dict__.update(prelude)
spec.loader.exec_module(module)
mem_dumps = module_get('mem_dumps', [])
+do_output = module_get('do_output')
if init := module_get('init'):
init()
@@ -391,7 +424,7 @@ def read_ctrl():
regs[reg] = int(value, 16)
else:
while_running()
- print(f'{COLOR_BLUE}{line}{COLOR_RESET}')
+ out(f'{COLOR_BLUE}{line}{COLOR_RESET}')
sel.register(sim_end_sock, selectors.EVENT_READ, read_ctrl)
while not done:
@@ -410,6 +443,8 @@ while not done:
mode = halt()
print('step' if mode == 'step' else 'continue', file=sim_end, flush=True)
+ flush_out()
+
if not halt:
break
@@ -425,7 +460,7 @@ if final := module_get('final'):
if os.getenv('SIM_DUMP', ''):
dump_regs()
for rng in mem_dumps:
- print(f'Memory range 0x{rng.start:08x}..0x{rng.stop:08x}')
- print(hexdump(rng.start, read_mem(rng.start, rng.stop - rng.start)))
+ out(f'Memory range 0x{rng.start:08x}..0x{rng.stop:08x}')
+ out(hexdump(rng.start, read_mem(rng.start, rng.stop - rng.start)))
exit(success=True)