diff --git a/.gitignore b/.gitignore index 6b7083d..2b7dcd1 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ CTestTestfile.cmake Testing result build -example/example \ No newline at end of file +example/example +example/example.raw \ No newline at end of file diff --git a/README.md b/README.md index 6f3ce66..d3a066d 100644 --- a/README.md +++ b/README.md @@ -14,27 +14,21 @@ eval "$configurePhase" ninja ``` -You should get the `rve` binary. Run it: - -```sh -./rve -``` - -At the moment the program is hardcoded into the `main()` function directly in machine code. As a follow-up, I'm working on compiling programs with the gcc cross-toolchain, but it will require some time to fiddle with options. +As a result you should get an executable called `rve` ## Cross-toolchain and an example program in C -There is an example program which you can compile, but not yet run. Do this: +There is an example program which you can compile. It requires some custom toolchain so currently not built with CMake. To compile it: ```sh cd example make ``` -As a result, you'll get an `example` binary. To view the disassembly: +As a result, you'll get an `example.raw` binary. To execute it: ```sh -riscv32-none-elf-objdump -D example +./rve ../example/example.raw ``` -As you can see, it's quite barebones, with the program starting at 0x1000. The reason it can't yet be run with the emulator is mostly in the absence of the boot section which initializes the stack and calls the main function. Stay tuned. +The expected output of the example program is `269`. diff --git a/example/Makefile b/example/Makefile index 372e116..73b7468 100644 --- a/example/Makefile +++ b/example/Makefile @@ -4,3 +4,6 @@ example: example.c Makefile boot.s linker.ld riscv32-none-elf-ld boot.o example.o -T linker.ld -o example riscv32-none-elf-strip -R .riscv.attributes example riscv32-none-elf-strip -R .comment example + riscv32-none-elf-objcopy -O binary example example.raw + #riscv32-none-elf-objcopy -O binary -j .text example example.text + #riscv32-none-elf-objcopy -O binary -j .sdata example example.data diff --git a/example/boot.s b/example/boot.s index 35fd4a2..af4f484 100644 --- a/example/boot.s +++ b/example/boot.s @@ -1,6 +1,6 @@ .globl _boot _boot: - lui x2, 0x80004 - jal main + li x2, 0x8000 + call main sbreak j . diff --git a/example/linker.ld b/example/linker.ld index bb9558b..aac741b 100644 --- a/example/linker.ld +++ b/example/linker.ld @@ -7,13 +7,23 @@ SECTIONS { *(.text) /* Place all .text sections (code) here */ } + . = 0x1000; + .data : { *(.data) /* Place all .data sections (initialized data) here */ } + . = 0x2000; + .bss : { *(.bss) /* Place all .bss sections (uninitialized data) here */ } + . = 0x3000; + + .stack : { + *(.stack) + } + /DISCARD/ : { *(.note.GNU-stack) } /* Discard stack section */ } diff --git a/src/rve.cpp b/src/rve.cpp index d179de5..7969cf5 100644 --- a/src/rve.cpp +++ b/src/rve.cpp @@ -3,40 +3,63 @@ #include #include #include +#include +#include -int main() { +void load_program(const std::string& filename, uint8_t* memory, size_t memory_size) { + std::ifstream file(filename, std::ios::binary|std::ios::ate); + + if (!file.is_open()) { + throw std::runtime_error("Failed to open file: " + filename); + } + + size_t file_size = file.tellg(); + + if (file_size > memory_size) { + throw std::runtime_error("File is too big"); + } + + file.seekg(0, std::ios::beg); + + file.read(reinterpret_cast(memory), file_size); + + if (!file) { + throw std::runtime_error("Failed to read the complete program into memory."); + } + + file.close(); +} + +int main(int argc, char* argv[]) { const size_t MEMORY_SIZE = 128*1024; uint8_t memory[MEMORY_SIZE] = {0}; - uint32_t program[] = { - 0x00000093, // addi x1, x0, 0 - 0x00100113, // addi x2, x0, 1 - 0x00a00193, // addi x3, x0, 10 - 0x10000213, // addi x4, x0, 256 - 0x002080b3, // add x1, x1, x2 <- loop - 0x00110113, // addi x2, x2, 1 - 0xfe311ce3, // bne x2, x3, loop - 0x00122023, // sw x1, 0(x4) - }; + if (argc <= 1) { + std::cerr << "Expected filename of program to execute" << std::endl; + return 1; + } - std::memcpy(memory, reinterpret_cast(program), sizeof(program)); + std::string program_filename = argv[1]; + + try { + load_program(program_filename, memory, MEMORY_SIZE); + } catch (const std::exception &e) { + std::cerr << e.what() << std::endl; + return 1; + } + + uint32_t *res1 = (uint32_t *)&memory[0x1000]; try { eval(memory, MEMORY_SIZE); } catch (const std::exception& e) { std::cerr << "Emulator error: " << e.what() << std::endl; - assert(false && "Emulator threw an exception!"); + return 1; } - // Check the result - uint32_t result = 0; - std::memcpy(&result, memory + 256, sizeof(uint32_t)); // Memory address 0x100 (256 in decimal) + uint32_t *res = (uint32_t *)&memory[0x1000]; - // Expected result is the sum of 0 to 9 = 55 - std::cout << "result: " << std::dec << result << "\n"; - assert(result == 45 && "Test failed: Sum of 1 to 10 is incorrect!"); + std::cout << "result: " << *res << std::endl; - // If we reach here, the test passed - std::cout << "Test passed: Sum of 1 to 10 = " << result << std::endl; return 0; } diff --git a/src/vm.cpp b/src/vm.cpp index c7f2098..4ed8489 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -27,7 +27,7 @@ void eval(uint8_t* memory, size_t memory_size) { while (pc < memory_size && running) { uint32_t instr = fetch_instruction(); if (instr == 0) break; - // std::cout << "pc: " << pc << "\n"; + //std::cout << "pc: " << std::hex << pc << std::dec << "\n"; // std::cout << "instr: " << std::hex << instr << "\n"; pc += 4; @@ -170,18 +170,19 @@ void eval(uint8_t* memory, size_t memory_size) { } case 0x03: { // I-type (loads) imm = sign_extend(instr >> 20, 12); // Extract 12-bit immediate - registers[rd] = 0; if (funct3 == 0x00) { // LB uint32_t addr = registers[rs1] + imm; if (addr + 1 > memory_size) { throw std::runtime_error("Memory access out of bounds"); } + registers[rd] = 0; std::memcpy(®isters[rd], memory + addr, sizeof(uint8_t)); } else if (funct3 == 0x01) { // LH uint32_t addr = registers[rs1] + imm; if (addr + 2 > memory_size) { throw std::runtime_error("Memory access out of bounds"); } + registers[rd] = 0; std::memcpy(®isters[rd], memory + addr, sizeof(uint16_t)); } else if (funct3 == 0x2) { // LW uint32_t addr = registers[rs1] + imm; @@ -195,7 +196,7 @@ void eval(uint8_t* memory, size_t memory_size) { break; } case 0x23: { // S-type (SW) - imm = ((instr >> 7) & 0x1F) | ((instr >> 25) << 5); + imm = ((instr >> 7) & 0x1F) | (((instr >> 25) & 0x7F) << 5); imm = sign_extend(imm, 12); // Sign-extend 12-bit immediate if (funct3 == 0x0) { // SB uint32_t addr = registers[rs1] + imm; @@ -225,7 +226,6 @@ void eval(uint8_t* memory, size_t memory_size) { ((instr >> 21) & 0x3FF) << 1 | // imm[10:1] ((instr >> 20) & 0x1) << 11 | // imm[11] ((instr & 0xFF000)); // imm[19:12] - offset <<= 1; // Multiply by 2 (JAL offsets are word-aligned) registers[rd] = pc; // Save return address pc += offset - 4; @@ -236,7 +236,7 @@ void eval(uint8_t* memory, size_t memory_size) { uint32_t target = (registers[rs1] + offset) & ~1; // Target address (LSB cleared) registers[rd] = pc; // Save return address - pc = target - 4; + pc = target; break; } case 0x37: { // LUI