almost fix
so the issue was of course something very simple
This commit is contained in:
parent
cb95bf4e85
commit
8467142c3e
6 changed files with 79 additions and 21 deletions
2
Makefile
2
Makefile
|
@ -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
|
||||||
|
|
|
@ -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
BIN
programs/return2.bin
Normal file → Executable file
Binary file not shown.
|
@ -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;
|
||||||
|
|
|
@ -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 -----");
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue