summaryrefslogtreecommitdiff
path: root/tb
diff options
context:
space:
mode:
authorAlejandro Soto <alejandro@34project.org>2022-12-06 13:04:15 -0600
committerAlejandro Soto <alejandro@34project.org>2022-12-06 13:04:15 -0600
commit064b72ae4eb22336438288a9664a37c0dd07f4bc (patch)
treebfbe072702b667299979d6ceb76a3ef444fb9c1a /tb
parentdf69f7b7c73be01968ba767ab112b227533bbd70 (diff)
Implement gdbstub
Diffstat (limited to 'tb')
-rw-r--r--tb/avalon.hpp3
-rw-r--r--tb/avalon.impl.hpp42
-rw-r--r--tb/sim/link.ld32
-rwxr-xr-xtb/sim/sim.py310
-rw-r--r--tb/sim/start.S34
-rw-r--r--tb/top/conspiracion.cpp187
6 files changed, 181 insertions, 427 deletions
diff --git a/tb/avalon.hpp b/tb/avalon.hpp
index 30eac2c..f37b306 100644
--- a/tb/avalon.hpp
+++ b/tb/avalon.hpp
@@ -81,6 +81,7 @@ namespace taller::avalon
void bail() noexcept;
std::uint32_t dump(std::uint32_t addr);
+ void patch(std::uint32_t addr, std::uint32_t readdata);
private:
struct binding
@@ -98,6 +99,8 @@ namespace taller::avalon
unsigned avl_byteenable = 0;
bool avl_read = false;
bool avl_write = false;
+
+ slave &resolve_external(std::uint32_t avl_address);
};
}
diff --git a/tb/avalon.impl.hpp b/tb/avalon.impl.hpp
index 5ba514f..3af60d0 100644
--- a/tb/avalon.impl.hpp
+++ b/tb/avalon.impl.hpp
@@ -128,25 +128,45 @@ namespace taller::avalon
std::uint32_t interconnect<Platform>::dump(std::uint32_t addr)
{
std::uint32_t avl_address = addr << 2;
+ auto &dev = resolve_external(avl_address);
+ auto pos = (avl_address & ~dev.address_mask()) >> dev.word_bits();
+
+ std::uint32_t readdata;
+ while(!dev.read(pos, readdata))
+ {
+ continue;
+ }
+
+ return readdata;
+ }
+
+ template<class Platform>
+ void interconnect<Platform>::patch(std::uint32_t addr, std::uint32_t writedata)
+ {
+ std::uint32_t avl_address = addr << 2;
+ auto &dev = resolve_external(avl_address);
+
+ auto pos = (avl_address & ~dev.address_mask()) >> dev.word_bits();
+
+ while(!dev.write(pos, writedata, 0b1111))
+ {
+ continue;
+ }
+ }
+
+ template<class Platform>
+ slave& interconnect<Platform>::resolve_external(std::uint32_t avl_address)
+ {
for(auto &binding : devices)
{
if((avl_address & binding.mask) == binding.base)
{
- auto &dev = binding.dev;
- auto pos = (avl_address & ~dev.address_mask()) >> 2;
-
- std::uint32_t readdata;
- while(!dev.read(pos, readdata))
- {
- continue;
- }
-
- return readdata;
+ return binding.dev;
}
}
- fprintf(stderr, "[avl] attempt to dump memory hole at 0x%08x\n", addr);
+ fprintf(stderr, "[avl] attempt to access hole at 0x%08x\n", avl_address);
assert(false);
}
}
diff --git a/tb/sim/link.ld b/tb/sim/link.ld
deleted file mode 100644
index d26cb2a..0000000
--- a/tb/sim/link.ld
+++ /dev/null
@@ -1,32 +0,0 @@
-MEMORY
-{
- HPS_SDRAM (rwx) : ORIGIN = 0x00000000, LENGTH = 512M
-}
-
-SECTIONS
-{
- ._img :
- {
- KEEP(*(.interrupt_vector))
- *(.text)
- *(.text*)
- *(.rodata)
- *(.rodata*)
- *(.data)
- *(.data*)
- *(.bss)
- *(.bss*)
- *(COMMON)
- . = ALIGN(4);
- } > HPS_SDRAM
-
- _stack_size = 4096;
- _stack_end = ORIGIN(HPS_SDRAM) + LENGTH(HPS_SDRAM);
- _stack_begin = _stack_end - _stack_size;
-
- . = _stack_begin;
- ._stack :
- {
- . = . + _stack_size;
- } > HPS_SDRAM
-}
diff --git a/tb/sim/sim.py b/tb/sim/sim.py
deleted file mode 100755
index 91fc348..0000000
--- a/tb/sim/sim.py
+++ /dev/null
@@ -1,310 +0,0 @@
-#!/usr/bin/env python3
-
-import importlib.util, os, pathlib, random, subprocess, sys
-
-module_path, verilated, image = sys.argv[1:]
-test_name = pathlib.Path(module_path).stem
-module = None
-
-seed = os.getenv('SIM_SEED', str(random.randint(0, 0x7fff_ffff)))
-
-all_regs = [
- ('r0', 'r0'),
- ('r1', 'r1'),
- ('r2', 'r2'),
- ('r3', 'r3'),
- ('r4', 'r4'),
- ('r5', 'r5'),
- ('r6', 'r6'),
- ('r7', 'r7'),
- ('r8', 'r8_usr'),
- ('r8_usr', 'r8_usr'),
- ('r8_fiq', 'r8_fiq'),
- ('r9', 'r9_usr'),
- ('r9_usr', 'r9_usr'),
- ('r9_fiq', 'r9_fiq'),
- ('r10', 'r10_usr'),
- ('r10_usr', 'r10_usr'),
- ('r10_fiq', 'r10_fiq'),
- ('r11', 'r11_usr'),
- ('r11_usr', 'r11_usr'),
- ('r11_fiq', 'r11_fiq'),
- ('r12', 'r12_usr'),
- ('r12_usr', 'r12_usr'),
- ('r12_fiq', 'r12_fiq'),
- ('sp', 'r13_usr'),
- ('sp_usr', 'r13_usr'),
- ('sp_svc', 'r13_svc'),
- ('sp_abt', 'r13_abt'),
- ('sp_und', 'r13_und'),
- ('sp_irq', 'r13_irq'),
- ('sp_fiq', 'r13_fiq'),
- ('r13', 'r13_usr'),
- ('r13_usr', 'r13_usr'),
- ('r13_svc', 'r13_svc'),
- ('r13_abt', 'r13_abt'),
- ('r13_und', 'r13_und'),
- ('r13_irq', 'r13_irq'),
- ('r13_fiq', 'r13_fiq'),
- ('lr', 'r14_usr'),
- ('lr_usr', 'r14_usr'),
- ('lr_svc', 'r14_svc'),
- ('lr_abt', 'r14_abt'),
- ('lr_und', 'r14_und'),
- ('lr_irq', 'r14_irq'),
- ('lr_fiq', 'r14_fiq'),
- ('r14', 'r14_usr'),
- ('r14_usr', 'r14_usr'),
- ('r14_svc', 'r14_svc'),
- ('r14_abt', 'r14_abt'),
- ('r14_und', 'r14_und'),
- ('r14_irq', 'r14_irq'),
- ('r14_fiq', 'r14_fiq'),
- ('pc', 'pc'),
- ('r15', 'pc'),
- ('cpsr', 'cpsr'),
- ('spsr_svc', 'spsr_svc'),
- ('spsr_abt', 'spsr_abt'),
- ('spsr_und', 'spsr_und'),
- ('spsr_irq', 'spsr_irq'),
- ('spsr_fiq', 'spsr_fiq'),
- ]
-
-regs = {}
-read_reg = lambda r: regs.setdefault(r, 0)
-
-dumped = []
-def read_mem(base, length):
- fragments = []
- i = 0
-
- while length > 0:
- assert i < len(dumped), f'memory at 0x{base:08x} not dumped'
- start, data = dumped[i]
- delta = base - start
-
- if delta < 0:
- i = len(dumped)
- elif delta < len(data):
- taken = min(length, len(data) - delta)
- fragments.append(data[delta:delta + taken])
-
- base += taken
- length -= taken
- else:
- i += 1
-
- return b''.join(fragments)
-
-def hexdump(base, memory):
- lines = []
- offset = 0
-
- while offset < len(memory) > 0:
- taken = min(16, len(memory) - offset)
- line_bytes = memory[offset:offset + taken]
-
- half = lambda rng: ' '.join(f'{line_bytes[i]:02x}' if i < taken else ' ' for i in rng)
- left, right = half(range(0, 8)), half(range(8, 16))
-
- ascii = ''.join(c if c.isascii() and c.isprintable() else '.' for c in map(chr, line_bytes))
- lines.append(f' {base:08x}: {left} {right} | {ascii}')
-
- base += 16
- offset += taken
-
- return '\n'.join(lines)
-
-def module_get(attr, default=None):
- return getattr(module, attr, default) if module else None
-
-COLOR_RESET = '\033[0m'
-COLOR_RED = '\033[31;1m'
-COLOR_GREEN = '\033[32m'
-COLOR_YELLOW = '\033[33;1m'
-COLOR_BLUE = '\033[34;1m'
-
-def exit(*, success):
- global seed
-
- if not success:
- while_running()
- if exec_args:
- print('cmdline:', subprocess.list2cmdline(exec_args), file=sys.stderr)
-
- status, color = ('passed', COLOR_GREEN) if success else (f'failed (seed: {seed})', COLOR_RED)
- print( \
- f'{color}Test \'{COLOR_YELLOW}{test_name}{COLOR_RESET}{color}\' ' +
- f'{status}{COLOR_RESET}', file=sys.stderr)
-
- sys.exit(0 if success else 1)
-
-def dump_regs():
- order = {item[0]: i for i, item in enumerate(all_regs)}
- next_col = 0
-
- for reg, value in sorted(regs.items(), key=lambda item: order[item[0]]):
- if next_col > 0:
- print(' ', end='', file=sys.stderr)
-
- print(f'{reg:<8} = 0x{value:08x}', end='', file=sys.stderr)
- if next_col == 3:
- print(file=sys.stderr)
- next_col = 0
- else:
- next_col += 1
-
- if next_col != 0:
- print(file=sys.stderr)
-
-printed_while_running = False
-def while_running():
- global printed_while_running
-
- if not printed_while_running:
- print(
- f'{COLOR_BLUE}While running test \'{COLOR_YELLOW}{test_name}' + \
- f'{COLOR_RESET}{COLOR_BLUE}\'{COLOR_RESET}')
-
- printed_while_running = True
-
-def test_assert(condition, message):
- if not condition:
- while_running()
- print(f'{COLOR_RED}{message()}{COLOR_RESET}', file=sys.stderr)
-
- if regs:
- dump_regs()
-
- exit(success=False)
-
-def unsigned(n):
- assert -0x8000_0000 <= n <= 0xffff_ffff
- return n + 0x1_0000_0000 if n < 0 else n
-
-def split_dword(n):
- assert -0x8000_0000_0000_0000 <= n <= 0xffff_ffff_ffff_ffff
- if n < 0:
- n += 0x1_0000_0000_0000_0000
-
- return (n >> 32, n & 0xffff_ffff)
-
-def int_bytes(n):
- return n.to_bytes(4, 'little', signed=n < 0) if type(n) is int else n
-
-def assert_reg(r, expected):
- actual = read_reg(r)
- expected = unsigned(expected)
-
- test_assert( \
- actual == expected, \
- lambda: f'Register {r} = 0x{actual:08x}, expected 0x{expected:08x}')
-
-def assert_mem(base, value):
- if type(value) is list:
- value = b''.join(int_bytes(w) for w in value)
- else:
- value = int_bytes(value)
-
- actual = read_mem(base, len(value))
- test_assert( \
- actual == value, \
- lambda: \
- f'Memory at 0x{base:08x} holds:\n{hexdump(base, actual)}\n' + \
- f'But this was expected instead:\n{hexdump(base, value)}')
-
-init_regs = {}
-
-def init_reg(r, value):
- global init_regs
- assert init_regs is not None
- init_regs[r] = unsigned(value)
-
-if test_name in os.getenv('SIM_SKIP', '').split(','):
- print( \
- f'{COLOR_BLUE}Test \'{COLOR_YELLOW}{test_name}{COLOR_RESET}' +
- f'{COLOR_BLUE}\' skipped{COLOR_RESET}', file=sys.stderr)
-
- exit(success=True)
-
-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,
- 'assert_reg': assert_reg,
- 'assert_mem': assert_mem,
- 'init_reg': init_reg,
- 'split_dword': split_dword,
- }
-
-prelude.update({k: v for k, v in all_regs})
-module.__dict__.update(prelude)
-spec.loader.exec_module(module)
-
-cycles = module_get('cycles', 1024)
-mem_dumps = module_get('mem_dumps', [])
-
-if init := module_get('init'):
- init()
-
-exec_args = [verilated, '--headless', '--no-tty', '--cycles', str(cycles), '--dump-regs']
-
-for rng in mem_dumps:
- length = rng.stop - rng.start
- assert rng.start >= 0 and rng.stop > rng.start \
- and rng.step == 1 and ((rng.start | length) & 3) == 0
-
- exec_args.extend(['--dump-mem', f'{rng.start >> 2},{length >> 2}'])
-
-for r, value in init_regs.items():
- exec_args.extend(['--init-reg', f'{r}={value}'])
-
-for addr, const in module_get('consts', {}).items():
- exec_args.extend(['--const', f'{addr},{const}'])
-
-for addr, filename in module_get('loads', {}).items():
- exec_args.extend(['--load', f'{addr},{filename}'])
-
-init_regs = None
-exec_args.append(image)
-
-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)
-
-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)
- else:
- while_running()
- print(f'{COLOR_BLUE}{line}{COLOR_RESET}')
-
-if final := module_get('final'):
- 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)))
-
-exit(success=True)
diff --git a/tb/sim/start.S b/tb/sim/start.S
deleted file mode 100644
index 7639513..0000000
--- a/tb/sim/start.S
+++ /dev/null
@@ -1,34 +0,0 @@
-.section .interrupt_vector
-
-__reset:
- b _start
-
-__undefined:
- b undefined
-
-__swi:
- b __data_abort
-
-__prefetch_abort:
- b __prefetch_abort
-
-__data_abort:
- b __data_abort
-
-__irq:
- b __irq
-
-__fiq:
- b __fiq
-
-.text
-
-.global _start
-_start:
- ldr sp, =_stack_end
- bl reset
- b .
-
-.weak undefined
-undefined:
- b undefined
diff --git a/tb/top/conspiracion.cpp b/tb/top/conspiracion.cpp
index 07b35a5..15da2ea 100644
--- a/tb/top/conspiracion.cpp
+++ b/tb/top/conspiracion.cpp
@@ -197,11 +197,21 @@ int main(int argc, char **argv)
parser, "no-tty", "Disable TTY takeoveer", {"no-tty"}
);
+ args::Flag start_halted
+ (
+ parser, "start-halted", "Halt before running the first instruction", {"start-halted"}
+ );
+
args::ValueFlag<unsigned> cycles
(
parser, "cycles", "Number of core cycles to run", {"cycles"}, 256
);
+ args::ValueFlag<int> control_fd
+ (
+ parser, "fd", "Control file descriptor", {"control-fd"}, -1
+ );
+
args::ValueFlagList<mem_region> dump_mem
(
parser, "addr,length", "Dump a memory region", {"dump-mem"}
@@ -209,7 +219,7 @@ int main(int argc, char **argv)
args::ValueFlagList<mem_init> const_
(
- parser, "addr,value", "Add a constant map", {"const"}
+ parser, "addr,value", "Add a constant mapping", {"const"}
);
args::ValueFlagList<file_load> loads
@@ -241,6 +251,13 @@ int main(int argc, char **argv)
return EXIT_FAILURE;
}
+ FILE *ctrl = stdout;
+ if(*control_fd != -1 && (ctrl = fdopen(*control_fd, "r+")) == nullptr)
+ {
+ std::perror("fdopen()");
+ return EXIT_FAILURE;
+ }
+
Vconspiracion top;
VerilatedVcdC trace;
@@ -369,17 +386,136 @@ int main(int argc, char **argv)
ttyJ0.takeover();
}
- top.halt = 0;
+ top.halt = start_halted;
top.rst_n = 0;
cycle();
top.rst_n = 1;
- for(unsigned i = 0; i < *cycles; ++i)
+ auto do_reg_dump = [&]()
+ {
+ std::fputs("=== dump-regs ===\n", ctrl);
+
+ const auto &core = *top.conspiracion->core;
+ const auto &regfile = core.regs->a->file;
+
+ int i = 0;
+ for(const auto *name : gp_regs)
+ {
+ std::fprintf(ctrl, "%08x %s\n", regfile[i++], name);
+ }
+
+ std::fprintf(ctrl, "%08x pc\n", core.control->pc << 2);
+ std::fprintf(ctrl, "%08x cpsr\n", core.psr->cpsr_word);
+ std::fprintf(ctrl, "%08x spsr_svc\n", core.psr->spsr_svc_word);
+ std::fprintf(ctrl, "%08x spsr_abt\n", core.psr->spsr_abt_word);
+ std::fprintf(ctrl, "%08x spsr_und\n", core.psr->spsr_und_word);
+ std::fprintf(ctrl, "%08x spsr_fiq\n", core.psr->spsr_fiq_word);
+ std::fprintf(ctrl, "%08x spsr_irq\n", core.psr->spsr_irq_word);
+ std::fputs("=== end-regs ===\n", ctrl);
+ };
+
+ auto do_mem_dump = [&](const mem_region *dumps, std::size_t count)
{
- cycle();
- if(failed)
+ std::fputs("=== dump-mem ===\n", ctrl);
+ for(std::size_t i = 0; i < count; ++i)
{
- break;
+ const auto &dump = dumps[i];
+
+ std::fprintf(ctrl, "%08x ", static_cast<std::uint32_t>(dump.start));
+ for(std::size_t i = 0; i < dump.length; ++i)
+ {
+ auto word = avl.dump(dump.start + i);
+ word = (word & 0xff) << 24
+ | ((word >> 8) & 0xff) << 16
+ | ((word >> 16) & 0xff) << 8
+ | ((word >> 24) & 0xff);
+
+ std::fprintf(ctrl, "%08x", word);
+ }
+
+ std::fputc('\n', ctrl);
+ }
+
+ std::fputs("=== end-mem ===\n", ctrl);
+ };
+
+ unsigned i = 0;
+ while(!failed && i < *cycles)
+ {
+ for(; i < *cycles; ++i)
+ {
+ cycle();
+ if(failed || top.cpu_halted) [[unlikely]]
+ {
+ break;
+ }
+ }
+
+ if(top.cpu_halted)
+ {
+ do_reg_dump();
+ std::fputs("=== halted ===\n", ctrl);
+
+ char *line = nullptr;
+ std::size_t buf_size = 0;
+
+ while(true)
+ {
+ ssize_t read = getline(&line, &buf_size, ctrl);
+ if(read == -1)
+ {
+ if(!std::feof(ctrl))
+ {
+ std::perror("getline()");
+ failed = true;
+ }
+
+ break;
+ }
+
+ if(read > 0 && line[read - 1] == '\n')
+ {
+ line[read - 1] = '\0';
+ }
+
+ const char *cmd = std::strtok(line, " ");
+ if(!std::strcmp(cmd, "continue"))
+ {
+ top.halt = false;
+ 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);
+
+ while(data && length >= 8)
+ {
+ std::uint32_t word;
+ std::sscanf(data, "%08x", &word);
+
+ data += 8;
+ length -= 8;
+
+ word = (word & 0xff) << 24
+ | ((word >> 8) & 0xff) << 16
+ | ((word >> 16) & 0xff) << 8
+ | ((word >> 24) & 0xff);
+
+ avl.patch(addr++, word);
+ }
+ }
+ }
+
+ std::free(line);
}
}
@@ -395,49 +531,20 @@ int main(int argc, char **argv)
if(dump_regs || failed)
{
- std::puts("=== dump-regs ===");
-
- const auto &core = *top.conspiracion->core;
- const auto &regfile = core.regs->a->file;
-
- int i = 0;
- for(const auto *name : gp_regs)
- {
- std::printf("%08x %s\n", regfile[i++], name);
- }
-
- std::printf("%08x pc\n", core.control->pc << 2);
- std::printf("%08x cpsr\n", core.psr->cpsr_word);
- std::printf("%08x spsr_svc\n", core.psr->spsr_svc_word);
- std::printf("%08x spsr_abt\n", core.psr->spsr_abt_word);
- std::printf("%08x spsr_und\n", core.psr->spsr_und_word);
- std::printf("%08x spsr_fiq\n", core.psr->spsr_fiq_word);
- std::printf("%08x spsr_irq\n", core.psr->spsr_irq_word);
+ do_reg_dump();
}
const auto &dumps = *dump_mem;
if(!dumps.empty())
{
- std::puts("=== dump-mem ===");
+ do_mem_dump(dumps.data(), dumps.size());
}
- for(const auto &dump : dumps)
+ top.final();
+ if(ctrl != stdout)
{
- std::printf("%08x ", static_cast<std::uint32_t>(dump.start));
- for(std::size_t i = 0; i < dump.length; ++i)
- {
- auto word = avl.dump(dump.start + i);
- word = (word & 0xff) << 24
- | ((word >> 8) & 0xff) << 16
- | ((word >> 16) & 0xff) << 8
- | ((word >> 24) & 0xff);
-
- std::printf("%08x", word);
- }
-
- std::putchar('\n');
+ std::fclose(ctrl);
}
- top.final();
return failed ? EXIT_FAILURE : EXIT_SUCCESS;
}