From 6c175d5dc428f630e3bd4caf707db4b77b0b87e7 Mon Sep 17 00:00:00 2001 From: Alejandro Soto Date: Mon, 12 Feb 2024 16:20:12 -0600 Subject: rtl, tb: add core.mk files --- tb/top/conspiracion.cpp | 792 ----------------------------------- tb/top/conspiracion/conspiracion.cpp | 792 +++++++++++++++++++++++++++++++++++ tb/top/conspiracion/mod.mk | 8 + tb/top/conspiracion/vga_domain.sv | 4 +- 4 files changed, 802 insertions(+), 794 deletions(-) delete mode 100644 tb/top/conspiracion.cpp create mode 100644 tb/top/conspiracion/conspiracion.cpp create mode 100644 tb/top/conspiracion/mod.mk (limited to 'tb/top') diff --git a/tb/top/conspiracion.cpp b/tb/top/conspiracion.cpp deleted file mode 100644 index e1f5f78..0000000 --- a/tb/top/conspiracion.cpp +++ /dev/null @@ -1,792 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#if VM_TRACE -#include -#endif - -#include "Vtop__Syms.h" - -#include "args.hxx" - -#include "avalon.hpp" -#include "const.hpp" -#include "mem.hpp" -#include "jtag_uart.hpp" -#include "interval_timer.hpp" -#include "null.hpp" -#include "sim_slave.hpp" -#include "window.hpp" -#include "vga.hpp" - -namespace -{ - volatile sig_atomic_t async_halt = 0; - - constexpr const char *gp_regs[30] = - { - [0] = "r0", - [1] = "r1", - [2] = "r2", - [3] = "r3", - [4] = "r4", - [5] = "r5", - [6] = "r6", - [7] = "r7", - [8] = "r8_usr", - [9] = "r9_usr", - [10] = "r10_usr", - [11] = "r11_usr", - [12] = "r12_usr", - [13] = "r13_usr", - [14] = "r14_usr", - [15] = "r8_fiq", - [16] = "r9_fiq", - [17] = "r10_fiq", - [18] = "r11_fiq", - [19] = "r12_fiq", - [20] = "r13_fiq", - [21] = "r14_fiq", - [22] = "r13_irq", - [23] = "r14_irq", - [24] = "r13_und", - [25] = "r14_und", - [26] = "r13_abt", - [27] = "r14_abt", - [28] = "r13_svc", - [29] = "r14_svc", - }; - - struct mem_region - { - std::size_t start; - std::size_t length; - }; - - struct reg_init - { - std::size_t index; - std::uint32_t value; - }; - - struct mem_init - { - std::uint32_t addr; - std::uint32_t value; - }; - - struct file_load - { - std::uint32_t addr; - std::string filename; - }; - - std::istream &operator>>(std::istream &stream, mem_region ®ion) - { - stream >> region.start; - if(stream.get() == ',') - { - stream >> region.length; - } else - { - stream.setstate(std::istream::failbit); - } - - return stream; - } - - std::istream &operator>>(std::istream &stream, mem_init &init) - { - stream >> init.addr; - if(stream.get() == ',') - { - stream >> init.value; - } else - { - stream.setstate(std::istream::failbit); - } - - return stream; - } - - std::istream &operator>>(std::istream &stream, file_load &load) - { - stream >> load.addr; - if(stream.get() == ',') - { - stream >> load.filename; - } else - { - stream.setstate(std::istream::failbit); - } - - return stream; - } - - std::istream &operator>>(std::istream &stream, reg_init &init) - { - char name[16]; - stream.getline(name, sizeof name, '='); - - std::size_t index = 0; - constexpr auto total_gp_regs = sizeof gp_regs / sizeof gp_regs[0]; - - while(index < total_gp_regs && std::strcmp(name, gp_regs[index])) - { - ++index; - } - - if(stream && !stream.eof() && index < total_gp_regs) - { - init.index = index; - stream >> init.value; - } else - { - stream.setstate(std::istream::failbit); - } - - return stream; - } - - void async_halt_handler(int) - { - async_halt = 1; - } -} - -int main(int argc, char **argv) -{ - using namespace taller::avalon; - using namespace taller::vga; - - Verilated::commandArgs(argc, argv); - - for(char **arg = argv; *arg; ++arg) - { - if(**arg == '+') - { - *arg = NULL; - argc = arg - argv; - break; - } - } - - args::ArgumentParser parser("Simulador proyecto final CE3201"); - - args::ValueFlagList init_regs - ( - parser, "reg=val", "Initialize a register", {"init-reg"} - ); - - args::Flag dump_regs - ( - parser, "dump-regs", "Dump all registers", {"dump-regs"} - ); - - args::Flag headless - ( - parser, "headless", "Disable video output", {"headless"} - ); - - args::Flag accurate_video - ( - parser, "accurate-video", "Enable signal-level video emulation", {"accurate-video"} - ); - - args::Flag no_tty - ( - 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 cycles - ( - parser, "cycles", "Max number of core cycles to run", {"cycles"}, 0 - ); - - args::ValueFlag control_fd - ( - parser, "fd", "Control file descriptor", {"control-fd"}, -1 - ); - - args::ValueFlagList dump_mem - ( - parser, "addr,length", "Dump a memory region", {"dump-mem"} - ); - - args::ValueFlagList const_ - ( - parser, "addr,value", "Add a constant mapping", {"const"} - ); - - args::ValueFlagList loads - ( - parser, "addr,filename", "Load a file", {"load"} - ); - - args::ValueFlag coverage_out - ( - parser, "coverage", "Coverage output file", {"coverage"} - ); - - args::ValueFlag trace_out - ( - parser, "trace", "trace output file", {"trace"} - ); - - args::Positional image - ( - parser, "image", "Executable image to run", args::Options::Required - ); - - try - { - parser.ParseCLI(argc, argv); - } catch(args::Help) - { - std::cout << parser; - return EXIT_SUCCESS; - } catch(args::ParseError e) - { - std::cerr << e.what() << std::endl; - std::cerr << parser; - return EXIT_FAILURE; - } catch(args::ValidationError e) - { - std::cerr << e.what() << std::endl; - std::cerr << parser; - return EXIT_FAILURE; - } - - FILE *ctrl = stdout; - if(*control_fd != -1) - { - if((ctrl = fdopen(*control_fd, "r+")) == nullptr) - { - std::perror("fdopen()"); - return EXIT_FAILURE; - } - - dup2(*control_fd, STDERR_FILENO); - } - - Vtop top; - -#if VM_TRACE - VerilatedFstC trace; - - auto enable_trace = static_cast(trace_out); - if (enable_trace) { - Verilated::traceEverOn(true); - top.trace(&trace, 0); - trace.open(trace_out->c_str()); - } -#else - bool enable_trace = false; -#endif - - mem hps_ddr3(0x0000'0000, 512 << 20); - jtag_uart ttyJ0(0x3000'0000); - interval_timer timer(0x3002'0000); - interrupt_controller intc(0x3007'0000); - - auto &irq_lines = intc.lines(); - irq_lines.jtaguart = &ttyJ0; - irq_lines.timer = &timer; - - mem vram(0x3800'0000, 64 << 20); - null vram_null(0x3800'0000, 64 << 20, 2); - window vram_window(vram, 0x0000'0000); - - Vtop_platform &plat = *top.conspiracion->plat; - display vga - ( - *plat.vga, 0x3800'0000, 25'175'000, 50'000'000 - ); - - sim_slave dbg_0(*plat.smp_dbg_0, 0x3010'0000, 32); - sim_slave dbg_1(*plat.smp_dbg_1, 0x3011'0000, 32); - sim_slave dbg_2(*plat.smp_dbg_2, 0x3012'0000, 32); - sim_slave dbg_3(*plat.smp_dbg_3, 0x3013'0000, 32); - sim_slave smp_ctrl(*plat.smp_sim, 0x3014'0000, 4); - sim_slave perf_monitor(*plat.perf_sim, 0x3015'0000, 256); - - interconnect avl(plat); - //interconnect avl_vga(plat->vga); - - std::vector consts; - for(const auto &init : *const_) - { - consts.emplace_back(init.addr, init.value); - } - - bool enable_fast_video = !headless && !accurate_video; - bool enable_accurate_video = !headless && accurate_video; - - avl.attach(hps_ddr3); - avl.attach(timer); - avl.attach(ttyJ0); - avl.attach(dbg_0); - avl.attach(dbg_1); - avl.attach(dbg_2); - avl.attach(dbg_3); - avl.attach(smp_ctrl); - avl.attach(perf_monitor); - avl.attach_intc(intc); - - for (auto &slave : consts) - avl.attach(slave); - - if (enable_fast_video) - avl.attach(vga); - else if(enable_accurate_video) { - avl.attach(vram); - //avl_vga.attach(vram_window); - } else - avl.attach(vram_null); - - FILE *img_file = std::fopen(image->c_str(), "rb"); - if(!img_file) - { - std::fprintf(stderr, "fopen(\"%s\"): %m\n", image->c_str()); - return EXIT_FAILURE; - } - - hps_ddr3.load([&](line *buffer, std::size_t lines) - { - return std::fread(buffer, sizeof *buffer, lines, img_file); - }); - - std::fclose(img_file); - - for(const auto &load : *loads) - { - FILE *img_file = std::fopen(load.filename.c_str(), "rb"); - if(!img_file) - { - std::fprintf(stderr, "fopen(\"%s\"): %m\n", load.filename.c_str()); - return EXIT_FAILURE; - } - - hps_ddr3.load([&](line *buffer, std::size_t lines) - { - return std::fread(buffer, sizeof *buffer, lines, img_file); - }, load.addr); - - std::fclose(img_file); - } - - Vtop_arm810 *const cores[] = { - plat.cpu_0->enable__DOT__cpu, - plat.cpu_1->enable__DOT__cpu, - plat.cpu_2->enable__DOT__cpu, - plat.cpu_3->enable__DOT__cpu - }; - - Vtop_cache_sram *const caches[] = { - plat.cache_0->enable__DOT__sram, - plat.cache_1->enable__DOT__sram, - plat.cache_2->enable__DOT__sram, - plat.cache_3->enable__DOT__sram - }; - - for (const auto &init : init_regs) { - cores[0]->regs->a->file[init.index] = init.value; - cores[0]->regs->b->file[init.index] = init.value; - } - - int time = 0; - top.clk_clk = 1; - - bool failed = false; - - auto tick = [&]() - { - top.clk_clk = !top.clk_clk; - top.eval(); - - if(!avl.tick(top.clk_clk)) - { - failed = true; - } - - if(enable_accurate_video) - { - /*if(!avl_vga.tick(top.clk_clk)) - { - failed = true; - }*/ - - vga.signal_tick(top.clk_clk); - } - -#if VM_TRACE - if (enable_trace) - trace.dump(time++); -#endif - }; - - auto cycle = [&]() - { - tick(); - tick(); - }; - - if (!no_tty) - ttyJ0.takeover(); - - for (auto *core : cores) { - // No toman efecto hasta que no se levante VforceEn - core->fetch->explicit_branch__VforceVal = 1; - core->halt__VforceVal = 1; - core->step__VforceVal = 1; - } - - top.rst_n = 0; - cycle(); - top.rst_n = 1; - - cores[0]->halt__VforceEn = static_cast(start_halted); - - auto do_reg_dump = [&]() - { - std::fputs("=== dump-regs ===\n", ctrl); - - // TODO: cores[i] - const auto &core = *cores[0]; - 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::fprintf(ctrl, "%08x sysctrl\n", core.cp15->syscfg->ctrl); - std::fprintf(ctrl, "%08x ttbr\n", core.cp15->ttbr->read); - std::fprintf(ctrl, "%08x far\n", core.cp15->far_->read); - std::fprintf(ctrl, "%08x fsr\n", core.cp15->fsr->read); - std::fprintf(ctrl, "%08x dacr\n", core.cp15->domain->mmu_dac); - std::fprintf(ctrl, "%08x bh0\n", core.control->ctrl_issue->bh0); - std::fprintf(ctrl, "%08x bh1\n", core.control->ctrl_issue->bh1); - std::fprintf(ctrl, "%08x bh2\n", core.control->ctrl_issue->bh2); - std::fprintf(ctrl, "%08x bh3\n", core.control->ctrl_issue->bh3); - std::fputs("=== end-regs ===\n", ctrl); - }; - - auto dump_coherent = [&](std::uint32_t addr, std::uint32_t &data) - { - bool ok = avl.dump(addr, data); - if (!ok || (ok >> 29)) - return ok; - - unsigned tag = (addr >> 14) & ((1 << 13) - 1); - unsigned index = (addr >> 2) & ((1 << 12) - 1); - - for (std::size_t i = 0; i < sizeof caches / sizeof caches[0]; ++i) { - const auto *cache = caches[i]; - - if (cache->state_file[index] != 0b00 && cache->tag_file[index] == tag) { - line line_data = cache->data_file[index]; - data = line_data.words[addr & 0b11]; - } - } - - return true; - }; - - auto pagewalk = [&](std::uint32_t &addr) - { - // TODO: core[i] - const auto &core = *cores[0]; - if (!core.mmu->mmu_enable) - return true; - - std::uint32_t ttbr = core.mmu->mmu_ttbr; - - std::uint32_t entry; - if (!dump_coherent(ttbr << 12 | addr >> 18, entry)) - return false; - - switch (entry & 0b11) { - case 0b01: - break; - - case 0b10: - addr = (entry & ~((1 << 20) - 1)) >> 2 | (addr & ((1 << 18) - 1)); - return true; - - default: - return false; - } - - std::uint32_t entryaddr = (entry & ~((1 << 10) - 1)) >> 2 | ((addr >> 10) & ((1 << 8) - 1)); - if (!dump_coherent(entryaddr, entry)) - return false; - - switch (entry & 0b11) { - case 0b01: - addr = (entry & ~((1 << 16) - 1)) >> 2 | (addr & ((1 << 14) - 1)); - return true; - - case 0b10: - case 0b11: - addr = (entry & ~((1 << 12) - 1)) >> 2 | (addr & ((1 << 10) - 1)); - return true; - - default: - return false; - } - }; - - auto do_mem_dump = [&](const mem_region *dumps, std::size_t count) - { - std::fputs("=== dump-mem ===\n", ctrl); - for(std::size_t i = 0; i < count; ++i) - { - const auto &dump = dumps[i]; - - std::fprintf(ctrl, "%08x ", static_cast(dump.start)); - for(std::size_t i = 0; i < dump.length; ++i) - { - std::uint32_t at = dump.start + i; - if(!pagewalk(at)) - { - break; - } - - std::uint32_t word; - if (!dump_coherent(at, word)) - break; - - 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); - }; - - std::signal(SIGUSR1, async_halt_handler); - - auto maybe_halt = [&]() - { - bool has_halted = false; - for (auto *core : cores) { - if (core->breakpoint || async_halt) - core->halt__VforceEn = 1; - - has_halted = has_halted || core->halt__VforceEn; - } - - return has_halted; - }; - - auto loop_fast = [&]() - { - do { - for (unsigned iters = 0; iters < 4096; ++iters) { - top.clk_clk = 0; - top.eval(); - avl.tick_falling(); - - top.clk_clk = 1; - top.eval(); - avl.tick_rising(); - } - } while (!maybe_halt()); - }; - - unsigned i = 0; - auto loop_accurate = [&]() - { - bool exit_loop = false; - - do { - cycle(); - - if (maybe_halt()) - for (const auto *core : cores) - if (core->halted) - exit_loop = true; - } while (!exit_loop && !failed && (*cycles == 0 || ++i < *cycles)); - }; - - const bool slow_path = *cycles > 0 || enable_accurate_video || enable_trace; - - while (true) { - bool needs_accurate = false; - for (const auto *core : cores) - if (core->halt__VforceEn || core->step__VforceEn) { - needs_accurate = true; - break; - } - - try { - if (slow_path || needs_accurate) - loop_accurate(); - else - loop_fast(); - } catch (const avl_bus_error&) { - failed = true; - break; - } - - if (failed || (*cycles > 0 && i >= *cycles)) - break; - - for (auto *core : cores) { - core->fetch->target__VforceVal = core->control->pc; - core->step__VforceEn = 0; - } - - // TODO: cores[i] - auto *current_core = cores[0]; - - do_reg_dump(); - std::fprintf(ctrl, "=== %s ===\n", failed ? "fault" : "halted"); - - 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")) - break; - else if(!std::strcmp(cmd, "step")) { - current_core->step__VforceEn = 1; - 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); - - std::uint32_t phys = addr++; - if (!pagewalk(phys)) - break; - - avl.patch(phys, word); - } - } else if (!std::strcmp(cmd, "patch-reg")) { - std::uint32_t value; - std::sscanf(std::strtok(nullptr, " "), "%u", &value); - - const char *name = std::strtok(nullptr, " "); - if (!std::strcmp(name, "pc")) - current_core->fetch->target__VforceVal = value >> 2; - else { - std::size_t index = 0; - for (const char *reg : gp_regs) { - if (!strcmp(name, reg)) { - current_core->regs->a->file[index] = value; - current_core->regs->b->file[index] = value; - break; - } - - ++index; - } - } - } - } - - std::free(line); - async_halt = 0; - - for (auto *core : cores) { - core->fetch->target__VforceEn = 0xffff'ffff; - core->fetch->explicit_branch__VforceEn = 1; - } - - cycle(); - - for (auto *core : cores) { - core->fetch->target__VforceEn = 0; - core->fetch->explicit_branch__VforceEn = 0; - core->halt__VforceEn = 0; - } - } - - if (!no_tty) - ttyJ0.release(); - -#if VM_TRACE - if (enable_trace) - trace.close(); -#endif - - if (dump_regs) - do_reg_dump(); - - const auto &dumps = *dump_mem; - if (!dumps.empty()) - do_mem_dump(dumps.data(), dumps.size()); - - top.final(); - if (ctrl != stdout) - std::fclose(ctrl); - -#if VM_COVERAGE - if (coverage_out) - Verilated::threadContextp()->coveragep()->write(coverage_out->c_str()); -#endif - - return failed ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/tb/top/conspiracion/conspiracion.cpp b/tb/top/conspiracion/conspiracion.cpp new file mode 100644 index 0000000..e1f5f78 --- /dev/null +++ b/tb/top/conspiracion/conspiracion.cpp @@ -0,0 +1,792 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#if VM_TRACE +#include +#endif + +#include "Vtop__Syms.h" + +#include "args.hxx" + +#include "avalon.hpp" +#include "const.hpp" +#include "mem.hpp" +#include "jtag_uart.hpp" +#include "interval_timer.hpp" +#include "null.hpp" +#include "sim_slave.hpp" +#include "window.hpp" +#include "vga.hpp" + +namespace +{ + volatile sig_atomic_t async_halt = 0; + + constexpr const char *gp_regs[30] = + { + [0] = "r0", + [1] = "r1", + [2] = "r2", + [3] = "r3", + [4] = "r4", + [5] = "r5", + [6] = "r6", + [7] = "r7", + [8] = "r8_usr", + [9] = "r9_usr", + [10] = "r10_usr", + [11] = "r11_usr", + [12] = "r12_usr", + [13] = "r13_usr", + [14] = "r14_usr", + [15] = "r8_fiq", + [16] = "r9_fiq", + [17] = "r10_fiq", + [18] = "r11_fiq", + [19] = "r12_fiq", + [20] = "r13_fiq", + [21] = "r14_fiq", + [22] = "r13_irq", + [23] = "r14_irq", + [24] = "r13_und", + [25] = "r14_und", + [26] = "r13_abt", + [27] = "r14_abt", + [28] = "r13_svc", + [29] = "r14_svc", + }; + + struct mem_region + { + std::size_t start; + std::size_t length; + }; + + struct reg_init + { + std::size_t index; + std::uint32_t value; + }; + + struct mem_init + { + std::uint32_t addr; + std::uint32_t value; + }; + + struct file_load + { + std::uint32_t addr; + std::string filename; + }; + + std::istream &operator>>(std::istream &stream, mem_region ®ion) + { + stream >> region.start; + if(stream.get() == ',') + { + stream >> region.length; + } else + { + stream.setstate(std::istream::failbit); + } + + return stream; + } + + std::istream &operator>>(std::istream &stream, mem_init &init) + { + stream >> init.addr; + if(stream.get() == ',') + { + stream >> init.value; + } else + { + stream.setstate(std::istream::failbit); + } + + return stream; + } + + std::istream &operator>>(std::istream &stream, file_load &load) + { + stream >> load.addr; + if(stream.get() == ',') + { + stream >> load.filename; + } else + { + stream.setstate(std::istream::failbit); + } + + return stream; + } + + std::istream &operator>>(std::istream &stream, reg_init &init) + { + char name[16]; + stream.getline(name, sizeof name, '='); + + std::size_t index = 0; + constexpr auto total_gp_regs = sizeof gp_regs / sizeof gp_regs[0]; + + while(index < total_gp_regs && std::strcmp(name, gp_regs[index])) + { + ++index; + } + + if(stream && !stream.eof() && index < total_gp_regs) + { + init.index = index; + stream >> init.value; + } else + { + stream.setstate(std::istream::failbit); + } + + return stream; + } + + void async_halt_handler(int) + { + async_halt = 1; + } +} + +int main(int argc, char **argv) +{ + using namespace taller::avalon; + using namespace taller::vga; + + Verilated::commandArgs(argc, argv); + + for(char **arg = argv; *arg; ++arg) + { + if(**arg == '+') + { + *arg = NULL; + argc = arg - argv; + break; + } + } + + args::ArgumentParser parser("Simulador proyecto final CE3201"); + + args::ValueFlagList init_regs + ( + parser, "reg=val", "Initialize a register", {"init-reg"} + ); + + args::Flag dump_regs + ( + parser, "dump-regs", "Dump all registers", {"dump-regs"} + ); + + args::Flag headless + ( + parser, "headless", "Disable video output", {"headless"} + ); + + args::Flag accurate_video + ( + parser, "accurate-video", "Enable signal-level video emulation", {"accurate-video"} + ); + + args::Flag no_tty + ( + 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 cycles + ( + parser, "cycles", "Max number of core cycles to run", {"cycles"}, 0 + ); + + args::ValueFlag control_fd + ( + parser, "fd", "Control file descriptor", {"control-fd"}, -1 + ); + + args::ValueFlagList dump_mem + ( + parser, "addr,length", "Dump a memory region", {"dump-mem"} + ); + + args::ValueFlagList const_ + ( + parser, "addr,value", "Add a constant mapping", {"const"} + ); + + args::ValueFlagList loads + ( + parser, "addr,filename", "Load a file", {"load"} + ); + + args::ValueFlag coverage_out + ( + parser, "coverage", "Coverage output file", {"coverage"} + ); + + args::ValueFlag trace_out + ( + parser, "trace", "trace output file", {"trace"} + ); + + args::Positional image + ( + parser, "image", "Executable image to run", args::Options::Required + ); + + try + { + parser.ParseCLI(argc, argv); + } catch(args::Help) + { + std::cout << parser; + return EXIT_SUCCESS; + } catch(args::ParseError e) + { + std::cerr << e.what() << std::endl; + std::cerr << parser; + return EXIT_FAILURE; + } catch(args::ValidationError e) + { + std::cerr << e.what() << std::endl; + std::cerr << parser; + return EXIT_FAILURE; + } + + FILE *ctrl = stdout; + if(*control_fd != -1) + { + if((ctrl = fdopen(*control_fd, "r+")) == nullptr) + { + std::perror("fdopen()"); + return EXIT_FAILURE; + } + + dup2(*control_fd, STDERR_FILENO); + } + + Vtop top; + +#if VM_TRACE + VerilatedFstC trace; + + auto enable_trace = static_cast(trace_out); + if (enable_trace) { + Verilated::traceEverOn(true); + top.trace(&trace, 0); + trace.open(trace_out->c_str()); + } +#else + bool enable_trace = false; +#endif + + mem hps_ddr3(0x0000'0000, 512 << 20); + jtag_uart ttyJ0(0x3000'0000); + interval_timer timer(0x3002'0000); + interrupt_controller intc(0x3007'0000); + + auto &irq_lines = intc.lines(); + irq_lines.jtaguart = &ttyJ0; + irq_lines.timer = &timer; + + mem vram(0x3800'0000, 64 << 20); + null vram_null(0x3800'0000, 64 << 20, 2); + window vram_window(vram, 0x0000'0000); + + Vtop_platform &plat = *top.conspiracion->plat; + display vga + ( + *plat.vga, 0x3800'0000, 25'175'000, 50'000'000 + ); + + sim_slave dbg_0(*plat.smp_dbg_0, 0x3010'0000, 32); + sim_slave dbg_1(*plat.smp_dbg_1, 0x3011'0000, 32); + sim_slave dbg_2(*plat.smp_dbg_2, 0x3012'0000, 32); + sim_slave dbg_3(*plat.smp_dbg_3, 0x3013'0000, 32); + sim_slave smp_ctrl(*plat.smp_sim, 0x3014'0000, 4); + sim_slave perf_monitor(*plat.perf_sim, 0x3015'0000, 256); + + interconnect avl(plat); + //interconnect avl_vga(plat->vga); + + std::vector consts; + for(const auto &init : *const_) + { + consts.emplace_back(init.addr, init.value); + } + + bool enable_fast_video = !headless && !accurate_video; + bool enable_accurate_video = !headless && accurate_video; + + avl.attach(hps_ddr3); + avl.attach(timer); + avl.attach(ttyJ0); + avl.attach(dbg_0); + avl.attach(dbg_1); + avl.attach(dbg_2); + avl.attach(dbg_3); + avl.attach(smp_ctrl); + avl.attach(perf_monitor); + avl.attach_intc(intc); + + for (auto &slave : consts) + avl.attach(slave); + + if (enable_fast_video) + avl.attach(vga); + else if(enable_accurate_video) { + avl.attach(vram); + //avl_vga.attach(vram_window); + } else + avl.attach(vram_null); + + FILE *img_file = std::fopen(image->c_str(), "rb"); + if(!img_file) + { + std::fprintf(stderr, "fopen(\"%s\"): %m\n", image->c_str()); + return EXIT_FAILURE; + } + + hps_ddr3.load([&](line *buffer, std::size_t lines) + { + return std::fread(buffer, sizeof *buffer, lines, img_file); + }); + + std::fclose(img_file); + + for(const auto &load : *loads) + { + FILE *img_file = std::fopen(load.filename.c_str(), "rb"); + if(!img_file) + { + std::fprintf(stderr, "fopen(\"%s\"): %m\n", load.filename.c_str()); + return EXIT_FAILURE; + } + + hps_ddr3.load([&](line *buffer, std::size_t lines) + { + return std::fread(buffer, sizeof *buffer, lines, img_file); + }, load.addr); + + std::fclose(img_file); + } + + Vtop_arm810 *const cores[] = { + plat.cpu_0->enable__DOT__cpu, + plat.cpu_1->enable__DOT__cpu, + plat.cpu_2->enable__DOT__cpu, + plat.cpu_3->enable__DOT__cpu + }; + + Vtop_cache_sram *const caches[] = { + plat.cache_0->enable__DOT__sram, + plat.cache_1->enable__DOT__sram, + plat.cache_2->enable__DOT__sram, + plat.cache_3->enable__DOT__sram + }; + + for (const auto &init : init_regs) { + cores[0]->regs->a->file[init.index] = init.value; + cores[0]->regs->b->file[init.index] = init.value; + } + + int time = 0; + top.clk_clk = 1; + + bool failed = false; + + auto tick = [&]() + { + top.clk_clk = !top.clk_clk; + top.eval(); + + if(!avl.tick(top.clk_clk)) + { + failed = true; + } + + if(enable_accurate_video) + { + /*if(!avl_vga.tick(top.clk_clk)) + { + failed = true; + }*/ + + vga.signal_tick(top.clk_clk); + } + +#if VM_TRACE + if (enable_trace) + trace.dump(time++); +#endif + }; + + auto cycle = [&]() + { + tick(); + tick(); + }; + + if (!no_tty) + ttyJ0.takeover(); + + for (auto *core : cores) { + // No toman efecto hasta que no se levante VforceEn + core->fetch->explicit_branch__VforceVal = 1; + core->halt__VforceVal = 1; + core->step__VforceVal = 1; + } + + top.rst_n = 0; + cycle(); + top.rst_n = 1; + + cores[0]->halt__VforceEn = static_cast(start_halted); + + auto do_reg_dump = [&]() + { + std::fputs("=== dump-regs ===\n", ctrl); + + // TODO: cores[i] + const auto &core = *cores[0]; + 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::fprintf(ctrl, "%08x sysctrl\n", core.cp15->syscfg->ctrl); + std::fprintf(ctrl, "%08x ttbr\n", core.cp15->ttbr->read); + std::fprintf(ctrl, "%08x far\n", core.cp15->far_->read); + std::fprintf(ctrl, "%08x fsr\n", core.cp15->fsr->read); + std::fprintf(ctrl, "%08x dacr\n", core.cp15->domain->mmu_dac); + std::fprintf(ctrl, "%08x bh0\n", core.control->ctrl_issue->bh0); + std::fprintf(ctrl, "%08x bh1\n", core.control->ctrl_issue->bh1); + std::fprintf(ctrl, "%08x bh2\n", core.control->ctrl_issue->bh2); + std::fprintf(ctrl, "%08x bh3\n", core.control->ctrl_issue->bh3); + std::fputs("=== end-regs ===\n", ctrl); + }; + + auto dump_coherent = [&](std::uint32_t addr, std::uint32_t &data) + { + bool ok = avl.dump(addr, data); + if (!ok || (ok >> 29)) + return ok; + + unsigned tag = (addr >> 14) & ((1 << 13) - 1); + unsigned index = (addr >> 2) & ((1 << 12) - 1); + + for (std::size_t i = 0; i < sizeof caches / sizeof caches[0]; ++i) { + const auto *cache = caches[i]; + + if (cache->state_file[index] != 0b00 && cache->tag_file[index] == tag) { + line line_data = cache->data_file[index]; + data = line_data.words[addr & 0b11]; + } + } + + return true; + }; + + auto pagewalk = [&](std::uint32_t &addr) + { + // TODO: core[i] + const auto &core = *cores[0]; + if (!core.mmu->mmu_enable) + return true; + + std::uint32_t ttbr = core.mmu->mmu_ttbr; + + std::uint32_t entry; + if (!dump_coherent(ttbr << 12 | addr >> 18, entry)) + return false; + + switch (entry & 0b11) { + case 0b01: + break; + + case 0b10: + addr = (entry & ~((1 << 20) - 1)) >> 2 | (addr & ((1 << 18) - 1)); + return true; + + default: + return false; + } + + std::uint32_t entryaddr = (entry & ~((1 << 10) - 1)) >> 2 | ((addr >> 10) & ((1 << 8) - 1)); + if (!dump_coherent(entryaddr, entry)) + return false; + + switch (entry & 0b11) { + case 0b01: + addr = (entry & ~((1 << 16) - 1)) >> 2 | (addr & ((1 << 14) - 1)); + return true; + + case 0b10: + case 0b11: + addr = (entry & ~((1 << 12) - 1)) >> 2 | (addr & ((1 << 10) - 1)); + return true; + + default: + return false; + } + }; + + auto do_mem_dump = [&](const mem_region *dumps, std::size_t count) + { + std::fputs("=== dump-mem ===\n", ctrl); + for(std::size_t i = 0; i < count; ++i) + { + const auto &dump = dumps[i]; + + std::fprintf(ctrl, "%08x ", static_cast(dump.start)); + for(std::size_t i = 0; i < dump.length; ++i) + { + std::uint32_t at = dump.start + i; + if(!pagewalk(at)) + { + break; + } + + std::uint32_t word; + if (!dump_coherent(at, word)) + break; + + 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); + }; + + std::signal(SIGUSR1, async_halt_handler); + + auto maybe_halt = [&]() + { + bool has_halted = false; + for (auto *core : cores) { + if (core->breakpoint || async_halt) + core->halt__VforceEn = 1; + + has_halted = has_halted || core->halt__VforceEn; + } + + return has_halted; + }; + + auto loop_fast = [&]() + { + do { + for (unsigned iters = 0; iters < 4096; ++iters) { + top.clk_clk = 0; + top.eval(); + avl.tick_falling(); + + top.clk_clk = 1; + top.eval(); + avl.tick_rising(); + } + } while (!maybe_halt()); + }; + + unsigned i = 0; + auto loop_accurate = [&]() + { + bool exit_loop = false; + + do { + cycle(); + + if (maybe_halt()) + for (const auto *core : cores) + if (core->halted) + exit_loop = true; + } while (!exit_loop && !failed && (*cycles == 0 || ++i < *cycles)); + }; + + const bool slow_path = *cycles > 0 || enable_accurate_video || enable_trace; + + while (true) { + bool needs_accurate = false; + for (const auto *core : cores) + if (core->halt__VforceEn || core->step__VforceEn) { + needs_accurate = true; + break; + } + + try { + if (slow_path || needs_accurate) + loop_accurate(); + else + loop_fast(); + } catch (const avl_bus_error&) { + failed = true; + break; + } + + if (failed || (*cycles > 0 && i >= *cycles)) + break; + + for (auto *core : cores) { + core->fetch->target__VforceVal = core->control->pc; + core->step__VforceEn = 0; + } + + // TODO: cores[i] + auto *current_core = cores[0]; + + do_reg_dump(); + std::fprintf(ctrl, "=== %s ===\n", failed ? "fault" : "halted"); + + 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")) + break; + else if(!std::strcmp(cmd, "step")) { + current_core->step__VforceEn = 1; + 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); + + std::uint32_t phys = addr++; + if (!pagewalk(phys)) + break; + + avl.patch(phys, word); + } + } else if (!std::strcmp(cmd, "patch-reg")) { + std::uint32_t value; + std::sscanf(std::strtok(nullptr, " "), "%u", &value); + + const char *name = std::strtok(nullptr, " "); + if (!std::strcmp(name, "pc")) + current_core->fetch->target__VforceVal = value >> 2; + else { + std::size_t index = 0; + for (const char *reg : gp_regs) { + if (!strcmp(name, reg)) { + current_core->regs->a->file[index] = value; + current_core->regs->b->file[index] = value; + break; + } + + ++index; + } + } + } + } + + std::free(line); + async_halt = 0; + + for (auto *core : cores) { + core->fetch->target__VforceEn = 0xffff'ffff; + core->fetch->explicit_branch__VforceEn = 1; + } + + cycle(); + + for (auto *core : cores) { + core->fetch->target__VforceEn = 0; + core->fetch->explicit_branch__VforceEn = 0; + core->halt__VforceEn = 0; + } + } + + if (!no_tty) + ttyJ0.release(); + +#if VM_TRACE + if (enable_trace) + trace.close(); +#endif + + if (dump_regs) + do_reg_dump(); + + const auto &dumps = *dump_mem; + if (!dumps.empty()) + do_mem_dump(dumps.data(), dumps.size()); + + top.final(); + if (ctrl != stdout) + std::fclose(ctrl); + +#if VM_COVERAGE + if (coverage_out) + Verilated::threadContextp()->coveragep()->write(coverage_out->c_str()); +#endif + + return failed ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/tb/top/conspiracion/mod.mk b/tb/top/conspiracion/mod.mk new file mode 100644 index 0000000..d775150 --- /dev/null +++ b/tb/top/conspiracion/mod.mk @@ -0,0 +1,8 @@ +cores := conspiracion/tb + +define core/conspiracion/tb + $(this)/deps := cache core perf smp interconnect + $(this)/rtl_files := platform.sv sim_slave.sv vga_domain.sv + $(this)/vl_files := interrupt.cpp interval_timer.cpp jtag_uart.cpp mem.cpp sim_slave.cpp + $(this)/vl_pkgconfig := ncursesw sdl2 +endef diff --git a/tb/top/conspiracion/vga_domain.sv b/tb/top/conspiracion/vga_domain.sv index 0c9aac5..55c564c 100644 --- a/tb/top/conspiracion/vga_domain.sv +++ b/tb/top/conspiracion/vga_domain.sv @@ -26,11 +26,11 @@ module vga_domain logic[7:0] vga_g /*verilator public*/; logic[7:0] vga_b /*verilator public*/; - vga crtc + /*vga crtc ( .clk(clk_clk), .rst_n(reset_reset_n), .* - ); + );*/ endmodule -- cgit v1.2.3