Make the C-compiled program actually work

This commit is contained in:
Konstantin Nazarov 2024-12-07 01:19:22 +00:00
parent 797a308764
commit 90a8c9c378
Signed by: knazarov
GPG key ID: 4CFE0A42FA409C22
7 changed files with 71 additions and 40 deletions

1
.gitignore vendored
View file

@ -19,3 +19,4 @@ Testing
result result
build build
example/example example/example
example/example.raw

View file

@ -14,27 +14,21 @@ eval "$configurePhase"
ninja ninja
``` ```
You should get the `rve` binary. Run it: As a result you should get an executable called `rve`
```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.
## Cross-toolchain and an example program in C ## 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 ```sh
cd example cd example
make 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 ```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`.

View file

@ -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-ld boot.o example.o -T linker.ld -o example
riscv32-none-elf-strip -R .riscv.attributes example riscv32-none-elf-strip -R .riscv.attributes example
riscv32-none-elf-strip -R .comment 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

View file

@ -1,6 +1,6 @@
.globl _boot .globl _boot
_boot: _boot:
lui x2, 0x80004 li x2, 0x8000
jal main call main
sbreak sbreak
j . j .

View file

@ -7,13 +7,23 @@ SECTIONS {
*(.text) /* Place all .text sections (code) here */ *(.text) /* Place all .text sections (code) here */
} }
. = 0x1000;
.data : { .data : {
*(.data) /* Place all .data sections (initialized data) here */ *(.data) /* Place all .data sections (initialized data) here */
} }
. = 0x2000;
.bss : { .bss : {
*(.bss) /* Place all .bss sections (uninitialized data) here */ *(.bss) /* Place all .bss sections (uninitialized data) here */
} }
. = 0x3000;
.stack : {
*(.stack)
}
/DISCARD/ : { *(.note.GNU-stack) } /* Discard stack section */ /DISCARD/ : { *(.note.GNU-stack) } /* Discard stack section */
} }

View file

@ -3,40 +3,63 @@
#include <cstdlib> #include <cstdlib>
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
#include <fstream>
#include <iostream>
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<char*>(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; const size_t MEMORY_SIZE = 128*1024;
uint8_t memory[MEMORY_SIZE] = {0}; uint8_t memory[MEMORY_SIZE] = {0};
uint32_t program[] = { if (argc <= 1) {
0x00000093, // addi x1, x0, 0 std::cerr << "Expected filename of program to execute" << std::endl;
0x00100113, // addi x2, x0, 1 return 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)
};
std::memcpy(memory, reinterpret_cast<uint8_t*>(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 { try {
eval(memory, MEMORY_SIZE); eval(memory, MEMORY_SIZE);
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << "Emulator error: " << e.what() << std::endl; std::cerr << "Emulator error: " << e.what() << std::endl;
assert(false && "Emulator threw an exception!"); return 1;
} }
// Check the result uint32_t *res = (uint32_t *)&memory[0x1000];
uint32_t result = 0;
std::memcpy(&result, memory + 256, sizeof(uint32_t)); // Memory address 0x100 (256 in decimal)
// Expected result is the sum of 0 to 9 = 55 std::cout << "result: " << *res << std::endl;
std::cout << "result: " << std::dec << result << "\n";
assert(result == 45 && "Test failed: Sum of 1 to 10 is incorrect!");
// If we reach here, the test passed
std::cout << "Test passed: Sum of 1 to 10 = " << result << std::endl;
return 0; return 0;
} }

View file

@ -27,7 +27,7 @@ void eval(uint8_t* memory, size_t memory_size) {
while (pc < memory_size && running) { while (pc < memory_size && running) {
uint32_t instr = fetch_instruction(); uint32_t instr = fetch_instruction();
if (instr == 0) break; 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"; // std::cout << "instr: " << std::hex << instr << "\n";
pc += 4; pc += 4;
@ -170,18 +170,19 @@ void eval(uint8_t* memory, size_t memory_size) {
} }
case 0x03: { // I-type (loads) case 0x03: { // I-type (loads)
imm = sign_extend(instr >> 20, 12); // Extract 12-bit immediate imm = sign_extend(instr >> 20, 12); // Extract 12-bit immediate
registers[rd] = 0;
if (funct3 == 0x00) { // LB if (funct3 == 0x00) { // LB
uint32_t addr = registers[rs1] + imm; uint32_t addr = registers[rs1] + imm;
if (addr + 1 > memory_size) { if (addr + 1 > memory_size) {
throw std::runtime_error("Memory access out of bounds"); throw std::runtime_error("Memory access out of bounds");
} }
registers[rd] = 0;
std::memcpy(&registers[rd], memory + addr, sizeof(uint8_t)); std::memcpy(&registers[rd], memory + addr, sizeof(uint8_t));
} else if (funct3 == 0x01) { // LH } else if (funct3 == 0x01) { // LH
uint32_t addr = registers[rs1] + imm; uint32_t addr = registers[rs1] + imm;
if (addr + 2 > memory_size) { if (addr + 2 > memory_size) {
throw std::runtime_error("Memory access out of bounds"); throw std::runtime_error("Memory access out of bounds");
} }
registers[rd] = 0;
std::memcpy(&registers[rd], memory + addr, sizeof(uint16_t)); std::memcpy(&registers[rd], memory + addr, sizeof(uint16_t));
} else if (funct3 == 0x2) { // LW } else if (funct3 == 0x2) { // LW
uint32_t addr = registers[rs1] + imm; uint32_t addr = registers[rs1] + imm;
@ -195,7 +196,7 @@ void eval(uint8_t* memory, size_t memory_size) {
break; break;
} }
case 0x23: { // S-type (SW) 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 imm = sign_extend(imm, 12); // Sign-extend 12-bit immediate
if (funct3 == 0x0) { // SB if (funct3 == 0x0) { // SB
uint32_t addr = registers[rs1] + imm; 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 >> 21) & 0x3FF) << 1 | // imm[10:1]
((instr >> 20) & 0x1) << 11 | // imm[11] ((instr >> 20) & 0x1) << 11 | // imm[11]
((instr & 0xFF000)); // imm[19:12] ((instr & 0xFF000)); // imm[19:12]
offset <<= 1; // Multiply by 2 (JAL offsets are word-aligned)
registers[rd] = pc; // Save return address registers[rd] = pc; // Save return address
pc += offset - 4; 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) uint32_t target = (registers[rs1] + offset) & ~1; // Target address (LSB cleared)
registers[rd] = pc; // Save return address registers[rd] = pc; // Save return address
pc = target - 4; pc = target;
break; break;
} }
case 0x37: { // LUI case 0x37: { // LUI