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..49b845a 100644 --- a/include/common/common.hh +++ b/include/common/common.hh @@ -37,9 +37,14 @@ constexpr std::uint16_t kOffsetBits = 12; constexpr std::string_view kCosimLoggerName = "cosim"; +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)...); } 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..bd3d281 100644 --- a/include/common/state.hh +++ b/include/common/state.hh @@ -8,6 +8,8 @@ #include #include +#include +#include #include #include "common/common.hh" @@ -29,7 +31,7 @@ public: if (!regnum) return; - cosimLog("x{}=0x{:08x}", regnum, val); + // cosimLog("x{}=0x{:08x}", regnum, val); regs.at(regnum) = val; } @@ -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..f88268d 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; + Executor() = default; Executor(const Executor &) = delete; Executor(Executor &&) = delete; @@ -30,20 +33,18 @@ public: template void execute(It begin, It end, State &state) { std::for_each(begin, end, [this, &state](const auto &inst) { - cosimLog("-----------------------"); - cosimLog("NUM={}", instrCount); + // cosimLog("-----------------------"); + // 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()); + // cosimLog("PC=0x{:08x}", state.pc); + // spdlog::trace("Instruction:\n [0x{:08x}]{}", state.pc, inst); + // spdlog::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..d6ba0b1 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 uint8_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,15 @@ 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 uint8_t PhysMemory::getPt(Addr addr) { + return static_cast( + getBits() - 1, kOffsetBits>(addr)); +} + inline Word Memory::loadWord(Addr addr) { stats.numLoads++; Word loadedWord = *physMem.getEntity(addr); @@ -216,7 +218,7 @@ inline void Memory::storeWord(Addr addr, Word word) { stats.numStores++; *physMem.getEntity(addr) = word; if (isProgramStored) { - cosimLog("M[0x{:08x}]=0x{:08x}", addr, word); + // cosimLog("M[0x{:08x}]=0x{:08x}", addr, word); } } @@ -257,18 +259,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..8edc9a2 100644 --- a/src/executor/executor.cc +++ b/src/executor/executor.cc @@ -51,166 +51,22 @@ 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 - // 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::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::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()); - }}}; +#define SIM2022_MAKE_EXEC_DEF(op, action) \ + static void execute##op([[maybe_unused]] const Instruction &inst, \ + State &state) { \ + action \ + } + +#include "executor.ii" + +#undef SIM2022_MAKE_EXEC_DEF + +const Executor::ExecutorMap Executor::execMap_{ +#define SIM2022_MAKE_EXEC_DEF(op, action) {OpType::op, execute##op}, + +#include "executor.ii" + +#undef SIM2022_MAKE_EXEC_DEF +}; } // namespace sim diff --git a/src/executor/executor.ii b/src/executor/executor.ii new file mode 100644 index 0000000..d97424a --- /dev/null +++ b/src/executor/executor.ii @@ -0,0 +1,154 @@ +#ifndef SIM2022_MAKE_EXEC_DEF +#define SIM2022_MAKE_EXEC_DEF +#error "Define the macro before including this file" +#endif + +SIM2022_MAKE_EXEC_DEF(ADD, { + executeRegisterRegisterOp(inst, state, std::plus()); +}) + +SIM2022_MAKE_EXEC_DEF(SUB, { + executeRegisterRegisterOp(inst, state, std::minus()); +}) + +SIM2022_MAKE_EXEC_DEF(MUL, { + executeRegisterRegisterOp(inst, state, std::multiplies()); +}) + +SIM2022_MAKE_EXEC_DEF(DIV, { + auto rs2 = state.regs.get(inst.rs2); + if (rs2 == 0) { + throw std::logic_error("division by zero"); + } + executeRegisterRegisterOp(inst, state, std::divides()); +}) + +SIM2022_MAKE_EXEC_DEF(LW, { + auto rs1 = state.regs.get(inst.rs1); + auto word = state.mem.loadWord(rs1 + inst.imm); + state.regs.set(inst.rd, word); +}) + +SIM2022_MAKE_EXEC_DEF(SW, { + auto rs1 = state.regs.get(inst.rs1); + auto rs2 = state.regs.get(inst.rs2); + state.mem.storeWord(rs1 + inst.imm, rs2); +}) + +SIM2022_MAKE_EXEC_DEF(JAL, { + state.branchIsTaken = true; + state.regs.set(inst.rd, state.pc + kXLENInBytes); + state.npc = state.pc + inst.imm; +}) + +SIM2022_MAKE_EXEC_DEF(JALR, { + 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. +}) + +SIM2022_MAKE_EXEC_DEF(ECALL, { state.complete = true; }) + +SIM2022_MAKE_EXEC_DEF(ADDI, { + executeRegisterImmidiateOp(inst, state, std::plus()); +}) + +SIM2022_MAKE_EXEC_DEF(ANDI, { + executeRegisterImmidiateOp(inst, state, std::bit_and()); +}) + +SIM2022_MAKE_EXEC_DEF(XORI, { + executeRegisterImmidiateOp(inst, state, std::bit_xor()); +}) + +SIM2022_MAKE_EXEC_DEF(ORI, { + executeRegisterImmidiateOp(inst, state, std::bit_or()); +}) + +SIM2022_MAKE_EXEC_DEF(SLTI, { + 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); +}) + +SIM2022_MAKE_EXEC_DEF(SLTIU, { + auto rs1 = state.regs.get(inst.rs1); + state.regs.set(inst.rd, executeSLT(rs1, inst.imm)); +}) + +SIM2022_MAKE_EXEC_DEF(LUI, { state.regs.set(inst.rd, inst.imm << 12); }) + +SIM2022_MAKE_EXEC_DEF(AUIPC, + { state.regs.set(inst.rd, state.pc + (inst.imm << 12)); }) + +SIM2022_MAKE_EXEC_DEF(SLLI, { + auto shamt = (getBits<4, 0>(inst.imm)); + auto rs1 = state.regs.get(inst.rs1); + state.regs.set(inst.rd, rs1 << shamt); +}) + +SIM2022_MAKE_EXEC_DEF(SRLI, { + auto rs1 = state.regs.get(inst.rs1); + auto res = executeSRLT(rs1, inst.imm); + state.regs.set(inst.rd, res); +}) + +SIM2022_MAKE_EXEC_DEF(SRAI, { + auto rs1 = state.regs.get(inst.rs1); + auto signedRs1 = signCast(rs1); + auto res = unsignedCast(executeSRLT(signedRs1, inst.imm)); + state.regs.set(inst.rd, res); +}) + +SIM2022_MAKE_EXEC_DEF(SLL, { + auto rs1 = state.regs.get(inst.rs1); + auto rs2 = state.regs.get(inst.rs2); + state.regs.set(inst.rd, rs1 << rs2); +}) + +SIM2022_MAKE_EXEC_DEF(SRL, { + executeRegisterRegisterOp(inst, state, executeSR); +}) + +SIM2022_MAKE_EXEC_DEF(SRA, { + 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); +}) + +SIM2022_MAKE_EXEC_DEF(BEQ, { + executeCondBranch(inst, state, std::equal_to()); +}) + +SIM2022_MAKE_EXEC_DEF(BNE, { + executeCondBranch(inst, state, std::not_equal_to()); +}) + +SIM2022_MAKE_EXEC_DEF(BLT, + { executeCondBranch(inst, state, std::less()); }) + +SIM2022_MAKE_EXEC_DEF(BLTU, { + executeCondBranch(inst, state, std::greater()); +}) + +SIM2022_MAKE_EXEC_DEF(BGEU, { + executeCondBranch(inst, state, std::greater_equal()); +}) + +SIM2022_MAKE_EXEC_DEF(BGE, { + 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; + } +}) + +SIM2022_MAKE_EXEC_DEF(XOR, { + executeRegisterRegisterOp(inst, state, std::bit_xor()); +}) diff --git a/src/hart/hart.cc b/src/hart/hart.cc index 66f892b..6a3b766 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:"); + // spdlog::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()); + // spdlog::trace("{}", inst); isBranch = inst.isBranch; bb.push_back(inst); } - spdlog::trace("Basic blok created."); + // spdlog::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);