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) ifeq ($(DEBUG),1)
CFLAGS := -Wall -Wextra -std=gnu23 -I include -O0 -g CFLAGS := -Wall -Wextra -std=gnu23 -I include -O0 -g
else else
CFLAGS := -Wall -Wextra -std=gnu23 -I include -O3 -Wno-unused-variable CFLAGS := -Wall -Wextra -std=gnu23 -I include -O3
endif endif
# Directory for build outputs # Directory for build outputs

View file

@ -14,8 +14,8 @@ Emulator exit codes:
To compile stuff: To compile stuff:
0. Get the toolchain obviously 0. Get the toolchain obviously
1. riscv32-unknown-elf-gcc -c -Oz program.c 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.o program.bin 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 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 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 #define VERBOSE_LOGGING 0
static void printBinary(int num) { static void printBinary(uint32_t num) {
if (num > 1) { if (num > 1) {
printBinary(num / 2); 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; uint8_t rd = instruction >> 7 & 0x1F;
if (VERBOSE_LOGGING == 1) { if (VERBOSE_LOGGING == 1) {
printf("\n\nPC: %d\n", cpu->programCounter); printf("\n\nPC: 0x%X (%d)\n", cpu->programCounter, cpu->programCounter);
printf("Instruction: "); printf("Instruction: ");
printBinary(instruction); printBinary(instruction);
printf("\nOpcode: 0x%X (", opcode); 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); printf("Dest. reg.: x%d\n", rd);
} else { } else {
// TODO perhaps no break between instructions? Would look better but there would be padding to fix // 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) { switch (opcode) {
@ -88,7 +88,7 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside
break; break;
default: default:
// TODO proper error handling // TODO proper error handling
fprintf(stderr, "Unrecognized OP-IMM funct3!"); fprintf(stderr, "Invalid OP-IMM funct3: 0x%X", funct3);
return 2; return 2;
break; break;
} }
@ -96,10 +96,10 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside
break; break;
} }
case 0b0110111: { // LUI load upper immediate (U type) 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; registers[rd] = imm;
printf("LUI - 0x%X (%d) -> x%u", imm, imm, rd); printf("LUI: 0x%X -> x%u", imm, rd);
break; break;
} }
case 0b1100111: { // JALR for unconditional jump and link register (I type) 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; return 2;
} }
int result = registers[rs1] + imm; uint32_t result = registers[rs1] + imm;
result &= ~1; // set LSB to 0 result &= ~1; // set LSB to 0
registers[rd] = cpu->programCounter + 4; 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 rs1 = instruction >> 15 & 0x1F;
uint8_t rs2 = instruction >> 20 & 0x1F; uint8_t rs2 = instruction >> 20 & 0x1F;
int32_t imm = ((int32_t)instruction >> 20 & 0xffffffe0) | rd; int32_t imm = ((int32_t)instruction >> 20 & 0xffffffe0) | rd;
uint32_t og = registers[rs1]; // for log only
uint32_t addr = registers[rs1] + imm; uint32_t addr = registers[rs1] + imm;
if (funct3 == 0b000) { // SB 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; uint8_t val = registers[rs2] & 0xFF;
write_address_space(addressSpace, addr, 1, &val); 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 } else if (funct3 == 0b001) { // SH
// SH stores a 16-bit value from the low bits of register rs2 to memory. // SH stores a 16-bit value from the low bits of register rs2 to memory.
uint16_t val = registers[rs2] & 0xFFFF; uint16_t val = registers[rs2] & 0xFFFF;
write_address_space(addressSpace, addr, 2, &val); 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 } else if (funct3 == 0b010) { // SW
// SW stores a 32-bit value from the low bits of register rs2 to memory. // SW stores a 32-bit value from the low bits of register rs2 to memory.
uint32_t val = registers[rs2]; uint32_t val = registers[rs2];
write_address_space(addressSpace, addr, 4, &val); 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 { } else {
// TODO illegal funct3 fprintf(stderr, "Invalid STORE funct3: 0x%X", funct3);
return 2;
} }
break; break;
} }
@ -182,26 +182,79 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside
} else if (funct3 == 0b100) { // LBU } else if (funct3 == 0b100) { // LBU
// LBU loads an 8-bit value from memory but then zero extends to 32-bits. // LBU loads an 8-bit value from memory but then zero extends to 32-bits.
read_address_space(addressSpace, addr, 1, &val); 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)"); printf("LBU (8bit)");
} else if (funct3 == 0b101) { // LHU } else if (funct3 == 0b101) { // LHU
// LHU loads a 16-bit value from memory but then zero extends to 32-bits. // LHU loads a 16-bit value from memory but then zero extends to 32-bits.
read_address_space(addressSpace, addr, 2, &val); read_address_space(addressSpace, addr, 2, &val);
val = (uint32_t)val >> 16; // TODO optimize that perhaps val = (uint32_t)val >> 16;
printf("LHU (16bit)"); printf("LHU (16bit)");
} else { } 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; registers[rd] = val;
break; 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: { default: {
// TODO illegal instruction // TODO illegal instruction, proper error handling
fprintf(stderr, "Unrecognized opcode!"); fprintf(stderr, "Unrecognized opcode!");
return 1; return 1;
break; break;

View file

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

View file

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