summaryrefslogtreecommitdiff
path: root/sim
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--sim/gdbstub.py134
-rw-r--r--sim/link.ld (renamed from tb/sim/link.ld)0
-rwxr-xr-xsim/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