no one will know the pain i go through

This commit is contained in:
Lily Tsuru 2023-07-23 18:13:03 -04:00
parent abe9b78b1a
commit 38e7fc4646
16 changed files with 600 additions and 111 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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,

View File

@ -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;

View File

@ -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 {};

View File

@ -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;
}; };

View File

@ -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();

View File

@ -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;

View File

@ -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.

View File

@ -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);
@ -16,7 +22,7 @@ namespace riscv {
// If this is because of an interrupt // If this is because of an interrupt
if(trapCode & 0x80000000) { if(trapCode & 0x80000000) {
// Set MIP.MEIP. // Set MIP.MEIP.
//mip |= 1 << 11; // mip |= 1 << 11;
// Always clear WFI bit. // Always clear WFI bit.
extraflags &= ~4; extraflags &= ~4;
} }
@ -25,57 +31,538 @@ namespace riscv {
void CPU::TimerInterrupt() { void CPU::TimerInterrupt() {
// Set MIP.MTIP. // Set MIP.MTIP.
mip |= 1 << 7; mip |= 1 << 7;
//extraflags &= ~4; // extraflags &= ~4;
//trapped = true; // trapped = true;
//trapCode = 0x80000007; // trapCode = 0x80000007;
} }
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

View File

@ -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;

View File

@ -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;
} }

View File

@ -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)

View File

@ -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;
} }