diff --git a/test/sim/common/init.S b/test/sim/common/init.S index e308d48..ebd63dc 100644 --- a/test/sim/common/init.S +++ b/test/sim/common/init.S @@ -6,8 +6,7 @@ #define IO_EXIT (IO_BASE + 0x8) // Provide trap vector table, reset handler and weak default trap handlers for -// Hazard5. This is not a crt0: the reset handler calls an external _start - +// Hazard3. This is not a crt0: the reset handler calls an external _start .option push .option norelax diff --git a/test/sim/common/src_only_app.mk b/test/sim/common/src_only_app.mk index 040969f..a40f201 100644 --- a/test/sim/common/src_only_app.mk +++ b/test/sim/common/src_only_app.mk @@ -22,7 +22,7 @@ TMP_PREFIX ?= tmp/ all: run run: $(TMP_PREFIX)$(APP).bin - $(TBDIR)/tb $(TMP_PREFIX)$(APP).bin $(TMP_PREFIX)$(APP)_run.vcd --cycles $(MAX_CYCLES) + $(TBDIR)/tb --bin $(TMP_PREFIX)$(APP).bin --vcd $(TMP_PREFIX)$(APP)_run.vcd --cycles $(MAX_CYCLES) view: run gtkwave $(TMP_PREFIX)$(APP)_run.vcd diff --git a/test/sim/openocd/.gitignore b/test/sim/openocd/.gitignore deleted file mode 100644 index 719db5b..0000000 --- a/test/sim/openocd/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -tb -dut.cpp diff --git a/test/sim/openocd/Makefile b/test/sim/openocd/Makefile deleted file mode 100644 index 67c8250..0000000 --- a/test/sim/openocd/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -TOP := tb - -all: tb - -SYNTH_CMD += read_verilog -I ../../../hdl $(shell listfiles tb.f); -SYNTH_CMD += hierarchy -top $(TOP); proc; opt_clean; async2sync; -SYNTH_CMD += write_cxxrtl -g4 dut.cpp - -dut.cpp: - yosys -p "$(SYNTH_CMD)" 2>&1 > cxxrtl.log - -clean:: - rm -f dut.cpp cxxrtl.log tb - -tb: dut.cpp - clang++ -O3 -std=c++14 $(addprefix -D,$(CDEFINES)) -I $(shell yosys-config --datdir)/include tb.cpp -o tb diff --git a/test/sim/openocd/tb.cpp b/test/sim/openocd/tb.cpp deleted file mode 100644 index 87e78f4..0000000 --- a/test/sim/openocd/tb.cpp +++ /dev/null @@ -1,371 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -// Device-under-test model generated by CXXRTL: -#include "dut.cpp" -#include - -static const unsigned int MEM_SIZE = 16 * 1024 * 1024; -uint8_t mem[MEM_SIZE]; - -static const unsigned int IO_BASE = 0x80000000; -enum { - IO_PRINT_CHAR = 0x000, - IO_PRINT_U32 = 0x004, - IO_EXIT = 0x008, - IO_MTIME = 0x100, - IO_MTIMEH = 0x104, - IO_MTIMECMP = 0x108, - IO_MTIMECMPH = 0x10c -}; - -static const int TCP_BUF_SIZE = 256; - -const char *help_str = -"Usage: tb [vcdfile] [--dump start end] [--cycles n] [--port n]\n" -" vcdfile : Path to dump waveforms to\n" -" --dump start end : Print out memory contents between start and end (exclusive)\n" -" after execution finishes. Can be passed multiple times.\n" -" --cycles n : Maximum number of cycles to run before exiting.\n" -" Default is 0 (no maximum).\n" -" --port n : Port number to listen for openocd remote bitbang\n" -; - -void exit_help(std::string errtext = "") { - std::cerr << errtext << help_str; - exit(-1); -} - -int main(int argc, char **argv) { - - bool dump_waves = false; - std::string waves_path; - std::vector> dump_ranges; - int64_t max_cycles = 0; - uint16_t port = 9824; - - for (int i = 1; i < argc; ++i) { - std::string s(argv[i]); - if (i == 1 && s.rfind("--", 0) != 0) { - // Optional positional argument: vcdfile - dump_waves = true; - waves_path = s; - } - else if (s == "--dump") { - if (argc - i < 3) - exit_help("Option --dump requires 2 arguments\n"); - dump_ranges.push_back(std::pair( - std::stoul(argv[i + 1], 0, 0), - std::stoul(argv[i + 2], 0, 0) - ));; - i += 2; - } - else if (s == "--cycles") { - if (argc - i < 2) - exit_help("Option --cycles requires an argument\n"); - max_cycles = std::stol(argv[i + 1], 0, 0); - i += 1; - } - else if (s == "--port") { - if (argc - i < 2) - exit_help("Option --port requires an argument\n"); - port = std::stol(argv[i + 1], 0, 0); - i += 1; - } - else { - std::cerr << "Unrecognised argument " << s << "\n"; - exit_help(""); - } - } - - - int server_fd, sock_fd; - struct sockaddr_in sock_addr; - int sock_opt = 1; - socklen_t sock_addr_len = sizeof(sock_addr); - char txbuf[TCP_BUF_SIZE], rxbuf[TCP_BUF_SIZE]; - int rx_ptr = 0, rx_remaining = 0, tx_ptr = 0; - - server_fd = socket(AF_INET, SOCK_STREAM, 0); - if (server_fd == 0) { - fprintf(stderr, "socket creation failed\n"); - exit(-1); - } - - int setsockopt_rc = setsockopt( - server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, - &sock_opt, sizeof(sock_opt) - ); - - if (setsockopt_rc) { - fprintf(stderr, "setsockopt failed\n"); - exit(-1); - } - - sock_addr.sin_family = AF_INET; - sock_addr.sin_addr.s_addr = INADDR_ANY; - sock_addr.sin_port = htons(port); - if (bind(server_fd, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) < 0) { - fprintf(stderr, "bind failed\n"); - exit(-1); - } - - printf("Waiting for connection on port %u\n", port); - if (listen(server_fd, 3) < 0) { - fprintf(stderr, "listen failed\n"); - exit(-1); - } - sock_fd = accept(server_fd, (struct sockaddr *)&sock_addr, &sock_addr_len); - if (sock_fd < 0) { - fprintf(stderr, "accept failed\n"); - exit(-1); - } - printf("Connected\n"); - - cxxrtl_design::p_tb top; - - std::fill(std::begin(mem), std::end(mem), 0); - - std::ofstream waves_fd; - cxxrtl::vcd_writer vcd; - if (dump_waves) { - waves_fd.open(waves_path); - cxxrtl::debug_items all_debug_items; - top.debug_info(all_debug_items); - vcd.timescale(1, "us"); - vcd.add(all_debug_items); - } - - bool bus_trans = false; - bool bus_write = false; - bool bus_trans_i = false; - uint32_t bus_addr_i = 0; - uint32_t bus_addr = 0; - uint8_t bus_size = 0; - // Never generate bus stalls - top.p_i__hready.set(true); - top.p_d__hready.set(true); - top.p_d__hexokay.set(true); - - uint64_t mtime = 0; - uint64_t mtimecmp = 0; - - // Reset + initial clock pulse - top.step(); - top.p_clk.set(true); - top.p_tck.set(true); - top.step(); - top.p_clk.set(false); - top.p_tck.set(false); - top.p_trst__n.set(true); - top.p_rst__n.set(true); - top.step(); - top.step(); // workaround for github.com/YosysHQ/yosys/issues/2780 - - for (int64_t cycle = 0; cycle < max_cycles || max_cycles == 0; ++cycle) { - top.p_clk.set(false); - top.step(); - if (dump_waves) - vcd.sample(cycle * 2); - top.p_clk.set(true); - top.step(); - top.step(); // workaround for github.com/YosysHQ/yosys/issues/2780 - - // Most bitbang commands complete in one cycle (e.g. TCK/TMS/TDI - // writes) but reads take 0 cycles, step=false. - bool got_exit_cmd = false; - bool step = false; - while (!step) { - if (rx_remaining > 0) { - char c = rxbuf[rx_ptr++]; - --rx_remaining; - - if (c == 'r' || c == 's') { - top.p_trst__n.set(true); - step = true; - } - else if (c == 't' || c == 'u') { - top.p_trst__n.set(false); - } - else if (c >= '0' && c <= '7') { - int mask = c - '0'; - top.p_tck.set(mask & 0x4); - top.p_tms.set(mask & 0x2); - top.p_tdi.set(mask & 0x1); - step = true; - } - else if (c == 'R') { - txbuf[tx_ptr++] = top.p_tdo.get() ? '1' : '0'; - if (tx_ptr >= TCP_BUF_SIZE || rx_remaining == 0) { - send(sock_fd, txbuf, tx_ptr, 0); - tx_ptr = 0; - } - } - else if (c == 'Q') { - printf("OpenOCD sent quit command\n"); - got_exit_cmd = true; - step = true; - } - } - else { - // Potentially the last command was not a read command, but - // OpenOCD is still waiting for a last response from its - // last command packet before it sends us any more, so now is - // the time to flush TX. - if (tx_ptr > 0) { - send(sock_fd, txbuf, tx_ptr, 0); - tx_ptr = 0; - } - rx_ptr = 0; - rx_remaining = read(sock_fd, &rxbuf, TCP_BUF_SIZE); - } - } - - // Default update logic for mtime, mtimecmp - ++mtime; - top.p_timer__irq.set(mtime >= mtimecmp); - - if (top.p_d__hready.get()) { - // Clear bus error by default - top.p_d__hresp.set(false); - // Handle current data phase - uint32_t rdata = 0; - bool bus_err = false; - if (bus_trans && bus_write) { - uint32_t wdata = top.p_d__hwdata.get(); - if (bus_addr <= MEM_SIZE - 4u) { - unsigned int n_bytes = 1u << bus_size; - // Note we are relying on hazard3's byte lane replication - for (unsigned int i = 0; i < n_bytes; ++i) { - mem[bus_addr + i] = wdata >> (8 * i) & 0xffu; - } - } - else if (bus_addr == IO_BASE + IO_PRINT_CHAR) { - putchar(wdata); - } - else if (bus_addr == IO_BASE + IO_PRINT_U32) { - printf("%08x\n", wdata); - } - else if (bus_addr == IO_BASE + IO_EXIT) { - printf("CPU requested halt. Exit code %d\n", wdata); - printf("Ran for %ld cycles\n", cycle + 1); - break; - } - else if (bus_addr == IO_BASE + IO_MTIME) { - mtime = (mtime & 0xffffffff00000000u) | wdata; - } - else if (bus_addr == IO_BASE + IO_MTIMEH) { - mtime = (mtime & 0x00000000ffffffffu) | ((uint64_t)wdata << 32); - } - else if (bus_addr == IO_BASE + IO_MTIMECMP) { - mtimecmp = (mtimecmp & 0xffffffff00000000u) | wdata; - } - else if (bus_addr == IO_BASE + IO_MTIMECMPH) { - mtimecmp = (mtimecmp & 0x00000000ffffffffu) | ((uint64_t)wdata << 32); - } - else { - bus_err = true; - } - } - else if (bus_trans && !bus_write) { - if (bus_addr <= MEM_SIZE - (1u << bus_size)) { - bus_addr &= ~0x3u; - rdata = - (uint32_t)mem[bus_addr] | - mem[bus_addr + 1] << 8 | - mem[bus_addr + 2] << 16 | - mem[bus_addr + 3] << 24; - } - else if (bus_addr == IO_BASE + IO_MTIME) { - rdata = mtime; - } - else if (bus_addr == IO_BASE + IO_MTIMEH) { - rdata = mtime >> 32; - } - else if (bus_addr == IO_BASE + IO_MTIMECMP) { - rdata = mtimecmp; - } - else if (bus_addr == IO_BASE + IO_MTIMECMPH) { - rdata = mtimecmp >> 32; - } - else { - bus_err = true; - } - } - if (bus_err) { - // Phase 1 of error response - top.p_d__hready.set(false); - top.p_d__hresp.set(true); - } - top.p_d__hrdata.set(rdata); - - // Progress current address phase to data phase - bus_trans = top.p_d__htrans.get() >> 1; - bus_write = top.p_d__hwrite.get(); - bus_size = top.p_d__hsize.get(); - bus_addr = top.p_d__haddr.get(); - } - else { - // hready=0. Currently this only happens when we're in the first - // phase of an error response, so go to phase 2. - top.p_d__hready.set(true); - } - - if (top.p_i__hready.get()) { - top.p_i__hresp.set(false); - if (bus_trans_i) { - bus_addr_i &= ~0x3u; - if (bus_addr_i < MEM_SIZE) { - top.p_i__hrdata.set( - (uint32_t)mem[bus_addr_i] | - mem[bus_addr_i + 1] << 8 | - mem[bus_addr_i + 2] << 16 | - mem[bus_addr_i + 3] << 24 - ); - } - else { - top.p_i__hready.set(false); - top.p_i__hresp.set(true); - } - } - bus_trans_i = top.p_i__htrans.get() >> 1; - bus_addr_i = top.p_i__haddr.get(); - } - else { - top.p_i__hready.set(true); - } - - if (dump_waves) { - // The extra step() is just here to get the bus responses to line up nicely - // in the VCD (hopefully is a quick update) - top.step(); - vcd.sample(cycle * 2 + 1); - waves_fd << vcd.buffer; - vcd.buffer.clear(); - } - - if (cycle + 1 == max_cycles) - printf("Max cycles reached\n"); - if (got_exit_cmd) - break; - } - - close(sock_fd); - - for (auto r : dump_ranges) { - printf("Dumping memory from %08x to %08x:\n", r.first, r.second); - for (int i = 0; i < r.second - r.first; ++i) - printf("%02x%c", mem[r.first + i], i % 16 == 15 ? '\n' : ' '); - printf("\n"); - } - - return 0; -} diff --git a/test/sim/riscv-compliance/Makefile b/test/sim/riscv-compliance/Makefile index 3c620cb..b959261 100644 --- a/test/sim/riscv-compliance/Makefile +++ b/test/sim/riscv-compliance/Makefile @@ -55,7 +55,7 @@ define make-test-target $(CROSS_PREFIX)objdump -h tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).elf > tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).dis $(CROSS_PREFIX)objdump -d tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).elf >> tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).dis $(CROSS_PREFIX)objcopy -O binary tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).elf tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).bin - $(SIM_EXEC) tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).bin tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).vcd --dump 0x400000 0x401000 > tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).log + $(SIM_EXEC) --bin tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).bin --vcd tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).vcd --dump 0x400000 0x401000 --cycles 1000000 > tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).log ./compare_testvec tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).log riscv-arch-test/riscv-test-suite/rv32i_m/$(TEST_ARCH)/references/$1.reference_output endef diff --git a/test/sim/riscv-tests/riscv-tests b/test/sim/riscv-tests/riscv-tests index 91329aa..0b2333b 160000 --- a/test/sim/riscv-tests/riscv-tests +++ b/test/sim/riscv-tests/riscv-tests @@ -1 +1 @@ -Subproject commit 91329aa8c23fe1317b035c2dd30681f29db3ef23 +Subproject commit 0b2333b898867a36bda612ac79fc1726b7a268da diff --git a/test/sim/riscv-tests/run-debug-tests.sh b/test/sim/riscv-tests/run-debug-tests.sh index c2860f8..385ad1d 100755 --- a/test/sim/riscv-tests/run-debug-tests.sh +++ b/test/sim/riscv-tests/run-debug-tests.sh @@ -1,6 +1,6 @@ set -e -make -C ../openocd/ clean tb +make -C ../tb_cxxrtl/ tb cd riscv-tests/debug # Clean up old logs and test binaries @@ -11,7 +11,7 @@ done # Only applicable tests are included ./gdbserver.py \ - --sim_cmd ../../../openocd/tb \ + --sim_cmd "../../../tb_cxxrtl/tb --port 9824" \ --server_cmd riscv-openocd \ --gdb riscv32-unknown-elf-gdb \ --gcc riscv32-unknown-elf-gcc \ diff --git a/test/sim/sw_testcases/runtests b/test/sim/sw_testcases/runtests index 2e6b72c..517ce98 100755 --- a/test/sim/sw_testcases/runtests +++ b/test/sim/sw_testcases/runtests @@ -34,7 +34,7 @@ for test in testlist: continue test_run_ret = subprocess.run( - ["../tb_cxxrtl/tb", f"tmp/{test}.bin", f"tmp/{test}_run.vcd", "--cycles", "1000000"], + ["../tb_cxxrtl/tb", "--bin", f"tmp/{test}.bin", "--vcd", f"tmp/{test}_run.vcd", "--cycles", "1000000"], stdout = subprocess.PIPE, timeout=10 ) diff --git a/test/sim/tb_cxxrtl/Makefile b/test/sim/tb_cxxrtl/Makefile index ff6d9ac..8b409ab 100644 --- a/test/sim/tb_cxxrtl/Makefile +++ b/test/sim/tb_cxxrtl/Makefile @@ -1,5 +1,4 @@ -TOP := hazard3_cpu_2port -CDEFINES := DUAL_PORT +TOP := tb CPU_RESET_VECTOR := 32'hc0 @@ -9,7 +8,7 @@ EXTENSION_ZBA := 1 EXTENSION_ZBB := 1 EXTENSION_ZBC := 1 EXTENSION_ZBS := 1 -DEBUG_SUPPORT := 0 +DEBUG_SUPPORT := 1 MULDIV_UNROLL := 2 MUL_FAST := 1 @@ -20,7 +19,7 @@ REDUCED_BYPASS := 0 all: tb -SYNTH_CMD += read_verilog -I ../../../hdl $(shell listfiles ../../../hdl/hazard3.f); +SYNTH_CMD += read_verilog -I ../../../hdl $(shell listfiles tb.f); SYNTH_CMD += chparam -set EXTENSION_C $(EXTENSION_C) $(TOP); SYNTH_CMD += chparam -set EXTENSION_M $(EXTENSION_M) $(TOP); SYNTH_CMD += chparam -set EXTENSION_ZBA $(EXTENSION_ZBA) $(TOP); @@ -34,13 +33,14 @@ SYNTH_CMD += chparam -set REDUCED_BYPASS $(REDUCED_BYPASS) $(TOP); SYNTH_CMD += chparam -set MULDIV_UNROLL $(MULDIV_UNROLL) $(TOP); SYNTH_CMD += chparam -set MUL_FAST $(MUL_FAST) $(TOP); SYNTH_CMD += chparam -set MULH_FAST $(MULH_FAST) $(TOP); +SYNTH_CMD += hierarchy -top $(TOP); SYNTH_CMD += write_cxxrtl dut.cpp -dut.cpp: $(shell listfiles ../../../hdl/hazard3.f); +dut.cpp: $(shell listfiles tb.f) yosys -p "$(SYNTH_CMD)" 2>&1 > cxxrtl.log clean:: rm -f dut.cpp cxxrtl.log tb -tb: dut.cpp +tb: dut.cpp tb.cpp clang++ -O3 -std=c++14 $(addprefix -D,$(CDEFINES)) -I $(shell yosys-config --datdir)/include tb.cpp -o tb diff --git a/test/sim/openocd/compliance.cfg b/test/sim/tb_cxxrtl/compliance.cfg similarity index 100% rename from test/sim/openocd/compliance.cfg rename to test/sim/tb_cxxrtl/compliance.cfg diff --git a/test/sim/openocd/gdbinit b/test/sim/tb_cxxrtl/gdbinit similarity index 100% rename from test/sim/openocd/gdbinit rename to test/sim/tb_cxxrtl/gdbinit diff --git a/test/sim/openocd/openocd.cfg b/test/sim/tb_cxxrtl/openocd.cfg similarity index 100% rename from test/sim/openocd/openocd.cfg rename to test/sim/tb_cxxrtl/openocd.cfg diff --git a/test/sim/tb_cxxrtl/tb.cpp b/test/sim/tb_cxxrtl/tb.cpp index abb676f..a84dd15 100644 --- a/test/sim/tb_cxxrtl/tb.cpp +++ b/test/sim/tb_cxxrtl/tb.cpp @@ -3,9 +3,12 @@ #include #include #include -// jesus fuck i forgot how bad iostream formatting was, give me printf or give me death #include +#include +#include +#include + // Device-under-test model generated by CXXRTL: #include "dut.cpp" #include @@ -24,13 +27,18 @@ enum { IO_MTIMECMPH = 0x10c }; +static const int TCP_BUF_SIZE = 256; + const char *help_str = -"Usage: tb binfile [vcdfile] [--dump start end] [--cycles n]\n" -" binfile : Binary to load into start of memory\n" -" vcdfile : Path to dump waveforms to\n" -" --dump start end : Print out memory contents between start and end (exclusive)\n" +"Usage: tb [--bin x.bin] [--vcd x.vcd] [--dump start end] [--cycles n] [--port n]\n" +" --bin x.bin : Flat binary file loaded to address 0x0 in RAM\n" +" --vcd x.vcd : Path to dump waveforms to\n" +" --dump start end : Print out memory contents from start to end (exclusive)\n" " after execution finishes. Can be passed multiple times.\n" " --cycles n : Maximum number of cycles to run before exiting.\n" +" Default is 0 (no maximum).\n" +" --port n : Port number to listen for openocd remote bitbang. Sim\n" +" runs in lockstep with JTAG bitbang, not free-running.\n" ; void exit_help(std::string errtext = "") { @@ -40,20 +48,33 @@ void exit_help(std::string errtext = "") { int main(int argc, char **argv) { - if (argc < 2) - exit_help(); - + bool load_bin = false; + std::string bin_path; bool dump_waves = false; std::string waves_path; std::vector> dump_ranges; - int64_t max_cycles = 100000; + int64_t max_cycles = 0; + uint16_t port = 0; - for (int i = 2; i < argc; ++i) { + for (int i = 1; i < argc; ++i) { std::string s(argv[i]); - if (i == 2 && s.rfind("--", 0) != 0) { - // Optional positional argument: vcdfile + if (s.rfind("--", 0) != 0) { + std::cerr << "Unexpected positional argument " << s << "\n"; + exit_help(""); + } + else if (s == "--bin") { + if (argc - i < 2) + exit_help("Option --bin requires an argument\n"); + load_bin = true; + bin_path = argv[i + 1]; + i += 1; + } + else if (s == "--vcd") { + if (argc - i < 2) + exit_help("Option --vcd requires an argument\n"); dump_waves = true; - waves_path = s; + waves_path = argv[i + 1]; + i += 1; } else if (s == "--dump") { if (argc - i < 3) @@ -70,28 +91,82 @@ int main(int argc, char **argv) { max_cycles = std::stol(argv[i + 1], 0, 0); i += 1; } + else if (s == "--port") { + if (argc - i < 2) + exit_help("Option --port requires an argument\n"); + port = std::stol(argv[i + 1], 0, 0); + i += 1; + } else { std::cerr << "Unrecognised argument " << s << "\n"; exit_help(""); } } - -#ifdef DUAL_PORT - cxxrtl_design::p_hazard3__cpu__2port top; -#else - cxxrtl_design::p_hazard3__cpu__1port top; -#endif + if (!(load_bin || port != 0)) + exit_help("At least one of --bin or --port must be specified.\n"); std::fill(std::begin(mem), std::end(mem), 0); - - std::ifstream fd(argv[1], std::ios::binary | std::ios::ate); - std::streamsize bin_size = fd.tellg(); - if (bin_size > MEM_SIZE) { - std::cerr << "Binary file (" << bin_size << " bytes) is larger than memory (" << MEM_SIZE << " bytes)\n"; - return -1; + if (load_bin) { + std::ifstream fd(bin_path, std::ios::binary | std::ios::ate); + if (!fd){ + std::cerr << "Failed to open \"" << bin_path << "\"\n"; + return -1; + } + std::streamsize bin_size = fd.tellg(); + if (bin_size > MEM_SIZE) { + std::cerr << "Binary file (" << bin_size << " bytes) is larger than memory (" << MEM_SIZE << " bytes)\n"; + return -1; + } + fd.seekg(0, std::ios::beg); + fd.read((char*)mem, bin_size); } - fd.seekg(0, std::ios::beg); - fd.read((char*)mem, bin_size); + + int server_fd, sock_fd; + struct sockaddr_in sock_addr; + int sock_opt = 1; + socklen_t sock_addr_len = sizeof(sock_addr); + char txbuf[TCP_BUF_SIZE], rxbuf[TCP_BUF_SIZE]; + int rx_ptr = 0, rx_remaining = 0, tx_ptr = 0; + + if (port != 0) { + server_fd = socket(AF_INET, SOCK_STREAM, 0); + if (server_fd == 0) { + fprintf(stderr, "socket creation failed\n"); + exit(-1); + } + + int setsockopt_rc = setsockopt( + server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, + &sock_opt, sizeof(sock_opt) + ); + + if (setsockopt_rc) { + fprintf(stderr, "setsockopt failed\n"); + exit(-1); + } + + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = INADDR_ANY; + sock_addr.sin_port = htons(port); + if (bind(server_fd, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) < 0) { + fprintf(stderr, "bind failed\n"); + exit(-1); + } + + printf("Waiting for connection on port %u\n", port); + if (listen(server_fd, 3) < 0) { + fprintf(stderr, "listen failed\n"); + exit(-1); + } + sock_fd = accept(server_fd, (struct sockaddr *)&sock_addr, &sock_addr_len); + if (sock_fd < 0) { + fprintf(stderr, "accept failed\n"); + exit(-1); + } + printf("Connected\n"); + } + + cxxrtl_design::p_tb top; std::ofstream waves_fd; cxxrtl::vcd_writer vcd; @@ -105,21 +180,14 @@ int main(int argc, char **argv) { bool bus_trans = false; bool bus_write = false; -#ifdef DUAL_PORT bool bus_trans_i = false; uint32_t bus_addr_i = 0; -#endif uint32_t bus_addr = 0; uint8_t bus_size = 0; // Never generate bus stalls -#ifdef DUAL_PORT top.p_i__hready.set(true); top.p_d__hready.set(true); top.p_d__hexokay.set(true); -#else - top.p_ahblm__hready.set(true); - top.p_ahblm__hexokay.set(true); -#endif uint64_t mtime = 0; uint64_t mtimecmp = 0; @@ -127,13 +195,16 @@ int main(int argc, char **argv) { // Reset + initial clock pulse top.step(); top.p_clk.set(true); + top.p_tck.set(true); top.step(); top.p_clk.set(false); + top.p_tck.set(false); + top.p_trst__n.set(true); top.p_rst__n.set(true); top.step(); top.step(); // workaround for github.com/YosysHQ/yosys/issues/2780 - for (int64_t cycle = 0; cycle < max_cycles; ++cycle) { + for (int64_t cycle = 0; cycle < max_cycles || max_cycles == 0; ++cycle) { top.p_clk.set(false); top.step(); if (dump_waves) @@ -142,104 +213,176 @@ int main(int argc, char **argv) { top.step(); top.step(); // workaround for github.com/YosysHQ/yosys/issues/2780 + // If --port is specified, we run the simulator in lockstep with the + // remote bitbang commands, to get more consistent simulation traces. + // This slows down simulation quite a bit compared with normal + // free-running. + // + // Most bitbang commands complete in one cycle (e.g. TCK/TMS/TDI + // writes) but reads take 0 cycles, step=false. + bool got_exit_cmd = false; + bool step = false; + if (port != 0) { + while (!step) { + if (rx_remaining > 0) { + char c = rxbuf[rx_ptr++]; + --rx_remaining; + + if (c == 'r' || c == 's') { + top.p_trst__n.set(true); + step = true; + } + else if (c == 't' || c == 'u') { + top.p_trst__n.set(false); + } + else if (c >= '0' && c <= '7') { + int mask = c - '0'; + top.p_tck.set(mask & 0x4); + top.p_tms.set(mask & 0x2); + top.p_tdi.set(mask & 0x1); + step = true; + } + else if (c == 'R') { + txbuf[tx_ptr++] = top.p_tdo.get() ? '1' : '0'; + if (tx_ptr >= TCP_BUF_SIZE || rx_remaining == 0) { + send(sock_fd, txbuf, tx_ptr, 0); + tx_ptr = 0; + } + } + else if (c == 'Q') { + printf("OpenOCD sent quit command\n"); + got_exit_cmd = true; + step = true; + } + } + else { + // Potentially the last command was not a read command, but + // OpenOCD is still waiting for a last response from its + // last command packet before it sends us any more, so now is + // the time to flush TX. + if (tx_ptr > 0) { + send(sock_fd, txbuf, tx_ptr, 0); + tx_ptr = 0; + } + rx_ptr = 0; + rx_remaining = read(sock_fd, &rxbuf, TCP_BUF_SIZE); + } + } + } + // Default update logic for mtime, mtimecmp ++mtime; top.p_timer__irq.set(mtime >= mtimecmp); - // Handle current data phase, then move current address phase to data phase - uint32_t rdata = 0; - if (bus_trans && bus_write) { -#ifdef DUAL_PORT - uint32_t wdata = top.p_d__hwdata.get(); -#else - uint32_t wdata = top.p_ahblm__hwdata.get(); -#endif - if (bus_addr <= MEM_SIZE - (1u << bus_size)) { - unsigned int n_bytes = 1u << bus_size; - // Note we are relying on hazard3's byte lane replication - for (unsigned int i = 0; i < n_bytes; ++i) { - mem[bus_addr + i] = wdata >> (8 * i) & 0xffu; + if (top.p_d__hready.get()) { + // Clear bus error by default + top.p_d__hresp.set(false); + // Handle current data phase + uint32_t rdata = 0; + bool bus_err = false; + if (bus_trans && bus_write) { + uint32_t wdata = top.p_d__hwdata.get(); + if (bus_addr <= MEM_SIZE - 4u) { + unsigned int n_bytes = 1u << bus_size; + // Note we are relying on hazard3's byte lane replication + for (unsigned int i = 0; i < n_bytes; ++i) { + mem[bus_addr + i] = wdata >> (8 * i) & 0xffu; + } + } + else if (bus_addr == IO_BASE + IO_PRINT_CHAR) { + putchar(wdata); + } + else if (bus_addr == IO_BASE + IO_PRINT_U32) { + printf("%08x\n", wdata); + } + else if (bus_addr == IO_BASE + IO_EXIT) { + printf("CPU requested halt. Exit code %d\n", wdata); + printf("Ran for %ld cycles\n", cycle + 1); + break; + } + else if (bus_addr == IO_BASE + IO_MTIME) { + mtime = (mtime & 0xffffffff00000000u) | wdata; + } + else if (bus_addr == IO_BASE + IO_MTIMEH) { + mtime = (mtime & 0x00000000ffffffffu) | ((uint64_t)wdata << 32); + } + else if (bus_addr == IO_BASE + IO_MTIMECMP) { + mtimecmp = (mtimecmp & 0xffffffff00000000u) | wdata; + } + else if (bus_addr == IO_BASE + IO_MTIMECMPH) { + mtimecmp = (mtimecmp & 0x00000000ffffffffu) | ((uint64_t)wdata << 32); + } + else { + bus_err = true; } } - else if (bus_addr == IO_BASE + IO_PRINT_CHAR) { - putchar(wdata); + else if (bus_trans && !bus_write) { + if (bus_addr <= MEM_SIZE - (1u << bus_size)) { + bus_addr &= ~0x3u; + rdata = + (uint32_t)mem[bus_addr] | + mem[bus_addr + 1] << 8 | + mem[bus_addr + 2] << 16 | + mem[bus_addr + 3] << 24; + } + else if (bus_addr == IO_BASE + IO_MTIME) { + rdata = mtime; + } + else if (bus_addr == IO_BASE + IO_MTIMEH) { + rdata = mtime >> 32; + } + else if (bus_addr == IO_BASE + IO_MTIMECMP) { + rdata = mtimecmp; + } + else if (bus_addr == IO_BASE + IO_MTIMECMPH) { + rdata = mtimecmp >> 32; + } + else { + bus_err = true; + } } - else if (bus_addr == IO_BASE + IO_PRINT_U32) { - printf("%08x\n", wdata); + if (bus_err) { + // Phase 1 of error response + top.p_d__hready.set(false); + top.p_d__hresp.set(true); } - else if (bus_addr == IO_BASE + IO_EXIT) { - printf("CPU requested halt. Exit code %d\n", wdata); - printf("Ran for %ld cycles\n", cycle + 1); - break; - } - else if (bus_addr == IO_BASE + IO_MTIME) { - mtime = (mtime & 0xffffffff00000000u) | wdata; - } - else if (bus_addr == IO_BASE + IO_MTIMEH) { - mtime = (mtime & 0x00000000ffffffffu) | ((uint64_t)wdata << 32); - } - else if (bus_addr == IO_BASE + IO_MTIMECMP) { - mtimecmp = (mtimecmp & 0xffffffff00000000u) | wdata; - } - else if (bus_addr == IO_BASE + IO_MTIMECMPH) { - mtimecmp = (mtimecmp & 0x00000000ffffffffu) | ((uint64_t)wdata << 32); - } - } - else if (bus_trans && !bus_write) { - bus_addr &= ~0x3u; - if (bus_addr <= MEM_SIZE - 4) { - rdata = - (uint32_t)mem[bus_addr] | - mem[bus_addr + 1] << 8 | - mem[bus_addr + 2] << 16 | - mem[bus_addr + 3] << 24; - } - else if (bus_addr == IO_BASE + IO_MTIME) { - rdata = mtime; - } - else if (bus_addr == IO_BASE + IO_MTIMEH) { - rdata = mtime >> 32; - } - else if (bus_addr == IO_BASE + IO_MTIMECMP) { - rdata = mtimecmp; - } - else if (bus_addr == IO_BASE + IO_MTIMECMPH) { - rdata = mtimecmp >> 32; - } - } -#ifdef DUAL_PORT - top.p_d__hrdata.set(rdata); - if (bus_trans_i) { - bus_addr_i &= ~0x3u; - if (bus_addr_i <= MEM_SIZE - 4) { - top.p_i__hrdata.set( - (uint32_t)mem[bus_addr_i] | - mem[bus_addr_i + 1] << 8 | - mem[bus_addr_i + 2] << 16 | - mem[bus_addr_i + 3] << 24 - ); - } - else { - top.p_i__hrdata.set(0); - } - } -#else - top.p_ahblm__hrdata.set(rdata); -#endif + top.p_d__hrdata.set(rdata); -#ifdef DUAL_PORT - bus_trans = top.p_d__htrans.get() >> 1; - bus_write = top.p_d__hwrite.get(); - bus_size = top.p_d__hsize.get(); - bus_addr = top.p_d__haddr.get(); - bus_trans_i = top.p_i__htrans.get() >> 1; - bus_addr_i = top.p_i__haddr.get(); -#else - bus_trans = top.p_ahblm__htrans.get() >> 1; - bus_write = top.p_ahblm__hwrite.get(); - bus_size = top.p_ahblm__hsize.get(); - bus_addr = top.p_ahblm__haddr.get(); -#endif + // Progress current address phase to data phase + bus_trans = top.p_d__htrans.get() >> 1; + bus_write = top.p_d__hwrite.get(); + bus_size = top.p_d__hsize.get(); + bus_addr = top.p_d__haddr.get(); + } + else { + // hready=0. Currently this only happens when we're in the first + // phase of an error response, so go to phase 2. + top.p_d__hready.set(true); + } + + if (top.p_i__hready.get()) { + top.p_i__hresp.set(false); + if (bus_trans_i) { + bus_addr_i &= ~0x3u; + if (bus_addr_i < MEM_SIZE) { + top.p_i__hrdata.set( + (uint32_t)mem[bus_addr_i] | + mem[bus_addr_i + 1] << 8 | + mem[bus_addr_i + 2] << 16 | + mem[bus_addr_i + 3] << 24 + ); + } + else { + top.p_i__hready.set(false); + top.p_i__hresp.set(true); + } + } + bus_trans_i = top.p_i__htrans.get() >> 1; + bus_addr_i = top.p_i__haddr.get(); + } + else { + top.p_i__hready.set(true); + } if (dump_waves) { // The extra step() is just here to get the bus responses to line up nicely @@ -249,8 +392,15 @@ int main(int argc, char **argv) { waves_fd << vcd.buffer; vcd.buffer.clear(); } + + if (cycle + 1 == max_cycles) + printf("Max cycles reached\n"); + if (got_exit_cmd) + break; } + close(sock_fd); + for (auto r : dump_ranges) { printf("Dumping memory from %08x to %08x:\n", r.first, r.second); for (int i = 0; i < r.second - r.first; ++i) diff --git a/test/sim/openocd/tb.f b/test/sim/tb_cxxrtl/tb.f similarity index 100% rename from test/sim/openocd/tb.f rename to test/sim/tb_cxxrtl/tb.f diff --git a/test/sim/openocd/tb.v b/test/sim/tb_cxxrtl/tb.v similarity index 94% rename from test/sim/openocd/tb.v rename to test/sim/tb_cxxrtl/tb.v index 92b5037..ad0041f 100644 --- a/test/sim/openocd/tb.v +++ b/test/sim/tb_cxxrtl/tb.v @@ -4,9 +4,7 @@ `default_nettype none module tb #( - parameter W_DATA = 32, - parameter W_ADDR = 32, - parameter NUM_IRQ = 16 +`include "hazard3_config.vh" ) ( // Global signals input wire clk, @@ -183,21 +181,7 @@ assign sys_reset_done = rst_n_cpu; assign hart_reset_done = rst_n_cpu; hazard3_cpu_2port #( - .RESET_VECTOR (32'hc0), - .MTVEC_INIT (32'h00), - .EXTENSION_C (1), - .EXTENSION_M (1), - .CSR_M_MANDATORY (1), - .CSR_M_TRAP (1), - .CSR_COUNTER (1), - .DEBUG_SUPPORT (1), - .NUM_IRQ (NUM_IRQ), - .MVENDORID_VAL (32'hdeadbeef), - .MIMPID_VAL (32'h12345678), - .MHARTID_VAL (32'h0), - .REDUCED_BYPASS (0), - .MULDIV_UNROLL (2), - .MUL_FAST (1), +`include "hazard3_config_inst.vh" ) cpu ( .clk (clk), .rst_n (rst_n_cpu), diff --git a/test/sim/openocd/waves.gtkw b/test/sim/tb_cxxrtl/waves.gtkw similarity index 100% rename from test/sim/openocd/waves.gtkw rename to test/sim/tb_cxxrtl/waves.gtkw