diff --git a/.gitignore b/.gitignore index ddd8d5c..76309d4 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,5 @@ build* .vscode/ .cache/ + +callgrind.out.* diff --git a/cmake/comp_flags.cmake b/cmake/comp_flags.cmake index b946254..8863ed1 100644 --- a/cmake/comp_flags.cmake +++ b/cmake/comp_flags.cmake @@ -81,17 +81,19 @@ set(GCC_WARNINGS -flto-odr-type-merging -fno-omit-frame-pointer) -function(apply_compiler_flags TARGET VISIBILIY) +function(apply_compiler_flags TARGET VISIBILITY) # Add sanitizers - target_link_options(${TARGET} ${VISIBILIY} "$<$:${SANITIZERS}>") - target_compile_options(${TARGET} ${VISIBILIY} + target_link_options(${TARGET} ${VISIBILITY} "$<$:${SANITIZERS}>") + target_compile_options(${TARGET} ${VISIBILITY} "$<$:${SANITIZERS}>") # Compile stuff - target_compile_options(${TARGET} ${VISIBILIY} + target_compile_options(${TARGET} ${VISIBILITY} "$<$:${COMMON_WARNINGS}>") + target_compile_options(${TARGET} ${VISIBILITY} + "$<$:-g>") target_compile_options( - ${TARGET} ${VISIBILIY} + ${TARGET} ${VISIBILITY} "$<$:$<$:${GCC_WARNINGS}>>") endfunction() diff --git a/include/common/common.hh b/include/common/common.hh index 0965535..ecbe08b 100644 --- a/include/common/common.hh +++ b/include/common/common.hh @@ -36,15 +36,26 @@ constexpr std::uint16_t kTLBBits = 10; constexpr std::uint16_t kOffsetBits = 12; constexpr std::string_view kCosimLoggerName = "cosim"; +constexpr std::string_view kGlobalLoggerName = "global"; + +inline const auto &getCosimLogger() { + static auto coSimLogger = spdlog::get(kCosimLoggerName.data()); + return coSimLogger; +} template void cosimLog(fmt::format_string str, Args &&...args) { - auto coSimLogger = spdlog::get(kCosimLoggerName.data()); + const auto &coSimLogger = getCosimLogger(); if (coSimLogger) { coSimLogger->info(str, std::forward(args)...); } } +inline const auto &getGlobalLogger() { + static auto logger = spdlog::get(kGlobalLoggerName.data()); + return logger; +} + template constexpr auto signCast(T val) { return static_cast>(val); } diff --git a/include/common/inst.hh b/include/common/inst.hh index ec92852..48a9307 100644 --- a/include/common/inst.hh +++ b/include/common/inst.hh @@ -6,6 +6,8 @@ #include #include +#include + #include "common.hh" namespace sim { @@ -31,4 +33,13 @@ using BasicBlock = std::vector; } // namespace sim +template <> struct fmt::formatter { + constexpr auto parse(format_parse_context &ctx) { return ctx.end(); } + + template + auto format(const sim::Instruction &inst, FormatContext &ctx) const { + return fmt::format_to(ctx.out(), "{}", inst.str()); + } +}; + #endif // __INCLUDE_COMMON_INST_HH__ diff --git a/include/common/state.hh b/include/common/state.hh index 0a13d87..7ee0bc0 100644 --- a/include/common/state.hh +++ b/include/common/state.hh @@ -8,6 +8,8 @@ #include #include +#include +#include #include #include "common/common.hh" @@ -60,4 +62,13 @@ struct State final { } // namespace sim +template <> struct fmt::formatter { + constexpr auto parse(format_parse_context &ctx) { return ctx.end(); } + + template + auto format(const sim::RegFile ®File, FormatContext &ctx) const { + return fmt::format_to(ctx.out(), "{}", regFile.str()); + } +}; + #endif // __INCLUDE_COMMON_STATE_HH__ diff --git a/include/executor/executor.hh b/include/executor/executor.hh index 031ed2b..353f440 100644 --- a/include/executor/executor.hh +++ b/include/executor/executor.hh @@ -19,6 +19,9 @@ concept InstForwardIterator = std::input_iterator && class Executor final { public: + using ExecutorMap = std::unordered_map< + OpType, std::function>; + Executor() = default; Executor(const Executor &) = delete; Executor(Executor &&) = delete; @@ -34,16 +37,14 @@ public: cosimLog("NUM={}", instrCount); this->execute(inst, state); cosimLog("PC=0x{:08x}", state.pc); - spdlog::trace("Instruction:\n [0x{:08x}]{}", state.pc, inst.str()); - spdlog::trace("Current regfile state:\n{}", state.regs.str()); + getGlobalLogger()->trace("Instruction:\n [0x{:08x}]{}", state.pc, inst); + getGlobalLogger()->trace("Current regfile state:\n{}", state.regs); this->instrCount += 1; }); } private: - static const std::unordered_map< - OpType, std::function> - execMap_; + static const ExecutorMap execMap_; std::size_t instrCount{1}; }; diff --git a/include/memory/memory.hh b/include/memory/memory.hh index 9bf198e..aab964f 100644 --- a/include/memory/memory.hh +++ b/include/memory/memory.hh @@ -74,14 +74,7 @@ public: uint16_t offset{}; AddrSections(uint32_t pt, uint16_t off) : indexPt(pt), offset(off) {} - AddrSections(Addr addr) { - constexpr std::pair of_bits{kOffsetBits - 1, 0}; - constexpr std::pair pt_bits{sizeofBits() - 1, - kOffsetBits}; - indexPt = getBits(addr); - offset = - static_cast(getBits(addr)); - } + AddrSections(Addr addr) : AddrSections(getPt(addr), getOffset(addr)) {} bool operator==(const AddrSections &) const = default; }; @@ -102,7 +95,8 @@ public: template PagePtr pageTableLookup(const AddrSections §); template T *getEntity(Addr addr); - uint16_t getOffset(Addr addr); + constexpr static uint16_t getOffset(Addr addr); + constexpr static uint32_t getPt(Addr addr); private: PT pageTable{}; @@ -149,19 +143,22 @@ public: template inline T *PhysMemory::getEntity(Addr addr) { - if (addr % sizeof(T) || ((getOffset(addr) + sizeof(T)) > (1 << kOffsetBits))) + auto offset = getOffset(addr); + + if (addr % sizeof(T) || ((offset + sizeof(T)) > (1 << kOffsetBits))) throw PhysMemory::MisAlignedAddrException( "Misaligned memory access is not supported!"); - AddrSections sections(addr); - auto offset = sections.offset; + auto isInTLB = tlb.tlbLookup(addr); PagePtr page; + if (isInTLB) { page = isInTLB; } else { - page = PhysMemory::pageTableLookup(sections); + page = PhysMemory::pageTableLookup({getPt(addr), offset}); tlb.tlbUpdate(addr, page); } + Word *word = &page->wordStorage.at(offset / sizeof(Word)); Byte *byte = reinterpret_cast(word) + (offset % sizeof(Word)); return reinterpret_cast(byte); @@ -172,16 +169,16 @@ PagePtr PhysMemory::pageTableLookup(const AddrSections §) { using MemOp = PhysMemory::MemoryOp; auto index = sect.indexPt; - auto it_PT = pageTable.find(index); - if (it_PT == pageTable.end()) { - if constexpr (op == MemOp::LOAD) + auto [it_PT, inserted] = pageTable.try_emplace(index); + + if constexpr (op == MemOp::LOAD) { + if (inserted) { throw PhysMemory::PageFaultException( "Load on unmapped region in physical mem"); - else { - pageTable[index] = Page(); } } - return &pageTable.at(index); + + return &it_PT->second; } template @@ -201,10 +198,14 @@ inline void Memory::printMemStats(std::ostream &ost) const { inline const Memory::MemoryStats &Memory::getMemStats() const { return stats; } -inline uint16_t PhysMemory::getOffset(Addr addr) { +constexpr inline uint16_t PhysMemory::getOffset(Addr addr) { return static_cast(getBits(addr)); } +constexpr inline uint32_t PhysMemory::getPt(Addr addr) { + return getBits() - 1, kOffsetBits>(addr); +} + inline Word Memory::loadWord(Addr addr) { stats.numLoads++; Word loadedWord = *physMem.getEntity(addr); @@ -257,18 +258,19 @@ inline PagePtr TLB::tlbLookup(Addr addr) { stats.TLBMisses++; return nullptr; } - if (it->second.virtualAddress != addr) { + const auto &entry = it->second; + if (entry.virtualAddress != addr) { stats.TLBMisses++; return nullptr; } stats.TLBHits++; - return it->second.physPage; + return entry.physPage; } inline void TLB::tlbUpdate(Addr addr, PagePtr page) { auto idx = getTLBIndex(addr); - tlb[idx] = TLBEntry(addr, page); + tlb.insert_or_assign(idx, TLBEntry(addr, page)); } inline const TLB::TLBStats &TLB::getTLBStats() const { return stats; } diff --git a/src/executor/executor.cc b/src/executor/executor.cc index bc6cf47..ddd87a6 100644 --- a/src/executor/executor.cc +++ b/src/executor/executor.cc @@ -51,166 +51,187 @@ void executeCondBranch(const Instruction &inst, State &state, Func op) { } } -const std::unordered_map> - Executor::execMap_{ - {OpType::ADD, - [](const Instruction &inst, State &state) { - executeRegisterRegisterOp(inst, state, std::plus()); - }}, - {OpType::SUB, - [](const Instruction &inst, State &state) { - executeRegisterRegisterOp(inst, state, std::minus()); - }}, - {OpType::MUL, - [](const Instruction &inst, State &state) { - executeRegisterRegisterOp(inst, state, std::multiplies()); - }}, - {OpType::DIV, - [](const Instruction &inst, State &state) { - auto rs2 = state.regs.get(inst.rs2); - if (rs2 == 0) { - throw std::logic_error("division by zero"); - } - executeRegisterRegisterOp(inst, state, std::divides()); - }}, - {OpType::LW, - [](const Instruction &inst, State &state) { - auto rs1 = state.regs.get(inst.rs1); - auto word = state.mem.loadWord(rs1 + inst.imm); - state.regs.set(inst.rd, word); - }}, - {OpType::SW, - [](const Instruction &inst, State &state) { - auto rs1 = state.regs.get(inst.rs1); - auto rs2 = state.regs.get(inst.rs2); - state.mem.storeWord(rs1 + inst.imm, rs2); - }}, - {OpType::JAL, - [](const Instruction &inst, State &state) { - state.branchIsTaken = true; - state.regs.set(inst.rd, state.pc + kXLENInBytes); - state.npc = state.pc + inst.imm; - }}, - {OpType::JALR, - [](const Instruction &inst, State &state) { - state.branchIsTaken = true; - state.regs.set(inst.rd, state.pc + kXLENInBytes); - auto rs1 = state.regs.get(inst.rs1); - state.npc = - setBit<0, 0>(rs1 + inst.imm); // setting the least-significant +const Executor:: + ExecutorMap + Executor::execMap_{{OpType::ADD, + [](const Instruction &inst, State &state) { + executeRegisterRegisterOp(inst, state, + std::plus()); + }}, + {OpType::SUB, + [](const Instruction &inst, State &state) { + executeRegisterRegisterOp(inst, state, + std::minus()); + }}, + {OpType::MUL, + [](const Instruction &inst, State &state) { + executeRegisterRegisterOp( + inst, state, std::multiplies()); + }}, + {OpType::DIV, + [](const Instruction &inst, State &state) { + auto rs2 = state.regs.get(inst.rs2); + if (rs2 == 0) { + throw std::logic_error("division by zero"); + } + executeRegisterRegisterOp(inst, state, + std::divides()); + }}, + {OpType::LW, + [](const Instruction &inst, State &state) { + auto rs1 = state.regs.get(inst.rs1); + auto word = state.mem.loadWord(rs1 + inst.imm); + state.regs.set(inst.rd, word); + }}, + {OpType::SW, + [](const Instruction &inst, State &state) { + auto rs1 = state.regs.get(inst.rs1); + auto rs2 = state.regs.get(inst.rs2); + state.mem.storeWord(rs1 + inst.imm, rs2); + }}, + {OpType::JAL, + [](const Instruction &inst, State &state) { + state.branchIsTaken = true; + state.regs.set(inst.rd, state.pc + kXLENInBytes); + state.npc = state.pc + inst.imm; + }}, + {OpType::JALR, + [](const Instruction &inst, State &state) { + state.branchIsTaken = true; + state.regs.set(inst.rd, state.pc + kXLENInBytes); + auto rs1 = state.regs.get(inst.rs1); + state.npc = setBit<0, 0>( + rs1 + + inst.imm); // setting the least-significant // bit of the result to zero. - }}, - {OpType::ECALL, - [](const Instruction &, State &state) { state.complete = true; }}, - {OpType::ADDI, - [](const Instruction &inst, State &state) { - executeRegisterImmidiateOp(inst, state, std::plus()); - }}, - {OpType::ANDI, - [](const Instruction &inst, State &state) { - executeRegisterImmidiateOp(inst, state, std::bit_and()); - }}, + }}, + {OpType::ECALL, + [](const Instruction &, State &state) { + state.complete = true; + }}, + {OpType::ADDI, + [](const Instruction &inst, State &state) { + executeRegisterImmidiateOp(inst, state, + std::plus()); + }}, + {OpType::ANDI, + [](const Instruction &inst, State &state) { + executeRegisterImmidiateOp( + inst, state, std::bit_and()); + }}, - {OpType::XORI, - [](const Instruction &inst, State &state) { - executeRegisterImmidiateOp(inst, state, std::bit_xor()); - }}, + {OpType::XORI, + [](const Instruction &inst, State &state) { + executeRegisterImmidiateOp( + inst, state, std::bit_xor()); + }}, - {OpType::ORI, - [](const Instruction &inst, State &state) { - executeRegisterImmidiateOp(inst, state, std::bit_or()); - }}, - {OpType::SLTI, - [](const Instruction &inst, State &state) { - auto rs1 = state.regs.get(inst.rs1); - auto lhs = signCast(rs1); - auto rhs = signCast(inst.imm); - auto res = unsignedCast(executeSLT(lhs, rhs)); - state.regs.set(inst.rd, res); - }}, - {OpType::SLTIU, - [](const Instruction &inst, State &state) { - auto rs1 = state.regs.get(inst.rs1); - state.regs.set(inst.rd, executeSLT(rs1, inst.imm)); - }}, + {OpType::ORI, + [](const Instruction &inst, State &state) { + executeRegisterImmidiateOp(inst, state, + std::bit_or()); + }}, + {OpType::SLTI, + [](const Instruction &inst, State &state) { + auto rs1 = state.regs.get(inst.rs1); + auto lhs = signCast(rs1); + auto rhs = signCast(inst.imm); + auto res = unsignedCast(executeSLT(lhs, rhs)); + state.regs.set(inst.rd, res); + }}, + {OpType::SLTIU, + [](const Instruction &inst, State &state) { + auto rs1 = state.regs.get(inst.rs1); + state.regs.set(inst.rd, + executeSLT(rs1, inst.imm)); + }}, - {OpType::LUI, - [](const Instruction &inst, State &state) { - state.regs.set(inst.rd, inst.imm << 12); - }}, - {OpType::AUIPC, - [](const Instruction &inst, State &state) { - state.regs.set(inst.rd, state.pc + (inst.imm << 12)); - }}, + {OpType::LUI, + [](const Instruction &inst, State &state) { + state.regs.set(inst.rd, inst.imm << 12); + }}, + {OpType::AUIPC, + [](const Instruction &inst, State &state) { + state.regs.set(inst.rd, + state.pc + (inst.imm << 12)); + }}, - {OpType::SLLI, - [](const Instruction &inst, State &state) { - auto shamt = getBits<4, 0>(inst.imm); - auto rs1 = state.regs.get(inst.rs1); - state.regs.set(inst.rd, rs1 << shamt); - }}, - {OpType::SRLI, - [](const Instruction &inst, State &state) { - auto rs1 = state.regs.get(inst.rs1); - auto res = executeSRLT(rs1, inst.imm); - state.regs.set(inst.rd, res); - }}, - {OpType::SRAI, - [](const Instruction &inst, State &state) { - auto rs1 = state.regs.get(inst.rs1); - auto signedRs1 = signCast(rs1); - auto res = unsignedCast(executeSRLT(signedRs1, inst.imm)); - state.regs.set(inst.rd, res); - }}, - {OpType::SLL, - [](const Instruction &inst, State &state) { - auto rs1 = state.regs.get(inst.rs1); - auto rs2 = state.regs.get(inst.rs2); - state.regs.set(inst.rd, rs1 << rs2); - }}, - {OpType::SRL, - [](const Instruction &inst, State &state) { - executeRegisterRegisterOp(inst, state, executeSR); - }}, - {OpType::SRA, - [](const Instruction &inst, State &state) { - auto rs1 = signCast(state.regs.get(inst.rs1)); - auto rs2 = signCast(state.regs.get(inst.rs2)); - auto res = unsignedCast(executeSR(rs1, rs2)); - state.regs.set(inst.rd, res); - }}, - {OpType::BEQ, - [](const Instruction &inst, State &state) { - executeCondBranch(inst, state, std::equal_to()); - }}, - {OpType::BNE, - [](const Instruction &inst, State &state) { - executeCondBranch(inst, state, std::not_equal_to()); - }}, - {OpType::BLT, - [](const Instruction &inst, State &state) { - executeCondBranch(inst, state, std::less()); - }}, - {OpType::BLTU, - [](const Instruction &inst, State &state) { - executeCondBranch(inst, state, std::greater()); - }}, - {OpType::BGEU, - [](const Instruction &inst, State &state) { - executeCondBranch(inst, state, std::greater_equal()); - }}, - {OpType::BGE, - [](const Instruction &inst, State &state) { - auto rs1 = signCast(state.regs.get(inst.rs1)); - auto rs2 = signCast(state.regs.get(inst.rs2)); - if (rs1 >= rs2) { - state.branchIsTaken = true; - state.npc = state.pc + inst.imm; - } - }}, - {OpType::XOR, [](const Instruction &inst, State &state) { - executeRegisterRegisterOp(inst, state, std::bit_xor()); - }}}; + {OpType::SLLI, + [](const Instruction &inst, State &state) { + auto shamt = getBits<4, 0>(inst.imm); + auto rs1 = state.regs.get(inst.rs1); + state.regs.set(inst.rd, rs1 << shamt); + }}, + {OpType::SRLI, + [](const Instruction &inst, State &state) { + auto rs1 = state.regs.get(inst.rs1); + auto res = executeSRLT(rs1, inst.imm); + state.regs.set(inst.rd, res); + }}, + {OpType::SRAI, + [](const Instruction &inst, State &state) { + auto rs1 = state.regs.get(inst.rs1); + auto signedRs1 = signCast(rs1); + auto res = unsignedCast( + executeSRLT(signedRs1, inst.imm)); + state.regs.set(inst.rd, res); + }}, + {OpType::SLL, + [](const Instruction &inst, State &state) { + auto rs1 = state.regs.get(inst.rs1); + auto rs2 = state.regs.get(inst.rs2); + state.regs.set(inst.rd, rs1 << rs2); + }}, + {OpType::SRL, + [](const Instruction &inst, State &state) { + executeRegisterRegisterOp(inst, state, + executeSR); + }}, + {OpType::SRA, + [](const Instruction &inst, State &state) { + auto rs1 = signCast(state.regs.get(inst.rs1)); + auto rs2 = signCast(state.regs.get(inst.rs2)); + auto res = unsignedCast(executeSR(rs1, rs2)); + state.regs.set(inst.rd, res); + }}, + {OpType::BEQ, + [](const Instruction &inst, State &state) { + executeCondBranch(inst, state, + std::equal_to()); + }}, + {OpType::BNE, + [](const Instruction &inst, State &state) { + executeCondBranch(inst, state, + std::not_equal_to()); + }}, + {OpType::BLT, + [](const Instruction &inst, State &state) { + executeCondBranch(inst, state, + std::less()); + }}, + {OpType::BLTU, + [](const Instruction &inst, State &state) { + executeCondBranch(inst, state, + std::greater()); + }}, + {OpType::BGEU, + [](const Instruction &inst, State &state) { + executeCondBranch(inst, state, + std::greater_equal()); + }}, + {OpType::BGE, + [](const Instruction &inst, State &state) { + auto rs1 = signCast(state.regs.get(inst.rs1)); + auto rs2 = signCast(state.regs.get(inst.rs2)); + if (rs1 >= rs2) { + state.branchIsTaken = true; + state.npc = state.pc + inst.imm; + } + }}, + {OpType::XOR, + [](const Instruction &inst, State &state) { + executeRegisterRegisterOp(inst, state, + std::bit_xor()); + }}}; } // namespace sim diff --git a/src/hart/hart.cc b/src/hart/hart.cc index 66f892b..e0e7df7 100644 --- a/src/hart/hart.cc +++ b/src/hart/hart.cc @@ -23,28 +23,30 @@ Hart::Hart(const fs::path &executable) { BasicBlock Hart::createBB(Addr addr) { BasicBlock bb{}; - spdlog::trace("Creating basic block:"); + getGlobalLogger()->trace("Creating basic block:"); for (bool isBranch = false; !isBranch; addr += kXLENInBytes) { auto inst = decoder_.decode(getMem().loadWord(addr)); if (inst.type == OpType::UNKNOWN) throw std::logic_error{ "Unknown instruction found while decoding basic block" + inst.str()}; - spdlog::trace(inst.str()); + getGlobalLogger()->trace("{}", inst); isBranch = inst.isBranch; bb.push_back(inst); } - spdlog::trace("Basic blok created."); + getGlobalLogger()->trace("Basic blok created."); return bb; } void Hart::run() { while (!state_.complete) { - if (cache_.find(getPC()) == cache_.end()) - cache_[getPC()] = createBB(getPC()); + auto [iter, inserted] = cache_.try_emplace(getPC()); - const auto &bb = cache_[getPC()]; + if (inserted) + iter->second = createBB(getPC()); + + const auto &bb = iter->second; exec_.execute(bb.begin(), bb.end(), state_); } } diff --git a/test/e2e/simulator/8-queens.c b/test/e2e/simulator/8-queens.c index e47cf01..94ba6ea 100644 --- a/test/e2e/simulator/8-queens.c +++ b/test/e2e/simulator/8-queens.c @@ -14,9 +14,9 @@ int solution_count; /* how many solutions are there? */ bool finished = FALSE; /* found all solutions yet? */ -bool is_a_solution(int[], int k, int n) { return (k == n); } +bool is_a_solution(int a[], int k, int n) { return (k == n); } -void process_solution(int[], int) { solution_count++; } +void process_solution(int a[], int b) { solution_count++; } void construct_candidates(int a[], int k, int n, int c[], int *ncandidates); diff --git a/test/unit/memory/memory.test.cc b/test/unit/memory/memory.test.cc index 6b4b8ad..c244136 100644 --- a/test/unit/memory/memory.test.cc +++ b/test/unit/memory/memory.test.cc @@ -43,7 +43,7 @@ TEST(PhysMemory, AddrSectionsCtor) { sim::PhysMemory phMem; // 0xDEADB, 0xEEF - EXPECT_EQ(AddrSections(0xDEADBEEF), AddrSections(912091, 3823)); + EXPECT_EQ(AddrSections(0xDEADBEEF), AddrSections(0xDEADB, 0xEEF)); EXPECT_EQ(AddrSections(0x0), AddrSections(0, 0)); // 2^20 - 1, 2^12 - 1 EXPECT_EQ(AddrSections(0xFFFFFFFF), AddrSections(1048575, 4095)); diff --git a/tools/simulator/main.cc b/tools/simulator/main.cc index ab7f82d..af945a8 100644 --- a/tools/simulator/main.cc +++ b/tools/simulator/main.cc @@ -21,7 +21,13 @@ void initCosimLogger(const fs::path &cosimFile, bool toStdout) { : spdlog::basic_logger_mt(sim::kCosimLoggerName.data(), cosimFile, true); logger->set_pattern("%v"); - logger->set_level(spdlog::level::info); + logger->set_level(lvl::info); +} + +void initGlobalLogger(lvl::level_enum lvl) { + auto logger = spdlog::stdout_color_mt(sim::kGlobalLoggerName.data()); + logger->set_pattern("%v"); + logger->set_level(lvl); } int main(int argc, char **argv) try { @@ -66,7 +72,7 @@ int main(int argc, char **argv) try { return app.exit(e); } - spdlog::set_level(loggingLevel); + initGlobalLogger(loggingLevel); if (*isCosimOpt) { initCosimLogger(cosimFile, !*cosimFileOpt); }