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
|
||||
BreakStringLiterals: false
|
||||
|
||||
ColumnLimit: 100
|
||||
ColumnLimit: 150
|
||||
CompactNamespaces: false
|
||||
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
|
||||
//namespace lucore {
|
||||
using u8 = std::uint8_t;
|
||||
using s8 = std::int8_t;
|
||||
using i8 = std::int8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using s16 = std::int16_t;
|
||||
using i16 = std::int16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using s32 = std::int32_t;
|
||||
using i32 = std::int32_t;
|
||||
using u64 = std::uint64_t;
|
||||
using s64 = std::int64_t;
|
||||
using i64 = std::int64_t;
|
||||
using usize = std::size_t;
|
||||
using ssize = std::intptr_t;
|
||||
using isize = std::intptr_t;
|
||||
|
||||
//} // namespace lucore
|
||||
|
|
|
@ -74,21 +74,21 @@ namespace riscv {
|
|||
|
||||
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?
|
||||
/// This should not change during execution.
|
||||
virtual AddressT Size() const = 0;
|
||||
virtual Address Size() const = 0;
|
||||
|
||||
/// Peek() -> reads a value from this device.
|
||||
virtual u8 PeekByte(AddressT address) = 0;
|
||||
virtual u16 PeekShort(AddressT address) = 0;
|
||||
virtual u32 PeekWord(AddressT address) = 0;
|
||||
virtual u8 PeekByte(Address address) = 0;
|
||||
virtual u16 PeekShort(Address address) = 0;
|
||||
virtual u32 PeekWord(Address address) = 0;
|
||||
|
||||
/// Poke() -> Writes a value to this device's space in memory
|
||||
virtual void PokeByte(AddressT address, u8 value) = 0;
|
||||
virtual void PokeShort(AddressT address, u16 value) = 0;
|
||||
virtual void PokeWord(AddressT address, u32 value) = 0;
|
||||
virtual void PokeByte(Address address, u8 value) = 0;
|
||||
virtual void PokeShort(Address address, u16 value) = 0;
|
||||
virtual void PokeWord(Address address, u32 value) = 0;
|
||||
};
|
||||
|
||||
/// A device in the MMIO region.
|
||||
|
@ -98,14 +98,14 @@ namespace riscv {
|
|||
|
||||
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?
|
||||
/// This should not change during execution.
|
||||
virtual AddressT Size() const = 0;
|
||||
virtual Address Size() const = 0;
|
||||
|
||||
virtual u32 Peek(AddressT address) = 0;
|
||||
virtual void Poke(AddressT address, u32 value) = 0;
|
||||
virtual u32 Peek(Address address) = 0;
|
||||
virtual void Poke(Address address, u32 value) = 0;
|
||||
};
|
||||
|
||||
/// Bus destructor.
|
||||
|
@ -128,20 +128,20 @@ namespace riscv {
|
|||
/// Clock all clocked devices mapped onto the bus..
|
||||
void Clock();
|
||||
|
||||
u8 PeekByte(AddressT address);
|
||||
u16 PeekShort(AddressT address);
|
||||
u32 PeekWord(AddressT address);
|
||||
u8 PeekByte(Address address);
|
||||
u16 PeekShort(Address address);
|
||||
u32 PeekWord(Address address);
|
||||
|
||||
void PokeByte(AddressT address, u8 value);
|
||||
void PokeShort(AddressT address, u16 value);
|
||||
void PokeWord(AddressT address, u32 value);
|
||||
void PokeByte(Address address, u8 value);
|
||||
void PokeShort(Address address, u16 value);
|
||||
void PokeWord(Address address, u32 value);
|
||||
|
||||
CPU* GetCPU() { return cpu; }
|
||||
|
||||
private:
|
||||
|
||||
// TODO: version which takes Device::BasicType
|
||||
Bus::Device* FindDeviceForAddress(AddressT address) const;
|
||||
Bus::Device* FindDeviceForAddress(Address address) const;
|
||||
|
||||
CPU* cpu;
|
||||
|
||||
|
@ -150,8 +150,8 @@ namespace riscv {
|
|||
|
||||
// TODO: if these end up being a hotpath replace with ankerl::unordered_dense
|
||||
// (or just use the [devices] vector, probably.)
|
||||
std::unordered_map<AddressT, MemoryDevice*> mapped_devices;
|
||||
std::unordered_map<AddressT, MmioDevice*> mmio_devices;
|
||||
std::unordered_map<Address, MemoryDevice*> mapped_devices;
|
||||
std::unordered_map<Address, MmioDevice*> mmio_devices;
|
||||
};
|
||||
|
||||
} // namespace riscv
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace riscv {
|
|||
bool Clocked() const override { return true; }
|
||||
void Clock() override;
|
||||
|
||||
/// Trap the CPU. Bus devices can call this.
|
||||
void Trap(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?)
|
||||
|
||||
/// CPU state
|
||||
GeneralPurposeRegisters gpr;
|
||||
u32 pc;
|
||||
u32 mstatus;
|
||||
|
@ -38,9 +40,9 @@ namespace riscv {
|
|||
|
||||
private:
|
||||
/// 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 };
|
||||
|
||||
/// Set by [CPU::Trap] for the trap code.
|
||||
u32 trapCode { 0 };
|
||||
|
||||
u32 Step(u32 instCount);
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
|
||||
namespace riscv {
|
||||
|
||||
// Note that
|
||||
/// Most possible trap codes.
|
||||
enum class TrapCode : u32 {
|
||||
InstructionAddressMisaligned,
|
||||
InstructionAddressFault,
|
||||
InstructionAccessFault,
|
||||
IllegalInstruction,
|
||||
Breakpoint, // I see what you did there ;)
|
||||
LoadAddressMisaligned,
|
||||
|
|
|
@ -7,17 +7,17 @@ namespace riscv::devices {
|
|||
/// Partial implementation of a CLINT.
|
||||
/// The timer device is implemented, SIP is not.
|
||||
struct ClntDevice : public Bus::MmioDevice {
|
||||
constexpr static AddressT BASE_ADDRESS = 0x11000000;
|
||||
constexpr static Address BASE_ADDRESS = 0x11000000;
|
||||
|
||||
AddressT Base() const override { return BASE_ADDRESS; }
|
||||
AddressT Size() const override { return 0xbfff; }
|
||||
Address Base() const override { return BASE_ADDRESS; }
|
||||
Address Size() const override { return 0xbfff; }
|
||||
|
||||
bool Clocked() const override { return true; }
|
||||
void Clock() override;
|
||||
|
||||
|
||||
u32 Peek(AddressT address) override;
|
||||
void Poke(AddressT address, u32 value) override;
|
||||
u32 Peek(Address address) override;
|
||||
void Poke(Address address, u32 value) override;
|
||||
|
||||
private:
|
||||
u32 timerCountHigh;
|
||||
|
|
|
@ -6,31 +6,31 @@ namespace riscv::devices {
|
|||
|
||||
/// A block of RAM which can be used by the CPU.
|
||||
struct RamDevice : public Bus::MemoryDevice {
|
||||
RamDevice(AddressT base, AddressT size);
|
||||
RamDevice(Address base, Address size);
|
||||
virtual ~RamDevice();
|
||||
|
||||
// Implementation of Device interface
|
||||
|
||||
AddressT Base() const override;
|
||||
AddressT Size() const override;
|
||||
Address Base() const override;
|
||||
Address Size() const override;
|
||||
|
||||
|
||||
u8 PeekByte(AddressT address) override;
|
||||
u16 PeekShort(AddressT address) override;
|
||||
u32 PeekWord(AddressT address) override;
|
||||
u8 PeekByte(Address address) override;
|
||||
u16 PeekShort(Address address) override;
|
||||
u32 PeekWord(Address address) override;
|
||||
|
||||
void PokeByte(AddressT address, u8 value) override;
|
||||
void PokeShort(AddressT address, u16 value) override;
|
||||
void PokeWord(AddressT address, u32 value) override;
|
||||
void PokeByte(Address address, u8 value) override;
|
||||
void PokeShort(Address address, u16 value) override;
|
||||
void PokeWord(Address address, u32 value) override;
|
||||
|
||||
private:
|
||||
/// helper used for implementing Peek/Poke API
|
||||
template <class T>
|
||||
constexpr usize AddressToIndex(AddressT address) {
|
||||
constexpr usize AddressToIndex(Address address) {
|
||||
return ((address - memoryBase) % memorySize) / sizeof(T);
|
||||
}
|
||||
|
||||
AddressT memoryBase {};
|
||||
Address memoryBase {};
|
||||
|
||||
u8* memory {};
|
||||
usize memorySize {};
|
||||
|
|
|
@ -7,14 +7,14 @@ namespace riscv { struct System; }
|
|||
namespace riscv::devices {
|
||||
/// RISC-V SYSCON device. This will later talk to the system to tell it things.
|
||||
struct SysconDevice : public Bus::MmioDevice {
|
||||
constexpr static AddressT BASE_ADDRESS = 0x11100000;
|
||||
constexpr static Address BASE_ADDRESS = 0x11100000;
|
||||
|
||||
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;
|
||||
void Poke(AddressT address, u32 value) override;
|
||||
u32 Peek(Address address) override;
|
||||
void Poke(Address address, u32 value) override;
|
||||
private:
|
||||
System* system;
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace riscv {
|
|||
struct System {
|
||||
/// Create a basic system with the basic periphials created.
|
||||
/// All other periphials should be managed by the creator of this System
|
||||
static System* WithMemory(AddressT ramSize);
|
||||
static System* WithMemory(Address ramSize);
|
||||
|
||||
~System();
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
namespace riscv {
|
||||
|
||||
/// 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();
|
||||
}
|
||||
|
||||
u8 Bus::PeekByte(AddressT address) {
|
||||
u8 Bus::PeekByte(Address address) {
|
||||
if(auto dev = FindDeviceForAddress(address); dev)
|
||||
return dev->Upcast<MemoryDevice*>()->PeekByte(address);
|
||||
else {
|
||||
|
@ -70,7 +70,7 @@ namespace riscv {
|
|||
}
|
||||
}
|
||||
|
||||
u16 Bus::PeekShort(AddressT address) {
|
||||
u16 Bus::PeekShort(Address address) {
|
||||
if(auto dev = FindDeviceForAddress(address); dev)
|
||||
return dev->Upcast<MemoryDevice*>()->PeekShort(address);
|
||||
else {
|
||||
|
@ -79,7 +79,7 @@ namespace riscv {
|
|||
}
|
||||
}
|
||||
|
||||
u32 Bus::PeekWord(AddressT address) {
|
||||
u32 Bus::PeekWord(Address address) {
|
||||
if(auto dev = FindDeviceForAddress(address); dev) {
|
||||
if(dev->IsA<MmioDevice*>())
|
||||
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)
|
||||
return dev->Upcast<MemoryDevice*>()->PokeByte(address, value);
|
||||
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)
|
||||
return dev->Upcast<MemoryDevice*>()->PokeShort(address, value);
|
||||
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(dev->IsA<MmioDevice*>())
|
||||
dev->Upcast<MmioDevice*>()->Poke(address, value);
|
||||
|
@ -118,8 +118,8 @@ namespace riscv {
|
|||
}
|
||||
}
|
||||
|
||||
Bus::Device* Bus::FindDeviceForAddress(AddressT address) const {
|
||||
auto try_find_device = [&](auto container, AddressT address) {
|
||||
Bus::Device* Bus::FindDeviceForAddress(Address address) const {
|
||||
auto try_find_device = [&](auto container, Address address) {
|
||||
return std::find_if(container.begin(), container.end(), [&](const auto& pair) {
|
||||
return
|
||||
// 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/CPU.hpp>
|
||||
|
||||
#include "riscv/CPUTypes.hpp"
|
||||
|
||||
namespace riscv {
|
||||
|
||||
constexpr static Address RamImageOffset = 0x80000000;
|
||||
|
||||
void CPU::Clock() {
|
||||
// do the thing
|
||||
Step(1024);
|
||||
|
@ -32,50 +38,531 @@ namespace riscv {
|
|||
|
||||
u32 CPU::Step(u32 instCount) {
|
||||
auto interruptsInFlight = [&]() {
|
||||
return
|
||||
(mip & (1 << 7) /*|| mip & (1 << 11)*/) &&
|
||||
(mie & (1 << 7) /*|| mie & (1 << 11)*/) &&
|
||||
(mstatus & 0x8 /*mie*/);
|
||||
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 rdid = 0;
|
||||
u32 rval = 0;
|
||||
u32 pc = this->pc;
|
||||
u32 cycle = this->cyclel;
|
||||
|
||||
if(interruptsInFlight()) {
|
||||
Trap(0x80000007);
|
||||
} else {
|
||||
for(u32 iInst = 0; iInst < instCount; ++iInst) {
|
||||
|
||||
auto ofs_pc = pc - RamImageOffset;
|
||||
cycle++;
|
||||
|
||||
if(pc & 3) {
|
||||
if(ofs_pc & 3) {
|
||||
Trap(TrapCode::InstructionAddressMisaligned);
|
||||
break;
|
||||
} else {
|
||||
auto ir = bus->PeekWord(pc);
|
||||
auto rdid = (ir >> 7) & 0x1f;
|
||||
//switch(ir & 0x7f) {
|
||||
|
||||
//}
|
||||
auto ir = bus->PeekWord(ofs_pc);
|
||||
if(trapped) {
|
||||
// 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) {
|
||||
mcause = trapCode;
|
||||
|
||||
// If prefixed with 1 in MSB, it's an interrupt, not a trap.
|
||||
if(trapCode & 0x80000000) {
|
||||
mtval = 0;
|
||||
// pc -= 4
|
||||
pc += 4; // PC needs to point to where the PC will return to.
|
||||
} 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
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
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,
|
||||
MATCHH_ADDRESS = ClntDevice::BASE_ADDRESS + 0x4004,
|
||||
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) {
|
||||
case TIMERL_ADDRESS:
|
||||
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) {
|
||||
case MATCHL_ADDRESS:
|
||||
timerMatchLow = value;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
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];
|
||||
LUCORE_CHECK(memory, "Could not allocate buffer for memory device with size 0x{:08x}.",
|
||||
size);
|
||||
|
@ -14,35 +14,35 @@ namespace riscv::devices {
|
|||
delete[] memory;
|
||||
}
|
||||
|
||||
AddressT RamDevice::Base() const {
|
||||
Address RamDevice::Base() const {
|
||||
return memoryBase;
|
||||
}
|
||||
|
||||
AddressT RamDevice::Size() const {
|
||||
Address RamDevice::Size() const {
|
||||
return memorySize;
|
||||
}
|
||||
|
||||
u8 RamDevice::PeekByte(AddressT address) {
|
||||
u8 RamDevice::PeekByte(Address 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)];
|
||||
}
|
||||
|
||||
u32 RamDevice::PeekWord(AddressT address) {
|
||||
u32 RamDevice::PeekWord(Address 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;
|
||||
}
|
||||
|
||||
void RamDevice::PokeShort(AddressT address, u16 value) {
|
||||
void RamDevice::PokeShort(Address address, u16 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
#include <lucore/Logger.hpp>
|
||||
#include <riscv/Devices/SysconDevice.hpp>
|
||||
#include <riscv/System.hpp>
|
||||
|
||||
#include <lucore/Logger.hpp>
|
||||
// TODO: This device is largely a stub. It needs to be implemented!
|
||||
|
||||
namespace riscv::devices {
|
||||
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void SysconDevice::Poke(AddressT address, u32 value) {
|
||||
lucore::LogInfo("SYSCON({}) Poke @ 0x{:08x}: 0x{:08x}", static_cast<void*>(this), address, 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(value == 0x5555)
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
#include <riscv/Bus.hpp>
|
||||
#include <riscv/CPU.hpp>
|
||||
#include <riscv/Devices/RamDevice.hpp>
|
||||
|
||||
#include "riscv/Types.hpp"
|
||||
//! A test harness for testing the riscv library.
|
||||
#include <riscv/System.hpp>
|
||||
|
||||
/// simple 16550 UART implementation
|
||||
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
|
||||
u32 Peek(riscv::AddressT address) override {
|
||||
u32 Peek(riscv::Address address) override {
|
||||
switch(address) {
|
||||
case BASE_ADDRESS:
|
||||
break;
|
||||
|
@ -24,7 +21,7 @@ struct SimpleUartDevice : public riscv::Bus::MmioDevice {
|
|||
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
|
||||
printf("%c\n", value);
|
||||
}
|
||||
|
@ -32,5 +29,7 @@ struct SimpleUartDevice : public riscv::Bus::MmioDevice {
|
|||
};
|
||||
|
||||
int main() {
|
||||
auto system = riscv::System::WithMemory(128 * 1024);
|
||||
system->AddDeviceToBus(new SimpleUartDevice);
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue