almost fix

so the issue was of course something very simple
This commit is contained in:
Minecon724 2024-10-18 19:01:00 +02:00
parent cb95bf4e85
commit 8467142c3e
Signed by: Minecon724
GPG key ID: 3CCC4D267742C8E8
6 changed files with 79 additions and 21 deletions

View file

@ -11,7 +11,7 @@ CC := gcc
ifeq ($(DEBUG),1)
CFLAGS := -Wall -Wextra -std=gnu23 -I include -O0 -g
else
CFLAGS := -Wall -Wextra -std=gnu23 -I include -O3 -Wno-unused-variable
CFLAGS := -Wall -Wextra -std=gnu23 -I include -O3
endif
# Directory for build outputs

View file

@ -14,8 +14,8 @@ Emulator exit codes:
To compile stuff:
0. Get the toolchain obviously
1. riscv32-unknown-elf-gcc -c -Oz program.c
2. riscv32-unknown-elf-objcopy -O binary program.o program.bin
1. riscv32-unknown-elf-gcc -ffreestanding -nostdlib -nostartfiles -Wl,--no-relax -Ttext=0x0 -e main -O0 -o program.elf program.c
2. riscv32-unknown-elf-objcopy -O binary program.elf program.bin
3. program.bin is the binary file with the program, pass it as an argument
rv32i, ilp32 compatible toolchain for 64bit Linux: https://lfs.m724.eu/toolchain.tar.zst adaa74f263dcba430da588b1109bc3b90bd90a84c67b06213bd03a7bbacd1a2a

BIN
programs/return2.bin Normal file → Executable file

Binary file not shown.

View file

@ -4,7 +4,7 @@
#define VERBOSE_LOGGING 0
static void printBinary(int num) {
static void printBinary(uint32_t num) {
if (num > 1) {
printBinary(num / 2);
}
@ -20,7 +20,7 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside
uint8_t rd = instruction >> 7 & 0x1F;
if (VERBOSE_LOGGING == 1) {
printf("\n\nPC: %d\n", cpu->programCounter);
printf("\n\nPC: 0x%X (%d)\n", cpu->programCounter, cpu->programCounter);
printf("Instruction: ");
printBinary(instruction);
printf("\nOpcode: 0x%X (", opcode);
@ -29,7 +29,7 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside
printf("Dest. reg.: x%d\n", rd);
} else {
// TODO perhaps no break between instructions? Would look better but there would be padding to fix
printf("\n\nPC: %d ", cpu->programCounter);
printf("\n\n0x%X ", cpu->programCounter);
}
switch (opcode) {
@ -88,7 +88,7 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside
break;
default:
// TODO proper error handling
fprintf(stderr, "Unrecognized OP-IMM funct3!");
fprintf(stderr, "Invalid OP-IMM funct3: 0x%X", funct3);
return 2;
break;
}
@ -96,10 +96,10 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside
break;
}
case 0b0110111: { // LUI load upper immediate (U type)
int32_t imm = instruction & 0xFFFFF000; // no need for cast because no sign extension
uint32_t imm = instruction & 0xFFFFF000; // no need for cast because no sign extension
registers[rd] = imm;
printf("LUI - 0x%X (%d) -> x%u", imm, imm, rd);
printf("LUI: 0x%X -> x%u", imm, rd);
break;
}
case 0b1100111: { // JALR for unconditional jump and link register (I type)
@ -113,7 +113,7 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside
return 2;
}
int result = registers[rs1] + imm;
uint32_t result = registers[rs1] + imm;
result &= ~1; // set LSB to 0
registers[rd] = cpu->programCounter + 4;
@ -128,7 +128,6 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside
uint8_t rs1 = instruction >> 15 & 0x1F;
uint8_t rs2 = instruction >> 20 & 0x1F;
int32_t imm = ((int32_t)instruction >> 20 & 0xffffffe0) | rd;
uint32_t og = registers[rs1]; // for log only
uint32_t addr = registers[rs1] + imm;
if (funct3 == 0b000) { // SB
@ -136,21 +135,22 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside
uint8_t val = registers[rs2] & 0xFF;
write_address_space(addressSpace, addr, 1, &val);
printf("SB (8bit): x%d 0x%X (%u) -> 0x%X + %d = 0x%X", rs2, rs2, val, og, imm, addr);
printf("SB (8bit): x%d 0x%X (%u) -> 0x%X + %d = 0x%X", rs2, rs2, val, registers[rs1], imm, addr);
} else if (funct3 == 0b001) { // SH
// SH stores a 16-bit value from the low bits of register rs2 to memory.
uint16_t val = registers[rs2] & 0xFFFF;
write_address_space(addressSpace, addr, 2, &val);
printf("SH (16bit): x%d 0x%X (%u) -> 0x%X + %d = 0x%X", rs2, rs2, val, og, imm, addr);
printf("SH (16bit): x%d 0x%X (%u) -> 0x%X + %d = 0x%X", rs2, rs2, val, registers[rs1], imm, addr);
} else if (funct3 == 0b010) { // SW
// SW stores a 32-bit value from the low bits of register rs2 to memory.
uint32_t val = registers[rs2];
write_address_space(addressSpace, addr, 4, &val);
printf("SW (32bit): x%d 0x%X (%u) -> 0x%X + %d = 0x%X", rs2, rs2, val, og, imm, addr);
printf("SW (32bit): x%d 0x%X (%u) -> 0x%X + %d = 0x%X", rs2, rs2, val, registers[rs1], imm, addr);
} else {
// TODO illegal funct3
fprintf(stderr, "Invalid STORE funct3: 0x%X", funct3);
return 2;
}
break;
}
@ -182,26 +182,79 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside
} else if (funct3 == 0b100) { // LBU
// LBU loads an 8-bit value from memory but then zero extends to 32-bits.
read_address_space(addressSpace, addr, 1, &val);
val = (uint32_t)val >> 24; // TODO optimize that perhaps
val = (uint32_t)val >> 24; // casting for zero extension
printf("LBU (8bit)");
} else if (funct3 == 0b101) { // LHU
// LHU loads a 16-bit value from memory but then zero extends to 32-bits.
read_address_space(addressSpace, addr, 2, &val);
val = (uint32_t)val >> 16; // TODO optimize that perhaps
val = (uint32_t)val >> 16;
printf("LHU (16bit)");
} else {
// TODO illegal funct3
fprintf(stderr, "Invalid LOAD funct3: 0x%X", funct3);
return 2;
}
printf(" - 0x%X -> x%u: 0x%X", addr, rd, val);
printf(": 0x%X -> x%u: 0x%X", addr, rd, val);
registers[rd] = val;
break;
}
case 0b0010111: { // AUIPC (Add Upper Immediate to PC) (U type)
int32_t imm = instruction & 0xFFFFF000;
registers[rd] = cpu->programCounter + imm;
printf("AUIPC: pc %u + imm %d = %u -> x%d", cpu->programCounter, imm, registers[rd], rd);
break;
}
case 0b1101111: { // JAL for unconditional jump (J type)
int32_t imm = ((int32_t)instruction >> 31) << 20; // Extract imm[20] and sign-extend
imm |= (instruction >> 21) & 0x3FF; // Extract imm[10:1]
imm |= (instruction >> 20) & 0x1; // Extract imm[11]
imm |= (instruction >> 12) & 0xFF; // Extract imm[19:12]
imm <<= 1; // Left-shift by 1 to account for the implicit 0
registers[rd] = cpu->programCounter; // program counter is always incremented after executing instruction
cpu->programCounter += imm;
printf("JAL: Jumped to %u + %d = %u (inst %d)", registers[rd], imm, cpu->programCounter, cpu->programCounter / 4);
break;
}
case 0b0110011: { // OP for Integer Register-Register Operations (R type)
uint8_t funct3 = instruction >> 12 & 0x7;
uint8_t funct7 = instruction >> 25 & 0x7F;
uint32_t rs1 = instruction >> 15 & 0x1F;
uint32_t rs2 = instruction >> 20 & 0x1F;
uint32_t n1 = registers[rs1];
uint32_t n2 = registers[rs2];
// TODO maybe it would be better to check funct7 first
if (funct3 == 0b000) { // ADD
if (funct7 == 0b0000000) {
registers[rd] = (uint64_t)n1 + n2; // to not overflow
printf("ADD: x%d 0x%X (%d) + x%d 0x%X (%d) = 0x%X (%d) -> x%d", rs1, n1, n1, rs2, n2, n2, n1 + n2, n1 + n2, rd);
} else if (funct7 == 0b0100000) {
registers[rd] = (uint64_t)n1 - n2; // to not overflow
printf("SUB: x%d 0x%X (%d) - x%d 0x%X (%d) = 0x%X (%d) -> x%d", rs1, n1, n1, rs2, n2, n2, n1 - n2, n1 - n2, rd);
} else {
fprintf(stderr, "Invalid funct7 for ADD: 0x%X", funct7);
return 2;
}
} else if (funct3 == 0b110) { // OR
registers[rd] = n1 | n2; // no need for uint64 here because there's no overflow to truncate
printf("OR: x%d 0x%X (%d) | x%d 0x%X (%d) = 0x%X (%d) -> x%d", rs1, n1, n1, rs2, n2, n2, n1 | n2, n1 | n2, rd);
} else {
fprintf(stderr, "Invalid OP funct3: 0x%X", funct3);
return 2;
}
break;
}
default: {
// TODO illegal instruction
// TODO illegal instruction, proper error handling
fprintf(stderr, "Unrecognized opcode!");
return 1;
break;

View file

@ -22,6 +22,7 @@ int main(int argc, char *argv[]) {
CPU cpu = create_cpu(addressSpace);
cpu.registers[1] = addressSpace->romSize + addressSpace->ramSize; // make jumping to x1 end the program
cpu.programCounter = 0x70;
printf("\n----- Start of program -----");

View file

@ -9,8 +9,9 @@ int load_to_rom(const char filename[], AddressSpace *addressSpace) {
return 1;
}
// size_t is not used because a program can't be larger than the 32bit address space
int romSize = addressSpace->romSize;
fread(addressSpace->rom, 1, romSize, file);
int bytesRead = fread(addressSpace->rom, 1, romSize, file);
fseek(file, 0, SEEK_END);
int fileLen = ftell(file);
@ -18,6 +19,9 @@ int load_to_rom(const char filename[], AddressSpace *addressSpace) {
if (fileLen > romSize) {
fprintf(stderr, "File has %d bytes, and doesn't fit in ROM of capacity %d bytes\n", fileLen, romSize);
return 1;
} else if (bytesRead != fileLen) {
fprintf(stderr, "File has %d bytes, but read %d bytes\n", fileLen, bytesRead);
return 1;
}
printf("%s has %d bytes or %d instructions\n", filename, fileLen, fileLen / 4);