wow holy shit lily finally figured out how to work on the cpu core!
In all seriousness, this commit is a major milestone; insofar that it is the first time I can actually work on the CPU core.
This commit is contained in:
parent
09b1969d22
commit
cd684e1f3e
|
@ -10,6 +10,8 @@ add_library(riscv
|
||||||
src/CPU.cpp
|
src/CPU.cpp
|
||||||
src/System.cpp
|
src/System.cpp
|
||||||
|
|
||||||
|
# Basic system devices
|
||||||
|
src/Devices/ClntDevice.cpp
|
||||||
src/Devices/SysconDevice.cpp
|
src/Devices/SysconDevice.cpp
|
||||||
src/Devices/RamDevice.cpp
|
src/Devices/RamDevice.cpp
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <lucore/Assert.hpp>
|
#include <lucore/Assert.hpp>
|
||||||
#include <riscv/Types.hpp>
|
#include <riscv/Types.hpp>
|
||||||
|
#include <riscv/CPUTypes.hpp>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -27,6 +28,10 @@ namespace riscv {
|
||||||
|
|
||||||
virtual ~Device() = default;
|
virtual ~Device() = default;
|
||||||
|
|
||||||
|
virtual void Attached(Bus* bus) {
|
||||||
|
this->bus = bus;
|
||||||
|
}
|
||||||
|
|
||||||
virtual BasicType Type() const { return BasicType::Device; }
|
virtual BasicType Type() const { return BasicType::Device; }
|
||||||
|
|
||||||
/// Does this device clock?
|
/// Does this device clock?
|
||||||
|
@ -58,6 +63,9 @@ 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:
|
||||||
|
/// The bus this device is attached to.
|
||||||
|
Bus* bus;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Interface plain memory bus devices use.
|
/// Interface plain memory bus devices use.
|
||||||
|
@ -67,8 +75,9 @@ namespace riscv {
|
||||||
virtual BasicType Type() const override { return BasicType::PlainMemory; }
|
virtual BasicType Type() const override { return BasicType::PlainMemory; }
|
||||||
|
|
||||||
virtual AddressT Base() const = 0;
|
virtual AddressT Base() const = 0;
|
||||||
/// How many bytes does this device occupy of address space? This should
|
|
||||||
/// effectively be a constant, and should not change during CPU execution.
|
/// How many bytes does this device occupy of address space?
|
||||||
|
/// This should not change during execution.
|
||||||
virtual AddressT Size() const = 0;
|
virtual AddressT Size() const = 0;
|
||||||
|
|
||||||
/// Peek() -> reads a value from this device.
|
/// Peek() -> reads a value from this device.
|
||||||
|
@ -91,8 +100,8 @@ namespace riscv {
|
||||||
|
|
||||||
virtual AddressT Base() const;
|
virtual AddressT Base() const;
|
||||||
|
|
||||||
/// How many bytes does this device occupy of address space? This should
|
/// How many bytes does this device occupy of address space?
|
||||||
/// effectively be a constant, and should not change during CPU execution.
|
/// This should not change during execution.
|
||||||
virtual AddressT Size() const = 0;
|
virtual AddressT Size() const = 0;
|
||||||
|
|
||||||
virtual u32 Peek(AddressT address) = 0;
|
virtual u32 Peek(AddressT address) = 0;
|
||||||
|
@ -127,7 +136,12 @@ namespace riscv {
|
||||||
void PokeShort(AddressT address, u16 value);
|
void PokeShort(AddressT address, u16 value);
|
||||||
void PokeWord(AddressT address, u32 value);
|
void PokeWord(AddressT address, u32 value);
|
||||||
|
|
||||||
|
CPU* GetCPU() { return cpu; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
void CpuTrap(u32 trapCode);
|
||||||
|
|
||||||
// TODO: version which takes Device::BasicType
|
// TODO: version which takes Device::BasicType
|
||||||
Bus::Device* FindDeviceForAddress(AddressT address) const;
|
Bus::Device* FindDeviceForAddress(AddressT address) const;
|
||||||
|
|
||||||
|
|
|
@ -1,48 +1,49 @@
|
||||||
#include <riscv/Bus.hpp>
|
#include <riscv/Bus.hpp>
|
||||||
|
#include <riscv/CPUTypes.hpp>
|
||||||
#include <riscv/Types.hpp>
|
#include <riscv/Types.hpp>
|
||||||
|
|
||||||
namespace riscv {
|
namespace riscv {
|
||||||
|
|
||||||
/// The CPU core.
|
/// The CPU core.
|
||||||
struct CPU : Bus::Device {
|
struct CPU : Bus::Device {
|
||||||
/// CPU core state.
|
bool Clocked() const override { return true; }
|
||||||
struct State {
|
|
||||||
u32 gpr[32];
|
|
||||||
u32 pc;
|
|
||||||
u32 mstatus;
|
|
||||||
u32 cyclel;
|
|
||||||
u32 cycleh;
|
|
||||||
|
|
||||||
u32 mscratch;
|
|
||||||
u32 mtvec;
|
|
||||||
u32 mie;
|
|
||||||
u32 mip;
|
|
||||||
|
|
||||||
u32 mepc;
|
|
||||||
u32 mtval;
|
|
||||||
u32 mcause;
|
|
||||||
|
|
||||||
// Note: only a few bits are used. (Machine = 3, User = 0)
|
|
||||||
// Bits 0..1 = privilege.
|
|
||||||
// Bit 2 = WFI (Wait for interrupt)
|
|
||||||
// Bit 3+ = Load/Store reservation LSBs.
|
|
||||||
u32 extraflags;
|
|
||||||
};
|
|
||||||
|
|
||||||
CPU(Bus* bus);
|
|
||||||
|
|
||||||
State& GetState() { return state; }
|
|
||||||
|
|
||||||
bool Clocked() const override { return true; }
|
|
||||||
void Clock() override;
|
void Clock() override;
|
||||||
|
|
||||||
// TODO: Handlers for CSR read/write
|
void Trap(u32 trapCode);
|
||||||
|
inline void Trap(TrapCode trapCode) { Trap(static_cast<u32>(trapCode)); }
|
||||||
|
|
||||||
|
void TimerInterrupt();
|
||||||
|
|
||||||
|
// TODO: Handlers for CSR read/write (if we need it?)
|
||||||
|
|
||||||
|
GeneralPurposeRegisters gpr;
|
||||||
|
u32 pc;
|
||||||
|
u32 mstatus;
|
||||||
|
u32 cyclel;
|
||||||
|
u32 cycleh;
|
||||||
|
u32 mscratch;
|
||||||
|
u32 mtvec;
|
||||||
|
u32 mie;
|
||||||
|
u32 mip;
|
||||||
|
|
||||||
|
u32 mepc;
|
||||||
|
u32 mtval;
|
||||||
|
u32 mcause;
|
||||||
|
|
||||||
|
// Note: only a few bits are used. (Machine = 3, User = 0)
|
||||||
|
// Bits 0..1 = privilege.
|
||||||
|
// Bit 2 = WFI (Wait for interrupt)
|
||||||
|
// Bit 3+ = Load/Store reservation LSBs.
|
||||||
|
u32 extraflags;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
State state;
|
/// Set by [CPU::Trap] to tell the CPU it was trapped.
|
||||||
Bus* bus;
|
/// Codes with the sign bit set are actually interrupts,
|
||||||
|
/// and are processed first.
|
||||||
|
bool trapped { false };
|
||||||
|
u32 trapCode { 0 };
|
||||||
|
|
||||||
u32 Step(u32 elapsedMicroseconds, u32 instCount);
|
u32 Step(u32 instCount);
|
||||||
|
|
||||||
// todo: counters for chrono/inst count.
|
// todo: counters for chrono/inst count.
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
#pragma once
|
||||||
|
#include <riscv/Types.hpp>
|
||||||
|
|
||||||
|
namespace riscv {
|
||||||
|
|
||||||
|
// Note that
|
||||||
|
enum class TrapCode : u32 {
|
||||||
|
InstructionAddressMisaligned,
|
||||||
|
InstructionAddressFault,
|
||||||
|
IllegalInstruction,
|
||||||
|
Breakpoint, // I see what you did there ;)
|
||||||
|
LoadAddressMisaligned,
|
||||||
|
LoadAccessFault,
|
||||||
|
StoreAddressMisaligned,
|
||||||
|
StoreAccessFault,
|
||||||
|
|
||||||
|
EnvCallUMode,
|
||||||
|
EnvCallSMode,
|
||||||
|
EnvCallMMode,
|
||||||
|
|
||||||
|
// Not used but documented
|
||||||
|
InstructionPageFault,
|
||||||
|
LoadPageFault,
|
||||||
|
StorePageFault
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Gpr : u8 {
|
||||||
|
X0, // zero
|
||||||
|
X1,
|
||||||
|
X2,
|
||||||
|
X3,
|
||||||
|
X4,
|
||||||
|
X5,
|
||||||
|
X6,
|
||||||
|
X7,
|
||||||
|
X8,
|
||||||
|
X9,
|
||||||
|
X10,
|
||||||
|
X11,
|
||||||
|
X12,
|
||||||
|
X13,
|
||||||
|
X14,
|
||||||
|
X15,
|
||||||
|
X16,
|
||||||
|
X17,
|
||||||
|
X18,
|
||||||
|
X19,
|
||||||
|
X20,
|
||||||
|
X21,
|
||||||
|
X22,
|
||||||
|
X23,
|
||||||
|
X24,
|
||||||
|
X25,
|
||||||
|
X26,
|
||||||
|
X27,
|
||||||
|
X28,
|
||||||
|
X29,
|
||||||
|
X30,
|
||||||
|
X31,
|
||||||
|
PC
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Container for GPRs which can be Statically Typed or not depending on use case.
|
||||||
|
/// Pretty cool, huh?
|
||||||
|
struct GeneralPurposeRegisters {
|
||||||
|
constexpr u32& operator[](Gpr gpr) { return operator[](static_cast<usize>(gpr)); }
|
||||||
|
constexpr u32& operator[](usize index) { return gprs_[index]; }
|
||||||
|
u32 gprs_[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace riscv
|
|
@ -6,7 +6,7 @@ namespace riscv {
|
||||||
|
|
||||||
Bus::~Bus() {
|
Bus::~Bus() {
|
||||||
// Free all devices
|
// Free all devices
|
||||||
for(auto device: devices)
|
for(auto device : devices)
|
||||||
delete device;
|
delete device;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,8 @@ namespace riscv {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(device->IsA<CPU*>()) {
|
if(device->IsA<CPU*>()) {
|
||||||
|
// Return early to avoid putting the CPU pointer inside the devices vector.
|
||||||
|
// We do not actually own the CPU.
|
||||||
cpu = device->Upcast<CPU*>();
|
cpu = device->Upcast<CPU*>();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -54,47 +56,65 @@ namespace riscv {
|
||||||
device->Clock();
|
device->Clock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The CPU is always clocked last to ensure devices can generate
|
||||||
|
// interrupts properly. I don't make the rules.
|
||||||
cpu->Clock();
|
cpu->Clock();
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 Bus::PeekByte(AddressT address) {
|
u8 Bus::PeekByte(AddressT address) {
|
||||||
if(auto dev = FindDeviceForAddress(address); dev)
|
if(auto dev = FindDeviceForAddress(address); dev)
|
||||||
return dev->Upcast<MemoryDevice*>()->PeekByte(address);
|
return dev->Upcast<MemoryDevice*>()->PeekByte(address);
|
||||||
return -1;
|
else {
|
||||||
|
cpu->Trap(TrapCode::LoadAccessFault);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 Bus::PeekShort(AddressT address) {
|
u16 Bus::PeekShort(AddressT address) {
|
||||||
if(auto dev = FindDeviceForAddress(address); dev)
|
if(auto dev = FindDeviceForAddress(address); dev)
|
||||||
return dev->Upcast<MemoryDevice*>()->PeekShort(address);
|
return dev->Upcast<MemoryDevice*>()->PeekShort(address);
|
||||||
return -1;
|
else {
|
||||||
|
cpu->Trap(TrapCode::LoadAccessFault);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Bus::PeekWord(AddressT address) {
|
u32 Bus::PeekWord(AddressT address) {
|
||||||
if(auto dev = FindDeviceForAddress(address); dev) {
|
if(auto dev = FindDeviceForAddress(address); dev) {
|
||||||
if(dev->IsA<MmioDevice*>())
|
if(dev->IsA<MmioDevice*>())
|
||||||
return dev->Upcast<MmioDevice*>()->Peek(address);
|
return dev->Upcast<MmioDevice*>()->Peek(address);
|
||||||
else
|
else
|
||||||
return dev->Upcast<MemoryDevice*>()->PeekWord(address);
|
return dev->Upcast<MemoryDevice*>()->PeekWord(address);
|
||||||
|
} else {
|
||||||
|
cpu->Trap(TrapCode::LoadAccessFault);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bus::PokeByte(AddressT address, u8 value) {
|
void Bus::PokeByte(AddressT address, u8 value) {
|
||||||
if(auto dev = FindDeviceForAddress(address); dev)
|
if(auto dev = FindDeviceForAddress(address); dev)
|
||||||
return dev->Upcast<MemoryDevice*>()->PokeByte(address, value);
|
return dev->Upcast<MemoryDevice*>()->PokeByte(address, value);
|
||||||
|
else {
|
||||||
|
cpu->Trap(TrapCode::StoreAccessFault);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bus::PokeShort(AddressT address, u16 value) {
|
void Bus::PokeShort(AddressT address, u16 value) {
|
||||||
if(auto dev = FindDeviceForAddress(address); dev)
|
if(auto dev = FindDeviceForAddress(address); dev)
|
||||||
return dev->Upcast<MemoryDevice*>()->PokeShort(address, value);
|
return dev->Upcast<MemoryDevice*>()->PokeShort(address, value);
|
||||||
|
else {
|
||||||
|
cpu->Trap(TrapCode::StoreAccessFault);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bus::PokeWord(AddressT address, u32 value) {
|
void Bus::PokeWord(AddressT address, u32 value) {
|
||||||
if(auto dev = FindDeviceForAddress(address); dev) {
|
if(auto dev = FindDeviceForAddress(address); dev) {
|
||||||
if(dev->IsA<MmioDevice*>())
|
if(dev->IsA<MmioDevice*>())
|
||||||
dev->Upcast<MmioDevice*>()->Poke(address, value);
|
dev->Upcast<MmioDevice*>()->Poke(address, value);
|
||||||
else
|
else
|
||||||
dev->Upcast<MemoryDevice*>()->PokeWord(address, value);
|
dev->Upcast<MemoryDevice*>()->PokeWord(address, value);
|
||||||
|
} else {
|
||||||
|
cpu->Trap(TrapCode::StoreAccessFault);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,81 @@
|
||||||
#include <riscv/CPU.hpp>
|
#include <riscv/CPU.hpp>
|
||||||
|
#include <riscv/Bus.hpp>
|
||||||
|
|
||||||
namespace riscv {
|
namespace riscv {
|
||||||
|
|
||||||
void CPU::Clock() {
|
void CPU::Clock() {
|
||||||
// do the thing
|
// do the thing
|
||||||
|
Step(1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::Trap(u32 trapCode) {
|
||||||
|
// The CPU core will get to this later.
|
||||||
|
trapped = true;
|
||||||
|
trapCode = trapCode;
|
||||||
|
|
||||||
|
// If this is because of an interrupt
|
||||||
|
if(trapCode & 0x80000000) {
|
||||||
|
// Set MIP.MEIP.
|
||||||
|
//mip |= 1 << 11;
|
||||||
|
// Always clear WFI bit.
|
||||||
|
extraflags &= ~4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::TimerInterrupt() {
|
||||||
|
// Set MIP.MTIP.
|
||||||
|
mip |= 1 << 7;
|
||||||
|
//extraflags &= ~4;
|
||||||
|
//trapped = true;
|
||||||
|
//trapCode = 0x80000007;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 CPU::Step(u32 instCount) {
|
||||||
|
auto interruptsInFlight = [&]() {
|
||||||
|
return
|
||||||
|
(mip & (1 << 7) /*|| mip & (1 << 11)*/) &&
|
||||||
|
(mie & (1 << 7) /*|| mie & (1 << 11)*/) &&
|
||||||
|
(mstatus & 0x8 /*mie*/);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Don't run if waiting for an interrupt
|
||||||
|
if(extraflags & 4)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if(interruptsInFlight()) {
|
||||||
|
Trap(0x80000007);
|
||||||
|
} else {
|
||||||
|
u32 rval = 0;
|
||||||
|
u32 pc = this->pc;
|
||||||
|
u32 cycle = this->cyclel;
|
||||||
|
|
||||||
|
for(u32 iInst = 0; iInst < instCount; ++iInst) {
|
||||||
|
|
||||||
|
cycle++;
|
||||||
|
|
||||||
|
if(pc & 3) {
|
||||||
|
Trap(TrapCode::InstructionAddressMisaligned);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
auto ir = bus->PeekWord(pc);
|
||||||
|
auto rdid = (ir >> 7) & 0x1f;
|
||||||
|
//switch(ir & 0x7f) {
|
||||||
|
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(trapped) {
|
||||||
|
mcause = trapCode;
|
||||||
|
if(trapCode & 0x80000000) {
|
||||||
|
mtval = 0;
|
||||||
|
// pc -= 4
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace riscv
|
} // namespace riscv
|
||||||
|
|
|
@ -1,58 +1,68 @@
|
||||||
#include <riscv/Devices/ClntDevice.hpp>
|
|
||||||
#include <lucore/Logger.hpp>
|
#include <lucore/Logger.hpp>
|
||||||
|
#include <riscv/Devices/ClntDevice.hpp>
|
||||||
|
#include <riscv/CPU.hpp>
|
||||||
|
|
||||||
namespace riscv::devices {
|
namespace riscv::devices {
|
||||||
|
|
||||||
/// anonymous enum to make documenting stuff easier
|
constexpr static AddressT MSIP_ADDRESS = ClntDevice::BASE_ADDRESS,
|
||||||
/// could be done with constexpr but Lazy lilypad
|
MATCHL_ADDRESS = ClntDevice::BASE_ADDRESS + 0x4000,
|
||||||
enum : AddressT {
|
MATCHH_ADDRESS = ClntDevice::BASE_ADDRESS + 0x4004,
|
||||||
MATCHL_ADDRESS = ClntDevice::BASE_ADDRESS + 0x4000,
|
TIMERL_ADDRESS = ClntDevice::BASE_ADDRESS + 0xbff8,
|
||||||
MATCHH_ADDRESS = ClntDevice::BASE_ADDRESS + 0x4004,
|
TIMERH_ADDRESS = ClntDevice::BASE_ADDRESS + 0xbffc;
|
||||||
|
|
||||||
TIMERL_ADDRESS = ClntDevice::BASE_ADDRESS + 0xbff8,
|
void ClntDevice::Clock() {
|
||||||
TIMERH_ADDRESS = ClntDevice::BASE_ADDRESS + 0xbffc,
|
// TODO: handle timer
|
||||||
};
|
// If match low and high match the timer during a clock
|
||||||
|
// we should fire the interrupt, otherwise not do so
|
||||||
|
|
||||||
void ClntDevice::Clock() {
|
u32 new_timer = timerCountLow + 1;
|
||||||
// TODO: handle timer
|
if(new_timer < timerCountLow)
|
||||||
// If match low and high match the timer during a clock
|
timerCountHigh++;
|
||||||
// we should fire the interrupt, otherwise not do so
|
timerCountLow = new_timer;
|
||||||
|
|
||||||
|
if((timerCountHigh > timerMatchHigh ||
|
||||||
|
(timerCountHigh == timerMatchHigh && timerCountLow == timerMatchLow)) &&
|
||||||
|
(timerMatchHigh || timerMatchLow)) {
|
||||||
|
// Fire the CLNT timer interrupt
|
||||||
|
bus->GetCPU()->TimerInterrupt();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 ClntDevice::Peek(AddressT address) {
|
||||||
|
switch(address) {
|
||||||
|
case TIMERL_ADDRESS:
|
||||||
|
return timerCountLow;
|
||||||
|
|
||||||
u32 ClntDevice::Peek(AddressT address) {
|
case TIMERH_ADDRESS:
|
||||||
switch(address) {
|
return timerCountHigh;
|
||||||
case TIMERL_ADDRESS:
|
|
||||||
return timerCountLow;
|
|
||||||
|
|
||||||
case TIMERH_ADDRESS:
|
case MATCHL_ADDRESS:
|
||||||
return timerCountHigh;
|
return timerMatchLow;
|
||||||
|
|
||||||
case MATCHL_ADDRESS:
|
case MATCHH_ADDRESS:
|
||||||
return timerMatchLow;
|
return timerMatchHigh;
|
||||||
|
|
||||||
case MATCHH_ADDRESS:
|
default:
|
||||||
return timerMatchHigh;
|
return 0x0;
|
||||||
|
|
||||||
default:
|
|
||||||
return 0x0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ClntDevice::Poke(AddressT address, u32 value) {
|
void ClntDevice::Poke(AddressT address, u32 value) {
|
||||||
switch(address) {
|
switch(address) {
|
||||||
case MATCHL_ADDRESS:
|
case MATCHL_ADDRESS:
|
||||||
timerMatchLow = value;
|
timerMatchLow = value;
|
||||||
break;
|
break;
|
||||||
case MATCHH_ADDRESS:
|
|
||||||
timerMatchHigh = value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// ?
|
case MATCHH_ADDRESS:
|
||||||
default:
|
timerMatchHigh = value;
|
||||||
lucore::LogInfo("CLNT({}) unhandled poke @ 0x{:08x} : 0x{:08x}", static_cast<void*>(this), address, value);
|
break;
|
||||||
break;
|
|
||||||
}
|
// ?
|
||||||
|
default:
|
||||||
|
lucore::LogInfo("CLNT({}) unhandled poke @ 0x{:08x} : 0x{:08x}",
|
||||||
|
static_cast<void*>(this), address, value);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
} // namespace riscv::devices
|
||||||
|
|
|
@ -11,8 +11,7 @@ namespace riscv::devices {
|
||||||
}
|
}
|
||||||
|
|
||||||
RamDevice::~RamDevice() {
|
RamDevice::~RamDevice() {
|
||||||
if(memory)
|
delete[] memory;
|
||||||
delete[] memory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AddressT RamDevice::Base() const {
|
AddressT RamDevice::Base() const {
|
||||||
|
|
Loading…
Reference in New Issue