Compare commits
No commits in common. "7af85f5601688caf7807d8e9a64c51897e854c30" and "38e7fc464697ca397a0cfc34f9de1159ed8eee64" have entirely different histories.
7af85f5601
...
38e7fc4646
|
@ -9,7 +9,3 @@ This is based off [cnlohr/mini-rv32ima](https://github.com/cnlohr/mini-rv32ima),
|
|||
- Moved *ALL* device and MMIO code to seperate interfaces
|
||||
|
||||
Depends on lucore.
|
||||
|
||||
# Usage
|
||||
|
||||
TBD (if this is moved to another repo). See the riscv_test_harness project.
|
||||
|
|
|
@ -6,7 +6,6 @@ namespace riscv {
|
|||
|
||||
/// The CPU core.
|
||||
struct CPU : Bus::Device {
|
||||
BasicType Type() const override { return BasicType::Cpu; }
|
||||
bool Clocked() const override { return true; }
|
||||
void Clock() override;
|
||||
|
||||
|
@ -16,17 +15,6 @@ namespace riscv {
|
|||
|
||||
void TimerInterrupt();
|
||||
|
||||
constexpr CPU() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
constexpr void Reset() {
|
||||
// Initalize some state. We're cool like that :)
|
||||
pc = 0x80000000;
|
||||
gpr[Gpr::A0] = 0x0; // HART id
|
||||
extraflags |= 3; // Start in Machine mode
|
||||
}
|
||||
|
||||
// TODO: Handlers for CSR read/write (if we need it?)
|
||||
|
||||
/// CPU state
|
||||
|
@ -57,7 +45,9 @@ namespace riscv {
|
|||
/// Set by [CPU::Trap] for the trap code.
|
||||
u32 trapCode { 0 };
|
||||
|
||||
void Step(u32 instCount);
|
||||
u32 Step(u32 instCount);
|
||||
|
||||
// todo: counters for chrono/inst count.
|
||||
};
|
||||
|
||||
} // namespace riscv
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#pragma once
|
||||
#include <riscv/Types.hpp>
|
||||
#include <string_view>
|
||||
|
||||
namespace riscv {
|
||||
|
||||
|
@ -26,78 +25,41 @@ namespace riscv {
|
|||
};
|
||||
|
||||
enum class Gpr : u8 {
|
||||
Zero,
|
||||
Ra,
|
||||
Sp,
|
||||
Gp,
|
||||
Tp,
|
||||
T0,
|
||||
T1,
|
||||
T2,
|
||||
S0, // also `fp`
|
||||
S1,
|
||||
A0,
|
||||
A1,
|
||||
A2,
|
||||
A3,
|
||||
A4,
|
||||
A5,
|
||||
A6,
|
||||
A7,
|
||||
S2,
|
||||
S3,
|
||||
S4,
|
||||
S5,
|
||||
S6,
|
||||
S7,
|
||||
S8,
|
||||
S9,
|
||||
S10,
|
||||
S11,
|
||||
T3,
|
||||
T4,
|
||||
T5,
|
||||
T6
|
||||
X0, // zero
|
||||
X1,
|
||||
X2,
|
||||
X3,
|
||||
X4,
|
||||
X5,
|
||||
X6,
|
||||
X7,
|
||||
X8,
|
||||
X9,
|
||||
X10,
|
||||
X11,
|
||||
X12,
|
||||
X13,
|
||||
X14,
|
||||
X15,
|
||||
X16,
|
||||
X17,
|
||||
X18,
|
||||
X19,
|
||||
X20,
|
||||
X21,
|
||||
X22,
|
||||
X23,
|
||||
X24,
|
||||
X25,
|
||||
X26,
|
||||
X27,
|
||||
X28,
|
||||
X29,
|
||||
X30,
|
||||
X31,
|
||||
PC
|
||||
};
|
||||
|
||||
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.
|
||||
/// Pretty cool, huh?
|
||||
struct GeneralPurposeRegisters {
|
||||
|
|
|
@ -14,9 +14,6 @@ namespace riscv::devices {
|
|||
Address Base() const override;
|
||||
Address Size() const override;
|
||||
|
||||
u8* Raw() const {
|
||||
return memory;
|
||||
}
|
||||
|
||||
u8 PeekByte(Address address) override;
|
||||
u16 PeekShort(Address address) override;
|
||||
|
|
|
@ -11,7 +11,6 @@ namespace riscv::devices {
|
|||
|
||||
SysconDevice(System* system);
|
||||
|
||||
Address Base() const override { return BASE_ADDRESS; }
|
||||
Address Size() const override { return sizeof(u32); } // I think this is right?
|
||||
|
||||
u32 Peek(Address address) override;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#include <riscv/Bus.hpp>
|
||||
#include <riscv/CPU.hpp>
|
||||
#include <riscv/Devices/ClntDevice.hpp>
|
||||
#include <riscv/Devices/RamDevice.hpp>
|
||||
#include <riscv/Devices/SysconDevice.hpp>
|
||||
#include <riscv/Devices/ClntDevice.hpp>
|
||||
|
||||
namespace riscv {
|
||||
|
||||
|
@ -10,13 +10,25 @@ 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* Create(Address ramSize);
|
||||
static System* WithMemory(Address ramSize);
|
||||
|
||||
~System();
|
||||
|
||||
void Step();
|
||||
void AddDeviceToBus(Bus::Device* device);
|
||||
|
||||
// TODO: callbacks for SYSCON PowerOff and Reboot.
|
||||
/// returns false if the cpu broke execution
|
||||
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;
|
||||
|
||||
|
@ -25,9 +37,6 @@ namespace riscv {
|
|||
devices::RamDevice* ram;
|
||||
devices::SysconDevice* syscon;
|
||||
devices::ClntDevice* clnt;
|
||||
|
||||
private:
|
||||
System() = default;
|
||||
};
|
||||
|
||||
} // namespace riscv
|
||||
|
|
|
@ -14,8 +14,6 @@ namespace riscv {
|
|||
if(!device)
|
||||
return false;
|
||||
|
||||
device->Attached(this);
|
||||
|
||||
if(device->IsA<CPU*>()) {
|
||||
// Return early to avoid putting the CPU pointer inside the devices vector.
|
||||
// We do not actually own the CPU.
|
||||
|
@ -121,7 +119,7 @@ namespace riscv {
|
|||
}
|
||||
|
||||
Bus::Device* Bus::FindDeviceForAddress(Address address) const {
|
||||
auto try_find_device = [&](const auto& container, Address address) {
|
||||
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,12 +1,16 @@
|
|||
//! Portions of this code are copyright 2022 Charles Lohr (CNLohr),
|
||||
//! from [mini-rv32ima](https://github.com/cnlohr/mini-rv32ima).
|
||||
//! 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,38 +36,39 @@ namespace riscv {
|
|||
// trapCode = 0x80000007;
|
||||
}
|
||||
|
||||
void CPU::Step(u32 instCount) {
|
||||
u32 CPU::Step(u32 instCount) {
|
||||
auto interruptsInFlight = [&]() {
|
||||
return (mip & (1 << 7) /*|| mip & (1 << 11)*/) && (mie & (1 << 7) /*|| mie & (1 << 11)*/) && (mstatus & 0x8 /*mie*/);
|
||||
};
|
||||
|
||||
// Don't run if waiting for an interrupt
|
||||
if(extraflags & 4)
|
||||
return;
|
||||
return 1;
|
||||
|
||||
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) {
|
||||
rdid = 0; // force it to gpr 0 (zero), which is not writable
|
||||
auto ofs_pc = pc - RamImageOffset;
|
||||
cycle++;
|
||||
|
||||
if((pc & 3)) {
|
||||
if(ofs_pc & 3) {
|
||||
Trap(TrapCode::InstructionAddressMisaligned);
|
||||
break;
|
||||
} else {
|
||||
auto ir = bus->PeekWord(pc);
|
||||
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 = pc;
|
||||
rval = ofs_pc + RamImageOffset;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -128,12 +133,12 @@ namespace riscv {
|
|||
break;
|
||||
|
||||
case 6: // BLTU
|
||||
if((u32)rs1 < (u32)rs2)
|
||||
if((uint32_t)rs1 < (uint32_t)rs2)
|
||||
pc = immm4;
|
||||
break;
|
||||
|
||||
case 7: // BGEU
|
||||
if((u32)rs1 >= (u32)rs2)
|
||||
if((uint32_t)rs1 >= (uint32_t)rs2)
|
||||
pc = immm4;
|
||||
break;
|
||||
default:
|
||||
|
@ -148,6 +153,8 @@ namespace riscv {
|
|||
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:
|
||||
|
@ -171,7 +178,7 @@ namespace riscv {
|
|||
}
|
||||
|
||||
if(trapped) {
|
||||
rval = rsval; // + RamImageOffset;
|
||||
rval = rsval + RamImageOffset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -181,7 +188,7 @@ namespace riscv {
|
|||
u32 addy = ((ir >> 7) & 0x1f) | ((ir & 0xfe000000) >> 20);
|
||||
if(addy & 0x800)
|
||||
addy |= 0xfffff000;
|
||||
addy += rs1;
|
||||
addy += rs1 - RamImageOffset;
|
||||
rdid = 0;
|
||||
|
||||
switch((ir >> 12) & 0x7) {
|
||||
|
@ -193,7 +200,6 @@ namespace riscv {
|
|||
bus->PokeShort(addy, rs2);
|
||||
break;
|
||||
case 2:
|
||||
// lucore::LogInfo("storeWord(0x{:08x}, 0x{:08x})", addy, rs2);
|
||||
bus->PokeWord(addy, rs2);
|
||||
break;
|
||||
default:
|
||||
|
@ -201,7 +207,7 @@ namespace riscv {
|
|||
}
|
||||
|
||||
if(trapped) {
|
||||
rval = addy; // + RamImageOffset;
|
||||
rval = addy + RamImageOffset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -411,7 +417,7 @@ namespace riscv {
|
|||
mstatus |= 8; // Enable interrupts
|
||||
extraflags |= 4; // Set inernal WFI bit
|
||||
this->pc = pc + 4;
|
||||
return;
|
||||
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,
|
||||
|
@ -449,9 +455,15 @@ namespace riscv {
|
|||
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;
|
||||
rval = rs1 + RamImageOffset;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -510,8 +522,10 @@ namespace riscv {
|
|||
if(trapped)
|
||||
break;
|
||||
|
||||
if(rdid)
|
||||
if(rdid) {
|
||||
gpr[rdid] = rval;
|
||||
}
|
||||
|
||||
pc += 4;
|
||||
}
|
||||
}
|
||||
|
@ -529,11 +543,13 @@ namespace riscv {
|
|||
else
|
||||
mtval = pc;
|
||||
}
|
||||
mepc = pc; // Interrupt handler will advance mepc
|
||||
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);
|
||||
|
||||
// Always enter machine mode when trapping.
|
||||
// If trapping, always enter machine mode.
|
||||
extraflags |= 3;
|
||||
|
||||
// Reset trap flags
|
||||
|
@ -545,6 +561,8 @@ namespace riscv {
|
|||
if(cyclel > cycle)
|
||||
cycleh++;
|
||||
cyclel = cycle;
|
||||
pc = pc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace riscv
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
#include <riscv/System.hpp>
|
||||
|
||||
namespace riscv {
|
||||
|
||||
System* System::Create(Address ramSize) {
|
||||
auto* system = new System;
|
||||
|
||||
// create all the devices we require.
|
||||
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);
|
||||
|
||||
// techinically this is done on construction but lets be hard about it
|
||||
system->cpu->Reset();
|
||||
|
||||
// attach everything into the bus
|
||||
if(!system->bus->AttachDevice(system->cpu)) return nullptr;
|
||||
if(!system->bus->AttachDevice(system->clnt)) return nullptr;
|
||||
if(!system->bus->AttachDevice(system->syscon)) return nullptr;
|
||||
if(!system->bus->AttachDevice(system->ram)) return nullptr;
|
||||
|
||||
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,10 +1,5 @@
|
|||
//! A test harness for testing if the riscv library actually works.
|
||||
//! A test harness for testing the riscv library.
|
||||
#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
|
||||
struct SimpleUartDevice : public riscv::Bus::MmioDevice {
|
||||
|
@ -12,56 +7,29 @@ struct SimpleUartDevice : public riscv::Bus::MmioDevice {
|
|||
|
||||
riscv::Address Base() const override { return BASE_ADDRESS; }
|
||||
|
||||
riscv::Address Size() const override { return 12; } // for now
|
||||
riscv::Address Size() const override { return 5; }
|
||||
|
||||
// TODO: emulate properly
|
||||
u32 Peek(riscv::Address address) override {
|
||||
switch(address) {
|
||||
case BASE_ADDRESS:
|
||||
return 0x60; // active, but no keyboard input
|
||||
break;
|
||||
case BASE_ADDRESS + 5:
|
||||
return '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Poke(riscv::Address address, u32 value) override {
|
||||
if(address == BASE_ADDRESS) {
|
||||
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);
|
||||
if(address == BASE_ADDRESS) { // write to data buffer
|
||||
printf("%c\n", value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
lucore::LoggerAttachStdout();
|
||||
|
||||
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);
|
||||
LUCORE_CHECK(system, "could not create system for some reason.");
|
||||
|
||||
// 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;
|
||||
int main() {
|
||||
auto system = riscv::System::WithMemory(128 * 1024);
|
||||
system->AddDeviceToBus(new SimpleUartDevice);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
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 $@
|
||||
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
# 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.
|
|
@ -1,73 +0,0 @@
|
|||
__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 = .;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
// 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(;;);
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
# 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