diff --git a/hdl/debug/cdc/hazard3_apb_async_bridge.v b/hdl/debug/cdc/hazard3_apb_async_bridge.v index 5eeede8..e3d236e 100644 --- a/hdl/debug/cdc/hazard3_apb_async_bridge.v +++ b/hdl/debug/cdc/hazard3_apb_async_bridge.v @@ -123,7 +123,6 @@ always @ (posedge clk_src or negedge rst_n_src) begin if (!rst_n_src) begin src_req <= 1'b0; src_waiting_for_downstream <= 1'b0; - src_paddr_pwdata_pwrite <= {W_ADDR + W_DATA + 1{1'b0}}; src_prdata_pslverr <= {W_DATA + 1{1'b0}}; src_pready_r <= 1'b1; end else if (src_waiting_for_downstream) begin @@ -163,25 +162,23 @@ assign src_pready = src_pready_r; // dst state machine wire dst_bus_finish = dst_penable && dst_pready; +reg dst_psel_r; +reg dst_penable_r; always @ (posedge clk_dst or negedge rst_n_dst) begin if (!rst_n_dst) begin dst_ack <= 1'b0; end else if (dst_req) begin dst_ack <= 1'b1; - end else if (!dst_req && dst_penable && dst_pready) begin + end else if (!dst_req && dst_ack && !dst_psel_r) begin dst_ack <= 1'b0; end end -reg dst_psel_r; -reg dst_penable_r; - always @ (posedge clk_dst or negedge rst_n_dst) begin if (!rst_n_dst) begin dst_psel_r <= 1'b0; dst_penable_r <= 1'b0; - dst_prdata_pslverr <= {W_DATA + 1{1'b0}}; end else if (dst_req && !dst_ack) begin dst_psel_r <= 1'b1; // Note this assignment is cross-domain. The src register has been diff --git a/hdl/debug/dtm/hazard3_jtag_dtm.v b/hdl/debug/dtm/hazard3_jtag_dtm.v index 52a0dd7..07a1bec 100644 --- a/hdl/debug/dtm/hazard3_jtag_dtm.v +++ b/hdl/debug/dtm/hazard3_jtag_dtm.v @@ -23,32 +23,32 @@ `default_nettype none module hazard3_jtag_dtm #( - parameter IDCODE = 32'h0000_0001, - parameter DTMCS_IDLE_HINT = 3'd4 + parameter IDCODE = 32'h0000_0001, + parameter DTMCS_IDLE_HINT = 3'd4 ) ( - // Standard JTAG signals -- the JTAG hardware is clocked directly by TCK. - input wire tck, - input wire trst_n, - input wire tms, - input wire tdi, - output reg tdo, + // Standard JTAG signals -- the JTAG hardware is clocked directly by TCK. + input wire tck, + input wire trst_n, + input wire tms, + input wire tdi, + output reg tdo, - // This is synchronous to TCK and asserted for one TCK cycle only - output reg dmihardreset_req, + // This is synchronous to TCK and asserted for one TCK cycle only + output reg dmihardreset_req, - // Bus clock + reset for Debug Module Interface - input wire clk_dmi, - input wire rst_n_dmi, + // Bus clock + reset for Debug Module Interface + input wire clk_dmi, + input wire rst_n_dmi, - // Debug Module Interface (APB) - output wire dmi_psel, - output wire dmi_penable, - output wire dmi_pwrite, - output wire [7:0] dmi_paddr, - output wire [31:0] dmi_pwdata, - input wire [31:0] dmi_prdata, - input wire dmi_pready, - input wire dmi_pslverr + // Debug Module Interface (APB) + output wire dmi_psel, + output wire dmi_penable, + output wire dmi_pwrite, + output wire [7:0] dmi_paddr, + output wire [31:0] dmi_pwdata, + input wire [31:0] dmi_prdata, + input wire dmi_pready, + input wire dmi_pslverr ); // ---------------------------------------------------------------------------- @@ -73,28 +73,28 @@ localparam S_EXIT2_IR = 4'd14; localparam S_UPDATE_IR = 4'd15; always @ (posedge tck or negedge trst_n) begin - if (!trst_n) begin - tap_state <= S_RESET; - end else case(tap_state) - S_RESET : tap_state <= tms ? S_RESET : S_RUN_IDLE ; - S_RUN_IDLE : tap_state <= tms ? S_SELECT_DR : S_RUN_IDLE ; + if (!trst_n) begin + tap_state <= S_RESET; + end else case(tap_state) + S_RESET : tap_state <= tms ? S_RESET : S_RUN_IDLE ; + S_RUN_IDLE : tap_state <= tms ? S_SELECT_DR : S_RUN_IDLE ; - S_SELECT_DR : tap_state <= tms ? S_SELECT_IR : S_CAPTURE_DR; - S_CAPTURE_DR : tap_state <= tms ? S_EXIT1_DR : S_SHIFT_DR ; - S_SHIFT_DR : tap_state <= tms ? S_EXIT1_DR : S_SHIFT_DR ; - S_EXIT1_DR : tap_state <= tms ? S_UPDATE_DR : S_PAUSE_DR ; - S_PAUSE_DR : tap_state <= tms ? S_EXIT2_DR : S_PAUSE_DR ; - S_EXIT2_DR : tap_state <= tms ? S_UPDATE_DR : S_SHIFT_DR ; - S_UPDATE_DR : tap_state <= tms ? S_SELECT_DR : S_RUN_IDLE ; + S_SELECT_DR : tap_state <= tms ? S_SELECT_IR : S_CAPTURE_DR; + S_CAPTURE_DR : tap_state <= tms ? S_EXIT1_DR : S_SHIFT_DR ; + S_SHIFT_DR : tap_state <= tms ? S_EXIT1_DR : S_SHIFT_DR ; + S_EXIT1_DR : tap_state <= tms ? S_UPDATE_DR : S_PAUSE_DR ; + S_PAUSE_DR : tap_state <= tms ? S_EXIT2_DR : S_PAUSE_DR ; + S_EXIT2_DR : tap_state <= tms ? S_UPDATE_DR : S_SHIFT_DR ; + S_UPDATE_DR : tap_state <= tms ? S_SELECT_DR : S_RUN_IDLE ; - S_SELECT_IR : tap_state <= tms ? S_RESET : S_CAPTURE_IR; - S_CAPTURE_IR : tap_state <= tms ? S_EXIT1_IR : S_SHIFT_IR ; - S_SHIFT_IR : tap_state <= tms ? S_EXIT1_IR : S_SHIFT_IR ; - S_EXIT1_IR : tap_state <= tms ? S_UPDATE_IR : S_PAUSE_IR ; - S_PAUSE_IR : tap_state <= tms ? S_EXIT2_IR : S_PAUSE_IR ; - S_EXIT2_IR : tap_state <= tms ? S_UPDATE_IR : S_SHIFT_IR ; - S_UPDATE_IR : tap_state <= tms ? S_SELECT_DR : S_RUN_IDLE ; - endcase + S_SELECT_IR : tap_state <= tms ? S_RESET : S_CAPTURE_IR; + S_CAPTURE_IR : tap_state <= tms ? S_EXIT1_IR : S_SHIFT_IR ; + S_SHIFT_IR : tap_state <= tms ? S_EXIT1_IR : S_SHIFT_IR ; + S_EXIT1_IR : tap_state <= tms ? S_UPDATE_IR : S_PAUSE_IR ; + S_PAUSE_IR : tap_state <= tms ? S_EXIT2_IR : S_PAUSE_IR ; + S_EXIT2_IR : tap_state <= tms ? S_UPDATE_IR : S_SHIFT_IR ; + S_UPDATE_IR : tap_state <= tms ? S_SELECT_DR : S_RUN_IDLE ; + endcase end // ---------------------------------------------------------------------------- @@ -110,19 +110,19 @@ reg [W_IR-1:0] ir_shift; reg [W_IR-1:0] ir; always @ (posedge tck or negedge trst_n) begin - if (!trst_n) begin - ir_shift <= {W_IR{1'b0}}; - ir <= IR_IDCODE; - end else if (tap_state == S_RESET) begin - ir_shift <= {W_IR{1'b0}}; - ir <= IR_IDCODE; - end else if (tap_state == S_CAPTURE_IR) begin - ir_shift <= ir; - end else if (tap_state == S_SHIFT_IR) begin - ir_shift <= {ir_shift[W_IR-2:0], tdi}; - end else if (tap_state == S_UPDATE_IR) begin - ir <= ir_shift; - end + if (!trst_n) begin + ir_shift <= {W_IR{1'b0}}; + ir <= IR_IDCODE; + end else if (tap_state == S_RESET) begin + ir_shift <= {W_IR{1'b0}}; + ir <= IR_IDCODE; + end else if (tap_state == S_CAPTURE_IR) begin + ir_shift <= ir; + end else if (tap_state == S_SHIFT_IR) begin + ir_shift <= {tdi, ir_shift} >> 1; + end else if (tap_state == S_UPDATE_IR) begin + ir <= ir_shift; + end end // ---------------------------------------------------------------------------- @@ -136,47 +136,47 @@ reg [W_DR_SHIFT-1:0] dr_shift; reg [1:0] dmi_cmderr; always @ (posedge tck or negedge trst_n) begin - if (!trst_n) begin - dr_shift <= {W_DR_SHIFT{1'b0}}; - end else if (tap_state == S_RESET) begin - dr_shift <= {W_DR_SHIFT{1'b0}}; - end else if (tap_state == S_SHIFT_DR) begin - dr_shift <= {tdi, dr_shift} >> 1; - // Shorten DR shift chain according to IR - if (ir == IR_DMI) - dr_shift[41] <= tdi; - else if (ir == IR_IDCODE || ir == IR_DTMCS) - dr_shift[31] <= tdi; - else // BYPASS - dr_shift[0] <= tdi; - end else if (tap_state == S_CAPTURE_DR) begin - if (ir == IR_DMI) - dr_shift <= { - 8'h0, - dtm_prdata, - dmi_busy && dmi_cmderr == 2'd0 ? 2'd3 : dmi_cmderr - }; - else if (ir == IR_DTMCS) - dr_shift <= { - 27'h0, - DTMCS_IDLE_HINT, - dmi_cmderr, - 6'd8, // abits - 4'd1 // version - }; - else if (ir == IR_IDCODE) - dr_shift <= {10'h0, IDCODE}; - else // BYPASS - dr_shift <= 42'h0; - end + if (!trst_n) begin + dr_shift <= {W_DR_SHIFT{1'b0}}; + end else if (tap_state == S_RESET) begin + dr_shift <= {W_DR_SHIFT{1'b0}}; + end else if (tap_state == S_SHIFT_DR) begin + dr_shift <= {tdi, dr_shift} >> 1; + // Shorten DR shift chain according to IR + if (ir == IR_DMI) + dr_shift[41] <= tdi; + else if (ir == IR_IDCODE || ir == IR_DTMCS) + dr_shift[31] <= tdi; + else // BYPASS + dr_shift[0] <= tdi; + end else if (tap_state == S_CAPTURE_DR) begin + if (ir == IR_DMI) + dr_shift <= { + 8'h0, + dtm_prdata, + dmi_busy && dmi_cmderr == 2'd0 ? 2'd3 : dmi_cmderr + }; + else if (ir == IR_DTMCS) + dr_shift <= { + 27'h0, + DTMCS_IDLE_HINT, + dmi_cmderr, + 6'd8, // abits + 4'd1 // version + }; + else if (ir == IR_IDCODE) + dr_shift <= {10'h0, IDCODE}; + else // BYPASS + dr_shift <= 42'h0; + end end always @ (posedge tck or negedge trst_n) begin - if (!trst_n) begin - dmihardreset_req <= 1'b0; - end else begin - dmihardreset_req <= tap_state == S_UPDATE_DR && ir == IR_DTMCS && dr_shift[17]; - end + if (!trst_n) begin + dmihardreset_req <= 1'b0; + end else begin + dmihardreset_req <= tap_state == S_UPDATE_DR && ir == IR_DTMCS && dr_shift[17]; + end end // ---------------------------------------------------------------------------- @@ -210,8 +210,8 @@ wire dtm_pslverr; // psel is only pulsed for one cycle, penable is not asserted. assign dtm_psel = tap_state == S_UPDATE_DR && ir == IR_DMI && - (dr_shift[1:0] == 2'd1 || dr_shift[1:0] == 2'd2) && - !(dmi_busy || dmi_cmderr != 2'd0) && dtm_pready; + (dr_shift[1:0] == 2'd1 || dr_shift[1:0] == 2'd2) && + !(dmi_busy || dmi_cmderr != 2'd0) && dtm_pready; assign dtm_penable = 1'b0; // paddr/pwdata/pwrite are valid momentarily when psel is asserted. @@ -220,72 +220,75 @@ assign dtm_pwrite = dr_shift[1]; assign dtm_pwdata = dr_shift[2 +: 32]; always @ (posedge tck or negedge trst_n) begin - if (!trst_n) begin - dmi_busy <= 1'b0; - dmi_cmderr <= 2'd0; - end else if (tap_state == S_CAPTURE_IR && ir == IR_DMI) begin - // Reading while busy sets the busy sticky error. Note the capture - // into shift register should also reflect this update on-the-fly - if (dmi_busy && dmi_cmderr == 2'd0) - dmi_cmderr <= 2'h3; - end else if (tap_state == S_UPDATE_DR && ir == IR_DTMCS) begin - // Writing dtmcs.dmireset = 1 clears a sticky error - if (dr_shift[16]) - dmi_cmderr <= 2'd0; - end else if (tap_state == S_UPDATE_DR && ir == IR_DMI) begin - if (dtm_psel) - dmi_busy <= 1'b1; - else if (dmi_cmderr == 2'd0) - dmi_cmderr <= 2'd3; - end else if (dmi_busy && dtm_pready) begin - dmi_busy <= 1'b0; - if (dmi_cmderr == 2'd0 && dtm_pslverr) - dmi_cmderr <= 2'd2; - end + if (!trst_n) begin + dmi_busy <= 1'b0; + dmi_cmderr <= 2'd0; + end else if (tap_state == S_CAPTURE_IR && ir == IR_DMI) begin + // Reading while busy sets the busy sticky error. Note the capture + // into shift register should also reflect this update on-the-fly + if (dmi_busy && dmi_cmderr == 2'd0) + dmi_cmderr <= 2'h3; + end else if (tap_state == S_UPDATE_DR && ir == IR_DTMCS) begin + // Writing dtmcs.dmireset = 1 clears a sticky error + if (dr_shift[16]) + dmi_cmderr <= 2'd0; + end else if (tap_state == S_UPDATE_DR && ir == IR_DMI) begin + if (dtm_psel) begin + dmi_busy <= 1'b1; + end else if (dr_shift[1:0] != 2'd0) begin + // DMI ignored operation, so set sticky busy + if (dmi_cmderr == 2'd0) + dmi_cmderr <= 2'd3; + end + end else if (dmi_busy && dtm_pready) begin + dmi_busy <= 1'b0; + if (dmi_cmderr == 2'd0 && dtm_pslverr) + dmi_cmderr <= 2'd2; + end end // DTM logic is in TCK domain, actual DMI + DM is in processor domain hazard3_apb_async_bridge #( - .W_ADDR (8), - .W_DATA (32), - .N_SYNC_STAGES (2) + .W_ADDR (8), + .W_DATA (32), + .N_SYNC_STAGES (2) ) inst_hazard3_apb_async_bridge ( - .clk_src (tck), - .rst_n_src (trst_n), + .clk_src (tck), + .rst_n_src (trst_n), - .clk_dst (clk_dmi), - .rst_n_dst (rst_n_dmi), + .clk_dst (clk_dmi), + .rst_n_dst (rst_n_dmi), - .src_psel (dtm_psel), - .src_penable (dtm_penable), - .src_pwrite (dtm_pwrite), - .src_paddr (dtm_paddr), - .src_pwdata (dtm_pwdata), - .src_prdata (dtm_prdata), - .src_pready (dtm_pready), - .src_pslverr (dtm_pslverr), + .src_psel (dtm_psel), + .src_penable (dtm_penable), + .src_pwrite (dtm_pwrite), + .src_paddr (dtm_paddr), + .src_pwdata (dtm_pwdata), + .src_prdata (dtm_prdata), + .src_pready (dtm_pready), + .src_pslverr (dtm_pslverr), - .dst_psel (dmi_psel), - .dst_penable (dmi_penable), - .dst_pwrite (dmi_pwrite), - .dst_paddr (dmi_paddr), - .dst_pwdata (dmi_pwdata), - .dst_prdata (dmi_prdata), - .dst_pready (dmi_pready), - .dst_pslverr (dmi_pslverr) + .dst_psel (dmi_psel), + .dst_penable (dmi_penable), + .dst_pwrite (dmi_pwrite), + .dst_paddr (dmi_paddr), + .dst_pwdata (dmi_pwdata), + .dst_prdata (dmi_prdata), + .dst_pready (dmi_pready), + .dst_pslverr (dmi_pslverr) ); // ---------------------------------------------------------------------------- // TDO negedge register always @ (negedge tck or negedge trst_n) begin - if (!trst_n) begin - tdo <= 1'b0; - end else begin - tdo <= tap_state == S_SHIFT_IR ? ir_shift[W_IR - 1] : - tap_state == S_SHIFT_DR ? dr_shift[W_DR_SHIFT - 1] : 1'b0; - end + if (!trst_n) begin + tdo <= 1'b0; + end else begin + tdo <= tap_state == S_SHIFT_IR ? ir_shift[0] : + tap_state == S_SHIFT_DR ? dr_shift[0] : 1'b0; + end end endmodule diff --git a/test/sim/debug_openocd_bitbang/.gitignore b/test/sim/debug_openocd_bitbang/.gitignore new file mode 100644 index 0000000..719db5b --- /dev/null +++ b/test/sim/debug_openocd_bitbang/.gitignore @@ -0,0 +1,2 @@ +tb +dut.cpp diff --git a/test/sim/debug_openocd_bitbang/Makefile b/test/sim/debug_openocd_bitbang/Makefile new file mode 100644 index 0000000..67c8250 --- /dev/null +++ b/test/sim/debug_openocd_bitbang/Makefile @@ -0,0 +1,16 @@ +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/debug_openocd_bitbang/openocd.cfg b/test/sim/debug_openocd_bitbang/openocd.cfg new file mode 100644 index 0000000..a671b38 --- /dev/null +++ b/test/sim/debug_openocd_bitbang/openocd.cfg @@ -0,0 +1,13 @@ +adapter driver remote_bitbang +remote_bitbang_host localhost +remote_bitbang_port 9824 +transport select jtag + +set _CHIPNAME hazard3 +jtag newtap $_CHIPNAME cpu -irlen 5 +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME riscv -chain-position $_TARGETNAME + +gdb_report_data_abort enable +init +halt diff --git a/test/sim/debug_openocd_bitbang/tb.cpp b/test/sim/debug_openocd_bitbang/tb.cpp new file mode 100644 index 0000000..af5ee85 --- /dev/null +++ b/test/sim/debug_openocd_bitbang/tb.cpp @@ -0,0 +1,298 @@ +#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 = 0, + IO_PRINT_U32 = 4, + IO_EXIT = 8 +}; + +const char *help_str = +"Usage: tb binfile [vcdfile] [--dump start end] [--cycles n] [--port 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" +" after execution finishes. Can be passed multiple times.\n" +" --cycles n : Maximum number of cycles to run before exiting.\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); +} + +enum cmdstate { + S_IDLE = 0, + S_WRITE_SETUP, + S_WRITE_ACCESS, + S_READ_SETUP, + S_READ_ACCESS +}; + +int main(int argc, char **argv) { + + if (argc < 2) + exit_help(); + + bool dump_waves = false; + std::string waves_path; + std::vector> dump_ranges; + int64_t max_cycles = 100000; + uint16_t port = 9824; + + for (int i = 2; i < argc; ++i) { + std::string s(argv[i]); + if (i == 2 && 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); + + 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::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; + } + fd.seekg(0, std::ios::beg); + fd.read((char*)mem, bin_size); + + std::ifstream cmdfile(argv[2]); + + 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); + + // 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(); + + cmdstate state = S_IDLE; + int idle_counter = 0; + + for (int64_t cycle = 0; cycle < max_cycles; ++cycle) { + top.p_clk.set(false); + top.step(); + if (dump_waves) + vcd.sample(cycle * 2); + top.p_clk.set(true); + top.step(); + + // 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; + char c; + while (!step) { + if (read(sock_fd, &c, 1) > 0) { + 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') { + char d = top.p_tdo.get() ? '1' : '0'; + send(sock_fd, &d, 1, 0); + } + else if (c == 'Q') { + got_exit_cmd = true; + step = true; + } + } + } + + // Handle current data phase, then move current address phase to data phase + uint32_t rdata = 0; + if (bus_trans && bus_write) { + uint32_t wdata = top.p_d__hwdata.get(); + if (bus_addr <= MEM_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; + } + } + 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_trans && !bus_write) { + if (bus_addr <= MEM_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; + } + } + top.p_d__hrdata.set(rdata); + if (bus_trans_i) { + bus_addr_i &= ~0x3u; + 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 + ); + } + + 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(); + + 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 (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/debug_openocd_bitbang/tb.f b/test/sim/debug_openocd_bitbang/tb.f new file mode 100644 index 0000000..e24f4f4 --- /dev/null +++ b/test/sim/debug_openocd_bitbang/tb.f @@ -0,0 +1,6 @@ +file tb.v +file $HDL/debug/cdc/hazard3_reset_sync.v + +list $HDL/hazard3.f +list $HDL/debug/dm/hazard3_dm.f +list $HDL/debug/dtm/hazard3_jtag_dtm.f diff --git a/test/sim/debug_openocd_bitbang/tb.v b/test/sim/debug_openocd_bitbang/tb.v new file mode 100644 index 0000000..de7f7cc --- /dev/null +++ b/test/sim/debug_openocd_bitbang/tb.v @@ -0,0 +1,249 @@ +// An integration of JTAG-DTM + DM + CPU for openocd to poke at over a remote +// bitbang socket + +`default_nettype none + +module tb #( + parameter W_DATA = 32, + parameter W_ADDR = 32, + parameter NUM_IRQ = 16 +) ( + // Global signals + input wire clk, + input wire rst_n, + + // JTAG port + input wire tck, + input wire trst_n, + input wire tms, + input wire tdi, + output wire tdo, + + // Instruction fetch port + output wire [W_ADDR-1:0] i_haddr, + output wire i_hwrite, + output wire [1:0] i_htrans, + output wire [2:0] i_hsize, + output wire [2:0] i_hburst, + output wire [3:0] i_hprot, + output wire i_hmastlock, + input wire i_hready, + input wire i_hresp, + output wire [W_DATA-1:0] i_hwdata, + input wire [W_DATA-1:0] i_hrdata, + + // Load/store port + output wire [W_ADDR-1:0] d_haddr, + output wire d_hwrite, + output wire [1:0] d_htrans, + output wire [2:0] d_hsize, + output wire [2:0] d_hburst, + output wire [3:0] d_hprot, + output wire d_hmastlock, + input wire d_hready, + input wire d_hresp, + output wire [W_DATA-1:0] d_hwdata, + input wire [W_DATA-1:0] d_hrdata, + + // Level-sensitive interrupt sources + input wire [NUM_IRQ-1:0] irq, // -> mip.meip + input wire soft_irq, // -> mip.msip + input wire timer_irq // -> mip.mtip +); + +// JTAG-DTM IDCODE, selected after TAP reset, would normally be a +// JEP106-compliant ID +localparam IDCODE = 32'hdeadbeef; + +wire dmi_psel; +wire dmi_penable; +wire dmi_pwrite; +wire [7:0] dmi_paddr; +wire [31:0] dmi_pwdata; +reg [31:0] dmi_prdata; +wire dmi_pready; +wire dmi_pslverr; + +wire dmihardreset_req; +wire assert_dmi_reset = !rst_n || dmihardreset_req; +wire rst_n_dmi; + +hazard3_reset_sync dmi_reset_sync_u ( + .clk (clk), + .rst_n_in (!assert_dmi_reset), + .rst_n_out (rst_n_dmi) +); + +hazard3_jtag_dtm #( + .IDCODE (IDCODE) +) inst_hazard3_jtag_dtm ( + .tck (tck), + .trst_n (trst_n), + .tms (tms), + .tdi (tdi), + .tdo (tdo), + + .dmihardreset_req (dmihardreset_req), + + .clk_dmi (clk), + .rst_n_dmi (rst_n_dmi), + + .dmi_psel (dmi_psel), + .dmi_penable (dmi_penable), + .dmi_pwrite (dmi_pwrite), + .dmi_paddr (dmi_paddr), + .dmi_pwdata (dmi_pwdata), + .dmi_prdata (dmi_prdata), + .dmi_pready (dmi_pready), + .dmi_pslverr (dmi_pslverr) +); + +localparam N_HARTS = 1; +localparam XLEN = 32; + +wire sys_reset_req; +wire sys_reset_done; +wire [N_HARTS-1:0] hart_reset_req; +wire [N_HARTS-1:0] hart_reset_done; + +wire [N_HARTS-1:0] hart_req_halt; +wire [N_HARTS-1:0] hart_req_halt_on_reset; +wire [N_HARTS-1:0] hart_req_resume; +wire [N_HARTS-1:0] hart_halted; +wire [N_HARTS-1:0] hart_running; + +wire [N_HARTS*XLEN-1:0] hart_data0_rdata; +wire [N_HARTS*XLEN-1:0] hart_data0_wdata; +wire [N_HARTS-1:0] hart_data0_wen; + +wire [N_HARTS*XLEN-1:0] hart_instr_data; +wire [N_HARTS-1:0] hart_instr_data_vld; +wire [N_HARTS-1:0] hart_instr_data_rdy; +wire [N_HARTS-1:0] hart_instr_caught_exception; +wire [N_HARTS-1:0] hart_instr_caught_ebreak; + +hazard3_dm #( + .N_HARTS (N_HARTS), + .NEXT_DM_ADDR (0) +) dm ( + .clk (clk), + .rst_n (rst_n), + + .dmi_psel (dmi_psel), + .dmi_penable (dmi_penable), + .dmi_pwrite (dmi_pwrite), + .dmi_paddr (dmi_paddr), + .dmi_pwdata (dmi_pwdata), + .dmi_prdata (dmi_prdata), + .dmi_pready (dmi_pready), + .dmi_pslverr (dmi_pslverr), + + .sys_reset_req (sys_reset_req), + .sys_reset_done (sys_reset_done), + .hart_reset_req (hart_reset_req), + .hart_reset_done (hart_reset_done), + + .hart_req_halt (hart_req_halt), + .hart_req_halt_on_reset (hart_req_halt_on_reset), + .hart_req_resume (hart_req_resume), + .hart_halted (hart_halted), + .hart_running (hart_running), + + .hart_data0_rdata (hart_data0_rdata), + .hart_data0_wdata (hart_data0_wdata), + .hart_data0_wen (hart_data0_wen), + + .hart_instr_data (hart_instr_data), + .hart_instr_data_vld (hart_instr_data_vld), + .hart_instr_data_rdy (hart_instr_data_rdy), + .hart_instr_caught_exception (hart_instr_caught_exception), + .hart_instr_caught_ebreak (hart_instr_caught_ebreak) +); + + +// Generate resynchronised reset for CPU based on upstream reset and +// on reset requests from DM. + +wire assert_cpu_reset = !rst_n || sys_reset_req || hart_reset_req[0]; +wire rst_n_cpu; + +hazard3_reset_sync cpu_reset_sync ( + .clk (clk), + .rst_n_in (!assert_cpu_reset), + .rst_n_out (rst_n_cpu) +); + +// Still some work to be done on the reset handshake -- this ought to be +// resynchronised to DM's reset domain here, and the DM should wait for a +// rising edge after it has asserted the reset pulse, to make sure the tail +// of the previous "done" is not passed on. +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), + .MARCHID_VAL (32'hfeedf00d), + .MIMPID_VAL (32'h12345678), + .MHARTID_VAL (32'h0), + .REDUCED_BYPASS (0), + .MULDIV_UNROLL (2), + .MUL_FAST (1), +) cpu ( + .clk (clk), + .rst_n (rst_n_cpu), + + .i_haddr (i_haddr), + .i_hwrite (i_hwrite), + .i_htrans (i_htrans), + .i_hsize (i_hsize), + .i_hburst (i_hburst), + .i_hprot (i_hprot), + .i_hmastlock (i_hmastlock), + .i_hready (i_hready), + .i_hresp (i_hresp), + .i_hwdata (i_hwdata), + .i_hrdata (i_hrdata), + + .d_haddr (d_haddr), + .d_hwrite (d_hwrite), + .d_htrans (d_htrans), + .d_hsize (d_hsize), + .d_hburst (d_hburst), + .d_hprot (d_hprot), + .d_hmastlock (d_hmastlock), + .d_hready (d_hready), + .d_hresp (d_hresp), + .d_hwdata (d_hwdata), + .d_hrdata (d_hrdata), + + .dbg_req_halt (hart_req_halt), + .dbg_req_halt_on_reset (hart_req_halt_on_reset), + .dbg_req_resume (hart_req_resume), + .dbg_halted (hart_halted), + .dbg_running (hart_running), + + .dbg_data0_rdata (hart_data0_rdata), + .dbg_data0_wdata (hart_data0_wdata), + .dbg_data0_wen (hart_data0_wen), + + .dbg_instr_data (hart_instr_data), + .dbg_instr_data_vld (hart_instr_data_vld), + .dbg_instr_data_rdy (hart_instr_data_rdy), + .dbg_instr_caught_exception (hart_instr_caught_exception), + .dbg_instr_caught_ebreak (hart_instr_caught_ebreak), + + .irq (irq), + .soft_irq (soft_irq), + .timer_irq (timer_irq) +); + +endmodule diff --git a/test/sim/debug_openocd_bitbang/waves.gtkw b/test/sim/debug_openocd_bitbang/waves.gtkw new file mode 100644 index 0000000..80fb43a --- /dev/null +++ b/test/sim/debug_openocd_bitbang/waves.gtkw @@ -0,0 +1,67 @@ +[*] +[*] GTKWave Analyzer v3.3.103 (w)1999-2019 BSI +[*] Mon Jul 12 17:15:59 2021 +[*] +[dumpfile] "/home/luke/proj/hazard3/test/sim/debug_openocd_bitbang/waves.vcd" +[dumpfile_mtime] "Mon Jul 12 17:11:32 2021" +[dumpfile_size] 2961061 +[savefile] "/home/luke/proj/hazard3/test/sim/debug_openocd_bitbang/waves.gtkw" +[timestart] 0 +[size] 1920 1043 +[pos] -1 -1 +*-13.000000 18597 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[treeopen] inst_hazard3_jtag_dtm. +[sst_width] 233 +[signals_width] 222 +[sst_expanded] 1 +[sst_vpaned_height] 298 +@28 +clk +trst_n +rst_n_dmi +@200 +- +@201 +-DTM +@28 +tck +tms +tdi +tdo +@200 +- +@22 +inst_hazard3_jtag_dtm.tap_state[3:0] +inst_hazard3_jtag_dtm.ir[4:0] +@28 +inst_hazard3_jtag_dtm.dmi_cmderr[1:0] +inst_hazard3_jtag_dtm.dmi_busy +@200 +- +-DMI +@22 +dm.dmi_paddr[7:0] +@28 +dm.dmi_penable +dm.dmi_pwrite +@22 +dm.dmi_pwdata[31:0] +dm.dmi_prdata[31:0] +@200 +- +-DM Internals +@28 +dm.dmactive +@200 +- +-Processor Debug Controls +@28 +cpu.dbg_req_halt +cpu.dbg_req_halt_on_reset +cpu.dbg_req_resume +cpu.dbg_halted +cpu.dbg_running +@200 +- +[pattern_trace] 1 +[pattern_trace] 0