riscv: IT WORKS
finally. once i'm sure it's 100% working i can probably like, develop this addon finally
This commit is contained in:
parent
38e7fc4646
commit
878990a921
|
@ -9,3 +9,7 @@ This is based off [cnlohr/mini-rv32ima](https://github.com/cnlohr/mini-rv32ima),
|
||||||
- Moved *ALL* device and MMIO code to seperate interfaces
|
- Moved *ALL* device and MMIO code to seperate interfaces
|
||||||
|
|
||||||
Depends on lucore.
|
Depends on lucore.
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
TBD (if this is moved to another repo). See the riscv_test_harness project.
|
||||||
|
|
|
@ -6,6 +6,7 @@ namespace riscv {
|
||||||
|
|
||||||
/// The CPU core.
|
/// The CPU core.
|
||||||
struct CPU : Bus::Device {
|
struct CPU : Bus::Device {
|
||||||
|
BasicType Type() const override { return BasicType::Cpu; }
|
||||||
bool Clocked() const override { return true; }
|
bool Clocked() const override { return true; }
|
||||||
void Clock() override;
|
void Clock() override;
|
||||||
|
|
||||||
|
@ -15,6 +16,17 @@ namespace riscv {
|
||||||
|
|
||||||
void TimerInterrupt();
|
void TimerInterrupt();
|
||||||
|
|
||||||
|
constexpr CPU() {
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void Reset() {
|
||||||
|
// Initalize some state. We're cool like that :)
|
||||||
|
pc = 0x80000000;
|
||||||
|
gpr[10] = 0x0; // HART id
|
||||||
|
extraflags |= 3; // Start in Machine mode
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Handlers for CSR read/write (if we need it?)
|
// TODO: Handlers for CSR read/write (if we need it?)
|
||||||
|
|
||||||
/// CPU state
|
/// CPU state
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <riscv/Types.hpp>
|
#include <riscv/Types.hpp>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
namespace riscv {
|
namespace riscv {
|
||||||
|
|
||||||
|
@ -25,41 +26,78 @@ namespace riscv {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Gpr : u8 {
|
enum class Gpr : u8 {
|
||||||
X0, // zero
|
Zero,
|
||||||
X1,
|
Ra,
|
||||||
X2,
|
Sp,
|
||||||
X3,
|
Gp,
|
||||||
X4,
|
Tp,
|
||||||
X5,
|
T0,
|
||||||
X6,
|
T1,
|
||||||
X7,
|
T2,
|
||||||
X8,
|
S0, // also `fp`
|
||||||
X9,
|
S1,
|
||||||
X10,
|
A0,
|
||||||
X11,
|
A1,
|
||||||
X12,
|
A2,
|
||||||
X13,
|
A3,
|
||||||
X14,
|
A4,
|
||||||
X15,
|
A5,
|
||||||
X16,
|
A6,
|
||||||
X17,
|
A7,
|
||||||
X18,
|
S2,
|
||||||
X19,
|
S3,
|
||||||
X20,
|
S4,
|
||||||
X21,
|
S5,
|
||||||
X22,
|
S6,
|
||||||
X23,
|
S7,
|
||||||
X24,
|
S8,
|
||||||
X25,
|
S9,
|
||||||
X26,
|
S10,
|
||||||
X27,
|
S11,
|
||||||
X28,
|
T3,
|
||||||
X29,
|
T4,
|
||||||
X30,
|
T5,
|
||||||
X31,
|
T6
|
||||||
PC
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr std::string_view RegName(Gpr gpr) {
|
||||||
|
std::string_view table[] = {
|
||||||
|
"zero",
|
||||||
|
"ra",
|
||||||
|
"sp",
|
||||||
|
"gp",
|
||||||
|
"tp",
|
||||||
|
"t0",
|
||||||
|
"t1",
|
||||||
|
"t2",
|
||||||
|
"s0",
|
||||||
|
"s1",
|
||||||
|
"a0",
|
||||||
|
"a1",
|
||||||
|
"a2",
|
||||||
|
"a3",
|
||||||
|
"a4",
|
||||||
|
"a5",
|
||||||
|
"a6",
|
||||||
|
"a7",
|
||||||
|
"s2",
|
||||||
|
"s3",
|
||||||
|
"s4",
|
||||||
|
"s5",
|
||||||
|
"s6",
|
||||||
|
"s7",
|
||||||
|
"s8",
|
||||||
|
"s9",
|
||||||
|
"s10",
|
||||||
|
"s11",
|
||||||
|
"t3",
|
||||||
|
"t4",
|
||||||
|
"t5",
|
||||||
|
"t6"
|
||||||
|
};
|
||||||
|
return table[static_cast<usize>(gpr)];
|
||||||
|
}
|
||||||
|
|
||||||
/// Container for GPRs which can be Statically Typed or not depending on use case.
|
/// Container for GPRs which can be Statically Typed or not depending on use case.
|
||||||
/// Pretty cool, huh?
|
/// Pretty cool, huh?
|
||||||
struct GeneralPurposeRegisters {
|
struct GeneralPurposeRegisters {
|
||||||
|
|
|
@ -14,6 +14,9 @@ namespace riscv::devices {
|
||||||
Address Base() const override;
|
Address Base() const override;
|
||||||
Address Size() const override;
|
Address Size() const override;
|
||||||
|
|
||||||
|
u8* Raw() const {
|
||||||
|
return memory;
|
||||||
|
}
|
||||||
|
|
||||||
u8 PeekByte(Address address) override;
|
u8 PeekByte(Address address) override;
|
||||||
u16 PeekShort(Address address) override;
|
u16 PeekShort(Address address) override;
|
||||||
|
|
|
@ -11,6 +11,7 @@ namespace riscv::devices {
|
||||||
|
|
||||||
SysconDevice(System* system);
|
SysconDevice(System* system);
|
||||||
|
|
||||||
|
Address Base() const override { return BASE_ADDRESS; }
|
||||||
Address 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(Address address) override;
|
u32 Peek(Address address) override;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#include <riscv/Bus.hpp>
|
#include <riscv/Bus.hpp>
|
||||||
#include <riscv/CPU.hpp>
|
#include <riscv/CPU.hpp>
|
||||||
|
#include <riscv/Devices/ClntDevice.hpp>
|
||||||
#include <riscv/Devices/RamDevice.hpp>
|
#include <riscv/Devices/RamDevice.hpp>
|
||||||
#include <riscv/Devices/SysconDevice.hpp>
|
#include <riscv/Devices/SysconDevice.hpp>
|
||||||
#include <riscv/Devices/ClntDevice.hpp>
|
|
||||||
|
|
||||||
namespace riscv {
|
namespace riscv {
|
||||||
|
|
||||||
|
@ -10,25 +10,13 @@ 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(Address ramSize);
|
static System* Create(Address ramSize);
|
||||||
|
|
||||||
~System();
|
~System();
|
||||||
|
|
||||||
void AddDeviceToBus(Bus::Device* device);
|
void Step();
|
||||||
|
|
||||||
/// returns false if the cpu broke execution
|
// TODO: callbacks for SYSCON PowerOff and Reboot.
|
||||||
bool Step();
|
|
||||||
|
|
||||||
CPU* GetCPU();
|
|
||||||
Bus* GetBus();
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// How many Cycle() calls will the bus get
|
|
||||||
/// (also decides ipsRate)
|
|
||||||
u32 cycleRate;
|
|
||||||
|
|
||||||
/// How many instructions will the CPU execute each step
|
|
||||||
u32 ipsRate;
|
|
||||||
|
|
||||||
Bus* bus;
|
Bus* bus;
|
||||||
|
|
||||||
|
@ -37,6 +25,9 @@ namespace riscv {
|
||||||
devices::RamDevice* ram;
|
devices::RamDevice* ram;
|
||||||
devices::SysconDevice* syscon;
|
devices::SysconDevice* syscon;
|
||||||
devices::ClntDevice* clnt;
|
devices::ClntDevice* clnt;
|
||||||
|
|
||||||
|
private:
|
||||||
|
System() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace riscv
|
} // namespace riscv
|
||||||
|
|
|
@ -14,6 +14,8 @@ namespace riscv {
|
||||||
if(!device)
|
if(!device)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
device->Attached(this);
|
||||||
|
|
||||||
if(device->IsA<CPU*>()) {
|
if(device->IsA<CPU*>()) {
|
||||||
// Return early to avoid putting the CPU pointer inside the devices vector.
|
// Return early to avoid putting the CPU pointer inside the devices vector.
|
||||||
// We do not actually own the CPU.
|
// We do not actually own the CPU.
|
||||||
|
@ -119,7 +121,7 @@ namespace riscv {
|
||||||
}
|
}
|
||||||
|
|
||||||
Bus::Device* Bus::FindDeviceForAddress(Address address) const {
|
Bus::Device* Bus::FindDeviceForAddress(Address address) const {
|
||||||
auto try_find_device = [&](auto container, Address address) {
|
auto try_find_device = [&](const 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.
|
||||||
|
|
|
@ -3,11 +3,12 @@
|
||||||
#include <riscv/Bus.hpp>
|
#include <riscv/Bus.hpp>
|
||||||
#include <riscv/CPU.hpp>
|
#include <riscv/CPU.hpp>
|
||||||
|
|
||||||
#include "riscv/CPUTypes.hpp"
|
#include <lucore/Logger.hpp>
|
||||||
|
|
||||||
namespace riscv {
|
namespace riscv {
|
||||||
|
|
||||||
constexpr static Address RamImageOffset = 0x80000000;
|
// Not needed
|
||||||
|
//constexpr static Address RamImageOffset = 0x80000000;
|
||||||
|
|
||||||
void CPU::Clock() {
|
void CPU::Clock() {
|
||||||
// do the thing
|
// do the thing
|
||||||
|
@ -47,31 +48,35 @@ namespace riscv {
|
||||||
|
|
||||||
u32 rdid = 0;
|
u32 rdid = 0;
|
||||||
u32 rval = 0;
|
u32 rval = 0;
|
||||||
u32 pc = this->pc;
|
|
||||||
u32 cycle = this->cyclel;
|
u32 cycle = this->cyclel;
|
||||||
|
|
||||||
if(interruptsInFlight()) {
|
if(interruptsInFlight()) {
|
||||||
Trap(0x80000007);
|
Trap(0x80000007);
|
||||||
} else {
|
} else {
|
||||||
for(u32 iInst = 0; iInst < instCount; ++iInst) {
|
for(u32 iInst = 0; iInst < instCount; ++iInst) {
|
||||||
auto ofs_pc = pc - RamImageOffset;
|
rdid = 0; // force it to gpr 0 (zero), which is not writable
|
||||||
cycle++;
|
cycle++;
|
||||||
|
|
||||||
if(ofs_pc & 3) {
|
//lucore::LogInfo("[CPU] pc @ 0x{:08x}", pc);
|
||||||
|
|
||||||
|
if((pc & 3)) {
|
||||||
|
lucore::LogWarning("[CPU] misaligned jump target.. 0x{:08x}", pc);
|
||||||
Trap(TrapCode::InstructionAddressMisaligned);
|
Trap(TrapCode::InstructionAddressMisaligned);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
auto ir = bus->PeekWord(ofs_pc);
|
auto ir = bus->PeekWord(pc);
|
||||||
if(trapped) {
|
if(trapped) {
|
||||||
// Overwrite the trap that the bus generated. This might not really work out
|
// 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
|
// 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
|
// space is emulated using the [Bus] class, so there is no heap write issue
|
||||||
// that could be caused by leaving it unbound).
|
// that could be caused by leaving it unbound).
|
||||||
Trap(TrapCode::InstructionAccessFault);
|
Trap(TrapCode::InstructionAccessFault);
|
||||||
rval = ofs_pc + RamImageOffset;
|
rval = pc;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//lucore::LogInfo("[CPU] fetch 0x{:08x} 0x{:08x}", pc, ir);
|
||||||
|
|
||||||
rdid = (ir >> 7) & 0x1f;
|
rdid = (ir >> 7) & 0x1f;
|
||||||
|
|
||||||
// Do the thing!
|
// Do the thing!
|
||||||
|
@ -91,6 +96,7 @@ namespace riscv {
|
||||||
if(reladdy & 0x00100000)
|
if(reladdy & 0x00100000)
|
||||||
reladdy |= 0xffe00000;
|
reladdy |= 0xffe00000;
|
||||||
rval = pc + 4;
|
rval = pc + 4;
|
||||||
|
//lucore::LogInfo("j/al 0x{:08x}", pc + reladdy);
|
||||||
pc = pc + reladdy - 4;
|
pc = pc + reladdy - 4;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -100,6 +106,7 @@ namespace riscv {
|
||||||
i32 imm_se = imm | ((imm & 0x800) ? 0xfffff000 : 0);
|
i32 imm_se = imm | ((imm & 0x800) ? 0xfffff000 : 0);
|
||||||
rval = pc + 4;
|
rval = pc + 4;
|
||||||
pc = ((gpr[((ir >> 15) & 0x1f)] + imm_se) & ~1) - 4;
|
pc = ((gpr[((ir >> 15) & 0x1f)] + imm_se) & ~1) - 4;
|
||||||
|
//lucore::LogInfo("jalr {}, 0x{:08x}", RegName(static_cast<Gpr>(((ir >> 15) & 0x1f))), ((gpr[((ir >> 15) & 0x1f)] + imm_se) & ~1) - 4);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,8 +160,6 @@ namespace riscv {
|
||||||
i32 imm_se = imm | ((imm & 0x800) ? 0xfffff000 : 0);
|
i32 imm_se = imm | ((imm & 0x800) ? 0xfffff000 : 0);
|
||||||
u32 rsval = rs1 + imm_se;
|
u32 rsval = rs1 + imm_se;
|
||||||
|
|
||||||
rsval -= RamImageOffset;
|
|
||||||
|
|
||||||
switch((ir >> 12) & 0x7) {
|
switch((ir >> 12) & 0x7) {
|
||||||
// LB, LH, LW, LBU, LHU
|
// LB, LH, LW, LBU, LHU
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -178,7 +183,7 @@ namespace riscv {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(trapped) {
|
if(trapped) {
|
||||||
rval = rsval + RamImageOffset;
|
rval = rsval;// + RamImageOffset;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -188,7 +193,7 @@ namespace riscv {
|
||||||
u32 addy = ((ir >> 7) & 0x1f) | ((ir & 0xfe000000) >> 20);
|
u32 addy = ((ir >> 7) & 0x1f) | ((ir & 0xfe000000) >> 20);
|
||||||
if(addy & 0x800)
|
if(addy & 0x800)
|
||||||
addy |= 0xfffff000;
|
addy |= 0xfffff000;
|
||||||
addy += rs1 - RamImageOffset;
|
addy += rs1;
|
||||||
rdid = 0;
|
rdid = 0;
|
||||||
|
|
||||||
switch((ir >> 12) & 0x7) {
|
switch((ir >> 12) & 0x7) {
|
||||||
|
@ -200,6 +205,7 @@ namespace riscv {
|
||||||
bus->PokeShort(addy, rs2);
|
bus->PokeShort(addy, rs2);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
|
//lucore::LogInfo("storeWord(0x{:08x}, 0x{:08x})", addy, rs2);
|
||||||
bus->PokeWord(addy, rs2);
|
bus->PokeWord(addy, rs2);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -207,7 +213,7 @@ namespace riscv {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(trapped) {
|
if(trapped) {
|
||||||
rval = addy + RamImageOffset;
|
rval = addy;// + RamImageOffset;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -457,13 +463,13 @@ namespace riscv {
|
||||||
|
|
||||||
// rs1 -= MINIRV32_RAM_IMAGE_OFFSET;
|
// rs1 -= MINIRV32_RAM_IMAGE_OFFSET;
|
||||||
|
|
||||||
rs1 -= RamImageOffset;
|
//rs1 -= RamImageOffset;
|
||||||
|
|
||||||
// We don't implement load/store from UART or CLNT with RV32A here.
|
// We don't implement load/store from UART or CLNT with RV32A here.
|
||||||
|
|
||||||
rval = bus->PeekWord(rs1);
|
rval = bus->PeekWord(rs1);
|
||||||
if(trapped) {
|
if(trapped) {
|
||||||
rval = rs1 + RamImageOffset;
|
rval = rs1;// + RamImageOffset;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -523,6 +529,7 @@ namespace riscv {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if(rdid) {
|
if(rdid) {
|
||||||
|
//lucore::LogInfo("writing register {} -> 0x{:08x}", RegName(static_cast<Gpr>(rdid)), rval);
|
||||||
gpr[rdid] = rval;
|
gpr[rdid] = rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
#include <riscv/System.hpp>
|
||||||
|
|
||||||
|
namespace riscv {
|
||||||
|
|
||||||
|
System* System::Create(Address ramSize) {
|
||||||
|
auto* system = new System;
|
||||||
|
|
||||||
|
system->bus = new Bus();
|
||||||
|
system->cpu = new CPU();
|
||||||
|
system->ram = new devices::RamDevice(0x80000000, ramSize);
|
||||||
|
system->clnt = new devices::ClntDevice();
|
||||||
|
system->syscon = new devices::SysconDevice(system);
|
||||||
|
|
||||||
|
system->cpu->Reset();
|
||||||
|
|
||||||
|
system->bus->AttachDevice(system->cpu);
|
||||||
|
system->bus->AttachDevice(system->clnt);
|
||||||
|
system->bus->AttachDevice(system->syscon);
|
||||||
|
system->bus->AttachDevice(system->ram);
|
||||||
|
|
||||||
|
return system;
|
||||||
|
}
|
||||||
|
|
||||||
|
System::~System() {
|
||||||
|
delete cpu;
|
||||||
|
delete bus; // the rest of the device pointers will be deleted by the bus.
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::Step() {
|
||||||
|
// Clock the bus, it'll do everything else.
|
||||||
|
bus->Clock();
|
||||||
|
|
||||||
|
// Later: handling for invalid cases!
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace riscv
|
|
@ -1,5 +1,10 @@
|
||||||
//! A test harness for testing the riscv library.
|
//! A test harness for testing if the riscv library actually works.
|
||||||
#include <riscv/System.hpp>
|
#include <riscv/System.hpp>
|
||||||
|
#include <lucore/StdoutSink.hpp>
|
||||||
|
|
||||||
|
#include <cstdio> // I know, I know, but this is a test program. Yell later :)
|
||||||
|
#include <lucore/Assert.hpp>
|
||||||
|
#include <lucore/Logger.hpp>
|
||||||
|
|
||||||
/// simple 16550 UART implementation
|
/// simple 16550 UART implementation
|
||||||
struct SimpleUartDevice : public riscv::Bus::MmioDevice {
|
struct SimpleUartDevice : public riscv::Bus::MmioDevice {
|
||||||
|
@ -7,29 +12,55 @@ struct SimpleUartDevice : public riscv::Bus::MmioDevice {
|
||||||
|
|
||||||
riscv::Address Base() const override { return BASE_ADDRESS; }
|
riscv::Address Base() const override { return BASE_ADDRESS; }
|
||||||
|
|
||||||
riscv::Address Size() const override { return 5; }
|
riscv::Address Size() const override { return 12; } // for now
|
||||||
|
|
||||||
// TODO: emulate properly
|
|
||||||
u32 Peek(riscv::Address address) override {
|
u32 Peek(riscv::Address address) override {
|
||||||
switch(address) {
|
switch(address) {
|
||||||
case BASE_ADDRESS:
|
case BASE_ADDRESS:
|
||||||
break;
|
return 0x60; // active, but no keyboard input
|
||||||
case BASE_ADDRESS + 5:
|
case BASE_ADDRESS + 5:
|
||||||
break;
|
return '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Poke(riscv::Address address, u32 value) override {
|
void Poke(riscv::Address address, u32 value) override {
|
||||||
if(address == BASE_ADDRESS) { // write to data buffer
|
if(address == BASE_ADDRESS) {
|
||||||
printf("%c\n", value);
|
char c = value & 0x000000ff;
|
||||||
|
//lucore::LogInfo("[UART] got data buffer poke of char {:02x} @ pc 0x{:08x}", c, bus->GetCPU()->pc);
|
||||||
|
//printf("%c", c);
|
||||||
|
fputc(c, stderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int main() {
|
int main(int argc, char** argv) {
|
||||||
auto system = riscv::System::WithMemory(128 * 1024);
|
lucore::LoggerAttachStdout();
|
||||||
system->AddDeviceToBus(new SimpleUartDevice);
|
|
||||||
|
LUCORE_CHECK(argc == 2, "this test harness expects one argument (the file to load into riscv memory and execute). got {} arguments", argc);
|
||||||
|
|
||||||
|
// 128 KB of ram. Won't be enough to boot linux but should be good enough to test most baremetal apps
|
||||||
|
auto system = riscv::System::Create(128 * 1024);
|
||||||
|
|
||||||
|
// Attach our UART device
|
||||||
|
system->bus->AttachDevice(new SimpleUartDevice);
|
||||||
|
|
||||||
|
auto fp = std::fopen(argv[1], "rb");
|
||||||
|
LUCORE_CHECK(fp, "could not open file \"{}\"", argv[1]);
|
||||||
|
|
||||||
|
fseek(fp, 0, SEEK_END);
|
||||||
|
auto len = ftell(fp);
|
||||||
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
|
||||||
|
fread(system->ram->Raw(), 1, len, fp);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
// Do the thing now!
|
||||||
|
while(true) {
|
||||||
|
system->Step();
|
||||||
|
}
|
||||||
|
|
||||||
|
delete system;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
PROJECT = test
|
||||||
|
|
||||||
|
# where your rv32 toolchain is
|
||||||
|
TCPATH = /home/lily/bin/riscv/bin
|
||||||
|
PREFIX = $(TCPATH)/riscv32-unknown-elf
|
||||||
|
|
||||||
|
CC = $(PREFIX)-gcc
|
||||||
|
CXX = $(PREFIX)-g++
|
||||||
|
|
||||||
|
CCFLAGS = -ffreestanding -fno-stack-protector
|
||||||
|
CCFLAGS += -static -static-libgcc -fdata-sections -ffunction-sections
|
||||||
|
CCFLAGS += -g -Os -march=rv32ima -mabi=ilp32
|
||||||
|
|
||||||
|
CXXFLAGS = $(CCFLAGS) -std=c++20 -fno-exceptions -fno-rtti
|
||||||
|
|
||||||
|
LDFLAGS = -T binary.ld -nostdlib -Wl,--gc-sections
|
||||||
|
|
||||||
|
OBJS := start.o main.o
|
||||||
|
|
||||||
|
.PHONY: all test clean
|
||||||
|
|
||||||
|
all: $(PROJECT).bin $(PROJECT).debug.txt
|
||||||
|
|
||||||
|
test: $(PROJECT).bin $(PROJECT).debug.txt
|
||||||
|
../../../../build/projects/riscv_test_harness/rvtest $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm $(PROJECT).elf $(PROJECT).bin $(PROJECT).debug.txt $(OBJS)
|
||||||
|
|
||||||
|
$(PROJECT).elf: $(OBJS)
|
||||||
|
$(CC) $(CCFLAGS) $(LDFLAGS) -o $@ $(OBJS)
|
||||||
|
|
||||||
|
$(PROJECT).bin : $(PROJECT).elf
|
||||||
|
$(PREFIX)-objcopy $^ -O binary $@
|
||||||
|
|
||||||
|
$(PROJECT).debug.txt : $(PROJECT).elf
|
||||||
|
$(PREFIX)-objdump -t $^ > $@
|
||||||
|
$(PREFIX)-objdump -S $^ >> $@
|
||||||
|
|
||||||
|
# Compile rules
|
||||||
|
|
||||||
|
%.o: %.cpp
|
||||||
|
$(CXX) -c $(CXXFLAGS) $< -o $@
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -c $(CCFLAGS) $< -o $@
|
||||||
|
|
||||||
|
%.o: %.S
|
||||||
|
$(CC) -x assembler-with-cpp -march=rv32ima -mabi=ilp32 -c $< -o $@
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
# what
|
||||||
|
|
||||||
|
This is a simple bare-metal riscv program that is able to run on the test harness that does some testing.
|
||||||
|
|
||||||
|
It is barebones, I know.
|
|
@ -0,0 +1,73 @@
|
||||||
|
__heap_size = 0x1000;
|
||||||
|
__stack_size = 0x1000;
|
||||||
|
|
||||||
|
ENTRY(_start)
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = 0x80000000;
|
||||||
|
.text : ALIGN(16) {
|
||||||
|
__TEXT_BEGIN__ = .;
|
||||||
|
*(.initial_jump)
|
||||||
|
*(.entry.text)
|
||||||
|
*(.init.literal)
|
||||||
|
*(.init)
|
||||||
|
*(.text)
|
||||||
|
*(.literal .text .literal.* .text.* .stub)
|
||||||
|
*(.out_jump.literal.*)
|
||||||
|
*(.out_jump.*)
|
||||||
|
__TEXT_END__ = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we're on a newer compiler */
|
||||||
|
/DISCARD/ :
|
||||||
|
{
|
||||||
|
*(.interp)
|
||||||
|
*(.dynsym)
|
||||||
|
*(.dynstr)
|
||||||
|
*(.header)
|
||||||
|
} : phdr
|
||||||
|
|
||||||
|
.data : ALIGN(16) {
|
||||||
|
__DATA_BEGIN__ = .;
|
||||||
|
*(.rodata)
|
||||||
|
*(.rodata.*)
|
||||||
|
*(.gnu.linkonce.r.*)
|
||||||
|
*(.rodata1)
|
||||||
|
*(.dynsbss)
|
||||||
|
*(.gnu.linkonce.sb.*)
|
||||||
|
*(.scommon)
|
||||||
|
*(.gnu.linkonce.sb2.*)
|
||||||
|
*(.sbss)
|
||||||
|
*(.sbss.*)
|
||||||
|
*(.sbss2)
|
||||||
|
*(.sbss2.*)
|
||||||
|
*(.dynbss)
|
||||||
|
*(.data)
|
||||||
|
*(.data.*)
|
||||||
|
*(.got)
|
||||||
|
*(.got.*)
|
||||||
|
__DATA_END__ = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bss : ALIGN( 16 ) {
|
||||||
|
__BSS_BEGIN__ = .;
|
||||||
|
*(.bss) /* Tricky: BSS needs to be allocated but not sent. GCC Will not populate these for calculating data size */
|
||||||
|
*(.bss.*)
|
||||||
|
__BSS_END__ = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heap : ALIGN( 16 ) {
|
||||||
|
_sheap = .;
|
||||||
|
. = . + __heap_size;
|
||||||
|
_eheap = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stack : ALIGN( 16 ) {
|
||||||
|
_estack = .;
|
||||||
|
. = . + __stack_size;
|
||||||
|
_sstack = .;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
// a simple test program
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
uint32_t strlen(const char* str) {
|
||||||
|
if(!str)
|
||||||
|
return 0;
|
||||||
|
const char* c = str;
|
||||||
|
while(*c++)
|
||||||
|
;
|
||||||
|
return c - str;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define UART_BASE 0x10000000
|
||||||
|
#define UART_DATA *(volatile uint32_t*)UART_BASE
|
||||||
|
#define UART_STATUS UART_DATA
|
||||||
|
|
||||||
|
void putc(char c) {
|
||||||
|
UART_DATA = (uint32_t)c;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((noinline)) void puts(const char* str) {
|
||||||
|
const uint32_t length = strlen(str);
|
||||||
|
for(uint32_t i = 0; i < length; ++i)
|
||||||
|
putc(str[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t value = 0;
|
||||||
|
static uint16_t shortvalue = 0;
|
||||||
|
static uint8_t bytevalue = 0;
|
||||||
|
|
||||||
|
#define COUNTER_TEST(var, max) \
|
||||||
|
for(int i = 0; i < max; ++i) { \
|
||||||
|
puts(#var " is (before modification): "); \
|
||||||
|
putc("0123456789"[var]); \
|
||||||
|
putc('\n'); \
|
||||||
|
\
|
||||||
|
var = i; \
|
||||||
|
puts(#var " is (after modification): "); \
|
||||||
|
putc("0123456789"[var]); \
|
||||||
|
putc('\n'); \
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
puts("hello world I guess\n");
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
COUNTER_TEST(value, 9);
|
||||||
|
COUNTER_TEST(shortvalue, 9);
|
||||||
|
COUNTER_TEST(bytevalue, 9);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// loop forever
|
||||||
|
for(;;);
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Simple bare-metal RISCV startup code.
|
||||||
|
|
||||||
|
.section .initial_jump
|
||||||
|
.global _start
|
||||||
|
|
||||||
|
.extern main
|
||||||
|
|
||||||
|
.align 4
|
||||||
|
_start:
|
||||||
|
la sp, _sstack # set up C stack
|
||||||
|
addi sp,sp,-16 # ...
|
||||||
|
sw ra,12(sp) # ...
|
||||||
|
jal ra, main # jump to C code!
|
Loading…
Reference in New Issue