no one will know the pain i go through
This commit is contained in:
parent
abe9b78b1a
commit
38e7fc4646
|
@ -22,7 +22,7 @@ BinPackParameters: true
|
||||||
BreakConstructorInitializers: BeforeColon
|
BreakConstructorInitializers: BeforeColon
|
||||||
BreakStringLiterals: false
|
BreakStringLiterals: false
|
||||||
|
|
||||||
ColumnLimit: 100
|
ColumnLimit: 150
|
||||||
CompactNamespaces: false
|
CompactNamespaces: false
|
||||||
|
|
||||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
|
|
||||||
//namespace lucore {
|
//namespace lucore {
|
||||||
using u8 = std::uint8_t;
|
using u8 = std::uint8_t;
|
||||||
using s8 = std::int8_t;
|
using i8 = std::int8_t;
|
||||||
using u16 = std::uint16_t;
|
using u16 = std::uint16_t;
|
||||||
using s16 = std::int16_t;
|
using i16 = std::int16_t;
|
||||||
using u32 = std::uint32_t;
|
using u32 = std::uint32_t;
|
||||||
using s32 = std::int32_t;
|
using i32 = std::int32_t;
|
||||||
using u64 = std::uint64_t;
|
using u64 = std::uint64_t;
|
||||||
using s64 = std::int64_t;
|
using i64 = std::int64_t;
|
||||||
using usize = std::size_t;
|
using usize = std::size_t;
|
||||||
using ssize = std::intptr_t;
|
using isize = std::intptr_t;
|
||||||
|
|
||||||
//} // namespace lucore
|
//} // namespace lucore
|
||||||
|
|
|
@ -74,21 +74,21 @@ namespace riscv {
|
||||||
|
|
||||||
virtual BasicType Type() const override { return BasicType::PlainMemory; }
|
virtual BasicType Type() const override { return BasicType::PlainMemory; }
|
||||||
|
|
||||||
virtual AddressT Base() const = 0;
|
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.
|
/// This should not change during execution.
|
||||||
virtual AddressT Size() const = 0;
|
virtual Address Size() const = 0;
|
||||||
|
|
||||||
/// Peek() -> reads a value from this device.
|
/// Peek() -> reads a value from this device.
|
||||||
virtual u8 PeekByte(AddressT address) = 0;
|
virtual u8 PeekByte(Address address) = 0;
|
||||||
virtual u16 PeekShort(AddressT address) = 0;
|
virtual u16 PeekShort(Address address) = 0;
|
||||||
virtual u32 PeekWord(AddressT address) = 0;
|
virtual u32 PeekWord(Address address) = 0;
|
||||||
|
|
||||||
/// Poke() -> Writes a value to this device's space in memory
|
/// Poke() -> Writes a value to this device's space in memory
|
||||||
virtual void PokeByte(AddressT address, u8 value) = 0;
|
virtual void PokeByte(Address address, u8 value) = 0;
|
||||||
virtual void PokeShort(AddressT address, u16 value) = 0;
|
virtual void PokeShort(Address address, u16 value) = 0;
|
||||||
virtual void PokeWord(AddressT address, u32 value) = 0;
|
virtual void PokeWord(Address address, u32 value) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A device in the MMIO region.
|
/// A device in the MMIO region.
|
||||||
|
@ -98,14 +98,14 @@ namespace riscv {
|
||||||
|
|
||||||
virtual BasicType Type() const override { return BasicType::Mmio; }
|
virtual BasicType Type() const override { return BasicType::Mmio; }
|
||||||
|
|
||||||
virtual AddressT Base() const;
|
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.
|
/// This should not change during execution.
|
||||||
virtual AddressT Size() const = 0;
|
virtual Address Size() const = 0;
|
||||||
|
|
||||||
virtual u32 Peek(AddressT address) = 0;
|
virtual u32 Peek(Address address) = 0;
|
||||||
virtual void Poke(AddressT address, u32 value) = 0;
|
virtual void Poke(Address address, u32 value) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Bus destructor.
|
/// Bus destructor.
|
||||||
|
@ -128,20 +128,20 @@ namespace riscv {
|
||||||
/// Clock all clocked devices mapped onto the bus..
|
/// Clock all clocked devices mapped onto the bus..
|
||||||
void Clock();
|
void Clock();
|
||||||
|
|
||||||
u8 PeekByte(AddressT address);
|
u8 PeekByte(Address address);
|
||||||
u16 PeekShort(AddressT address);
|
u16 PeekShort(Address address);
|
||||||
u32 PeekWord(AddressT address);
|
u32 PeekWord(Address address);
|
||||||
|
|
||||||
void PokeByte(AddressT address, u8 value);
|
void PokeByte(Address address, u8 value);
|
||||||
void PokeShort(AddressT address, u16 value);
|
void PokeShort(Address address, u16 value);
|
||||||
void PokeWord(AddressT address, u32 value);
|
void PokeWord(Address address, u32 value);
|
||||||
|
|
||||||
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(AddressT address) const;
|
Bus::Device* FindDeviceForAddress(Address address) const;
|
||||||
|
|
||||||
CPU* cpu;
|
CPU* cpu;
|
||||||
|
|
||||||
|
@ -150,8 +150,8 @@ namespace riscv {
|
||||||
|
|
||||||
// TODO: if these end up being a hotpath replace with ankerl::unordered_dense
|
// TODO: if these end up being a hotpath replace with ankerl::unordered_dense
|
||||||
// (or just use the [devices] vector, probably.)
|
// (or just use the [devices] vector, probably.)
|
||||||
std::unordered_map<AddressT, MemoryDevice*> mapped_devices;
|
std::unordered_map<Address, MemoryDevice*> mapped_devices;
|
||||||
std::unordered_map<AddressT, MmioDevice*> mmio_devices;
|
std::unordered_map<Address, MmioDevice*> mmio_devices;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace riscv
|
} // namespace riscv
|
||||||
|
|
|
@ -9,6 +9,7 @@ namespace riscv {
|
||||||
bool Clocked() const override { return true; }
|
bool Clocked() const override { return true; }
|
||||||
void Clock() override;
|
void Clock() override;
|
||||||
|
|
||||||
|
/// Trap the CPU. Bus devices can call this.
|
||||||
void Trap(u32 trapCode);
|
void Trap(u32 trapCode);
|
||||||
inline void Trap(TrapCode trapCode) { Trap(static_cast<u32>(trapCode)); }
|
inline void Trap(TrapCode trapCode) { Trap(static_cast<u32>(trapCode)); }
|
||||||
|
|
||||||
|
@ -16,6 +17,7 @@ namespace riscv {
|
||||||
|
|
||||||
// TODO: Handlers for CSR read/write (if we need it?)
|
// TODO: Handlers for CSR read/write (if we need it?)
|
||||||
|
|
||||||
|
/// CPU state
|
||||||
GeneralPurposeRegisters gpr;
|
GeneralPurposeRegisters gpr;
|
||||||
u32 pc;
|
u32 pc;
|
||||||
u32 mstatus;
|
u32 mstatus;
|
||||||
|
@ -38,9 +40,9 @@ namespace riscv {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Set by [CPU::Trap] to tell the CPU it was trapped.
|
/// Set by [CPU::Trap] to tell the CPU it was trapped.
|
||||||
/// Codes with the sign bit set are actually interrupts,
|
|
||||||
/// and are processed first.
|
|
||||||
bool trapped { false };
|
bool trapped { false };
|
||||||
|
|
||||||
|
/// Set by [CPU::Trap] for the trap code.
|
||||||
u32 trapCode { 0 };
|
u32 trapCode { 0 };
|
||||||
|
|
||||||
u32 Step(u32 instCount);
|
u32 Step(u32 instCount);
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
|
|
||||||
namespace riscv {
|
namespace riscv {
|
||||||
|
|
||||||
// Note that
|
/// Most possible trap codes.
|
||||||
enum class TrapCode : u32 {
|
enum class TrapCode : u32 {
|
||||||
InstructionAddressMisaligned,
|
InstructionAddressMisaligned,
|
||||||
InstructionAddressFault,
|
InstructionAccessFault,
|
||||||
IllegalInstruction,
|
IllegalInstruction,
|
||||||
Breakpoint, // I see what you did there ;)
|
Breakpoint, // I see what you did there ;)
|
||||||
LoadAddressMisaligned,
|
LoadAddressMisaligned,
|
||||||
|
|
|
@ -7,17 +7,17 @@ namespace riscv::devices {
|
||||||
/// Partial implementation of a CLINT.
|
/// Partial implementation of a CLINT.
|
||||||
/// The timer device is implemented, SIP is not.
|
/// The timer device is implemented, SIP is not.
|
||||||
struct ClntDevice : public Bus::MmioDevice {
|
struct ClntDevice : public Bus::MmioDevice {
|
||||||
constexpr static AddressT BASE_ADDRESS = 0x11000000;
|
constexpr static Address BASE_ADDRESS = 0x11000000;
|
||||||
|
|
||||||
AddressT Base() const override { return BASE_ADDRESS; }
|
Address Base() const override { return BASE_ADDRESS; }
|
||||||
AddressT Size() const override { return 0xbfff; }
|
Address Size() const override { return 0xbfff; }
|
||||||
|
|
||||||
bool Clocked() const override { return true; }
|
bool Clocked() const override { return true; }
|
||||||
void Clock() override;
|
void Clock() override;
|
||||||
|
|
||||||
|
|
||||||
u32 Peek(AddressT address) override;
|
u32 Peek(Address address) override;
|
||||||
void Poke(AddressT address, u32 value) override;
|
void Poke(Address address, u32 value) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
u32 timerCountHigh;
|
u32 timerCountHigh;
|
||||||
|
|
|
@ -6,31 +6,31 @@ namespace riscv::devices {
|
||||||
|
|
||||||
/// A block of RAM which can be used by the CPU.
|
/// A block of RAM which can be used by the CPU.
|
||||||
struct RamDevice : public Bus::MemoryDevice {
|
struct RamDevice : public Bus::MemoryDevice {
|
||||||
RamDevice(AddressT base, AddressT size);
|
RamDevice(Address base, Address size);
|
||||||
virtual ~RamDevice();
|
virtual ~RamDevice();
|
||||||
|
|
||||||
// Implementation of Device interface
|
// Implementation of Device interface
|
||||||
|
|
||||||
AddressT Base() const override;
|
Address Base() const override;
|
||||||
AddressT Size() const override;
|
Address Size() const override;
|
||||||
|
|
||||||
|
|
||||||
u8 PeekByte(AddressT address) override;
|
u8 PeekByte(Address address) override;
|
||||||
u16 PeekShort(AddressT address) override;
|
u16 PeekShort(Address address) override;
|
||||||
u32 PeekWord(AddressT address) override;
|
u32 PeekWord(Address address) override;
|
||||||
|
|
||||||
void PokeByte(AddressT address, u8 value) override;
|
void PokeByte(Address address, u8 value) override;
|
||||||
void PokeShort(AddressT address, u16 value) override;
|
void PokeShort(Address address, u16 value) override;
|
||||||
void PokeWord(AddressT address, u32 value) override;
|
void PokeWord(Address address, u32 value) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// helper used for implementing Peek/Poke API
|
/// helper used for implementing Peek/Poke API
|
||||||
template <class T>
|
template <class T>
|
||||||
constexpr usize AddressToIndex(AddressT address) {
|
constexpr usize AddressToIndex(Address address) {
|
||||||
return ((address - memoryBase) % memorySize) / sizeof(T);
|
return ((address - memoryBase) % memorySize) / sizeof(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
AddressT memoryBase {};
|
Address memoryBase {};
|
||||||
|
|
||||||
u8* memory {};
|
u8* memory {};
|
||||||
usize memorySize {};
|
usize memorySize {};
|
||||||
|
|
|
@ -7,14 +7,14 @@ 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.
|
||||||
struct SysconDevice : public Bus::MmioDevice {
|
struct SysconDevice : public Bus::MmioDevice {
|
||||||
constexpr static AddressT BASE_ADDRESS = 0x11100000;
|
constexpr static Address BASE_ADDRESS = 0x11100000;
|
||||||
|
|
||||||
SysconDevice(System* system);
|
SysconDevice(System* system);
|
||||||
|
|
||||||
AddressT Size() const override { return sizeof(u32); } // I think this is right?
|
Address Size() const override { return sizeof(u32); } // I think this is right?
|
||||||
|
|
||||||
u32 Peek(AddressT address) override;
|
u32 Peek(Address address) override;
|
||||||
void Poke(AddressT address, u32 value) override;
|
void Poke(Address address, u32 value) override;
|
||||||
private:
|
private:
|
||||||
System* system;
|
System* system;
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace riscv {
|
||||||
struct System {
|
struct System {
|
||||||
/// Create a basic system with the basic periphials created.
|
/// Create a basic system with the basic periphials created.
|
||||||
/// All other periphials should be managed by the creator of this System
|
/// All other periphials should be managed by the creator of this System
|
||||||
static System* WithMemory(AddressT ramSize);
|
static System* WithMemory(Address ramSize);
|
||||||
|
|
||||||
~System();
|
~System();
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
namespace riscv {
|
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 AddressT = u32;
|
using Address = u32;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ namespace riscv {
|
||||||
cpu->Clock();
|
cpu->Clock();
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 Bus::PeekByte(AddressT address) {
|
u8 Bus::PeekByte(Address 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);
|
||||||
else {
|
else {
|
||||||
|
@ -70,7 +70,7 @@ namespace riscv {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 Bus::PeekShort(AddressT address) {
|
u16 Bus::PeekShort(Address 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);
|
||||||
else {
|
else {
|
||||||
|
@ -79,7 +79,7 @@ namespace riscv {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Bus::PeekWord(AddressT address) {
|
u32 Bus::PeekWord(Address 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);
|
||||||
|
@ -91,7 +91,7 @@ namespace riscv {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bus::PokeByte(AddressT address, u8 value) {
|
void Bus::PokeByte(Address 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 {
|
else {
|
||||||
|
@ -99,7 +99,7 @@ namespace riscv {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bus::PokeShort(AddressT address, u16 value) {
|
void Bus::PokeShort(Address 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 {
|
else {
|
||||||
|
@ -107,7 +107,7 @@ namespace riscv {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bus::PokeWord(AddressT address, u32 value) {
|
void Bus::PokeWord(Address 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);
|
||||||
|
@ -118,8 +118,8 @@ namespace riscv {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Bus::Device* Bus::FindDeviceForAddress(AddressT address) const {
|
Bus::Device* Bus::FindDeviceForAddress(Address address) const {
|
||||||
auto try_find_device = [&](auto container, AddressT address) {
|
auto try_find_device = [&](auto container, Address address) {
|
||||||
return std::find_if(container.begin(), container.end(), [&](const auto& pair) {
|
return std::find_if(container.begin(), container.end(), [&](const auto& pair) {
|
||||||
return
|
return
|
||||||
// We can shorcut region checking if the requested addess matches base address.
|
// We can shorcut region checking if the requested addess matches base address.
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
#include <riscv/CPU.hpp>
|
//! Portions of this code are copyright 2022 Charles Lohr (CNLohr).
|
||||||
|
|
||||||
#include <riscv/Bus.hpp>
|
#include <riscv/Bus.hpp>
|
||||||
|
#include <riscv/CPU.hpp>
|
||||||
|
|
||||||
|
#include "riscv/CPUTypes.hpp"
|
||||||
|
|
||||||
namespace riscv {
|
namespace riscv {
|
||||||
|
|
||||||
|
constexpr static Address RamImageOffset = 0x80000000;
|
||||||
|
|
||||||
void CPU::Clock() {
|
void CPU::Clock() {
|
||||||
// do the thing
|
// do the thing
|
||||||
Step(1024);
|
Step(1024);
|
||||||
|
@ -32,50 +38,531 @@ namespace riscv {
|
||||||
|
|
||||||
u32 CPU::Step(u32 instCount) {
|
u32 CPU::Step(u32 instCount) {
|
||||||
auto interruptsInFlight = [&]() {
|
auto interruptsInFlight = [&]() {
|
||||||
return
|
return (mip & (1 << 7) /*|| mip & (1 << 11)*/) && (mie & (1 << 7) /*|| mie & (1 << 11)*/) && (mstatus & 0x8 /*mie*/);
|
||||||
(mip & (1 << 7) /*|| mip & (1 << 11)*/) &&
|
|
||||||
(mie & (1 << 7) /*|| mie & (1 << 11)*/) &&
|
|
||||||
(mstatus & 0x8 /*mie*/);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Don't run if waiting for an interrupt
|
// Don't run if waiting for an interrupt
|
||||||
if(extraflags & 4)
|
if(extraflags & 4)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if(interruptsInFlight()) {
|
u32 rdid = 0;
|
||||||
Trap(0x80000007);
|
|
||||||
} else {
|
|
||||||
u32 rval = 0;
|
u32 rval = 0;
|
||||||
u32 pc = this->pc;
|
u32 pc = this->pc;
|
||||||
u32 cycle = this->cyclel;
|
u32 cycle = this->cyclel;
|
||||||
|
|
||||||
|
if(interruptsInFlight()) {
|
||||||
|
Trap(0x80000007);
|
||||||
|
} else {
|
||||||
for(u32 iInst = 0; iInst < instCount; ++iInst) {
|
for(u32 iInst = 0; iInst < instCount; ++iInst) {
|
||||||
|
auto ofs_pc = pc - RamImageOffset;
|
||||||
cycle++;
|
cycle++;
|
||||||
|
|
||||||
if(pc & 3) {
|
if(ofs_pc & 3) {
|
||||||
Trap(TrapCode::InstructionAddressMisaligned);
|
Trap(TrapCode::InstructionAddressMisaligned);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
auto ir = bus->PeekWord(pc);
|
auto ir = bus->PeekWord(ofs_pc);
|
||||||
auto rdid = (ir >> 7) & 0x1f;
|
if(trapped) {
|
||||||
//switch(ir & 0x7f) {
|
// Overwrite the trap that the bus generated. This might not really work out
|
||||||
|
// in practice but should at least kind-of replicate behaviour (our address
|
||||||
//}
|
// space is emulated using the [Bus] class, so there is no heap write issue
|
||||||
|
// that could be caused by leaving it unbound).
|
||||||
|
Trap(TrapCode::InstructionAccessFault);
|
||||||
|
rval = ofs_pc + RamImageOffset;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rdid = (ir >> 7) & 0x1f;
|
||||||
|
|
||||||
|
// Do the thing!
|
||||||
|
switch(ir & 0x7f) {
|
||||||
|
case 0x37: // lui
|
||||||
|
rval = (ir & 0xfffff000);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x17: // auipc
|
||||||
|
rval = pc + (ir & 0xfffff000);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x6f: { // jal
|
||||||
|
i32 reladdy = ((ir & 0x80000000) >> 11) | ((ir & 0x7fe00000) >> 20) | ((ir & 0x00100000) >> 9) | ((ir & 0x000ff000));
|
||||||
|
|
||||||
|
// Sign extend in this case
|
||||||
|
if(reladdy & 0x00100000)
|
||||||
|
reladdy |= 0xffe00000;
|
||||||
|
rval = pc + 4;
|
||||||
|
pc = pc + reladdy - 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0x67: { // jalr
|
||||||
|
u32 imm = ir >> 20;
|
||||||
|
i32 imm_se = imm | ((imm & 0x800) ? 0xfffff000 : 0);
|
||||||
|
rval = pc + 4;
|
||||||
|
pc = ((gpr[((ir >> 15) & 0x1f)] + imm_se) & ~1) - 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0x63: { // branch
|
||||||
|
u32 immm4 = ((ir & 0xf00) >> 7) | ((ir & 0x7e000000) >> 20) | ((ir & 0x80) << 4) | ((ir >> 31) << 12);
|
||||||
|
if(immm4 & 0x1000)
|
||||||
|
immm4 |= 0xffffe000;
|
||||||
|
i32 rs1 = gpr[(ir >> 15) & 0x1f];
|
||||||
|
i32 rs2 = gpr[(ir >> 20) & 0x1f];
|
||||||
|
immm4 = pc + immm4 - 4;
|
||||||
|
rdid = 0;
|
||||||
|
switch((ir >> 12) & 0x7) {
|
||||||
|
// BEQ, BNE, BLT, BGE, BLTU, BGEU
|
||||||
|
case 0: // BEQ
|
||||||
|
if(rs1 == rs2)
|
||||||
|
pc = immm4;
|
||||||
|
break;
|
||||||
|
case 1: // BNE
|
||||||
|
if(rs1 != rs2)
|
||||||
|
pc = immm4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4: // BLT
|
||||||
|
if(rs1 < rs2)
|
||||||
|
pc = immm4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 5: // BGE
|
||||||
|
if(rs1 >= rs2)
|
||||||
|
pc = immm4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 6: // BLTU
|
||||||
|
if((uint32_t)rs1 < (uint32_t)rs2)
|
||||||
|
pc = immm4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 7: // BGEU
|
||||||
|
if((uint32_t)rs1 >= (uint32_t)rs2)
|
||||||
|
pc = immm4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Trap(TrapCode::IllegalInstruction);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0x03: { // load
|
||||||
|
u32 rs1 = gpr[(ir >> 15) & 0x1f];
|
||||||
|
u32 imm = ir >> 20;
|
||||||
|
i32 imm_se = imm | ((imm & 0x800) ? 0xfffff000 : 0);
|
||||||
|
u32 rsval = rs1 + imm_se;
|
||||||
|
|
||||||
|
rsval -= RamImageOffset;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(trapped) {
|
||||||
|
rval = rsval + RamImageOffset;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0x23: { // store
|
||||||
|
u32 rs1 = gpr[(ir >> 15) & 0x1f];
|
||||||
|
u32 rs2 = gpr[(ir >> 20) & 0x1f];
|
||||||
|
u32 addy = ((ir >> 7) & 0x1f) | ((ir & 0xfe000000) >> 20);
|
||||||
|
if(addy & 0x800)
|
||||||
|
addy |= 0xfffff000;
|
||||||
|
addy += rs1 - RamImageOffset;
|
||||||
|
rdid = 0;
|
||||||
|
|
||||||
|
switch((ir >> 12) & 0x7) {
|
||||||
|
// SB, SH, SW
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0x13: // op-imm
|
||||||
|
case 0x33: { // op
|
||||||
|
u32 imm = ir >> 20;
|
||||||
|
imm = imm | ((imm & 0x800) ? 0xfffff000 : 0);
|
||||||
|
u32 rs1 = gpr[(ir >> 15) & 0x1f];
|
||||||
|
u32 is_reg = !!(ir & 0x20);
|
||||||
|
u32 rs2 = is_reg ? gpr[imm & 0x1f] : imm;
|
||||||
|
|
||||||
|
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 4:
|
||||||
|
if(rs2 == 0)
|
||||||
|
rval = -1;
|
||||||
|
else
|
||||||
|
rval = ((i32)rs1 == INT32_MIN && (i32)rs2 == -1) ? rs1 : ((i32)rs1 / (i32)rs2);
|
||||||
|
break; // DIV
|
||||||
|
case 5:
|
||||||
|
if(rs2 == 0)
|
||||||
|
rval = 0xffffffff;
|
||||||
|
else
|
||||||
|
rval = rs1 / rs2;
|
||||||
|
break; // DIVU
|
||||||
|
case 6:
|
||||||
|
if(rs2 == 0)
|
||||||
|
rval = rs1;
|
||||||
|
else
|
||||||
|
rval = ((i32)rs1 == INT32_MIN && (i32)rs2 == -1) ? 0 : ((u32)((i32)rs1 % (i32)rs2));
|
||||||
|
break; // REM
|
||||||
|
case 7:
|
||||||
|
if(rs2 == 0)
|
||||||
|
rval = rs1;
|
||||||
|
else
|
||||||
|
rval = rs1 % rs2;
|
||||||
|
break; // REMU
|
||||||
|
}
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0x0f: // 0b0001111 (ifenze?)
|
||||||
|
rdid = 0; // fencetype = (ir >> 12) & 0b111; We ignore fences in this impl.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x73: { // Zifencei+Zicsr (0b1110011)
|
||||||
|
|
||||||
|
u32 csrno = ir >> 20;
|
||||||
|
u32 microop = (ir >> 12) & 0x7;
|
||||||
|
// Zicsr
|
||||||
|
if((microop & 3)) {
|
||||||
|
int rs1imm = (ir >> 15) & 0x1f;
|
||||||
|
u32 rs1 = gpr[rs1imm];
|
||||||
|
u32 writeval = rs1;
|
||||||
|
|
||||||
|
// 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 0x301:
|
||||||
|
rval = 0x40401101;
|
||||||
|
break; // misa (XLEN=32, IMA+X)
|
||||||
|
// case 0x3B0: rval = 0; break; //pmpaddr0
|
||||||
|
// case 0x3a0: rval = 0; break; //pmpcfg0
|
||||||
|
// case 0xf12: rval = 0x00000000; break; //marchid
|
||||||
|
// case 0xf13: rval = 0x00000000; break; //mimpid
|
||||||
|
// case 0xf14: rval = 0x00000000; break; //mhartid
|
||||||
|
default:
|
||||||
|
// MINIRV32_OTHERCSR_READ(csrno, rval);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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 0x3a0: break; //pmpcfg0
|
||||||
|
// case 0x3B0: break; //pmpaddr0
|
||||||
|
// case 0xf11: break; //mvendorid
|
||||||
|
// case 0xf12: break; //marchid
|
||||||
|
// case 0xf13: break; //mimpid
|
||||||
|
// case 0xf14: break; //mhartid
|
||||||
|
// case 0x301: break; //misa
|
||||||
|
default:
|
||||||
|
// MINIRV32_OTHERCSR_WRITE(csrno, writeval);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if(microop == 0x0) { // "SYSTEM" 0b000
|
||||||
|
rdid = 0;
|
||||||
|
if(csrno == 0x105) { // WFI (Wait for interrupts)
|
||||||
|
mstatus |= 8; // Enable interrupts
|
||||||
|
extraflags |= 4; // Set inernal WFI bit
|
||||||
|
this->pc = pc + 4;
|
||||||
|
return 1;
|
||||||
|
} else if(((csrno & 0xff) == 0x02)) { // MRET
|
||||||
|
// https://raw.githubusercontent.com/riscv/virtual-memory/main/specs/663-Svpbmt.pdf
|
||||||
|
// Table 7.6. MRET then in mstatus/mstatush sets MPV=0, MPP=0,
|
||||||
|
// MIE=MPIE, and MPIE=1. La
|
||||||
|
// Should also update mstatus to reflect correct mode.
|
||||||
|
u32 startmstatus = mstatus;
|
||||||
|
u32 startextraflags = extraflags;
|
||||||
|
mstatus = ((startmstatus & 0x80) >> 4) | ((startextraflags & 3) << 11) | 0x80;
|
||||||
|
extraflags = (startextraflags & ~3) | ((startmstatus >> 11) & 3);
|
||||||
|
pc = mepc - 4;
|
||||||
|
} else {
|
||||||
|
switch(csrno) {
|
||||||
|
case 0:
|
||||||
|
if(extraflags & 3) {
|
||||||
|
Trap(TrapCode::EnvCallMMode);
|
||||||
|
} else {
|
||||||
|
Trap(TrapCode::EnvCallUMode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1: // breakpoint
|
||||||
|
Trap(TrapCode::Breakpoint);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Trap(TrapCode::IllegalInstruction);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
Trap(TrapCode::IllegalInstruction);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0x2f: // RV32A (0b00101111)
|
||||||
|
{
|
||||||
|
u32 rs1 = gpr[(ir >> 15) & 0x1f];
|
||||||
|
u32 rs2 = gpr[(ir >> 20) & 0x1f];
|
||||||
|
u32 irmid = (ir >> 27) & 0x1f;
|
||||||
|
|
||||||
|
// rs1 -= MINIRV32_RAM_IMAGE_OFFSET;
|
||||||
|
|
||||||
|
rs1 -= RamImageOffset;
|
||||||
|
|
||||||
|
// We don't implement load/store from UART or CLNT with RV32A here.
|
||||||
|
|
||||||
|
rval = bus->PeekWord(rs1);
|
||||||
|
if(trapped) {
|
||||||
|
rval = rs1 + RamImageOffset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 dowrite = 1;
|
||||||
|
switch(irmid) {
|
||||||
|
case 2: // LR.W (0b00010)
|
||||||
|
dowrite = 0;
|
||||||
|
extraflags = (extraflags & 0x07) | (rs1 << 3);
|
||||||
|
break;
|
||||||
|
case 3: // SC.W (0b00011) (Make sure we have a slot, and, it's
|
||||||
|
// valid)
|
||||||
|
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)
|
||||||
|
default:
|
||||||
|
Trap(TrapCode::IllegalInstruction);
|
||||||
|
dowrite = 0;
|
||||||
|
break; // Not supported.
|
||||||
|
}
|
||||||
|
if(dowrite)
|
||||||
|
bus->PokeWord(rs1, rs2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
Trap(TrapCode::IllegalInstruction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If some operation caused a trap break out after executing the instruction
|
||||||
|
if(trapped)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(rdid) {
|
||||||
|
gpr[rdid] = rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
pc += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(trapped) {
|
if(trapped) {
|
||||||
mcause = trapCode;
|
mcause = trapCode;
|
||||||
|
|
||||||
|
// If prefixed with 1 in MSB, it's an interrupt, not a trap.
|
||||||
if(trapCode & 0x80000000) {
|
if(trapCode & 0x80000000) {
|
||||||
mtval = 0;
|
mtval = 0;
|
||||||
// pc -= 4
|
pc += 4; // PC needs to point to where the PC will return to.
|
||||||
} else {
|
} else {
|
||||||
|
if(trapCode > 5 && trapCode <= 8)
|
||||||
|
mtval = rval;
|
||||||
|
else
|
||||||
|
mtval = pc;
|
||||||
|
}
|
||||||
|
mepc = pc; // TRICKY: The kernel advances mepc automatically.
|
||||||
|
// CSR( mstatus ) & 8 = MIE, & 0x80 = MPIE
|
||||||
|
// On an interrupt, the system moves current MIE into MPIE
|
||||||
|
mstatus = (mstatus & 0x08) << 4 | ((extraflags)&3) << 11;
|
||||||
|
pc = (mtvec - 4);
|
||||||
|
|
||||||
|
// If trapping, always enter machine mode.
|
||||||
|
extraflags |= 3;
|
||||||
|
|
||||||
|
// Reset trap flags
|
||||||
|
trapped = false;
|
||||||
|
trapCode = 0;
|
||||||
|
pc += 4;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if(cyclel > cycle)
|
||||||
|
cycleh++;
|
||||||
|
cyclel = cycle;
|
||||||
|
pc = pc;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace riscv
|
} // namespace riscv
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
namespace riscv::devices {
|
namespace riscv::devices {
|
||||||
|
|
||||||
constexpr static AddressT 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,
|
MATCHH_ADDRESS = ClntDevice::BASE_ADDRESS + 0x4004,
|
||||||
TIMERL_ADDRESS = ClntDevice::BASE_ADDRESS + 0xbff8,
|
TIMERL_ADDRESS = ClntDevice::BASE_ADDRESS + 0xbff8,
|
||||||
|
@ -28,7 +28,7 @@ namespace riscv::devices {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 ClntDevice::Peek(AddressT address) {
|
u32 ClntDevice::Peek(Address address) {
|
||||||
switch(address) {
|
switch(address) {
|
||||||
case TIMERL_ADDRESS:
|
case TIMERL_ADDRESS:
|
||||||
return timerCountLow;
|
return timerCountLow;
|
||||||
|
@ -47,7 +47,7 @@ namespace riscv::devices {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClntDevice::Poke(AddressT address, u32 value) {
|
void ClntDevice::Poke(Address address, u32 value) {
|
||||||
switch(address) {
|
switch(address) {
|
||||||
case MATCHL_ADDRESS:
|
case MATCHL_ADDRESS:
|
||||||
timerMatchLow = value;
|
timerMatchLow = value;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
namespace riscv::devices {
|
namespace riscv::devices {
|
||||||
|
|
||||||
RamDevice::RamDevice(AddressT base, AddressT 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);
|
||||||
|
@ -14,35 +14,35 @@ namespace riscv::devices {
|
||||||
delete[] memory;
|
delete[] memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
AddressT RamDevice::Base() const {
|
Address RamDevice::Base() const {
|
||||||
return memoryBase;
|
return memoryBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
AddressT RamDevice::Size() const {
|
Address RamDevice::Size() const {
|
||||||
return memorySize;
|
return memorySize;
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 RamDevice::PeekByte(AddressT address) {
|
u8 RamDevice::PeekByte(Address address) {
|
||||||
return memory[AddressToIndex<u8>(address)];
|
return memory[AddressToIndex<u8>(address)];
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 RamDevice::PeekShort(AddressT address) {
|
u16 RamDevice::PeekShort(Address address) {
|
||||||
return std::bit_cast<u16*>(memory)[AddressToIndex<u16>(address)];
|
return std::bit_cast<u16*>(memory)[AddressToIndex<u16>(address)];
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 RamDevice::PeekWord(AddressT address) {
|
u32 RamDevice::PeekWord(Address address) {
|
||||||
return std::bit_cast<u32*>(memory)[AddressToIndex<u32>(address)];
|
return std::bit_cast<u32*>(memory)[AddressToIndex<u32>(address)];
|
||||||
}
|
}
|
||||||
|
|
||||||
void RamDevice::PokeByte(AddressT address, u8 value) {
|
void RamDevice::PokeByte(Address address, u8 value) {
|
||||||
memory[AddressToIndex<u8>(address)] = value;
|
memory[AddressToIndex<u8>(address)] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RamDevice::PokeShort(AddressT address, u16 value) {
|
void RamDevice::PokeShort(Address address, u16 value) {
|
||||||
std::bit_cast<u16*>(memory)[AddressToIndex<u16>(address)] = value;
|
std::bit_cast<u16*>(memory)[AddressToIndex<u16>(address)] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RamDevice::PokeWord(AddressT address, u32 value) {
|
void RamDevice::PokeWord(Address address, u32 value) {
|
||||||
std::bit_cast<u32*>(memory)[AddressToIndex<u32>(address)] = value;
|
std::bit_cast<u32*>(memory)[AddressToIndex<u32>(address)] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
|
#include <lucore/Logger.hpp>
|
||||||
#include <riscv/Devices/SysconDevice.hpp>
|
#include <riscv/Devices/SysconDevice.hpp>
|
||||||
#include <riscv/System.hpp>
|
#include <riscv/System.hpp>
|
||||||
|
|
||||||
#include <lucore/Logger.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(AddressT address) {
|
u32 SysconDevice::Peek(Address address) {
|
||||||
|
|
||||||
lucore::LogInfo("SYSCON({}) Peek @ 0x{:08x}", static_cast<void*>(this), address);
|
lucore::LogInfo("SYSCON({}) Peek @ 0x{:08x}", static_cast<void*>(this), address);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysconDevice::Poke(AddressT address, u32 value) {
|
void SysconDevice::Poke(Address address, u32 value) {
|
||||||
lucore::LogInfo("SYSCON({}) Poke @ 0x{:08x}: 0x{:08x}", static_cast<void*>(this), address, 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)
|
||||||
|
|
|
@ -1,19 +1,16 @@
|
||||||
#include <riscv/Bus.hpp>
|
//! A test harness for testing the riscv library.
|
||||||
#include <riscv/CPU.hpp>
|
#include <riscv/System.hpp>
|
||||||
#include <riscv/Devices/RamDevice.hpp>
|
|
||||||
|
|
||||||
#include "riscv/Types.hpp"
|
|
||||||
|
|
||||||
/// simple 16550 UART implementation
|
/// simple 16550 UART implementation
|
||||||
struct SimpleUartDevice : public riscv::Bus::MmioDevice {
|
struct SimpleUartDevice : public riscv::Bus::MmioDevice {
|
||||||
constexpr static riscv::AddressT BASE_ADDRESS = 0x10000000;
|
constexpr static riscv::Address BASE_ADDRESS = 0x10000000;
|
||||||
|
|
||||||
riscv::AddressT Base() const override { return BASE_ADDRESS; }
|
riscv::Address Base() const override { return BASE_ADDRESS; }
|
||||||
|
|
||||||
riscv::AddressT Size() const override { return 5; }
|
riscv::Address Size() const override { return 5; }
|
||||||
|
|
||||||
// TODO: emulate properly
|
// TODO: emulate properly
|
||||||
u32 Peek(riscv::AddressT address) override {
|
u32 Peek(riscv::Address address) override {
|
||||||
switch(address) {
|
switch(address) {
|
||||||
case BASE_ADDRESS:
|
case BASE_ADDRESS:
|
||||||
break;
|
break;
|
||||||
|
@ -24,7 +21,7 @@ struct SimpleUartDevice : public riscv::Bus::MmioDevice {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Poke(riscv::AddressT address, u32 value) override {
|
void Poke(riscv::Address address, u32 value) override {
|
||||||
if(address == BASE_ADDRESS) { // write to data buffer
|
if(address == BASE_ADDRESS) { // write to data buffer
|
||||||
printf("%c\n", value);
|
printf("%c\n", value);
|
||||||
}
|
}
|
||||||
|
@ -32,5 +29,7 @@ struct SimpleUartDevice : public riscv::Bus::MmioDevice {
|
||||||
};
|
};
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
auto system = riscv::System::WithMemory(128 * 1024);
|
||||||
|
system->AddDeviceToBus(new SimpleUartDevice);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue