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!
This commit is contained in:
parent
7af85f5601
commit
90e684e1e3
|
@ -16,6 +16,7 @@ AllowShortBlocksOnASingleLine: false
|
||||||
AllowShortFunctionsOnASingleLine: InlineOnly
|
AllowShortFunctionsOnASingleLine: InlineOnly
|
||||||
AllowShortIfStatementsOnASingleLine: Never
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
AllowShortLoopsOnASingleLine: false
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AllowShortCaseLabelsOnASingleLine: true
|
||||||
|
|
||||||
BinPackArguments: true
|
BinPackArguments: true
|
||||||
BinPackParameters: true
|
BinPackParameters: true
|
||||||
|
|
|
@ -84,8 +84,7 @@ namespace lcpu {
|
||||||
|
|
||||||
void SourceSink::OutputMessage(const lucore::Logger::MessageData& data) {
|
void SourceSink::OutputMessage(const lucore::Logger::MessageData& data) {
|
||||||
auto formatted =
|
auto formatted =
|
||||||
std::format("[LCPU Native/{}] [{}] {}", lucore::Logger::SeverityToString(data.severity),
|
std::format("[LCPU Native/{}] [{}] {}", lucore::Logger::SeverityToString(data.severity), data.time, std::vformat(data.format, data.args));
|
||||||
data.time, std::vformat(data.format, data.args));
|
|
||||||
|
|
||||||
tier0::Msg("%s\n", formatted.c_str());
|
tier0::Msg("%s\n", formatted.c_str());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
#include <GarrysMod/Lua/Interface.h>
|
#include <GarrysMod/Lua/Interface.h>
|
||||||
|
|
||||||
#include "SourceSink.hpp"
|
|
||||||
|
|
||||||
#include <lucore/Assert.hpp>
|
#include <lucore/Assert.hpp>
|
||||||
|
|
||||||
LUA_FUNCTION(lcpu_native_test) {
|
#include "SourceSink.hpp"
|
||||||
|
|
||||||
|
LUA_FUNCTION(lcpu_native_test) {
|
||||||
}
|
}
|
||||||
|
|
||||||
GMOD_MODULE_OPEN() {
|
GMOD_MODULE_OPEN() {
|
||||||
|
@ -13,7 +12,6 @@ GMOD_MODULE_OPEN() {
|
||||||
|
|
||||||
lucore::LogInfo("LCPU Native Module loading");
|
lucore::LogInfo("LCPU Native Module loading");
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,7 @@ namespace lucore {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
#define LUCORE_ASSERT(expr, fmt, ...) \
|
#define LUCORE_ASSERT(expr, fmt, ...) \
|
||||||
if(!(expr)) [[unlikely]] { \
|
if(!(expr)) [[unlikely]] { \
|
||||||
auto msg = std::format("Assertion \"{}\" @ {}:{} failed with message: {}", #expr, \
|
auto msg = std::format("Assertion \"{}\" @ {}:{} failed with message: {}", #expr, __FILE__, __LINE__, std::format(fmt, ##__VA_ARGS__)); \
|
||||||
__FILE__, __LINE__, std::format(fmt, ##__VA_ARGS__)); \
|
|
||||||
::lucore::ExitMsg(msg.c_str()); \
|
::lucore::ExitMsg(msg.c_str()); \
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -22,7 +21,6 @@ namespace lucore {
|
||||||
// CHECK() is always active, even in release builds
|
// CHECK() is always active, even in release builds
|
||||||
#define LUCORE_CHECK(expr, fmt, ...) \
|
#define LUCORE_CHECK(expr, fmt, ...) \
|
||||||
if(!(expr)) [[unlikely]] { \
|
if(!(expr)) [[unlikely]] { \
|
||||||
auto msg = std::format("Check \"{}\" @ {}:{} failed with message: {}", #expr, __FILE__, \
|
auto msg = std::format("Check \"{}\" @ {}:{} failed with message: {}", #expr, __FILE__, __LINE__, std::format(fmt, ##__VA_ARGS__)); \
|
||||||
__LINE__, std::format(fmt, ##__VA_ARGS__)); \
|
|
||||||
::lucore::ExitMsg(msg.c_str()); \
|
::lucore::ExitMsg(msg.c_str()); \
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,20 +20,16 @@ namespace lucore::detail {
|
||||||
/// Do *not* give this class invalid references.
|
/// Do *not* give this class invalid references.
|
||||||
template <class T>
|
template <class T>
|
||||||
struct OptionalRef {
|
struct OptionalRef {
|
||||||
constexpr OptionalRef() : ptr(nullptr) {
|
constexpr OptionalRef() : ptr(nullptr) {}
|
||||||
}
|
|
||||||
|
|
||||||
// trigger explicit null construction
|
// 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<U>
|
// polymorphic downconstruction from another OptionalRef<U>
|
||||||
template <class U>
|
template <class U>
|
||||||
constexpr OptionalRef(const OptionalRef<U>& other) : ptr(&other.ptr) {
|
constexpr OptionalRef(const OptionalRef<U>& other) : ptr(&other.ptr) {}
|
||||||
}
|
|
||||||
|
|
||||||
constexpr T& Value() const {
|
constexpr T& Value() const {
|
||||||
// this is a CHECK() since allowing unchecked access in release builds is probably a
|
// this is a CHECK() since allowing unchecked access in release builds is probably a
|
||||||
|
@ -42,22 +38,14 @@ namespace lucore::detail {
|
||||||
return *ptr;
|
return *ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool HasValue() const {
|
constexpr bool HasValue() const { return ptr != nullptr; }
|
||||||
return ptr != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr T& operator*() const {
|
constexpr T& operator*() const { return Value(); }
|
||||||
return Value();
|
|
||||||
}
|
|
||||||
|
|
||||||
// unchecked access: DO NOT use this without checking beforehand
|
// unchecked access: DO NOT use this without checking beforehand
|
||||||
constexpr T* operator->() const {
|
constexpr T* operator->() const { return ptr; }
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr operator bool() const {
|
constexpr operator bool() const { return HasValue(); }
|
||||||
return HasValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T* ptr {};
|
T* ptr {};
|
||||||
|
|
|
@ -14,4 +14,4 @@ namespace lucore {
|
||||||
/// Attach the stdout logger sink to the global Lucore logger.
|
/// Attach the stdout logger sink to the global Lucore logger.
|
||||||
void LoggerAttachStdout();
|
void LoggerAttachStdout();
|
||||||
|
|
||||||
}
|
} // namespace lucore
|
||||||
|
|
|
@ -23,14 +23,10 @@ namespace lucore {
|
||||||
if(severity < logLevel)
|
if(severity < logLevel)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MessageData data { .time = std::chrono::system_clock::now(),
|
MessageData data { .time = std::chrono::system_clock::now(), .severity = severity, .format = format, .args = args };
|
||||||
.severity = severity,
|
|
||||||
.format = format,
|
|
||||||
.args = args };
|
|
||||||
|
|
||||||
for(auto sink : sinks)
|
for(auto sink : sinks)
|
||||||
sink->OutputMessage(data);
|
sink->OutputMessage(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace lucore
|
} // namespace lucore
|
||||||
|
|
|
@ -31,8 +31,7 @@ namespace lucore {
|
||||||
};
|
};
|
||||||
|
|
||||||
auto it = FputcIterator(data.severity < Logger::MessageSeverity::Error ? stdout : stderr);
|
auto it = FputcIterator(data.severity < Logger::MessageSeverity::Error ? stdout : stderr);
|
||||||
std::format_to(it, "[Lucore/{}] [{}] {}\n", Logger::SeverityToString(data.severity),
|
std::format_to(it, "[Lucore/{}] [{}] {}\n", Logger::SeverityToString(data.severity), data.time, std::vformat(data.format, data.args));
|
||||||
data.time, std::vformat(data.format, data.args));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoggerAttachStdout() {
|
void LoggerAttachStdout() {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <lucore/Assert.hpp>
|
#include <lucore/Assert.hpp>
|
||||||
#include <riscv/Types.hpp>
|
|
||||||
#include <riscv/CPUTypes.hpp>
|
#include <riscv/CPUTypes.hpp>
|
||||||
|
#include <riscv/Types.hpp>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -28,9 +28,7 @@ namespace riscv {
|
||||||
|
|
||||||
virtual ~Device() = default;
|
virtual ~Device() = default;
|
||||||
|
|
||||||
virtual void Attached(Bus* bus) {
|
virtual void Attached(Bus* bus) { this->bus = bus; }
|
||||||
this->bus = bus;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual BasicType Type() const { return BasicType::Device; }
|
virtual BasicType Type() const { return BasicType::Device; }
|
||||||
|
|
||||||
|
@ -63,6 +61,7 @@ namespace riscv {
|
||||||
LUCORE_ASSERT(IsA<T>(), "Upcast failure: this is not a T");
|
LUCORE_ASSERT(IsA<T>(), "Upcast failure: this is not a T");
|
||||||
return static_cast<T>(this);
|
return static_cast<T>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// The bus this device is attached to.
|
/// The bus this device is attached to.
|
||||||
Bus* bus;
|
Bus* bus;
|
||||||
|
@ -139,7 +138,6 @@ namespace riscv {
|
||||||
CPU* GetCPU() { return cpu; }
|
CPU* GetCPU() { return cpu; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// TODO: version which takes Device::BasicType
|
// TODO: version which takes Device::BasicType
|
||||||
Bus::Device* FindDeviceForAddress(Address address) const;
|
Bus::Device* FindDeviceForAddress(Address address) const;
|
||||||
|
|
||||||
|
|
|
@ -16,14 +16,11 @@ namespace riscv {
|
||||||
|
|
||||||
void TimerInterrupt();
|
void TimerInterrupt();
|
||||||
|
|
||||||
constexpr CPU() {
|
constexpr CPU() { Reset(); }
|
||||||
Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr void Reset() {
|
constexpr void Reset() {
|
||||||
// Initalize some state. We're cool like that :)
|
// Initalize some state. We're cool like that :)
|
||||||
pc = 0x80000000;
|
pc = 0x80000000;
|
||||||
gpr[Gpr::A0] = 0x0; // HART id
|
|
||||||
extraflags |= 3; // Start in Machine mode
|
extraflags |= 3; // Start in Machine mode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,40 +61,8 @@ namespace riscv {
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr std::string_view RegName(Gpr gpr) {
|
constexpr std::string_view RegName(Gpr gpr) {
|
||||||
std::string_view table[] = {
|
std::string_view table[] = { "zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5",
|
||||||
"zero",
|
"a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6" };
|
||||||
"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<usize>(gpr)];
|
return table[static_cast<usize>(gpr)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@ namespace riscv::devices {
|
||||||
bool Clocked() const override { return true; }
|
bool Clocked() const override { return true; }
|
||||||
void Clock() override;
|
void Clock() override;
|
||||||
|
|
||||||
|
|
||||||
u32 Peek(Address address) override;
|
u32 Peek(Address address) override;
|
||||||
void Poke(Address address, u32 value) override;
|
void Poke(Address address, u32 value) override;
|
||||||
|
|
||||||
|
@ -27,4 +26,4 @@ namespace riscv::devices {
|
||||||
u32 timerMatchLow;
|
u32 timerMatchLow;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // namespace riscv::devices
|
||||||
|
|
|
@ -14,9 +14,7 @@ namespace riscv::devices {
|
||||||
Address Base() const override;
|
Address Base() const override;
|
||||||
Address Size() const override;
|
Address Size() const override;
|
||||||
|
|
||||||
u8* Raw() const {
|
u8* Raw() const { return memory; }
|
||||||
return memory;
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 PeekByte(Address address) override;
|
u8 PeekByte(Address address) override;
|
||||||
u16 PeekShort(Address address) override;
|
u16 PeekShort(Address address) override;
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
#include <riscv/Bus.hpp>
|
#include <riscv/Bus.hpp>
|
||||||
|
|
||||||
namespace riscv { struct System; }
|
namespace riscv {
|
||||||
|
struct System;
|
||||||
|
}
|
||||||
|
|
||||||
namespace riscv::devices {
|
namespace riscv::devices {
|
||||||
/// RISC-V SYSCON device. This will later talk to the system to tell it things.
|
/// 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;
|
u32 Peek(Address address) override;
|
||||||
void Poke(Address address, u32 value) override;
|
void Poke(Address address, u32 value) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
System* system;
|
System* system;
|
||||||
};
|
};
|
||||||
}
|
} // namespace riscv::devices
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <functional> // use function_ref when we get c++23?
|
||||||
#include <riscv/Bus.hpp>
|
#include <riscv/Bus.hpp>
|
||||||
#include <riscv/CPU.hpp>
|
#include <riscv/CPU.hpp>
|
||||||
#include <riscv/Devices/ClntDevice.hpp>
|
#include <riscv/Devices/ClntDevice.hpp>
|
||||||
|
@ -16,7 +17,8 @@ namespace riscv {
|
||||||
|
|
||||||
void Step();
|
void Step();
|
||||||
|
|
||||||
// TODO: callbacks for SYSCON PowerOff and Reboot.
|
std::function<void()> OnPowerOff;
|
||||||
|
std::function<void()> OnReboot;
|
||||||
|
|
||||||
Bus* bus;
|
Bus* bus;
|
||||||
|
|
||||||
|
@ -27,6 +29,11 @@ namespace riscv {
|
||||||
devices::ClntDevice* clnt;
|
devices::ClntDevice* clnt;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend struct devices::SysconDevice;
|
||||||
|
|
||||||
|
void SysconPowerOff();
|
||||||
|
void SysconReboot();
|
||||||
|
|
||||||
System() = default;
|
System() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,4 @@ namespace riscv {
|
||||||
/// A type that can repressent address space or address space offsets.
|
/// A type that can repressent address space or address space offsets.
|
||||||
using Address = u32;
|
using Address = u32;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace riscv
|
} // namespace riscv
|
||||||
|
|
|
@ -54,6 +54,7 @@ namespace riscv {
|
||||||
|
|
||||||
if((pc & 3)) {
|
if((pc & 3)) {
|
||||||
Trap(TrapCode::InstructionAddressMisaligned);
|
Trap(TrapCode::InstructionAddressMisaligned);
|
||||||
|
rval = pc;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
auto ir = bus->PeekWord(pc);
|
auto ir = bus->PeekWord(pc);
|
||||||
|
@ -136,8 +137,7 @@ namespace riscv {
|
||||||
if((u32)rs1 >= (u32)rs2)
|
if((u32)rs1 >= (u32)rs2)
|
||||||
pc = immm4;
|
pc = immm4;
|
||||||
break;
|
break;
|
||||||
default:
|
default: Trap(TrapCode::IllegalInstruction);
|
||||||
Trap(TrapCode::IllegalInstruction);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -150,31 +150,20 @@ namespace riscv {
|
||||||
|
|
||||||
switch((ir >> 12) & 0x7) {
|
switch((ir >> 12) & 0x7) {
|
||||||
// LB, LH, LW, LBU, LHU
|
// LB, LH, LW, LBU, LHU
|
||||||
case 0:
|
case 0: rval = (i8)bus->PeekByte(rsval); break;
|
||||||
rval = (i8)bus->PeekByte(rsval);
|
case 1: rval = (i16)bus->PeekShort(rsval); break;
|
||||||
break;
|
case 2: rval = bus->PeekWord(rsval); break;
|
||||||
case 1:
|
case 4: rval = bus->PeekByte(rsval); break;
|
||||||
rval = (i16)bus->PeekShort(rsval);
|
case 5: rval = bus->PeekShort(rsval); break;
|
||||||
break;
|
default: Trap(TrapCode::IllegalInstruction); 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) {
|
if(trapped) {
|
||||||
rval = rsval; // + RamImageOffset;
|
rval = rsval;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x23: { // store
|
case 0x23: { // store
|
||||||
u32 rs1 = gpr[(ir >> 15) & 0x1f];
|
u32 rs1 = gpr[(ir >> 15) & 0x1f];
|
||||||
u32 rs2 = gpr[(ir >> 20) & 0x1f];
|
u32 rs2 = gpr[(ir >> 20) & 0x1f];
|
||||||
|
@ -186,22 +175,14 @@ namespace riscv {
|
||||||
|
|
||||||
switch((ir >> 12) & 0x7) {
|
switch((ir >> 12) & 0x7) {
|
||||||
// SB, SH, SW
|
// SB, SH, SW
|
||||||
case 0:
|
case 0: bus->PokeByte(addy, rs2); break;
|
||||||
bus->PokeByte(addy, rs2);
|
case 1: bus->PokeShort(addy, rs2); break;
|
||||||
break;
|
case 2: bus->PokeWord(addy, rs2); break;
|
||||||
case 1:
|
default: Trap(TrapCode::IllegalInstruction);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(trapped) {
|
if(trapped) {
|
||||||
rval = addy; // + RamImageOffset;
|
rval = addy;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -217,19 +198,10 @@ namespace riscv {
|
||||||
if(is_reg && (ir & 0x02000000)) {
|
if(is_reg && (ir & 0x02000000)) {
|
||||||
switch((ir >> 12) & 7) // 0x02000000 = RV32M
|
switch((ir >> 12) & 7) // 0x02000000 = RV32M
|
||||||
{
|
{
|
||||||
case 0:
|
case 0: rval = rs1 * rs2; break; // MUL
|
||||||
rval = rs1 * rs2;
|
case 1: rval = ((i64)((i32)rs1) * (i64)((i32)rs2)) >> 32; break; // MULH
|
||||||
break; // MUL
|
case 2: rval = ((i64)((i32)rs1) * (u64)rs2) >> 32; break; // MULHSU
|
||||||
|
case 3: rval = ((u64)rs1 * (u64)rs2) >> 32; break; // MULHU
|
||||||
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:
|
case 4:
|
||||||
if(rs2 == 0)
|
if(rs2 == 0)
|
||||||
|
@ -259,30 +231,14 @@ namespace riscv {
|
||||||
} else {
|
} else {
|
||||||
// These could be either op-immediate or op commands. Be careful.
|
// These could be either op-immediate or op commands. Be careful.
|
||||||
switch((ir >> 12) & 7) {
|
switch((ir >> 12) & 7) {
|
||||||
case 0:
|
case 0: rval = (is_reg && (ir & 0x40000000)) ? (rs1 - rs2) : (rs1 + rs2); break;
|
||||||
rval = (is_reg && (ir & 0x40000000)) ? (rs1 - rs2) : (rs1 + rs2);
|
case 1: rval = rs1 << (rs2 & 0x1F); break;
|
||||||
break;
|
case 2: rval = (i32)rs1 < (i32)rs2; break;
|
||||||
case 1:
|
case 3: rval = rs1 < rs2; break;
|
||||||
rval = rs1 << (rs2 & 0x1F);
|
case 4: rval = rs1 ^ rs2; break;
|
||||||
break;
|
case 5: rval = (ir & 0x40000000) ? (((i32)rs1) >> (rs2 & 0x1F)) : (rs1 >> (rs2 & 0x1F)); break;
|
||||||
case 2:
|
case 6: rval = rs1 | rs2; break;
|
||||||
rval = (i32)rs1 < (i32)rs2;
|
case 7: rval = rs1 & rs2; break;
|
||||||
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;
|
break;
|
||||||
|
@ -305,36 +261,16 @@ namespace riscv {
|
||||||
// https://raw.githubusercontent.com/riscv/virtual-memory/main/specs/663-Svpbmt.pdf
|
// https://raw.githubusercontent.com/riscv/virtual-memory/main/specs/663-Svpbmt.pdf
|
||||||
// Generally, support for Zicsr
|
// Generally, support for Zicsr
|
||||||
switch(csrno) {
|
switch(csrno) {
|
||||||
case 0x340:
|
case 0x340: rval = mscratch; break;
|
||||||
rval = mscratch;
|
case 0x305: rval = mtvec; break;
|
||||||
break;
|
case 0x304: rval = mie; break;
|
||||||
case 0x305:
|
case 0xC00: rval = cycle; break;
|
||||||
rval = mtvec;
|
case 0x344: rval = mip; break;
|
||||||
break;
|
case 0x341: rval = mepc; break;
|
||||||
case 0x304:
|
case 0x300: rval = mstatus; break; // mstatus
|
||||||
rval = mie;
|
case 0x342: rval = mcause; break;
|
||||||
break;
|
case 0x343: rval = mtval; break;
|
||||||
case 0xC00:
|
case 0xf11: rval = 0xff0ff0ff; break; // mvendorid
|
||||||
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:
|
case 0x301:
|
||||||
rval = 0x40401101;
|
rval = 0x40401101;
|
||||||
break; // misa (XLEN=32, IMA+X)
|
break; // misa (XLEN=32, IMA+X)
|
||||||
|
@ -349,51 +285,23 @@ namespace riscv {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(microop) {
|
switch(microop) {
|
||||||
case 1:
|
case 1: writeval = rs1; break; // CSRRW
|
||||||
writeval = rs1;
|
case 2: writeval = rval | rs1; break; // CSRRS
|
||||||
break; // CSRRW
|
case 3: writeval = rval & ~rs1; break; // CSRRC
|
||||||
case 2:
|
case 5: writeval = rs1imm; break; // CSRRWI
|
||||||
writeval = rval | rs1;
|
case 6: writeval = rval | rs1imm; break; // CSRRSI
|
||||||
break; // CSRRS
|
case 7: writeval = rval & ~rs1imm; break; // CSRRCI
|
||||||
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) {
|
switch(csrno) {
|
||||||
case 0x340:
|
case 0x340: mscratch = writeval; break;
|
||||||
mscratch = writeval;
|
case 0x305: mtvec = writeval; break;
|
||||||
break;
|
case 0x304: mie = writeval; break;
|
||||||
case 0x305:
|
case 0x344: mip = writeval; break;
|
||||||
mtvec = writeval;
|
case 0x341: mepc = writeval; break;
|
||||||
break;
|
case 0x300: mstatus = writeval; break; // mstatus
|
||||||
case 0x304:
|
case 0x342: mcause = writeval; break;
|
||||||
mie = writeval;
|
case 0x343: mtval = writeval; break;
|
||||||
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 0x3a0: break; //pmpcfg0
|
||||||
// case 0x3B0: break; //pmpaddr0
|
// case 0x3B0: break; //pmpaddr0
|
||||||
// case 0xf11: break; //mvendorid
|
// case 0xf11: break; //mvendorid
|
||||||
|
@ -434,15 +342,14 @@ namespace riscv {
|
||||||
case 1: // breakpoint
|
case 1: // breakpoint
|
||||||
Trap(TrapCode::Breakpoint);
|
Trap(TrapCode::Breakpoint);
|
||||||
break;
|
break;
|
||||||
default:
|
default: Trap(TrapCode::IllegalInstruction); break;
|
||||||
Trap(TrapCode::IllegalInstruction);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
Trap(TrapCode::IllegalInstruction);
|
Trap(TrapCode::IllegalInstruction);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x2f: // RV32A (0b00101111)
|
case 0x2f: // RV32A (0b00101111)
|
||||||
{
|
{
|
||||||
u32 rs1 = gpr[(ir >> 15) & 0x1f];
|
u32 rs1 = gpr[(ir >> 15) & 0x1f];
|
||||||
|
@ -451,7 +358,7 @@ namespace riscv {
|
||||||
|
|
||||||
rval = bus->PeekWord(rs1);
|
rval = bus->PeekWord(rs1);
|
||||||
if(trapped) {
|
if(trapped) {
|
||||||
rval = rs1; // + RamImageOffset;
|
rval = rs1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,32 +373,15 @@ namespace riscv {
|
||||||
rval = (extraflags >> 3 != (rs1 & 0x1fffffff)); // Validate that our reservation slot is OK.
|
rval = (extraflags >> 3 != (rs1 & 0x1fffffff)); // Validate that our reservation slot is OK.
|
||||||
dowrite = !rval; // Only write if slot is valid.
|
dowrite = !rval; // Only write if slot is valid.
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1: break; // AMOSWAP.W (0b00001)
|
||||||
break; // AMOSWAP.W (0b00001)
|
case 0: rs2 += rval; break; // AMOADD.W (0b00000)
|
||||||
case 0:
|
case 4: rs2 ^= rval; break; // AMOXOR.W (0b00100)
|
||||||
rs2 += rval;
|
case 12: rs2 &= rval; break; // AMOAND.W (0b01100)
|
||||||
break; // AMOADD.W (0b00000)
|
case 8: rs2 |= rval; break; // AMOOR.W (0b01000)
|
||||||
case 4:
|
case 16: rs2 = ((i32)rs2 < (i32)rval) ? rs2 : rval; break; // AMOMIN.W (0b10000)
|
||||||
rs2 ^= rval;
|
case 20: rs2 = ((i32)rs2 > (i32)rval) ? rs2 : rval; break; // AMOMAX.W (0b10100)
|
||||||
break; // AMOXOR.W (0b00100)
|
case 24: rs2 = (rs2 < rval) ? rs2 : rval; break; // AMOMINU.W (0b11000)
|
||||||
case 12:
|
case 28: rs2 = (rs2 > rval) ? rs2 : rval; break; // AMOMAXU.W (0b11100)
|
||||||
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:
|
default:
|
||||||
Trap(TrapCode::IllegalInstruction);
|
Trap(TrapCode::IllegalInstruction);
|
||||||
dowrite = 0;
|
dowrite = 0;
|
||||||
|
@ -501,8 +391,7 @@ namespace riscv {
|
||||||
bus->PokeWord(rs1, rs2);
|
bus->PokeWord(rs1, rs2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default: Trap(TrapCode::IllegalInstruction);
|
||||||
Trap(TrapCode::IllegalInstruction);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
#include <lucore/Logger.hpp>
|
#include <lucore/Logger.hpp>
|
||||||
#include <riscv/Devices/ClntDevice.hpp>
|
|
||||||
#include <riscv/CPU.hpp>
|
#include <riscv/CPU.hpp>
|
||||||
|
#include <riscv/Devices/ClntDevice.hpp>
|
||||||
|
|
||||||
namespace riscv::devices {
|
namespace riscv::devices {
|
||||||
|
|
||||||
constexpr static Address MSIP_ADDRESS = ClntDevice::BASE_ADDRESS,
|
constexpr static Address MSIP_ADDRESS = ClntDevice::BASE_ADDRESS, MATCHL_ADDRESS = ClntDevice::BASE_ADDRESS + 0x4000,
|
||||||
MATCHL_ADDRESS = ClntDevice::BASE_ADDRESS + 0x4000,
|
MATCHH_ADDRESS = ClntDevice::BASE_ADDRESS + 0x4004, TIMERL_ADDRESS = ClntDevice::BASE_ADDRESS + 0xbff8,
|
||||||
MATCHH_ADDRESS = ClntDevice::BASE_ADDRESS + 0x4004,
|
|
||||||
TIMERL_ADDRESS = ClntDevice::BASE_ADDRESS + 0xbff8,
|
|
||||||
TIMERH_ADDRESS = ClntDevice::BASE_ADDRESS + 0xbffc;
|
TIMERH_ADDRESS = ClntDevice::BASE_ADDRESS + 0xbffc;
|
||||||
|
|
||||||
void ClntDevice::Clock() {
|
void ClntDevice::Clock() {
|
||||||
|
@ -20,8 +18,7 @@ namespace riscv::devices {
|
||||||
timerCountHigh++;
|
timerCountHigh++;
|
||||||
timerCountLow = new_timer;
|
timerCountLow = new_timer;
|
||||||
|
|
||||||
if((timerCountHigh > timerMatchHigh ||
|
if((timerCountHigh > timerMatchHigh || (timerCountHigh == timerMatchHigh && timerCountLow == timerMatchLow)) &&
|
||||||
(timerCountHigh == timerMatchHigh && timerCountLow == timerMatchLow)) &&
|
|
||||||
(timerMatchHigh || timerMatchLow)) {
|
(timerMatchHigh || timerMatchLow)) {
|
||||||
// Fire the CLNT timer interrupt
|
// Fire the CLNT timer interrupt
|
||||||
bus->GetCPU()->TimerInterrupt();
|
bus->GetCPU()->TimerInterrupt();
|
||||||
|
@ -30,38 +27,28 @@ namespace riscv::devices {
|
||||||
|
|
||||||
u32 ClntDevice::Peek(Address address) {
|
u32 ClntDevice::Peek(Address address) {
|
||||||
switch(address) {
|
switch(address) {
|
||||||
case TIMERL_ADDRESS:
|
case TIMERL_ADDRESS: return timerCountLow;
|
||||||
return timerCountLow;
|
|
||||||
|
|
||||||
case TIMERH_ADDRESS:
|
case TIMERH_ADDRESS: return timerCountHigh;
|
||||||
return timerCountHigh;
|
|
||||||
|
|
||||||
case MATCHL_ADDRESS:
|
case MATCHL_ADDRESS: return timerMatchLow;
|
||||||
return timerMatchLow;
|
|
||||||
|
|
||||||
case MATCHH_ADDRESS:
|
case MATCHH_ADDRESS: return timerMatchHigh;
|
||||||
return timerMatchHigh;
|
|
||||||
|
|
||||||
default:
|
default: return 0x0;
|
||||||
return 0x0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClntDevice::Poke(Address address, u32 value) {
|
void ClntDevice::Poke(Address address, u32 value) {
|
||||||
switch(address) {
|
switch(address) {
|
||||||
case MATCHL_ADDRESS:
|
case MATCHL_ADDRESS: timerMatchLow = value; break;
|
||||||
timerMatchLow = value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MATCHH_ADDRESS:
|
case MATCHH_ADDRESS:
|
||||||
timerMatchHigh = value;
|
timerMatchHigh = value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// ?
|
// ?
|
||||||
default:
|
default: lucore::LogInfo("CLNT({}) unhandled poke @ 0x{:08x} : 0x{:08x}", static_cast<void*>(this), address, value); break;
|
||||||
lucore::LogInfo("CLNT({}) unhandled poke @ 0x{:08x} : 0x{:08x}",
|
|
||||||
static_cast<void*>(this), address, value);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,7 @@ namespace riscv::devices {
|
||||||
|
|
||||||
RamDevice::RamDevice(Address base, Address size) : memoryBase(base), memorySize(size) {
|
RamDevice::RamDevice(Address base, Address size) : memoryBase(base), memorySize(size) {
|
||||||
memory = new u8[size];
|
memory = new u8[size];
|
||||||
LUCORE_CHECK(memory, "Could not allocate buffer for memory device with size 0x{:08x}.",
|
LUCORE_CHECK(memory, "Could not allocate buffer for memory device with size 0x{:08x}.", size);
|
||||||
size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RamDevice::~RamDevice() {
|
RamDevice::~RamDevice() {
|
||||||
|
|
|
@ -2,30 +2,22 @@
|
||||||
#include <riscv/Devices/SysconDevice.hpp>
|
#include <riscv/Devices/SysconDevice.hpp>
|
||||||
#include <riscv/System.hpp>
|
#include <riscv/System.hpp>
|
||||||
|
|
||||||
// TODO: This device is largely a stub. It needs to be implemented!
|
|
||||||
|
|
||||||
namespace riscv::devices {
|
namespace riscv::devices {
|
||||||
|
|
||||||
SysconDevice::SysconDevice(System* system) : system(system) {
|
SysconDevice::SysconDevice(System* system) : system(system) {
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 SysconDevice::Peek(Address address) {
|
u32 SysconDevice::Peek(Address address) {
|
||||||
lucore::LogInfo("SYSCON({}) Peek @ 0x{:08x}", static_cast<void*>(this), address);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysconDevice::Poke(Address address, u32 value) {
|
void SysconDevice::Poke(Address address, u32 value) {
|
||||||
lucore::LogInfo("SYSCON({}) Poke @ 0x{:08x}: 0x{:08x}", static_cast<void*>(this), address,
|
|
||||||
value);
|
|
||||||
/*
|
|
||||||
if(address == BASE_ADDRESS) {
|
if(address == BASE_ADDRESS) {
|
||||||
if(value == 0x5555)
|
if(value == 0x5555)
|
||||||
system->PowerOff();
|
system->SysconPowerOff();
|
||||||
else if(value == 0x7777)
|
else if(value == 0x7777)
|
||||||
system->Reset();
|
system->SysconReboot();
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace riscv::devices
|
} // namespace riscv::devices
|
||||||
|
|
|
@ -16,10 +16,14 @@ namespace riscv {
|
||||||
system->cpu->Reset();
|
system->cpu->Reset();
|
||||||
|
|
||||||
// attach everything into the bus
|
// attach everything into the bus
|
||||||
if(!system->bus->AttachDevice(system->cpu)) return nullptr;
|
if(!system->bus->AttachDevice(system->cpu))
|
||||||
if(!system->bus->AttachDevice(system->clnt)) return nullptr;
|
return nullptr;
|
||||||
if(!system->bus->AttachDevice(system->syscon)) return nullptr;
|
if(!system->bus->AttachDevice(system->clnt))
|
||||||
if(!system->bus->AttachDevice(system->ram)) return nullptr;
|
return nullptr;
|
||||||
|
if(!system->bus->AttachDevice(system->syscon))
|
||||||
|
return nullptr;
|
||||||
|
if(!system->bus->AttachDevice(system->ram))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
return system;
|
return system;
|
||||||
}
|
}
|
||||||
|
@ -36,4 +40,15 @@ namespace riscv {
|
||||||
// Later: handling for invalid cases!
|
// Later: handling for invalid cases!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void System::SysconPowerOff() {
|
||||||
|
if(OnPowerOff)
|
||||||
|
OnPowerOff();
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::SysconReboot() {
|
||||||
|
if(OnReboot)
|
||||||
|
OnReboot();
|
||||||
|
bus->GetCPU()->Reset();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace riscv
|
} // namespace riscv
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
//! A test harness for testing if the riscv library actually works.
|
//! A test harness for testing if the riscv library actually works.
|
||||||
#include <riscv/System.hpp>
|
|
||||||
#include <lucore/StdoutSink.hpp>
|
|
||||||
|
|
||||||
#include <cstdio> // I know, I know, but this is a test program. Yell later :)
|
#include <cstdio> // I know, I know, but this is a test program. Yell later :)
|
||||||
#include <lucore/Assert.hpp>
|
#include <lucore/Assert.hpp>
|
||||||
#include <lucore/Logger.hpp>
|
#include <lucore/Logger.hpp>
|
||||||
|
#include <lucore/StdoutSink.hpp>
|
||||||
|
#include <riscv/System.hpp>
|
||||||
|
|
||||||
/// simple 16550 UART implementation
|
/// simple 16550 UART implementation
|
||||||
struct SimpleUartDevice : public riscv::Bus::MmioDevice {
|
struct SimpleUartDevice : public riscv::Bus::MmioDevice {
|
||||||
|
@ -16,10 +15,8 @@ struct SimpleUartDevice : public riscv::Bus::MmioDevice {
|
||||||
|
|
||||||
u32 Peek(riscv::Address address) override {
|
u32 Peek(riscv::Address address) override {
|
||||||
switch(address) {
|
switch(address) {
|
||||||
case BASE_ADDRESS:
|
case BASE_ADDRESS: return 0x60; // active, but no keyboard input
|
||||||
return 0x60; // active, but no keyboard input
|
case BASE_ADDRESS + 5: return '\0';
|
||||||
case BASE_ADDRESS + 5:
|
|
||||||
return '\0';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -28,8 +25,6 @@ struct SimpleUartDevice : public riscv::Bus::MmioDevice {
|
||||||
void Poke(riscv::Address address, u32 value) override {
|
void Poke(riscv::Address address, u32 value) override {
|
||||||
if(address == BASE_ADDRESS) {
|
if(address == BASE_ADDRESS) {
|
||||||
char c = value & 0x000000ff;
|
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);
|
fputc(c, stderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,15 +45,19 @@ int main(int argc, char** argv) {
|
||||||
auto fp = std::fopen(argv[1], "rb");
|
auto fp = std::fopen(argv[1], "rb");
|
||||||
LUCORE_CHECK(fp, "could not open file \"{}\"", argv[1]);
|
LUCORE_CHECK(fp, "could not open file \"{}\"", argv[1]);
|
||||||
|
|
||||||
fseek(fp, 0, SEEK_END);
|
std::fseek(fp, 0, SEEK_END);
|
||||||
auto len = ftell(fp);
|
auto len = std::ftell(fp);
|
||||||
fseek(fp, 0, SEEK_SET);
|
std::fseek(fp, 0, SEEK_SET);
|
||||||
|
|
||||||
fread(system->ram->Raw(), 1, len, fp);
|
std::fread(system->ram->Raw(), 1, len, fp);
|
||||||
fclose(fp);
|
std::fclose(fp);
|
||||||
|
|
||||||
// Do the thing now!
|
// This allows the host program running under the test
|
||||||
while(true) {
|
// harness to tell us to shut down.
|
||||||
|
bool shouldExit = false;
|
||||||
|
system->OnPowerOff = [&shouldExit]() { shouldExit = true; };
|
||||||
|
|
||||||
|
while(!shouldExit) {
|
||||||
system->Step();
|
system->Step();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,26 +7,28 @@ PREFIX = $(TCPATH)/riscv32-unknown-elf
|
||||||
CC = $(PREFIX)-gcc
|
CC = $(PREFIX)-gcc
|
||||||
CXX = $(PREFIX)-g++
|
CXX = $(PREFIX)-g++
|
||||||
|
|
||||||
CCFLAGS = -ffreestanding -fno-stack-protector
|
ARCHFLAGS = -ffreestanding -fno-stack-protector -fdata-sections -ffunction-sections -march=rv32ima -mabi=ilp32
|
||||||
CCFLAGS += -static -static-libgcc -fdata-sections -ffunction-sections
|
CCFLAGS = -g -Os $(ARCHFLAGS) -std=c18
|
||||||
CCFLAGS += -g -Os -march=rv32ima -mabi=ilp32
|
CXXFLAGS = $(ARCHFLAGS) -g -Os -std=c++20 -fno-exceptions -fno-rtti
|
||||||
|
|
||||||
CXXFLAGS = $(CCFLAGS) -std=c++20 -fno-exceptions -fno-rtti
|
|
||||||
|
|
||||||
LDFLAGS = -T binary.ld -nostdlib -Wl,--gc-sections
|
LDFLAGS = -T binary.ld -nostdlib -Wl,--gc-sections
|
||||||
|
|
||||||
OBJS := start.o main.o
|
OBJS = start.o \
|
||||||
|
main.o
|
||||||
|
|
||||||
.PHONY: all test clean
|
.PHONY: all test clean
|
||||||
|
|
||||||
all: $(PROJECT).bin $(PROJECT).debug.txt
|
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
|
test: $(PROJECT).bin $(PROJECT).debug.txt
|
||||||
../../../../build/projects/riscv_test_harness/rvtest $<
|
../../../../build/projects/riscv_test_harness/rvtest $<
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm $(PROJECT).elf $(PROJECT).bin $(PROJECT).debug.txt $(OBJS)
|
rm $(PROJECT).elf $(PROJECT).bin $(PROJECT).debug.txt $(OBJS)
|
||||||
|
|
||||||
|
# Link rules
|
||||||
|
|
||||||
$(PROJECT).elf: $(OBJS)
|
$(PROJECT).elf: $(OBJS)
|
||||||
$(CC) $(CCFLAGS) $(LDFLAGS) -o $@ $(OBJS)
|
$(CC) $(CCFLAGS) $(LDFLAGS) -o $@ $(OBJS)
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@ uint32_t strlen(const char* str) {
|
||||||
return c - str;
|
return c - str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define SYSCON *(volatile uint32_t*)0x11100000
|
||||||
|
|
||||||
#define UART_BASE 0x10000000
|
#define UART_BASE 0x10000000
|
||||||
#define UART_DATA *(volatile uint32_t*)UART_BASE
|
#define UART_DATA *(volatile uint32_t*)UART_BASE
|
||||||
#define UART_STATUS UART_DATA
|
#define UART_STATUS UART_DATA
|
||||||
|
@ -49,6 +51,10 @@ void main() {
|
||||||
COUNTER_TEST(bytevalue, 9);
|
COUNTER_TEST(bytevalue, 9);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Shut down the test harness once we're done testing.
|
||||||
|
puts("Tests done, shutting down test harness...\n");
|
||||||
|
SYSCON = 0x5555;
|
||||||
|
|
||||||
// loop forever
|
// loop forever
|
||||||
for(;;);
|
// for(;;);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue