diff options
Diffstat (limited to '')
| -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 | ||||
| -rw-r--r-- | tb/avalon.hpp | 3 | ||||
| -rw-r--r-- | tb/avalon.impl.hpp | 42 | ||||
| -rw-r--r-- | tb/top/conspiracion.cpp | 187 |
6 files changed, 259 insertions, 72 deletions
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 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/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 ®file = 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 ®file = 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; } |
