From 90e684e1e3e593ebc0496d7ea60ad93dbb09bf7b Mon Sep 17 00:00:00 2001 From: modeco80 Date: Mon, 24 Jul 2023 01:56:50 -0400 Subject: [PATCH] riscv: implement syscon + system hooks This finally allows the test harness to cleanly shut down. Awesome! This commit also reformats the whole project's native code. Oops! --- .clang-format | 1 + native/projects/lcpu/src/SourceSink.cpp | 3 +- native/projects/lcpu/src/main.cpp | 6 +- .../projects/lucore/include/lucore/Assert.hpp | 18 +- .../lucore/include/lucore/OptionalRef.hpp | 28 +- .../lucore/include/lucore/StdoutSink.hpp | 2 +- .../projects/lucore/include/lucore/Types.hpp | 22 +- native/projects/lucore/src/Logger.cpp | 6 +- native/projects/lucore/src/StdoutSink.cpp | 3 +- native/projects/riscv/include/riscv/Bus.hpp | 24 +- native/projects/riscv/include/riscv/CPU.hpp | 7 +- .../projects/riscv/include/riscv/CPUTypes.hpp | 36 +-- .../include/riscv/Devices/ClntDevice.hpp | 9 +- .../riscv/include/riscv/Devices/RamDevice.hpp | 4 +- .../include/riscv/Devices/SysconDevice.hpp | 9 +- .../projects/riscv/include/riscv/System.hpp | 9 +- native/projects/riscv/include/riscv/Types.hpp | 2 - native/projects/riscv/src/CPU.cpp | 239 +++++------------- .../projects/riscv/src/Devices/ClntDevice.cpp | 37 +-- .../projects/riscv/src/Devices/RamDevice.cpp | 3 +- .../riscv/src/Devices/SysconDevice.cpp | 20 +- native/projects/riscv/src/System.cpp | 25 +- native/projects/riscv_test_harness/main.cpp | 33 ++- .../projects/riscv_test_harness/test/Makefile | 16 +- .../projects/riscv_test_harness/test/main.c | 24 +- 25 files changed, 211 insertions(+), 375 deletions(-) diff --git a/.clang-format b/.clang-format index ae4b473..b774987 100755 --- a/.clang-format +++ b/.clang-format @@ -16,6 +16,7 @@ AllowShortBlocksOnASingleLine: false AllowShortFunctionsOnASingleLine: InlineOnly AllowShortIfStatementsOnASingleLine: Never AllowShortLoopsOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: true BinPackArguments: true BinPackParameters: true diff --git a/native/projects/lcpu/src/SourceSink.cpp b/native/projects/lcpu/src/SourceSink.cpp index 80a60e9..643becf 100644 --- a/native/projects/lcpu/src/SourceSink.cpp +++ b/native/projects/lcpu/src/SourceSink.cpp @@ -84,8 +84,7 @@ namespace lcpu { void SourceSink::OutputMessage(const lucore::Logger::MessageData& data) { auto formatted = - std::format("[LCPU Native/{}] [{}] {}", lucore::Logger::SeverityToString(data.severity), - data.time, std::vformat(data.format, data.args)); + std::format("[LCPU Native/{}] [{}] {}", lucore::Logger::SeverityToString(data.severity), data.time, std::vformat(data.format, data.args)); tier0::Msg("%s\n", formatted.c_str()); } diff --git a/native/projects/lcpu/src/main.cpp b/native/projects/lcpu/src/main.cpp index fc0ebc9..bb6918c 100644 --- a/native/projects/lcpu/src/main.cpp +++ b/native/projects/lcpu/src/main.cpp @@ -1,11 +1,10 @@ #include -#include "SourceSink.hpp" - #include -LUA_FUNCTION(lcpu_native_test) { +#include "SourceSink.hpp" +LUA_FUNCTION(lcpu_native_test) { } GMOD_MODULE_OPEN() { @@ -13,7 +12,6 @@ GMOD_MODULE_OPEN() { lucore::LogInfo("LCPU Native Module loading"); - return 0; } diff --git a/native/projects/lucore/include/lucore/Assert.hpp b/native/projects/lucore/include/lucore/Assert.hpp index ef0841f..402583a 100644 --- a/native/projects/lucore/include/lucore/Assert.hpp +++ b/native/projects/lucore/include/lucore/Assert.hpp @@ -9,20 +9,18 @@ namespace lucore { } // namespace lucore #ifndef NDEBUG - #define LUCORE_ASSERT(expr, fmt, ...) \ - if(!(expr)) [[unlikely]] { \ - auto msg = std::format("Assertion \"{}\" @ {}:{} failed with message: {}", #expr, \ - __FILE__, __LINE__, std::format(fmt, ##__VA_ARGS__)); \ - ::lucore::ExitMsg(msg.c_str()); \ + #define LUCORE_ASSERT(expr, fmt, ...) \ + if(!(expr)) [[unlikely]] { \ + auto msg = std::format("Assertion \"{}\" @ {}:{} failed with message: {}", #expr, __FILE__, __LINE__, std::format(fmt, ##__VA_ARGS__)); \ + ::lucore::ExitMsg(msg.c_str()); \ } #else #define LUCORE_ASSERT(expr, format, ...) #endif // CHECK() is always active, even in release builds -#define LUCORE_CHECK(expr, fmt, ...) \ - if(!(expr)) [[unlikely]] { \ - auto msg = std::format("Check \"{}\" @ {}:{} failed with message: {}", #expr, __FILE__, \ - __LINE__, std::format(fmt, ##__VA_ARGS__)); \ - ::lucore::ExitMsg(msg.c_str()); \ +#define LUCORE_CHECK(expr, fmt, ...) \ + if(!(expr)) [[unlikely]] { \ + auto msg = std::format("Check \"{}\" @ {}:{} failed with message: {}", #expr, __FILE__, __LINE__, std::format(fmt, ##__VA_ARGS__)); \ + ::lucore::ExitMsg(msg.c_str()); \ } diff --git a/native/projects/lucore/include/lucore/OptionalRef.hpp b/native/projects/lucore/include/lucore/OptionalRef.hpp index 9b62005..042e82b 100644 --- a/native/projects/lucore/include/lucore/OptionalRef.hpp +++ b/native/projects/lucore/include/lucore/OptionalRef.hpp @@ -20,20 +20,16 @@ namespace lucore::detail { /// Do *not* give this class invalid references. template struct OptionalRef { - constexpr OptionalRef() : ptr(nullptr) { - } + constexpr OptionalRef() : ptr(nullptr) {} // trigger explicit null construction - constexpr OptionalRef(Nullref_t) : OptionalRef() { - } + constexpr OptionalRef(Nullref_t) : OptionalRef() {} - constexpr OptionalRef(T& ref) : ptr(&ref) { - } + constexpr OptionalRef(T& ref) : ptr(&ref) {} // polymorphic downconstruction from another OptionalRef template - constexpr OptionalRef(const OptionalRef& other) : ptr(&other.ptr) { - } + constexpr OptionalRef(const OptionalRef& other) : ptr(&other.ptr) {} constexpr T& Value() const { // this is a CHECK() since allowing unchecked access in release builds is probably a @@ -42,22 +38,14 @@ namespace lucore::detail { return *ptr; } - constexpr bool HasValue() const { - return ptr != nullptr; - } + constexpr bool HasValue() const { return ptr != nullptr; } - constexpr T& operator*() const { - return Value(); - } + constexpr T& operator*() const { return Value(); } // unchecked access: DO NOT use this without checking beforehand - constexpr T* operator->() const { - return ptr; - } + constexpr T* operator->() const { return ptr; } - constexpr operator bool() const { - return HasValue(); - } + constexpr operator bool() const { return HasValue(); } private: T* ptr {}; diff --git a/native/projects/lucore/include/lucore/StdoutSink.hpp b/native/projects/lucore/include/lucore/StdoutSink.hpp index e22580b..6388d0f 100644 --- a/native/projects/lucore/include/lucore/StdoutSink.hpp +++ b/native/projects/lucore/include/lucore/StdoutSink.hpp @@ -14,4 +14,4 @@ namespace lucore { /// Attach the stdout logger sink to the global Lucore logger. void LoggerAttachStdout(); -} +} // namespace lucore diff --git a/native/projects/lucore/include/lucore/Types.hpp b/native/projects/lucore/include/lucore/Types.hpp index 1fac33c..ccfa178 100644 --- a/native/projects/lucore/include/lucore/Types.hpp +++ b/native/projects/lucore/include/lucore/Types.hpp @@ -1,15 +1,15 @@ #include -//namespace lucore { - using u8 = std::uint8_t; - using i8 = std::int8_t; - using u16 = std::uint16_t; - using i16 = std::int16_t; - using u32 = std::uint32_t; - using i32 = std::int32_t; - using u64 = std::uint64_t; - using i64 = std::int64_t; - using usize = std::size_t; - using isize = std::intptr_t; +// namespace lucore { +using u8 = std::uint8_t; +using i8 = std::int8_t; +using u16 = std::uint16_t; +using i16 = std::int16_t; +using u32 = std::uint32_t; +using i32 = std::int32_t; +using u64 = std::uint64_t; +using i64 = std::int64_t; +using usize = std::size_t; +using isize = std::intptr_t; //} // namespace lucore diff --git a/native/projects/lucore/src/Logger.cpp b/native/projects/lucore/src/Logger.cpp index 26164d5..66a04bf 100644 --- a/native/projects/lucore/src/Logger.cpp +++ b/native/projects/lucore/src/Logger.cpp @@ -23,14 +23,10 @@ namespace lucore { if(severity < logLevel) return; - MessageData data { .time = std::chrono::system_clock::now(), - .severity = severity, - .format = format, - .args = args }; + MessageData data { .time = std::chrono::system_clock::now(), .severity = severity, .format = format, .args = args }; for(auto sink : sinks) sink->OutputMessage(data); } - } // namespace lucore diff --git a/native/projects/lucore/src/StdoutSink.cpp b/native/projects/lucore/src/StdoutSink.cpp index fbef82f..4da7da1 100644 --- a/native/projects/lucore/src/StdoutSink.cpp +++ b/native/projects/lucore/src/StdoutSink.cpp @@ -31,8 +31,7 @@ namespace lucore { }; auto it = FputcIterator(data.severity < Logger::MessageSeverity::Error ? stdout : stderr); - std::format_to(it, "[Lucore/{}] [{}] {}\n", Logger::SeverityToString(data.severity), - data.time, std::vformat(data.format, data.args)); + std::format_to(it, "[Lucore/{}] [{}] {}\n", Logger::SeverityToString(data.severity), data.time, std::vformat(data.format, data.args)); } void LoggerAttachStdout() { diff --git a/native/projects/riscv/include/riscv/Bus.hpp b/native/projects/riscv/include/riscv/Bus.hpp index 16b8d9a..aacc81a 100644 --- a/native/projects/riscv/include/riscv/Bus.hpp +++ b/native/projects/riscv/include/riscv/Bus.hpp @@ -1,8 +1,8 @@ #pragma once #include -#include #include +#include #include #include @@ -28,9 +28,7 @@ namespace riscv { virtual ~Device() = default; - virtual void Attached(Bus* bus) { - this->bus = bus; - } + virtual void Attached(Bus* bus) { this->bus = bus; } virtual BasicType Type() const { return BasicType::Device; } @@ -46,11 +44,11 @@ namespace riscv { template constexpr bool IsA() { - if constexpr (std::is_same_v) { + if constexpr(std::is_same_v) { return this->Type() == BasicType::Cpu; - } else if constexpr (std::is_same_v) { + } else if constexpr(std::is_same_v) { return this->Type() == BasicType::PlainMemory; - } else if constexpr (std::is_same_v) { + } else if constexpr(std::is_same_v) { return this->Type() == BasicType::Mmio; } else { // Invalid types should do this. @@ -63,7 +61,8 @@ namespace riscv { LUCORE_ASSERT(IsA(), "Upcast failure: this is not a T"); return static_cast(this); } - protected: + + protected: /// The bus this device is attached to. Bus* bus; }; @@ -75,8 +74,8 @@ namespace riscv { virtual BasicType Type() const override { return BasicType::PlainMemory; } virtual Address Base() const = 0; - - /// How many bytes does this device occupy of address space? + + /// How many bytes does this device occupy of address space? /// This should not change during execution. virtual Address Size() const = 0; @@ -100,7 +99,7 @@ namespace riscv { virtual Address Base() const = 0; - /// How many bytes does this device occupy of address space? + /// How many bytes does this device occupy of address space? /// This should not change during execution. virtual Address Size() const = 0; @@ -139,8 +138,7 @@ namespace riscv { CPU* GetCPU() { return cpu; } private: - - // TODO: version which takes Device::BasicType + // TODO: version which takes Device::BasicType Bus::Device* FindDeviceForAddress(Address address) const; CPU* cpu; diff --git a/native/projects/riscv/include/riscv/CPU.hpp b/native/projects/riscv/include/riscv/CPU.hpp index aef4fb4..e0e359b 100644 --- a/native/projects/riscv/include/riscv/CPU.hpp +++ b/native/projects/riscv/include/riscv/CPU.hpp @@ -6,7 +6,7 @@ namespace riscv { /// The CPU core. struct CPU : Bus::Device { - BasicType Type() const override { return BasicType::Cpu; } + BasicType Type() const override { return BasicType::Cpu; } bool Clocked() const override { return true; } void Clock() override; @@ -16,14 +16,11 @@ namespace riscv { void TimerInterrupt(); - constexpr CPU() { - Reset(); - } + constexpr CPU() { Reset(); } constexpr void Reset() { // Initalize some state. We're cool like that :) pc = 0x80000000; - gpr[Gpr::A0] = 0x0; // HART id extraflags |= 3; // Start in Machine mode } diff --git a/native/projects/riscv/include/riscv/CPUTypes.hpp b/native/projects/riscv/include/riscv/CPUTypes.hpp index 6bbde18..30ff47d 100644 --- a/native/projects/riscv/include/riscv/CPUTypes.hpp +++ b/native/projects/riscv/include/riscv/CPUTypes.hpp @@ -61,40 +61,8 @@ namespace riscv { }; constexpr std::string_view RegName(Gpr gpr) { - std::string_view table[] = { - "zero", - "ra", - "sp", - "gp", - "tp", - "t0", - "t1", - "t2", - "s0", - "s1", - "a0", - "a1", - "a2", - "a3", - "a4", - "a5", - "a6", - "a7", - "s2", - "s3", - "s4", - "s5", - "s6", - "s7", - "s8", - "s9", - "s10", - "s11", - "t3", - "t4", - "t5", - "t6" - }; + std::string_view table[] = { "zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", + "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6" }; return table[static_cast(gpr)]; } diff --git a/native/projects/riscv/include/riscv/Devices/ClntDevice.hpp b/native/projects/riscv/include/riscv/Devices/ClntDevice.hpp index 2e8ae82..5c0d90c 100644 --- a/native/projects/riscv/include/riscv/Devices/ClntDevice.hpp +++ b/native/projects/riscv/include/riscv/Devices/ClntDevice.hpp @@ -12,19 +12,18 @@ namespace riscv::devices { Address Base() const override { return BASE_ADDRESS; } Address Size() const override { return 0xbfff; } - bool Clocked() const override { return true; } + bool Clocked() const override { return true; } void Clock() override; - u32 Peek(Address address) override; void Poke(Address address, u32 value) override; - private: + private: u32 timerCountHigh; u32 timerCountLow; u32 timerMatchHigh; - u32 timerMatchLow; + u32 timerMatchLow; }; -} +} // namespace riscv::devices diff --git a/native/projects/riscv/include/riscv/Devices/RamDevice.hpp b/native/projects/riscv/include/riscv/Devices/RamDevice.hpp index 6463bfa..56e3ea4 100644 --- a/native/projects/riscv/include/riscv/Devices/RamDevice.hpp +++ b/native/projects/riscv/include/riscv/Devices/RamDevice.hpp @@ -14,9 +14,7 @@ namespace riscv::devices { Address Base() const override; Address Size() const override; - u8* Raw() const { - return memory; - } + u8* Raw() const { return memory; } u8 PeekByte(Address address) override; u16 PeekShort(Address address) override; diff --git a/native/projects/riscv/include/riscv/Devices/SysconDevice.hpp b/native/projects/riscv/include/riscv/Devices/SysconDevice.hpp index 1427ebb..f588dd0 100644 --- a/native/projects/riscv/include/riscv/Devices/SysconDevice.hpp +++ b/native/projects/riscv/include/riscv/Devices/SysconDevice.hpp @@ -2,7 +2,9 @@ #include -namespace riscv { struct System; } +namespace riscv { + struct System; +} namespace riscv::devices { /// RISC-V SYSCON device. This will later talk to the system to tell it things. @@ -16,7 +18,8 @@ namespace riscv::devices { u32 Peek(Address address) override; void Poke(Address address, u32 value) override; - private: + + private: System* system; }; -} +} // namespace riscv::devices diff --git a/native/projects/riscv/include/riscv/System.hpp b/native/projects/riscv/include/riscv/System.hpp index 81038ba..c509e7c 100644 --- a/native/projects/riscv/include/riscv/System.hpp +++ b/native/projects/riscv/include/riscv/System.hpp @@ -1,3 +1,4 @@ +#include // use function_ref when we get c++23? #include #include #include @@ -16,7 +17,8 @@ namespace riscv { void Step(); - // TODO: callbacks for SYSCON PowerOff and Reboot. + std::function OnPowerOff; + std::function OnReboot; Bus* bus; @@ -27,6 +29,11 @@ namespace riscv { devices::ClntDevice* clnt; private: + friend struct devices::SysconDevice; + + void SysconPowerOff(); + void SysconReboot(); + System() = default; }; diff --git a/native/projects/riscv/include/riscv/Types.hpp b/native/projects/riscv/include/riscv/Types.hpp index b996a74..4ee438e 100644 --- a/native/projects/riscv/include/riscv/Types.hpp +++ b/native/projects/riscv/include/riscv/Types.hpp @@ -8,6 +8,4 @@ namespace riscv { /// A type that can repressent address space or address space offsets. using Address = u32; - - } // namespace riscv diff --git a/native/projects/riscv/src/CPU.cpp b/native/projects/riscv/src/CPU.cpp index a8dee6c..fb2a8e6 100644 --- a/native/projects/riscv/src/CPU.cpp +++ b/native/projects/riscv/src/CPU.cpp @@ -54,6 +54,7 @@ namespace riscv { if((pc & 3)) { Trap(TrapCode::InstructionAddressMisaligned); + rval = pc; break; } else { auto ir = bus->PeekWord(pc); @@ -136,8 +137,7 @@ namespace riscv { if((u32)rs1 >= (u32)rs2) pc = immm4; break; - default: - Trap(TrapCode::IllegalInstruction); + default: Trap(TrapCode::IllegalInstruction); } break; } @@ -150,31 +150,20 @@ namespace riscv { switch((ir >> 12) & 0x7) { // LB, LH, LW, LBU, LHU - case 0: - rval = (i8)bus->PeekByte(rsval); - break; - case 1: - rval = (i16)bus->PeekShort(rsval); - break; - case 2: - rval = bus->PeekWord(rsval); - break; - case 4: - rval = bus->PeekByte(rsval); - break; - case 5: - rval = bus->PeekShort(rsval); - break; - default: - Trap(TrapCode::IllegalInstruction); - break; + case 0: rval = (i8)bus->PeekByte(rsval); break; + case 1: rval = (i16)bus->PeekShort(rsval); break; + case 2: rval = bus->PeekWord(rsval); break; + case 4: rval = bus->PeekByte(rsval); break; + case 5: rval = bus->PeekShort(rsval); break; + default: Trap(TrapCode::IllegalInstruction); break; } if(trapped) { - rval = rsval; // + RamImageOffset; + rval = rsval; } break; } + case 0x23: { // store u32 rs1 = gpr[(ir >> 15) & 0x1f]; u32 rs2 = gpr[(ir >> 20) & 0x1f]; @@ -186,22 +175,14 @@ namespace riscv { switch((ir >> 12) & 0x7) { // SB, SH, SW - case 0: - bus->PokeByte(addy, rs2); - break; - case 1: - bus->PokeShort(addy, rs2); - break; - case 2: - // lucore::LogInfo("storeWord(0x{:08x}, 0x{:08x})", addy, rs2); - bus->PokeWord(addy, rs2); - break; - default: - Trap(TrapCode::IllegalInstruction); + case 0: bus->PokeByte(addy, rs2); break; + case 1: bus->PokeShort(addy, rs2); break; + case 2: bus->PokeWord(addy, rs2); break; + default: Trap(TrapCode::IllegalInstruction); } if(trapped) { - rval = addy; // + RamImageOffset; + rval = addy; } break; } @@ -217,19 +198,10 @@ namespace riscv { if(is_reg && (ir & 0x02000000)) { switch((ir >> 12) & 7) // 0x02000000 = RV32M { - case 0: - rval = rs1 * rs2; - break; // MUL - - case 1: - rval = ((i64)((i32)rs1) * (i64)((i32)rs2)) >> 32; - break; // MULH - case 2: - rval = ((i64)((i32)rs1) * (u64)rs2) >> 32; - break; // MULHSU - case 3: - rval = ((u64)rs1 * (u64)rs2) >> 32; - break; // MULHU + case 0: rval = rs1 * rs2; break; // MUL + case 1: rval = ((i64)((i32)rs1) * (i64)((i32)rs2)) >> 32; break; // MULH + case 2: rval = ((i64)((i32)rs1) * (u64)rs2) >> 32; break; // MULHSU + case 3: rval = ((u64)rs1 * (u64)rs2) >> 32; break; // MULHU case 4: if(rs2 == 0) @@ -259,30 +231,14 @@ namespace riscv { } else { // These could be either op-immediate or op commands. Be careful. switch((ir >> 12) & 7) { - case 0: - rval = (is_reg && (ir & 0x40000000)) ? (rs1 - rs2) : (rs1 + rs2); - break; - case 1: - rval = rs1 << (rs2 & 0x1F); - break; - case 2: - rval = (i32)rs1 < (i32)rs2; - break; - case 3: - rval = rs1 < rs2; - break; - case 4: - rval = rs1 ^ rs2; - break; - case 5: - rval = (ir & 0x40000000) ? (((i32)rs1) >> (rs2 & 0x1F)) : (rs1 >> (rs2 & 0x1F)); - break; - case 6: - rval = rs1 | rs2; - break; - case 7: - rval = rs1 & rs2; - break; + case 0: rval = (is_reg && (ir & 0x40000000)) ? (rs1 - rs2) : (rs1 + rs2); break; + case 1: rval = rs1 << (rs2 & 0x1F); break; + case 2: rval = (i32)rs1 < (i32)rs2; break; + case 3: rval = rs1 < rs2; break; + case 4: rval = rs1 ^ rs2; break; + case 5: rval = (ir & 0x40000000) ? (((i32)rs1) >> (rs2 & 0x1F)) : (rs1 >> (rs2 & 0x1F)); break; + case 6: rval = rs1 | rs2; break; + case 7: rval = rs1 & rs2; break; } } break; @@ -305,36 +261,16 @@ namespace riscv { // https://raw.githubusercontent.com/riscv/virtual-memory/main/specs/663-Svpbmt.pdf // Generally, support for Zicsr switch(csrno) { - case 0x340: - rval = mscratch; - break; - case 0x305: - rval = mtvec; - break; - case 0x304: - rval = mie; - break; - case 0xC00: - rval = cycle; - break; - case 0x344: - rval = mip; - break; - case 0x341: - rval = mepc; - break; - case 0x300: - rval = mstatus; - break; // mstatus - case 0x342: - rval = mcause; - break; - case 0x343: - rval = mtval; - break; - case 0xf11: - rval = 0xff0ff0ff; - break; // mvendorid + case 0x340: rval = mscratch; break; + case 0x305: rval = mtvec; break; + case 0x304: rval = mie; break; + case 0xC00: rval = cycle; break; + case 0x344: rval = mip; break; + case 0x341: rval = mepc; break; + case 0x300: rval = mstatus; break; // mstatus + case 0x342: rval = mcause; break; + case 0x343: rval = mtval; break; + case 0xf11: rval = 0xff0ff0ff; break; // mvendorid case 0x301: rval = 0x40401101; break; // misa (XLEN=32, IMA+X) @@ -349,51 +285,23 @@ namespace riscv { } switch(microop) { - case 1: - writeval = rs1; - break; // CSRRW - case 2: - writeval = rval | rs1; - break; // CSRRS - case 3: - writeval = rval & ~rs1; - break; // CSRRC - case 5: - writeval = rs1imm; - break; // CSRRWI - case 6: - writeval = rval | rs1imm; - break; // CSRRSI - case 7: - writeval = rval & ~rs1imm; - break; // CSRRCI + case 1: writeval = rs1; break; // CSRRW + case 2: writeval = rval | rs1; break; // CSRRS + case 3: writeval = rval & ~rs1; break; // CSRRC + case 5: writeval = rs1imm; break; // CSRRWI + case 6: writeval = rval | rs1imm; break; // CSRRSI + case 7: writeval = rval & ~rs1imm; break; // CSRRCI } switch(csrno) { - case 0x340: - mscratch = writeval; - break; - case 0x305: - mtvec = writeval; - break; - case 0x304: - mie = writeval; - break; - case 0x344: - mip = writeval; - break; - case 0x341: - mepc = writeval; - break; - case 0x300: - mstatus = writeval; - break; // mstatus - case 0x342: - mcause = writeval; - break; - case 0x343: - mtval = writeval; - break; + case 0x340: mscratch = writeval; break; + case 0x305: mtvec = writeval; break; + case 0x304: mie = writeval; break; + case 0x344: mip = writeval; break; + case 0x341: mepc = writeval; break; + case 0x300: mstatus = writeval; break; // mstatus + case 0x342: mcause = writeval; break; + case 0x343: mtval = writeval; break; // case 0x3a0: break; //pmpcfg0 // case 0x3B0: break; //pmpaddr0 // case 0xf11: break; //mvendorid @@ -434,15 +342,14 @@ namespace riscv { case 1: // breakpoint Trap(TrapCode::Breakpoint); break; - default: - Trap(TrapCode::IllegalInstruction); - break; + default: Trap(TrapCode::IllegalInstruction); break; } } } else Trap(TrapCode::IllegalInstruction); break; } + case 0x2f: // RV32A (0b00101111) { u32 rs1 = gpr[(ir >> 15) & 0x1f]; @@ -451,7 +358,7 @@ namespace riscv { rval = bus->PeekWord(rs1); if(trapped) { - rval = rs1; // + RamImageOffset; + rval = rs1; break; } @@ -466,32 +373,15 @@ namespace riscv { rval = (extraflags >> 3 != (rs1 & 0x1fffffff)); // Validate that our reservation slot is OK. dowrite = !rval; // Only write if slot is valid. break; - case 1: - break; // AMOSWAP.W (0b00001) - case 0: - rs2 += rval; - break; // AMOADD.W (0b00000) - case 4: - rs2 ^= rval; - break; // AMOXOR.W (0b00100) - case 12: - rs2 &= rval; - break; // AMOAND.W (0b01100) - case 8: - rs2 |= rval; - break; // AMOOR.W (0b01000) - case 16: - rs2 = ((i32)rs2 < (i32)rval) ? rs2 : rval; - break; // AMOMIN.W (0b10000) - case 20: - rs2 = ((i32)rs2 > (i32)rval) ? rs2 : rval; - break; // AMOMAX.W (0b10100) - case 24: - rs2 = (rs2 < rval) ? rs2 : rval; - break; // AMOMINU.W (0b11000) - case 28: - rs2 = (rs2 > rval) ? rs2 : rval; - break; // AMOMAXU.W (0b11100) + case 1: break; // AMOSWAP.W (0b00001) + case 0: rs2 += rval; break; // AMOADD.W (0b00000) + case 4: rs2 ^= rval; break; // AMOXOR.W (0b00100) + case 12: rs2 &= rval; break; // AMOAND.W (0b01100) + case 8: rs2 |= rval; break; // AMOOR.W (0b01000) + case 16: rs2 = ((i32)rs2 < (i32)rval) ? rs2 : rval; break; // AMOMIN.W (0b10000) + case 20: rs2 = ((i32)rs2 > (i32)rval) ? rs2 : rval; break; // AMOMAX.W (0b10100) + case 24: rs2 = (rs2 < rval) ? rs2 : rval; break; // AMOMINU.W (0b11000) + case 28: rs2 = (rs2 > rval) ? rs2 : rval; break; // AMOMAXU.W (0b11100) default: Trap(TrapCode::IllegalInstruction); dowrite = 0; @@ -501,8 +391,7 @@ namespace riscv { bus->PokeWord(rs1, rs2); break; } - default: - Trap(TrapCode::IllegalInstruction); + default: Trap(TrapCode::IllegalInstruction); } } diff --git a/native/projects/riscv/src/Devices/ClntDevice.cpp b/native/projects/riscv/src/Devices/ClntDevice.cpp index ea3546a..2e6f911 100644 --- a/native/projects/riscv/src/Devices/ClntDevice.cpp +++ b/native/projects/riscv/src/Devices/ClntDevice.cpp @@ -1,14 +1,12 @@ #include -#include #include +#include namespace riscv::devices { - constexpr static Address MSIP_ADDRESS = ClntDevice::BASE_ADDRESS, - MATCHL_ADDRESS = ClntDevice::BASE_ADDRESS + 0x4000, - MATCHH_ADDRESS = ClntDevice::BASE_ADDRESS + 0x4004, - TIMERL_ADDRESS = ClntDevice::BASE_ADDRESS + 0xbff8, - TIMERH_ADDRESS = ClntDevice::BASE_ADDRESS + 0xbffc; + constexpr static Address MSIP_ADDRESS = ClntDevice::BASE_ADDRESS, MATCHL_ADDRESS = ClntDevice::BASE_ADDRESS + 0x4000, + MATCHH_ADDRESS = ClntDevice::BASE_ADDRESS + 0x4004, TIMERL_ADDRESS = ClntDevice::BASE_ADDRESS + 0xbff8, + TIMERH_ADDRESS = ClntDevice::BASE_ADDRESS + 0xbffc; void ClntDevice::Clock() { // TODO: handle timer @@ -20,8 +18,7 @@ namespace riscv::devices { timerCountHigh++; timerCountLow = new_timer; - if((timerCountHigh > timerMatchHigh || - (timerCountHigh == timerMatchHigh && timerCountLow == timerMatchLow)) && + if((timerCountHigh > timerMatchHigh || (timerCountHigh == timerMatchHigh && timerCountLow == timerMatchLow)) && (timerMatchHigh || timerMatchLow)) { // Fire the CLNT timer interrupt bus->GetCPU()->TimerInterrupt(); @@ -30,38 +27,28 @@ namespace riscv::devices { u32 ClntDevice::Peek(Address address) { switch(address) { - case TIMERL_ADDRESS: - return timerCountLow; + case TIMERL_ADDRESS: return timerCountLow; - case TIMERH_ADDRESS: - return timerCountHigh; + case TIMERH_ADDRESS: return timerCountHigh; - case MATCHL_ADDRESS: - return timerMatchLow; + case MATCHL_ADDRESS: return timerMatchLow; - case MATCHH_ADDRESS: - return timerMatchHigh; + case MATCHH_ADDRESS: return timerMatchHigh; - default: - return 0x0; + default: return 0x0; } } void ClntDevice::Poke(Address address, u32 value) { switch(address) { - case MATCHL_ADDRESS: - timerMatchLow = value; - break; + case MATCHL_ADDRESS: timerMatchLow = value; break; case MATCHH_ADDRESS: timerMatchHigh = value; break; // ? - default: - lucore::LogInfo("CLNT({}) unhandled poke @ 0x{:08x} : 0x{:08x}", - static_cast(this), address, value); - break; + default: lucore::LogInfo("CLNT({}) unhandled poke @ 0x{:08x} : 0x{:08x}", static_cast(this), address, value); break; } } diff --git a/native/projects/riscv/src/Devices/RamDevice.cpp b/native/projects/riscv/src/Devices/RamDevice.cpp index cc943d9..3ce512c 100644 --- a/native/projects/riscv/src/Devices/RamDevice.cpp +++ b/native/projects/riscv/src/Devices/RamDevice.cpp @@ -6,8 +6,7 @@ namespace riscv::devices { RamDevice::RamDevice(Address base, Address size) : memoryBase(base), memorySize(size) { memory = new u8[size]; - LUCORE_CHECK(memory, "Could not allocate buffer for memory device with size 0x{:08x}.", - size); + LUCORE_CHECK(memory, "Could not allocate buffer for memory device with size 0x{:08x}.", size); } RamDevice::~RamDevice() { diff --git a/native/projects/riscv/src/Devices/SysconDevice.cpp b/native/projects/riscv/src/Devices/SysconDevice.cpp index 7a40abc..2e4f54c 100644 --- a/native/projects/riscv/src/Devices/SysconDevice.cpp +++ b/native/projects/riscv/src/Devices/SysconDevice.cpp @@ -2,30 +2,22 @@ #include #include -// TODO: This device is largely a stub. It needs to be implemented! - namespace riscv::devices { SysconDevice::SysconDevice(System* system) : system(system) { } u32 SysconDevice::Peek(Address address) { - lucore::LogInfo("SYSCON({}) Peek @ 0x{:08x}", static_cast(this), address); return -1; } void SysconDevice::Poke(Address address, u32 value) { - lucore::LogInfo("SYSCON({}) Poke @ 0x{:08x}: 0x{:08x}", static_cast(this), address, - value); - /* - if(address == BASE_ADDRESS) { - if(value == 0x5555) - system->PowerOff(); - else if (value == 0x7777) - system->Reset(); - } - */ - return; + if(address == BASE_ADDRESS) { + if(value == 0x5555) + system->SysconPowerOff(); + else if(value == 0x7777) + system->SysconReboot(); + } } } // namespace riscv::devices diff --git a/native/projects/riscv/src/System.cpp b/native/projects/riscv/src/System.cpp index bdbd08e..a0c51b4 100644 --- a/native/projects/riscv/src/System.cpp +++ b/native/projects/riscv/src/System.cpp @@ -16,11 +16,15 @@ namespace riscv { system->cpu->Reset(); // attach everything into the bus - if(!system->bus->AttachDevice(system->cpu)) return nullptr; - if(!system->bus->AttachDevice(system->clnt)) return nullptr; - if(!system->bus->AttachDevice(system->syscon)) return nullptr; - if(!system->bus->AttachDevice(system->ram)) return nullptr; - + if(!system->bus->AttachDevice(system->cpu)) + return nullptr; + if(!system->bus->AttachDevice(system->clnt)) + return nullptr; + if(!system->bus->AttachDevice(system->syscon)) + return nullptr; + if(!system->bus->AttachDevice(system->ram)) + return nullptr; + return system; } @@ -36,4 +40,15 @@ namespace riscv { // Later: handling for invalid cases! } + void System::SysconPowerOff() { + if(OnPowerOff) + OnPowerOff(); + } + + void System::SysconReboot() { + if(OnReboot) + OnReboot(); + bus->GetCPU()->Reset(); + } + } // namespace riscv diff --git a/native/projects/riscv_test_harness/main.cpp b/native/projects/riscv_test_harness/main.cpp index b77fe23..1cb65fc 100644 --- a/native/projects/riscv_test_harness/main.cpp +++ b/native/projects/riscv_test_harness/main.cpp @@ -1,10 +1,9 @@ //! A test harness for testing if the riscv library actually works. -#include -#include - #include // I know, I know, but this is a test program. Yell later :) #include #include +#include +#include /// simple 16550 UART implementation struct SimpleUartDevice : public riscv::Bus::MmioDevice { @@ -16,10 +15,8 @@ struct SimpleUartDevice : public riscv::Bus::MmioDevice { u32 Peek(riscv::Address address) override { switch(address) { - case BASE_ADDRESS: - return 0x60; // active, but no keyboard input - case BASE_ADDRESS + 5: - return '\0'; + case BASE_ADDRESS: return 0x60; // active, but no keyboard input + case BASE_ADDRESS + 5: return '\0'; } return 0; @@ -28,8 +25,6 @@ struct SimpleUartDevice : public riscv::Bus::MmioDevice { void Poke(riscv::Address address, u32 value) override { if(address == BASE_ADDRESS) { char c = value & 0x000000ff; - //lucore::LogInfo("[UART] got data buffer poke of char {:02x} @ pc 0x{:08x}", c, bus->GetCPU()->pc); - //printf("%c", c); fputc(c, stderr); } } @@ -39,7 +34,7 @@ int main(int argc, char** argv) { lucore::LoggerAttachStdout(); LUCORE_CHECK(argc == 2, "this test harness expects one argument (the file to load into riscv memory and execute). got {} arguments", argc); - + // 128 KB of ram. Won't be enough to boot linux but should be good enough to test most baremetal apps auto system = riscv::System::Create(128 * 1024); LUCORE_CHECK(system, "could not create system for some reason."); @@ -50,15 +45,19 @@ int main(int argc, char** argv) { auto fp = std::fopen(argv[1], "rb"); LUCORE_CHECK(fp, "could not open file \"{}\"", argv[1]); - fseek(fp, 0, SEEK_END); - auto len = ftell(fp); - fseek(fp, 0, SEEK_SET); + std::fseek(fp, 0, SEEK_END); + auto len = std::ftell(fp); + std::fseek(fp, 0, SEEK_SET); - fread(system->ram->Raw(), 1, len, fp); - fclose(fp); + std::fread(system->ram->Raw(), 1, len, fp); + std::fclose(fp); - // Do the thing now! - while(true) { + // This allows the host program running under the test + // harness to tell us to shut down. + bool shouldExit = false; + system->OnPowerOff = [&shouldExit]() { shouldExit = true; }; + + while(!shouldExit) { system->Step(); } diff --git a/native/projects/riscv_test_harness/test/Makefile b/native/projects/riscv_test_harness/test/Makefile index 0cb1e33..33e4f65 100644 --- a/native/projects/riscv_test_harness/test/Makefile +++ b/native/projects/riscv_test_harness/test/Makefile @@ -7,26 +7,28 @@ PREFIX = $(TCPATH)/riscv32-unknown-elf CC = $(PREFIX)-gcc CXX = $(PREFIX)-g++ -CCFLAGS = -ffreestanding -fno-stack-protector -CCFLAGS += -static -static-libgcc -fdata-sections -ffunction-sections -CCFLAGS += -g -Os -march=rv32ima -mabi=ilp32 - -CXXFLAGS = $(CCFLAGS) -std=c++20 -fno-exceptions -fno-rtti - +ARCHFLAGS = -ffreestanding -fno-stack-protector -fdata-sections -ffunction-sections -march=rv32ima -mabi=ilp32 +CCFLAGS = -g -Os $(ARCHFLAGS) -std=c18 +CXXFLAGS = $(ARCHFLAGS) -g -Os -std=c++20 -fno-exceptions -fno-rtti LDFLAGS = -T binary.ld -nostdlib -Wl,--gc-sections -OBJS := start.o main.o +OBJS = start.o \ + main.o .PHONY: all test clean all: $(PROJECT).bin $(PROJECT).debug.txt +# this assumes the lcpu project build dir you're using is +# [lcpu repo root]/build test: $(PROJECT).bin $(PROJECT).debug.txt ../../../../build/projects/riscv_test_harness/rvtest $< clean: rm $(PROJECT).elf $(PROJECT).bin $(PROJECT).debug.txt $(OBJS) +# Link rules + $(PROJECT).elf: $(OBJS) $(CC) $(CCFLAGS) $(LDFLAGS) -o $@ $(OBJS) diff --git a/native/projects/riscv_test_harness/test/main.c b/native/projects/riscv_test_harness/test/main.c index d8e14dc..f47208e 100644 --- a/native/projects/riscv_test_harness/test/main.c +++ b/native/projects/riscv_test_harness/test/main.c @@ -10,6 +10,8 @@ uint32_t strlen(const char* str) { return c - str; } +#define SYSCON *(volatile uint32_t*)0x11100000 + #define UART_BASE 0x10000000 #define UART_DATA *(volatile uint32_t*)UART_BASE #define UART_STATUS UART_DATA @@ -28,16 +30,16 @@ static uint32_t value = 0; static uint16_t shortvalue = 0; static uint8_t bytevalue = 0; -#define COUNTER_TEST(var, max) \ - for(int i = 0; i < max; ++i) { \ +#define COUNTER_TEST(var, max) \ + for(int i = 0; i < max; ++i) { \ puts(#var " is (before modification): "); \ - putc("0123456789"[var]); \ - putc('\n'); \ - \ - var = i; \ + putc("0123456789"[var]); \ + putc('\n'); \ + \ + var = i; \ puts(#var " is (after modification): "); \ - putc("0123456789"[var]); \ - putc('\n'); \ + putc("0123456789"[var]); \ + putc('\n'); \ } void main() { @@ -49,6 +51,10 @@ void main() { COUNTER_TEST(bytevalue, 9); #endif + // Shut down the test harness once we're done testing. + puts("Tests done, shutting down test harness...\n"); + SYSCON = 0x5555; + // loop forever - for(;;); + // for(;;); }