diff --git a/inc/BASE_ISA.h b/inc/BASE_ISA.h index c51e237..e39c4fc 100644 --- a/inc/BASE_ISA.h +++ b/inc/BASE_ISA.h @@ -336,7 +336,7 @@ public: * @param inst instruction to execute * @return true if PC is affected by instruction */ - bool process_instruction(Instruction *inst); + bool process_instruction(Instruction *inst, bool *breakpoint = nullptr); /** * @brief Decodes opcode of instruction diff --git a/inc/C_extension.h b/inc/C_extension.h index 48e9804..e11de2a 100644 --- a/inc/C_extension.h +++ b/inc/C_extension.h @@ -405,7 +405,7 @@ public: bool Exec_C_JAL(int m_rd); bool Exec_C_EBREAK(); - bool process_instruction(Instruction *inst); + bool process_instruction(Instruction *inst, bool *breakpoint = nullptr); }; #endif diff --git a/inc/Debug.h b/inc/Debug.h new file mode 100644 index 0000000..097924e --- /dev/null +++ b/inc/Debug.h @@ -0,0 +1,46 @@ +/*! + \file Debug.h + \brief GDB connector + \author Màrius Montón + \date February 2021 + */ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef INC_DEBUG_H_ +#define INC_DEBUG_H_ + +#define SC_INCLUDE_DYNAMIC_PROCESSES + +#include "systemc" + +#include "tlm.h" +#include "tlm_utils/simple_initiator_socket.h" + + +#include "CPU.h" +#include "Memory.h" + +class Debug: sc_core::sc_module { +public: + + Debug(sc_core::sc_module_name name, uint32_t PC, CPU *cpu, Memory* mem); + ~Debug(); + +private: + std::string compute_checksum_string(const std::string &msg); + void send_packet(int conn, const std::string &msg); + std::string receive_packet(); + void handle_gdb_loop(); + + static constexpr size_t bufsize = 1024 * 8; + char iobuf[bufsize]{}; + int conn; + CPU *dbg_cpu; + Memory *dbg_mem; + tlm::tlm_generic_payload dbg_trans; + unsigned char pyld_array[128]; + std::unordered_set breakpoints; +}; + + +#endif /* INC_DEBUG_H_ */ diff --git a/src/BASE_ISA.cpp b/src/BASE_ISA.cpp index 0e3db5b..95bbcaf 100644 --- a/src/BASE_ISA.cpp +++ b/src/BASE_ISA.cpp @@ -1160,9 +1160,10 @@ bool BASE_ISA::Exec_SFENCE() const { return true; } -bool BASE_ISA::process_instruction(Instruction *inst) { +bool BASE_ISA::process_instruction(Instruction *inst, bool *breakpoint) { bool PC_not_affected = true; + *breakpoint = false; setInstr(inst->getInstr()); switch (decode()) { @@ -1290,9 +1291,13 @@ bool BASE_ISA::process_instruction(Instruction *inst) { break; case OP_ECALL: Exec_ECALL(); + *breakpoint = true; + std::cout << "ECALL" << std::endl; break; case OP_EBREAK: Exec_EBREAK(); + *breakpoint = true; + std::cout << "EBREAK" << std::endl; break; case OP_CSRRW: Exec_CSRRW(); diff --git a/src/C_extension.cpp b/src/C_extension.cpp index 4d10a74..f5ad7f9 100644 --- a/src/C_extension.cpp +++ b/src/C_extension.cpp @@ -680,9 +680,11 @@ bool C_extension::Exec_C_EBREAK() { return true; } -bool C_extension::process_instruction(Instruction *inst) { +bool C_extension::process_instruction(Instruction *inst, bool *breakpoint) { bool PC_not_affected = true; + *breakpoint = false; + setInstr(inst->getInstr()); switch (decode()) { @@ -766,6 +768,8 @@ bool C_extension::process_instruction(Instruction *inst) { break; case OP_C_EBREAK: Exec_C_EBREAK(); + std::cout << "C_EBREAK" << std::endl; + *breakpoint = true; break; [[unlikely]] default: std::cout << "C instruction not implemented yet" << "\n"; diff --git a/src/Debug.cpp b/src/Debug.cpp new file mode 100644 index 0000000..2cbdee7 --- /dev/null +++ b/src/Debug.cpp @@ -0,0 +1,310 @@ +/*! + \file Debug.cpp + \brief GDB connector + \author Màrius Montón + \date February 2021 + */ +// SPDX-License-Identifier: GPL-3.0-or-later + + +#include +#include +#include +#include +#include +#include +#include + +#include "Debug.h" + +constexpr char nibble_to_hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + +Debug::Debug(sc_core::sc_module_name name, uint32_t PC, CPU *cpu, Memory* mem) { + + std::cout << "Debug constructor\n"; + + dbg_cpu = cpu; + dbg_mem = mem; + + int sock = socket(AF_INET, SOCK_STREAM, 0); + + int optval = 1; + int ans = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, + sizeof(optval)); + + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(1234); + + ans = bind(sock, (struct sockaddr *) &addr, sizeof(addr)); + + ans = listen(sock, 1); + + socklen_t len = sizeof(addr); + conn = accept(sock, (struct sockaddr *) &addr, &len); + + handle_gdb_loop(); +} + +Debug::~Debug() { + +} + +void Debug::send_packet(int conn, const std::string &msg) { + std::string frame = "+$" + msg + "#" + compute_checksum_string(msg); + + memcpy(iobuf, frame.c_str(), frame.size()); + + int nbytes = ::send(conn, iobuf, frame.size(), 0); +} + +std::string Debug::receive_packet() { + int nbytes = ::recv(conn, iobuf, bufsize, 0); + + if (nbytes == 0) { + return ""; + } else if (nbytes == 1) { + return std::string("+"); + } else { + // 1) find $ + // 2) find # + // 3) assert that two chars follow # + + char *start = strchr(iobuf, '$'); + char *end = strchr(iobuf, '#'); + + std::string message(start + 1, end - (start + 1)); + + { + //std::string local_checksum = compute_checksum_string(message); + //std::string recv_checksum(end + 1, 2); + //assert(local_checksum == recv_checksum); + } + + return message; + } +} + +void Debug::handle_gdb_loop() { + std::cout << "Handle_GDB_Loop\n"; + + Registers *register_bank = dbg_cpu->getRegisterBank(); + + while (true) { + std::string msg = receive_packet(); + +// std::cout << "msg: " << msg << std::endl; + if (msg.size() == 0) { + std::cout << "remote connection seems to be closed, terminating ..." + << std::endl; + break; + } else if (msg == "+") { + // NOTE: just ignore this message, nothing to do in this case + } else if (boost::starts_with(msg, "qSupported")) { + printf("qSupported\n"); + send_packet(conn, "PacketSize=1024;swbreak-;hwbreak+;vContSupported+"); + } else if (msg == "vMustReplyEmpty") { + printf("vMustReplyEmpty\n"); + send_packet(conn, ""); + } else if (msg == "Hg0") { + printf("Hq0\n"); + send_packet(conn, "OK"); + } else if (msg == "Hc0") { + printf("Hc0\n"); + send_packet(conn, ""); + } else if (msg == "qTStatus") { + printf("qTStatus\n"); + send_packet(conn, ""); + } else if (msg == "?") { + printf("?\n"); + send_packet(conn, "S05"); + } else if (msg == "qfThreadInfo") { + printf("qThreadInfo\n"); + send_packet(conn, ""); + } else if (boost::starts_with(msg, "qL")) { + printf("qL\n"); + send_packet(conn, ""); + } else if (msg == "Hc-1") { + printf("Hc-1\n"); + send_packet(conn, "OK"); + } else if (msg == "qC") { + printf("qC\n"); + send_packet(conn, "-1"); + } else if (msg == "qAttached") { + printf("qAttached\n"); + send_packet(conn, "0"); // 0 process started, 1 attached to process + } else if (msg == "g") { +// std::cout << msg << '\n'; + + std::stringstream stream; + stream << std::setfill('0') << std::hex; + for (int i = 1; i < 32; i++) { + stream << std::setw(8) << register_bank->getValue(i); + } + send_packet(conn, stream.str()); + } else if (boost::starts_with(msg, "p")) { + std::cout << "P : " << msg << std::endl; + long n = strtol(msg.c_str() + 1, 0, 16); + + int reg_value; + if (n < 32) { + reg_value = register_bank->getValue(n); + } else if (n == 32) { + reg_value = register_bank->getPC(); + } else { + // see: https://github.com/riscv/riscv-gnu-toolchain/issues/217 + // risc-v register 834 + reg_value = register_bank->getCSR(n - 65); + } + std::stringstream stream; + stream << std::setfill('0') << std::hex; + stream << std::setw(8) << htonl(reg_value); + send_packet(conn, stream.str()); + } else if (boost::starts_with(msg, "P")) { + printf("P\n"); + char * pEnd; + long reg = strtol(msg.c_str() + 1, &pEnd, 16); + std::cout << "n: " << reg; + int val = strtol(pEnd + 1, 0, 16); + std::cout << "= " << val << std::endl; + register_bank->setValue(reg + 1, val); + send_packet(conn, "OK"); + } else if (boost::starts_with(msg, "m")) { + printf("m\n"); + + char * pEnd; + long addr = strtol(msg.c_str() + 1, &pEnd, 16);; + int len = strtol(pEnd + 1, &pEnd, 16); + std::cout << "msg m: " << msg << std::endl; + std::cout << std::hex << "addr: " << addr << " , " << len << std::endl; + + dbg_trans.set_data_ptr(pyld_array); + dbg_trans.set_command(tlm::TLM_READ_COMMAND); + dbg_trans.set_address(addr); + dbg_trans.set_data_length(len); + dbg_mem->transport_dbg(dbg_trans); + + std::stringstream stream; + stream << std::setfill('0') << std::hex; + for (auto &c : pyld_array) { + stream << std::setw(2) << (0xFF & c); + } + + send_packet(conn, stream.str()); + + } else if (boost::starts_with(msg, "M")) { + printf("M\n"); + //memory_access_t m = parse_memory_access(msg); + //std::string data = msg.substr(msg.find(":") + 1); + //write_memory(m.start, m.nbytes, data); + send_packet(conn, "OK"); + } else if (boost::starts_with(msg, "X")) { + printf("X\n"); + send_packet(conn, ""); // binary data unsupported, gdb will send + // text based message M + } else if (msg == "qOffsets") { + printf("qOffsets\n"); + // NOTE: seems to be additional offsets wrt. the exec. elf file + send_packet(conn, "Text=0;Data=0;Bss=0"); + } else if (msg == "qSymbol::") { + printf("qSymbol\n"); + send_packet(conn, "OK"); + } else if (msg == "vCont?") { + printf("vCont?\n"); + send_packet(conn, "vCont;cs"); + } else if (msg == "c") { + printf("c\n"); + //try { + // core.run(); + //if (core.status == CoreExecStatus::HitBreakpoint) { + // send_packet(conn, "S05"); + // core.status = CoreExecStatus::Runnable; + //} else if (core.status == CoreExecStatus::Terminated) { + //send_packet(conn, "S03"); + //} else { + // assert(false && "invalid core status (apparently still marked as runnable)"); + //} + //} catch (std::exception &e) { + // send_packet(conn, "S04"); + //} + bool breakpoint_hit = false; + bool bkpt = false; + do { +// std::cout << "PC: " << std::hex << register_bank->getPC() << std::endl; + bkpt = dbg_cpu->CPU_step(); + uint32_t currentPC = register_bank->getPC(); + + auto search = breakpoints.find(currentPC); + if (search != breakpoints.end()) { + breakpoint_hit = true; + } + } while ((breakpoint_hit == false) && (bkpt == false)); + + std::cout << "Breakpoint hit at 0x" << std::hex << register_bank->getPC() << std::endl; + + //send_packet(conn, "S05"); + send_packet(conn, "S03"); + } else if (msg == "s") { + printf("s\n"); + bool breakpoint; + + dbg_cpu->CPU_step(); + + uint32_t currentPC = register_bank->getPC(); + auto search = breakpoints.find(currentPC); + if (search != breakpoints.end()) { + breakpoint = true; + } else { + breakpoint = false; + } + + if (breakpoint) { + send_packet(conn, "S03"); + } else { + send_packet(conn, "S05"); + } + + } else if (boost::starts_with(msg, "vKill")) { + printf("vKill\n"); + send_packet(conn, "OK"); + break; + } else if (boost::starts_with(msg, "Z1")) { + char * pEnd; + long addr = strtol(msg.c_str() + 3, &pEnd, 16);; + std::cout << "msg m: " << msg << std::endl; + breakpoints.insert(addr); + std::cout << "Breakpoint set to addres 0x"<< std::hex << addr << std::endl; + send_packet(conn, "OK"); + } else if (boost::starts_with(msg, "z1")) { + send_packet(conn, "OK"); + } else if (boost::starts_with(msg, "z0")) { + + } else if (boost::starts_with(msg, "Z0")) { + char * pEnd; + long addr = strtol(msg.c_str() + 3, &pEnd, 16);; + std::cout << "msg m: " << msg << std::endl; + breakpoints.insert(addr); + std::cout << "Breakpoint set to address 0x"<< std::hex << addr << std::endl; + send_packet(conn, "OK"); + } else { + std::cout << "unsupported message '" << msg + << "' detected, terminating ..." << std::endl; + break; + } + } +} + +std::string Debug::compute_checksum_string(const std::string &msg) { + unsigned sum = 0; + for (auto c : msg) { + sum += unsigned(c); + } + sum = sum % 256; + + char low = nibble_to_hex[sum & 0xf]; + char high = nibble_to_hex[(sum & (0xf << 4)) >> 4]; + + return {high, low}; +} diff --git a/src/Simulator.cpp b/src/Simulator.cpp index a503e7f..3218f1a 100644 --- a/src/Simulator.cpp +++ b/src/Simulator.cpp @@ -21,8 +21,10 @@ #include "BusCtrl.h" #include "Trace.h" #include "Timer.h" +#include "Debug.h" std::string filename; +bool debug_session = false; /** * @class Simulator @@ -44,7 +46,7 @@ SC_MODULE(Simulator) { MainMemory = new Memory("Main_Memory", filename); start_PC = MainMemory->getPCfromHEX(); - cpu = new CPU("cpu", start_PC); + cpu = new CPU("cpu", start_PC, debug_session); Bus = new BusCtrl("BusCtrl"); trace = new Trace("Trace"); @@ -58,6 +60,10 @@ SC_MODULE(Simulator) { Bus->timer_socket.bind(timer->socket); timer->irq_line.bind(cpu->irq_line_socket); + + if (debug_session) { + Debug debug("Debug", start_PC, cpu, MainMemory); + } } ~Simulator() { @@ -87,9 +93,14 @@ void process_arguments(int argc, char *argv[]) { log = Log::getInstance(); log->setLogLevel(Log::ERROR); - while ((c = getopt(argc, argv, "D:f:?")) != -1) { + debug_session = false; + + while ((c = getopt(argc, argv, "DL:f:?")) != -1) { switch (c) { case 'D': + debug_session = true; + break; + case 'L': debug_level = std::atoi(optarg); switch (debug_level) { @@ -114,7 +125,7 @@ void process_arguments(int argc, char *argv[]) { filename = std::string(optarg); break; case '?': - std::cout << "Call ./RISCV_TLM -D (0..3) filename.hex" + std::cout << "Call ./RISCV_TLM -D -L (0..3) filename.hex" << std::endl; break; default: @@ -126,6 +137,8 @@ void process_arguments(int argc, char *argv[]) { if (filename.empty()) { filename = std::string(argv[optind]); } + + std::cout << "file: " << filename << '\n'; } int sc_main(int argc, char *argv[]) {