From e1d004b5cdabb047ca6d2de24c7ecb89c7544985 Mon Sep 17 00:00:00 2001 From: Saurabh Singh Date: Fri, 16 Jul 2021 02:22:17 +0530 Subject: [PATCH] Code Refactoring: Atomsim Frontend, Backend & SCAR - All target specific backends inherit from backend class. - All target specific code moved to its corresponding derived backend class. - add support for hex & binary addresses in mem command in debug cli. - AtomSim debug UI changes. - Exiting AtomSim by any means calls the backend destructor. - SCAR UI Changes. - Runtime failed tests are now ignored instead of marking them as passed. --- Makefile | 2 +- rtl/Config.vh | 2 +- sim/AtomSim.cpp | 349 +++++++++++++++++--------------------- sim/Backend.hpp | 162 ++++++++++++++++++ sim/Backend_AtomBones.hpp | 312 +++++++++++++++------------------- sim/Testbench.hpp | 4 +- sim/defs.hpp | 23 +-- test/scar/Makefile | 2 + test/scar/scar.py | 59 +++++-- 9 files changed, 516 insertions(+), 399 deletions(-) create mode 100644 sim/Backend.hpp diff --git a/Makefile b/Makefile index 5b9cef7e..5f19fe18 100644 --- a/Makefile +++ b/Makefile @@ -148,7 +148,7 @@ $(vobject_dir)/V$(verilog_topmodule)__ALLsup.o $(vobject_dir)/V$(verilog_topmodu cd $(vobject_dir) && make -f V$(verilog_topmodule).mk # Compile C++ files -$(cobject_dir)/atomsim.o: $(sim_dir)/AtomSim.cpp $(sim_dir)/defs.hpp $(sim_dir)/Testbench.hpp $(sim_cpp_backend) +$(cobject_dir)/atomsim.o: $(sim_dir)/AtomSim.cpp $(sim_dir)/defs.hpp $(sim_dir)/Backend.hpp $(sim_dir)/Testbench.hpp $(sim_cpp_backend) $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(cobject_dir)/verilated.o: /usr/share/verilator/include/verilated.cpp diff --git a/rtl/Config.vh b/rtl/Config.vh index 47bc3b8f..97967a61 100644 --- a/rtl/Config.vh +++ b/rtl/Config.vh @@ -1,5 +1,5 @@ // Config file -`define RESET_PC_ADDRESS 32'h0000100 +`define RESET_PC_ADDRESS 32'h0000000 `define __NOP_INSTRUCTION__ 32'h00000013 // NOP = addi x0, x0, 0 diff --git a/sim/AtomSim.cpp b/sim/AtomSim.cpp index 70459d32..c531dce6 100644 --- a/sim/AtomSim.cpp +++ b/sim/AtomSim.cpp @@ -1,3 +1,33 @@ +/** + * @file AtomSim.cpp + * @author Saurabh Singh (saurabh.s99100@gmail.com) + * @brief This is the main C++ file for the AtomSim. + * + * @copyright + * MIT License + * + * Copyright (c) 2021 Saurabh Singh + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + #include #include #include @@ -8,46 +38,86 @@ #include "include/cxxopts/cxxopts.hpp" -// ===== Definitions ===== -const char default_trace_dir[] = "build/trace"; -const char default_dump_dir[] = "build/dump"; +// ===== Prototypes ===== +void ExitAtomSim(std::string message, bool exit_with_error=false); + // ===== Global flags ===== bool verbose_flag = false; bool debug_mode = false; bool trace_enabled = false; -bool dump_regs_on_ebreak = false; +bool dump_regs_on_ebreak = false; // Used by SCAR framework + -// ===== Global vars ===== +// ===== Global Variables ===== +// Input File std::string ifile; -#ifdef TARGET_ATOMBONES -std::string signature_file = ""; -const unsigned long int default_mem_size = 134217731; // 128MB (Code & Data) + 3 Bytes (Serial IO) -unsigned long int mem_size = default_mem_size; -#endif -const unsigned int default_entry_point = 0x00000000; +// Max Iteation const unsigned long int default_maxitr = 10000000; unsigned long int maxitr = default_maxitr; +// Trace Directory +const char default_trace_dir[] = "build/trace"; std::string trace_dir = default_trace_dir; + +// Dump Directory +const char default_dump_dir[] = "build/dump"; std::string dump_dir = default_dump_dir; -std::string end_simulation_reason; // This is used to display reason for simulation termination -// ===== Configurations ===== +#include "defs.hpp" + +// ===== Other Configurations ===== #define DEBUG_PRINT_T2B // print registers in top to bottom fashion //#define DEBUG_PRINT_L2R // print registers in left to right fashion -#include "defs.hpp" -// ===== Backend selection ===== -// These macros are defined in command line during compiling. +// ====== Backend specific definitions ===== #ifdef TARGET_ATOMBONES +// Signature file +std::string signature_file = ""; + +// Include Backend #include "Backend_AtomBones.hpp" + +// Backend name const std::string AtomSimBackend = "AtomBones"; + +// Backend Object +Backend_AtomBones *bkend; + +// Default mem size for atomBones +const unsigned long int default_mem_size = 134217728 + 3; // 128MB (Code & Data) + 3 Bytes (Serial IO) +unsigned long int mem_size = default_mem_size; #endif +/** + * @brief Exit AtomSim + * + * @param message Exit message + * @param exit_with_error + */ +void ExitAtomSim(std::string message, bool exit_with_error) +{ + // ===== Pre-Exit Pocedure ===== + // if trace file is open, close it before exiting + if(trace_enabled) + bkend->tb->closeTrace(); + + // ===== Exit Message ===== + if(verbose_flag && message.length() != 0) + std::cout << message << "\n"; + + // Destroy backend + bkend->~Backend_AtomBones(); + + // ===== Exit ===== + if(exit_with_error) + exit(EXIT_FAILURE); + else + exit(EXIT_SUCCESS); +} /** @@ -75,9 +145,10 @@ void parse_commandline_args(int argc, char**argv, std::string &infile) ("i,input", "Specify an input file", cxxopts::value(infile)); options.add_options("Config") - ("maxitr", "Specify maximum simulation iterations", cxxopts::value(maxitr)) + ("maxitr", "Specify maximum simulation iterations", cxxopts::value(maxitr)->default_value(std::to_string(default_maxitr))) #ifdef TARGET_ATOMBONES - ("memsize", "Specify size of memory to simulate", cxxopts::value(mem_size)) + ("memsize", "Specify size of memory to simulate", cxxopts::value(mem_size)->default_value(std::to_string(default_mem_size))) + ("uart-broadcast", "enable uart broadcasting over", cxxopts::value(mem_size)->default_value(std::to_string(default_mem_size))) #endif ; @@ -112,22 +183,22 @@ void parse_commandline_args(int argc, char**argv, std::string &infile) if (result.count("help")) { std::cout << options.help() << std::endl; - exit(0); + exit(EXIT_SUCCESS); } if (result.count("version")) { std::cout << Info_version << std::endl << Info_copyright << std::endl; - exit(0); + exit(EXIT_SUCCESS); } if (result.count("input")>1) { throwError("CLI1", "Multiple input files specified", true); - exit(0); + exit(EXIT_SUCCESS); } if (result.count("input")==0) { throwError("CLI2", "No input files specified", true); - exit(0); + exit(EXIT_SUCCESS); } if (verbose_flag) @@ -145,131 +216,18 @@ void parse_commandline_args(int argc, char**argv, std::string &infile) * @brief Run specified cycles of simulation * * @param cycles no to cycles to run for - * @param b pointer to backend object - * @param show_data if true, show execution data. */ -void tick(long unsigned int cycles, Backend * b, const bool show_data = true) +void run(long unsigned int cycles) { // Run for specified cycles - for(long unsigned int i=0; idone(); i++) + for(long unsigned int i=0; idone()) // Encountered $finish() in RTL - { - break; - } - if(show_data) - { - b->tick(); - b->refreshData(); - b->displayData(); - } - else - { - if(dump_regs_on_ebreak) - b->refreshData(); - b->tick(); - } - - if (b->tb->m_core->AtomBones->atom_core->InstructionRegister == 0x100073) // Hault condition - { - if (verbose_flag) - std::cout << "Haulting @ tick " << b->tb->m_tickcount << "\n"; - - if(dump_regs_on_ebreak) // dump contents of registers into a text file, to be used by scar framework - { - std::vector fcontents; - - for(int i=0; i<34; i++) - { - char temp [50]; - unsigned int tmpval; - - switch(i-2) - { - case -2: tmpval = b->pc_e; sprintf(temp, "pc 0x%08x", tmpval); break; - case -1: tmpval = b->ins_e; sprintf(temp, "ir 0x%08x", tmpval); break; - default: tmpval = b->rf[i-2]; sprintf(temp, "x%d 0x%08x",i-2, tmpval); break; - } - fcontents.push_back(std::string(temp)); - } - fWrite(fcontents, std::string(trace_dir)+"/dump.txt"); - } - - #ifdef TARGET_ATOMBONES - // Dunmp signature on hault condition, to be used by riscv-arch-test (official riscv compliance testing framework) - if(signature_file.length()!=0) - { - // ============= Get start and end address of signature. ============= - long int begin_signature_at = -1; - long int end_signature_at = -1; - - ELFIO::elfio reader; - - if (!reader.load(ifile)) - { - throwError("SIG", "Can't find or process ELF file : " + ifile + "\n", true); - } - - ELFIO::Elf_Half n = reader.sections.size(); - for ( ELFIO::Elf_Half i = 0; i < n; ++i ) // For all sections - { - ELFIO::section* sec = reader.sections[i]; - if ( SHT_SYMTAB == sec->get_type() || SHT_DYNSYM == sec->get_type() ) - { - ELFIO::symbol_section_accessor symbols( reader, sec ); - ELFIO::Elf_Xword sym_no = symbols.get_symbols_num(); - if ( sym_no > 0 ) - { - for ( ELFIO::Elf_Xword i = 0; i < sym_no; ++i ) { - std::string name; - ELFIO::Elf64_Addr value = 0; - ELFIO::Elf_Xword size = 0; - unsigned char bind = 0; - unsigned char type = 0; - ELFIO::Elf_Half section = 0; - unsigned char other = 0; - symbols.get_symbol( i, name, value, size, bind, type, - section, other ); - - if (name == "begin_signature") - begin_signature_at = value; - if (name == "end_signature") - end_signature_at = value; - } - } - } - } - - if(begin_signature_at == -1 || end_signature_at == -1) - { - throwError("SIG", "One or both of 'begin_signature' & 'end_signature' symbols missing from ELF symbol table: " + ifile + "\n", true); - } - - // ========= dump data to signature file ============== - std::vector fcontents; - printf("Dumping signature region [0x%08lx-0x%08lx]\n", begin_signature_at, end_signature_at); - - for(long int i=begin_signature_at; imem->fetchWord(i)); - fcontents.push_back(temp); - } - fWrite(fcontents, signature_file); - } - #endif - - exit(EXIT_SUCCESS); - } - if(b->tb->m_tickcount > maxitr) - { - throwWarning("SIM0", "Simulation iterations exceeded maxitr("+std::to_string(maxitr)+")\n"); - exit(EXIT_SUCCESS); - } + bkend->tick(); } } +/////////////////////////////////////////////////////////////////////////////////////////////////// /** * @brief Main function * @@ -289,12 +247,14 @@ int main(int argc, char **argv) parse_commandline_args(argc, argv, ifile); // Create a new backend instance - Backend bkend(ifile, default_mem_size); + #ifdef TARGET_ATOMBONES + bkend = new Backend_AtomBones(ifile, default_mem_size); + #endif if(trace_enabled == true) { std::string tracefile = trace_dir; - bkend.tb->openTrace(tracefile.c_str()); + bkend->tb->openTrace(tracefile.c_str()); std::cout << "Trace enabled : \"" << tracefile << "\" opened for output.\n"; trace_enabled = true; } @@ -305,12 +265,6 @@ int main(int argc, char **argv) std::string input; while(true) { - if(bkend.done()) // if $finish encountered by verilator - { - end_simulation_reason = "$finish encountered"; - break; - } - // Parse Input std::cout << ": "; getline(std::cin, input); @@ -323,23 +277,30 @@ int main(int argc, char **argv) if((token[0] == "q") | (token[0] == "quit")) { // Quit simulator - end_simulation_reason = "User interruption"; - break; + ExitAtomSim("Exiting due to user interuption"); } else if(token[0] == "r") { // Run indefinitely - tick(-1, &bkend); + run(-1); } else if(token[0] == "rst") { // Reset Simulator - bkend.reset(); + bkend->reset(); } else if(token[0] == "") { // Run for 1 cycles - tick(1, &bkend); + run(1); + } + else if(token[0] == "for") + { + // run for specified cycles + if(token.size()<2) + throwError("CMD2", "\"for\" command expects one argument\n"); + else + run(std::stoi(token[1])); } else if(token[0] == "verbose-on") { @@ -351,39 +312,6 @@ int main(int argc, char **argv) // turn on verbose verbose_flag = false; } - #ifdef TARGET_ATOMBONES // Atombones specific commands - else if(token[0] == "mem") - { - if(token.size()<2) - throwError("CMD0", "\"mem\" command expects address as argument\n"); - unsigned int addr = std::stoi(token[1]); - printf("%08x : %02x %02x %02x %02x\n", addr, bkend.mem->fetchByte(addr), - bkend.mem->fetchByte(addr+1),bkend.mem->fetchByte(addr+2), bkend.mem->fetchByte(addr+3)); - } - else if(token[0] == "dumpmem") - { - if(token.size()<2) - throwError("CMD1", "\"dumpmem\" command expects filename as argument\n"); - - // turn on verbose - std::vector fcontents; - for(unsigned int i=0; isize-4; i+=4) - { - char hex [30]; - sprintf(hex, "0x%08x\t:\t0x%08x", i, bkend.mem->fetchWord(i)); - fcontents.push_back(hex); - } - fWrite(fcontents, token[1]); - } - #endif - else if(token[0] == "for") - { - // run for specified cycles - if(token.size()<2) - throwError("CMD2", "\"for\" command expects one argument\n"); - else - tick(std::stoi(token[1]), &bkend); - } else if(token[0] == "trace-on") { // Enable trace @@ -394,7 +322,7 @@ int main(int argc, char **argv) if(trace_enabled == false) { std::string tracefile = trace_dir + "/"+token[1]; - bkend.tb->openTrace(tracefile.c_str()); + bkend->tb->openTrace(tracefile.c_str()); std::cout << "Trace enabled : \"" << tracefile << "\" opened for output.\n"; trace_enabled = true; } @@ -407,13 +335,49 @@ int main(int argc, char **argv) // Disable trace if(trace_enabled == true) { - bkend.tb->closeTrace(); + bkend->tb->closeTrace(); std::cout << "Trace disabled\n"; trace_enabled = false; } else std::cout << "Trace was not enabled \n"; } + // ============== BACKEND SPECIFIC COMMANDS ================== + #ifdef TARGET_ATOMBONES + else if(token[0] == "mem") + { + if(token.size()<2) + throwError("CMD0", "\"mem\" command expects address as argument\n", false); + else + { + uint32_t addr; + if(token[1].substr(0, 2) == "0x") // Hex Number + addr = std::stoi(token[1], nullptr, 16); + else if(token[1].substr(0, 2) == "0b") // Binary Number + addr = std::stoi(token[1], nullptr , 2); + else // Decimal Number + addr = std::stoi(token[1], nullptr, 10); + + printf("%08x : %02x %02x %02x %02x\n", addr, bkend->mem->fetchByte(addr), + bkend->mem->fetchByte(addr+1),bkend->mem->fetchByte(addr+2), bkend->mem->fetchByte(addr+3)); + } + } + else if(token[0] == "dumpmem") + { + if(token.size()<2) + throwError("CMD1", "\"dumpmem\" command expects filename as argument\n"); + + std::vector fcontents; + for(unsigned int i=0; imem->size-4; i+=4) + { + char hex [30]; + sprintf(hex, "0x%08x\t:\t0x%08x", i, bkend->mem->fetchWord(i)); + fcontents.push_back(hex); + } + fWrite(fcontents, token[1]); + } + #endif + else { throwError("CMD4", "Unknown command \"" + token[0] + "\"\n"); @@ -423,13 +387,10 @@ int main(int argc, char **argv) } else { - tick(-1, &bkend, false); + run(-1); } - if(trace_enabled) // if trace file is open, close it before exiting - bkend.tb->closeTrace(); - - if (verbose_flag) - std::cout << "Simulation ended @ tick " << bkend.tb->m_tickcount_total << " due to : " << end_simulation_reason << std::endl; - exit(EXIT_SUCCESS); + // Control must never Reach Here // + ExitAtomSim("PROGRAM CONTROL FAULT", true); + return 0; } diff --git a/sim/Backend.hpp b/sim/Backend.hpp new file mode 100644 index 00000000..62e8f7dc --- /dev/null +++ b/sim/Backend.hpp @@ -0,0 +1,162 @@ +#ifndef __BACKEND_HPP__ +#define __BACKEND_HPP__ + +#include +#include +#include +#include "Testbench.hpp" + +/** + * @brief Backend class + * Backend class encapsulates the data + * probing and printing operations + */ +template +class Backend +{ + public: + /** + * @brief Pointer to testbench object + */ + Testbench *tb; + + /** + * @brief Struct to hold CPU state + */ + struct CPUState + { + // Fetch-Stage + unsigned int pc_f; + unsigned int ins_f; + + // Execute Stage + unsigned int pc_e; + unsigned int ins_e; + + // Register File + unsigned int rf[32]; + } state; + + /** + * @brief Struct to hold CPU signal values + */ + struct CPUSignals + { + bool jump_decision; + } signals; + + /** + * @brief Disassembly of input file + */ + std::map disassembly; + + private: + /** + * @brief Register ABI names used in debug display + */ + const std::vector reg_names = + { + "x0 (zero) ", "x1 (ra) ", "x2 (sp) ", "x3 (gp) ", "x4 (tp) ", "x5 (t0) ", "x6 (t1) ", "x7 (t2) ", + "x8 (s0/fp)", "x9 (s1) ", "x10 (a0) ", "x11 (a1) ", "x12 (a2) ", "x13 (a3) ", "x14 (a4) ", "x15 (a5) ", + "x16 (a6) ", "x17 (a7) ", "x18 (s2) ", "x19 (s3) ", "x20 (s4) ", "x21 (s5) ", "x22 (s6) ", "x23 (s7) ", + "x24 (s8) ", "x25 (s9) ", "x26 (s10) ", "x27 (s11) ", "x28 (t3) ", "x29 (t4) ", "x30 (t5) ", "x31 (t6) " + }; + + public: + /** + * @brief reset the backend + */ + void reset() + { + if(verbose_flag) + std::cout << "Resetting..\n"; + tb->reset(); + } + + /** + * @brief probe all internal signals and regsters and + * update backend state + */ + virtual void refreshData() = 0; + + /** + * @brief Display state data on console + */ + void displayData() + { + // calculate change in PC. + unsigned int pc_change = state.pc_f - state.pc_e; + + bool isJump = signals.jump_decision; + static bool wasJump = false; + + // Print debug screen + std::cout << "-< " << tb->m_tickcount_total <<" >------------------------------------------------\n"; + printf("F pc : 0x%08x (%+d) <%s> \n", state.pc_f , pc_change, (isJump ? "jump": " ")); + + #define STYLE_BOLD "\033[1m" + #define STYLE_NO_BOLD "\033[22m" + + #define STYLE_UNDERLINE "\033[4m" + #define STYLE_NO_UNDERLINE "\033[24m" + + printf("E "); + + printf(STYLE_BOLD); + printf("pc : 0x%08x ir : 0x%08x\n", state.pc_e , state.ins_e); + printf(STYLE_NO_BOLD); + + std::cout << "[ " << disassembly[state.pc_e] << " ]"; + + if(wasJump) + std::cout << " => nop (pipeline flush)"; + + std::cout << "\n\n"; + wasJump = isJump; + + // Print Register File + if(verbose_flag) + { + int cols = 2; // no of coloumns per rows + #ifndef DEBUG_PRINT_T2B + for(int i=0; i<32; i++) // print in left-right fashion + { + printf("r%-2d: 0x%08x ", i, state.rf[i]); + if(i%cols == cols-1) + printf("\n"); + } + #else + for(int i=0; i<32/cols; i++) // print in topdown fashion + { + for(int j=0; jtick(); + } + + /** + * @brief check if simulation is done + * + * @return true + * @return false + */ + bool done() + { + return tb->done(); + } +}; + +#endif //__BACKEND_HPP__ \ No newline at end of file diff --git a/sim/Backend_AtomBones.hpp b/sim/Backend_AtomBones.hpp index 451ed05f..32d339fb 100644 --- a/sim/Backend_AtomBones.hpp +++ b/sim/Backend_AtomBones.hpp @@ -1,57 +1,21 @@ +#include "Backend.hpp" + #include "include/elfio/elfio.hpp" #include "verilated.h" #include + #include "../build/vobj_dir/VAtomBones.h" #include "../build/vobj_dir/VAtomBones_AtomBones.h" #include "../build/vobj_dir/VAtomBones_AtomRV.h" #include "../build/vobj_dir/VAtomBones_RegisterFile__R20_RB5.h" -#include "Testbench.hpp" - +// Configuration const unsigned int default_UART_RX_ADDRESS = 0x08000000; const unsigned int default_UART_TX_ADDRESS = 0x08000001; const unsigned int default_UART_SREG_ADDRESS = 0x08000002; -/** - * @brief Register ABI names used in debug display - * - */ -const std::vector reg_names = -{ - "x0 (zero) ", - "x1 (ra) ", - "x2 (sp) ", - "x3 (gp) ", - "x4 (tp) ", - "x5 (t0) ", - "x6 (t1) ", - "x7 (t2) ", - "x8 (s0/fp)", - "x9 (s1) ", - "x10 (a0) ", - "x11 (a1) ", - "x12 (a2) ", - "x13 (a3) ", - "x14 (a4) ", - "x15 (a5) ", - "x16 (a6) ", - "x17 (a7) ", - "x18 (s2) ", - "x19 (s3) ", - "x20 (s4) ", - "x21 (s5) ", - "x22 (s6) ", - "x23 (s7) ", - "x24 (s8) ", - "x25 (s9) ", - "x26 (s10) ", - "x27 (s11) ", - "x28 (t3) ", - "x29 (t4) ", - "x30 (t5) ", - "x31 (t6) " -}; +const unsigned int hault_opcode = 0x100073; // ebreak /** * @brief Memory class @@ -291,76 +255,40 @@ class Memory * Backend class encapsulates the data * probing and printing operations */ -class Backend +class Backend_AtomBones: public Backend { public: - /** - * @brief Pointer to testbench object - */ - Testbench *tb; - - // memory /** * @brief Pointer to memoy object */ Memory * mem; - // ==== STATE ==== - /** - * @brief Program counter value in fetch stage - */ - unsigned int pc_f; - - /** - * @brief Instruction in fetch stage - */ - unsigned int ins_f; - - /** - * @brief Program counter value in execute stage - */ - unsigned int pc_e; - - /** - * @brief Instuction value in execute stage - */ - unsigned int ins_e; - - /** - * @brief Registe file values - */ - unsigned int rf[32]; - - /** - * @brief Disassembly of input file - */ - std::map disassembly; /** * @brief Construct a new Backend object - * - * @param mem_init_file init_file */ - Backend(std::string mem_init_file, unsigned int mem_size) + Backend_AtomBones(std::string ifile, unsigned long mem_size) { - tb = new Testbench(); + // Construct Testbench object + tb = new Testbench(); + + // Constuct Memory object mem = new Memory(mem_size); - unsigned int entry_point = mem->initFromElf(mem_init_file, std::vector{5, 6}); // load text & data sections - if (verbose_flag) - printf("Entry point : 0x%08x\n", entry_point); + // get input file disassembly + disassembly = getDisassembly(ifile); - // Set entry point - tb->m_core->AtomBones->atom_core->ProgramCounter = entry_point; + // ====== Initialize ======== + // Initialize memory + mem->initFromElf(ifile, std::vector{5, 6}); // load text & data sections + // Initialize CPU state by resetting + reset(); tb->m_core->eval(); - // get initial signal values - refreshData(); + // get initial signal values + refreshData(); - // get input file disassembly - disassembly = getDisassembly(mem_init_file); - if (verbose_flag) std::cout << "Initialization complete!\n"; } @@ -369,22 +297,12 @@ class Backend /** * @brief Destroy the Backend object */ - ~Backend() + ~Backend_AtomBones() { delete tb; delete mem; } - - /** - * @brief reset the backend - */ - void reset() - { - tb->reset(); - } - - void serviceMemoryRequest() { // Clear all ack signals @@ -414,7 +332,6 @@ class Backend default: std::cout << std::hex << (int)tb->m_core->dmem_sel_o << std::endl; throwError("RTL", "signal 'dmem_sel_o' has unexpected value"); - } tb->m_core->dmem_ack_i = 1; } @@ -427,81 +344,144 @@ class Backend */ void refreshData() { - pc_f = tb->m_core->AtomBones->atom_core->ProgramCounter; - pc_e = tb->m_core->AtomBones->atom_core->ProgramCounter_Old; + state.pc_f = tb->m_core->AtomBones->atom_core->ProgramCounter; + state.pc_e = tb->m_core->AtomBones->atom_core->ProgramCounter_Old; - ins_e = tb->m_core->AtomBones->atom_core->InstructionRegister; + state.ins_e = tb->m_core->AtomBones->atom_core->InstructionRegister; for(int i=0; i<32; i++) { if(i==0) - rf[i] = 0; + state.rf[i] = 0; else - rf[i] = tb->m_core->AtomBones->atom_core->rf->regs[i-1]; + state.rf[i] = tb->m_core->AtomBones->atom_core->rf->regs[i-1]; } - } + signals.jump_decision = tb->m_core->AtomBones->atom_core->__PVT__jump_decision; + } /** - * @brief Display state data on console + * @brief Tick for one cycle + * */ - void displayData() + void tick() { - unsigned int change = pc_f-pc_e; - std::string jump = " "; - bool isJump = tb->m_core->AtomBones->atom_core->__PVT__jump_decision; - if(isJump) - jump = "jump"; - else - jump = " "; - static bool wasJump = false; - std::cout << "-< " << tb->m_tickcount <<" >--------------------------------------------\n"; - printf("F-STAGE | pc : 0x%08x (%+d) (%s) \n", pc_f , change, jump.c_str()); - printf("E-STAGE V pc : 0x%08x ir : 0x%08x \n", pc_e , ins_e); - - std::cout << "[ " << disassembly[pc_e] << " ] "; - - if(wasJump) - std::cout << " => nop (pipeline flush)"; - - std::cout << "\n"; - - wasJump = isJump; - - std::cout << "---------------------------------------------------\n"; - - if(verbose_flag) + // + if(done()) + { + ExitAtomSim("Encountered $finish()"); + } + + // Service Memory Request + serviceMemoryRequest(); + + // Tick clock once + tb->tick(); + + // Refresh Data + if(debug_mode || dump_regs_on_ebreak) { - int cols = 2; // no of coloumns per rows - #ifndef DEBUG_PRINT_T2B - for(int i=0; i<32; i++) // print in left-right fashion + refreshData(); + } + + if(debug_mode) + displayData(); + + + // ===== Check Hault Condition ===== + if (tb->m_core->AtomBones->atom_core->InstructionRegister == hault_opcode) + { + // ============ REGISTER FILE DUMP (For SCAR) ============== + if(dump_regs_on_ebreak) { - printf("r%-2d: 0x%08x ", i, rf[i]); - if(i%cols == cols-1) - printf("\n"); + std::vector fcontents; + + for(int i=0; i<34; i++) + { + char temp [50]; + unsigned int tmpval; + + switch(i-2) + { + case -2: tmpval = state.pc_e; sprintf(temp, "pc 0x%08x", tmpval); break; + case -1: tmpval = state.ins_e; sprintf(temp, "ir 0x%08x", tmpval); break; + default: tmpval = state.rf[i-2]; sprintf(temp, "x%d 0x%08x",i-2, tmpval); break; + } + fcontents.push_back(std::string(temp)); + } + fWrite(fcontents, std::string(trace_dir)+"/dump.txt"); } - #else - for(int i=0; i<32/cols; i++) // print in topdown fashion + + // ========== MEM SIGNATURE DUMP (For RISCV-Arch Tests) ============= + if(signature_file.length()!=0) { - for(int j=0; jget_type() || SHT_DYNSYM == sec->get_type() ) + { + ELFIO::symbol_section_accessor symbols( reader, sec ); + ELFIO::Elf_Xword sym_no = symbols.get_symbols_num(); + if ( sym_no > 0 ) + { + for ( ELFIO::Elf_Xword i = 0; i < sym_no; ++i ) { + std::string name; + ELFIO::Elf64_Addr value = 0; + ELFIO::Elf_Xword size = 0; + unsigned char bind = 0; + unsigned char type = 0; + ELFIO::Elf_Half section = 0; + unsigned char other = 0; + symbols.get_symbol( i, name, value, size, bind, type, + section, other ); + + if (name == "begin_signature") + begin_signature_at = value; + if (name == "end_signature") + end_signature_at = value; + } + } + } + } - /** - * @brief Tick for one cycle - * - */ - void tick() - { - serviceMemoryRequest(); - tb->tick(); - ins_f = tb->m_core->imem_data_i; + if(begin_signature_at == -1 || end_signature_at == -1) + { + throwError("SIG", "One or both of 'begin_signature' & 'end_signature' symbols missing from ELF symbol table: " + ifile + "\n", true); + } + + // dump data to signature file + std::vector fcontents; + printf("Dumping signature region [0x%08lx-0x%08lx]\n", begin_signature_at, end_signature_at); + + for(long int i=begin_signature_at; ifetchWord(i)); + fcontents.push_back(temp); + } + fWrite(fcontents, signature_file); + } + + if (verbose_flag) + std::cout << "Haulting @ tick " << tb->m_tickcount_total; + ExitAtomSim("\n"); + } + if(tb->m_tickcount_total > maxitr) + { + throwWarning("SIM0", "Simulation iterations exceeded maxitr("+std::to_string(maxitr)+")\n"); + ExitAtomSim(""); + } // Serial port Emulator: Rx Listener static bool prev_tx_we = false; @@ -512,16 +492,4 @@ class Backend } prev_tx_we = cur_tx_we; } - - - /** - * @brief check if simulation is done - * - * @return true - * @return false - */ - bool done() - { - return tb->done(); - } }; diff --git a/sim/Testbench.hpp b/sim/Testbench.hpp index f1e79a63..38543f3b 100644 --- a/sim/Testbench.hpp +++ b/sim/Testbench.hpp @@ -14,8 +14,8 @@ class Testbench VTop * m_core = NULL; VerilatedVcdC * m_trace = NULL; - unsigned long m_tickcount; // TickCounter to count clock cycles fom last reset - unsigned long m_tickcount_total; // TickCounter to count clock cycles + unsigned long m_tickcount = 0; // TickCounter to count clock cycles fom last reset + unsigned long m_tickcount_total = 0; // TickCounter to count clock cycles /** * @brief Construct a new TESTBENCH object diff --git a/sim/defs.hpp b/sim/defs.hpp index 0fc6857f..0941419d 100644 --- a/sim/defs.hpp +++ b/sim/defs.hpp @@ -1,7 +1,7 @@ /** * @brief Version information */ -const char Info_version[] = "AtomSim v1.0"; +const char Info_version[] = "AtomSim v1.1"; /** * @brief Copyright message @@ -37,15 +37,6 @@ const std::string COLOR_YELLOW = "\033[33m"; // ================================ THROWING ERRORS ======================================= -/** - * @brief Exits the program - */ -void Exit() -{ - std::exit(EXIT_FAILURE); -} - - /** * @brief Throws error generated in the std::cerr stream * @@ -53,12 +44,12 @@ void Exit() * @param message error message * @param exit flag that tells weather to exit immidiately */ -void throwError(std::string er_code, std::string message, bool exit = false) +void throwError(std::string er_code, std::string message, bool Exit = false) { std::cerr << COLOR_RED <<"! ERROR (E"<< er_code <<"): " << COLOR_RESET << message << std::endl; - if(exit) + if(Exit) { - Exit(); + ExitAtomSim("", true); } } @@ -80,9 +71,13 @@ void throwWarning(std::string wr_code, std::string message) * * @param message Success message */ -void throwSuccessMessage(std::string message) +void throwSuccessMessage(std::string message, bool Exit = false) { std::cout << COLOR_GREEN <<"SUCCESS : " << COLOR_RESET << message < Stage-3:"+Style.RESET_ALL+" Executing & verifying dumps...") failed_tests = [] + ignored_tests = [] + passed_tests = [] + for t in tests: - execute(t, mute=True) - if verify(t) == False: - failed_tests = failed_tests+[t] + execute_status = execute(t, mute=True) + verify_status = verify(t) + + if execute_status == True: + if verify_status == True: + passed_tests = passed_tests + [t] + elif verify_status == False: + failed_tests = failed_tests + [t] + elif verify_status == None: + ignored_tests = ignored_tests + [t] + else: + print("Unknown return Value from execute function") + else: + ignored_tests = ignored_tests + [t] + print(80*"=") @@ -242,12 +260,23 @@ def verify(test): print("|----------------------------|") print("Total tests : " + str(len(tests))) - - print(Fore.GREEN+"\nPassed tests : " + str(len(tests) - len(failed_tests)) + '/' + str(len(tests))+ Style.RESET_ALL) + + count = 1 for t in tests: - if t not in failed_tests: - print("\t"+t) + print(str(count)+").\t"+t, end="") + + print(" "*(30-len(t)), end="- ") - print(Fore.RED+"\nFailed tests : " + str(len(failed_tests)) + '/' +str(len(tests))+ Style.RESET_ALL) - for t in failed_tests: - print("\t"+t) \ No newline at end of file + if t in passed_tests: + print(Fore.GREEN+"Passed"+Style.RESET_ALL) + elif t in ignored_tests: + print(Fore.YELLOW+"Ignored"+Style.RESET_ALL) + else: + print(Fore.RED+"Failed"+Style.RESET_ALL) + + count=count+1 + + + print(Fore.GREEN+"\nPassed tests : " + str(len(passed_tests)) + '/' + str(len(tests))+ Style.RESET_ALL) + print(Fore.YELLOW+"Ignored tests : " + str(len(ignored_tests)) + '/' + str(len(tests))+ Style.RESET_ALL) + print(Fore.RED+"Failed tests : " + str(len(failed_tests)) + '/' +str(len(tests))+ Style.RESET_ALL) \ No newline at end of file