update
something's very wrong idk what
This commit is contained in:
parent
c5173df335
commit
86d73692f9
5 changed files with 166 additions and 19 deletions
|
@ -4,6 +4,13 @@ So don't use it.
|
||||||
|
|
||||||
Example programs:
|
Example programs:
|
||||||
- return.bin: returns -1094647826 (puts it to register 10)
|
- return.bin: returns -1094647826 (puts it to register 10)
|
||||||
|
- return2.bin: returns a different value with more steps
|
||||||
|
|
||||||
|
Emulator exit codes:
|
||||||
|
- <0 = OK
|
||||||
|
- 0 = never happens
|
||||||
|
- 1 - invalid opcode
|
||||||
|
- 2 - illegal instruction argument (like funct3)
|
||||||
|
|
||||||
To compile stuff:
|
To compile stuff:
|
||||||
0. Get the toolchain obviously
|
0. Get the toolchain obviously
|
||||||
|
|
BIN
programs/return2.bin
Normal file
BIN
programs/return2.bin
Normal file
Binary file not shown.
|
@ -33,9 +33,9 @@ int cpu_cycle(CPU *cpu) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
execute_instruction_on_cpu(cpu, instruction);
|
int result = execute_instruction_on_cpu(cpu, instruction);
|
||||||
|
|
||||||
cpu->programCounter += 4;
|
cpu->programCounter += 4;
|
||||||
|
|
||||||
return 0;
|
return result;
|
||||||
}
|
}
|
|
@ -1,5 +1,8 @@
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define VERBOSE_LOGGING 0
|
||||||
|
|
||||||
static void printBinary(int num) {
|
static void printBinary(int num) {
|
||||||
if (num > 1) {
|
if (num > 1) {
|
||||||
|
@ -8,7 +11,7 @@ static void printBinary(int num) {
|
||||||
printf("%d", num % 2);
|
printf("%d", num % 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) {
|
int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO consider making instruction int
|
||||||
AddressSpace *addressSpace = cpu->addressSpace;
|
AddressSpace *addressSpace = cpu->addressSpace;
|
||||||
uint32_t *registers = cpu->registers;
|
uint32_t *registers = cpu->registers;
|
||||||
registers[0] = 0; // x0 is always 0
|
registers[0] = 0; // x0 is always 0
|
||||||
|
@ -16,61 +19,192 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) {
|
||||||
uint8_t opcode = instruction & 0x7F;
|
uint8_t opcode = instruction & 0x7F;
|
||||||
uint8_t rd = instruction >> 7 & 0x1F;
|
uint8_t rd = instruction >> 7 & 0x1F;
|
||||||
|
|
||||||
printf("\nPC: %d\n", cpu->programCounter);
|
if (VERBOSE_LOGGING == 1) {
|
||||||
|
printf("\n\nPC: %d\n", cpu->programCounter);
|
||||||
if (opcode != 0) {
|
|
||||||
printf("Instruction: ");
|
printf("Instruction: ");
|
||||||
printBinary(instruction);
|
printBinary(instruction);
|
||||||
printf("\nOpcode: 0x%X (", opcode);
|
printf("\nOpcode: 0x%X (", opcode);
|
||||||
printBinary(opcode);
|
printBinary(opcode);
|
||||||
printf(")\n");
|
printf(")\n");
|
||||||
printf("Dest. reg.: x%d\n", rd);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
|
case 0b1100011: { // BRANCH instruction (B type)
|
||||||
|
uint8_t funct3 = instruction >> 12 & 0x7;
|
||||||
|
uint8_t rs1 = instruction >> 15 & 0x1F;
|
||||||
|
uint8_t rs2 = instruction >> 20 & 0x1F;
|
||||||
|
|
||||||
|
int32_t imm = ((int32_t)instruction >> 31) << 12; // imm[12] and sign extension
|
||||||
|
imm |= ((instruction >> 7) & 0x1) << 11; // imm[11]
|
||||||
|
imm |= ((instruction >> 25) & 0x3f) << 5; // imm[10:5]
|
||||||
|
imm |= ((instruction >> 8) & 0xf) << 1; // imm[4:1]
|
||||||
|
// imm[0] is implicitly 0
|
||||||
|
|
||||||
|
bool branch = false;
|
||||||
|
|
||||||
|
if (funct3 == 0b000) { // BEQ
|
||||||
|
printf("BEQ: x%d %d == x%d %d?", rs1, registers[rs1], rs2, registers[rs2]);
|
||||||
|
branch = registers[rs1] == registers[rs2];
|
||||||
|
} else if (funct3 == 0b001) { // BNE
|
||||||
|
printf("BNE: x%d %d != x%d %d?", rs1, registers[rs1], rs2, registers[rs2]);
|
||||||
|
branch = registers[rs1] != registers[rs2];
|
||||||
|
} else if (funct3 == 0b100) { // BLT
|
||||||
|
printf("BLT: x%d %d < x%d %d?", rs1, registers[rs1], rs2, registers[rs2]);
|
||||||
|
branch = (int32_t)registers[rs1] < (int32_t)registers[rs2];
|
||||||
|
} else if (funct3 == 0b101) { // BGE
|
||||||
|
printf("BGE: x%d %d > x%d %d?", rs1, registers[rs1], rs2, registers[rs2]);
|
||||||
|
branch = (int32_t)registers[rs1] > (int32_t)registers[rs2];
|
||||||
|
} else if (funct3 == 0b110) { // BLTU
|
||||||
|
printf("BLTU: x%d %u < x%d %u?", rs1, registers[rs1], rs2, registers[rs2]);
|
||||||
|
branch = registers[rs1] < registers[rs2];
|
||||||
|
} else if (funct3 == 0b111) { // BGEU
|
||||||
|
printf("BGEU: x%d %u > x%d %u?", rs1, registers[rs1], rs2, registers[rs2]);
|
||||||
|
branch = registers[rs1] > registers[rs2];
|
||||||
|
} else {
|
||||||
|
// TODO illegal instruction
|
||||||
|
}
|
||||||
|
|
||||||
|
if (branch) {
|
||||||
|
printf(" Yes, branching");
|
||||||
|
cpu->programCounter += imm - 4; // program counter is always incremented after executing instruction so -4
|
||||||
|
} else {
|
||||||
|
printf(" No, not branching");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 0b0010011: { // OP-IMM for Integer Register-Immediate Instructions (I type)
|
case 0b0010011: { // OP-IMM for Integer Register-Immediate Instructions (I type)
|
||||||
uint8_t funct3 = instruction >> 12 & 0x7;
|
uint8_t funct3 = instruction >> 12 & 0x7;
|
||||||
uint8_t rs1 = instruction >> 15 & 0x1F;
|
uint8_t rs1 = instruction >> 15 & 0x1F;
|
||||||
uint32_t imm = (int32_t)instruction >> 20;
|
int32_t imm = (int32_t)instruction >> 20;
|
||||||
|
|
||||||
switch (funct3) {
|
switch (funct3) {
|
||||||
case 0b000: // ADDI
|
case 0b000: // ADDI
|
||||||
|
printf("ADDI - x%u %d + %d = %d -> x%u", rs1, registers[rs1], imm, registers[rs1] + imm, rd);
|
||||||
registers[rd] = registers[rs1] + imm;
|
registers[rd] = registers[rs1] + imm;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// TODO other and illegal instruction
|
// TODO proper error handling
|
||||||
|
fprintf(stderr, "Unrecognized OP-IMM funct3!");
|
||||||
|
return 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* code */
|
/* code */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0b0110111: { // LUI load upper immediate (U type)
|
case 0b0110111: { // LUI load upper immediate (U type)
|
||||||
uint32_t imm = instruction & 0xFFFFF000;
|
int32_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);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0b1100111: { // JALR for unconditional jump and link register (I type)
|
case 0b1100111: { // JALR for unconditional jump and link register (I type)
|
||||||
uint8_t funct3 = instruction >> 12 & 0x7;
|
uint8_t funct3 = instruction >> 12 & 0x7;
|
||||||
|
uint8_t rs1 = instruction >> 15 & 0x1F;
|
||||||
|
int32_t imm = (int32_t)instruction >> 20;
|
||||||
|
|
||||||
if (funct3 != 0) {
|
if (funct3 != 0) {
|
||||||
// TODO illegal instruction
|
// TODO proper error handling
|
||||||
|
fprintf(stderr, "Invalid JALR funct3! Should always be 0");
|
||||||
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t rs1 = instruction >> 15 & 0x1F;
|
|
||||||
uint32_t imm = (int32_t)instruction >> 20;
|
|
||||||
|
|
||||||
int result = registers[rs1] + imm;
|
int 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;
|
||||||
cpu->programCounter = result - 4; // program counter is always incremented after executing instruction
|
cpu->programCounter = result - 4; // program counter is always incremented after executing instruction
|
||||||
|
|
||||||
|
printf("JALR - x%u %u + %u = %u", rs1, result - imm, imm, result);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
case 0b0100011: { // STORE instruction (S type)
|
||||||
// TODO illegal instruction
|
uint8_t funct3 = instruction >> 12 & 0x7;
|
||||||
|
uint8_t rs1 = instruction >> 15 & 0x1F;
|
||||||
|
uint8_t rs2 = instruction >> 20 & 0x1F;
|
||||||
|
int32_t imm = ((int32_t)instruction >> 20 & 0xffffffe0) | rd;
|
||||||
|
uint32_t addr = registers[rs1] + imm;
|
||||||
|
|
||||||
|
if (funct3 == 0b000) { // SB
|
||||||
|
// SB stores an 8-bit value from the low bits of register rs2 to memory.
|
||||||
|
uint8_t val = registers[rs2] & 0xFF;
|
||||||
|
write_address_space(addressSpace, addr, 1, &val);
|
||||||
|
|
||||||
|
printf("SB: x%d %u -> %u", rs2, val, addr);
|
||||||
|
} else if (funct3 == 0b001) { // SH
|
||||||
|
// SH stores a 16-bit value from the low bits of register rs2 to memory.
|
||||||
|
uint8_t val = registers[rs2] & 0xFFFF;
|
||||||
|
write_address_space(addressSpace, addr, 2, &val);
|
||||||
|
|
||||||
|
printf("SH: x%d %u -> %u", rs2, val, addr);
|
||||||
|
} else if (funct3 == 0b010) { // SW
|
||||||
|
// SW stores a 32-bit value from the low bits of register rs2 to memory.
|
||||||
|
write_address_space(addressSpace, addr, 4, registers + rs2);
|
||||||
|
|
||||||
|
printf("SW: x%d %u -> %u", rs2, registers[rs2], addr);
|
||||||
|
} else {
|
||||||
|
// TODO illegal funct3
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 0b0000011: { // LOAD instruction (I type)
|
||||||
|
uint8_t funct3 = instruction >> 12 & 0x7;
|
||||||
|
uint8_t rs1 = instruction >> 15 & 0x1F;
|
||||||
|
int32_t imm = (int32_t)instruction >> 20; // sign extends automatically
|
||||||
|
uint32_t addr = registers[rs1] + imm;
|
||||||
|
|
||||||
|
int32_t val;
|
||||||
|
|
||||||
|
if (funct3 == 0b000) { // LB
|
||||||
|
// LB loads an 8-bit value from memory, then sign-extends to 32-bits.
|
||||||
|
read_address_space(addressSpace, addr, 1, &val);
|
||||||
|
val >>= 24;
|
||||||
|
|
||||||
|
printf("LB (8bit)");
|
||||||
|
} else if (funct3 == 0b001) { // LH
|
||||||
|
// LH loads a 16-bit value from memory, then sign-extends to 32-bits.
|
||||||
|
read_address_space(addressSpace, addr, 2, &val);
|
||||||
|
val >>= 16;
|
||||||
|
|
||||||
|
printf("LH (16bit)");
|
||||||
|
} else if (funct3 == 0b010) { // LW
|
||||||
|
// LW instruction loads a 32-bit value from memory into rd.
|
||||||
|
read_address_space(addressSpace, addr, 4, &val); // TODO we could assign directly
|
||||||
|
|
||||||
|
printf("LW (32bit)");
|
||||||
|
} 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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
printf("LHU (16bit)");
|
||||||
|
} else {
|
||||||
|
// TODO illegal funct3
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(" - 0x%X -> x%u: 0x%X", addr, rd, val);
|
||||||
|
|
||||||
|
registers[rd] = val;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// TODO illegal instruction
|
||||||
|
fprintf(stderr, "Unrecognized opcode!");
|
||||||
|
return 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
|
|
||||||
|
|
14
src/main.c
14
src/main.c
|
@ -12,7 +12,7 @@ int main(int argc, char *argv[]) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
AddressSpace *addressSpace = create_address_space(1024, 1024);
|
AddressSpace *addressSpace = create_address_space(256, 256);
|
||||||
printf("Address space: %dB ROM, %dB RAM\n", addressSpace->romSize, addressSpace->ramSize);
|
printf("Address space: %dB ROM, %dB RAM\n", addressSpace->romSize, addressSpace->ramSize);
|
||||||
|
|
||||||
if (load_to_rom(argv[1], addressSpace) != 0) {
|
if (load_to_rom(argv[1], addressSpace) != 0) {
|
||||||
|
@ -23,14 +23,20 @@ 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
|
||||||
|
|
||||||
|
printf("\n----- Start of program -----");
|
||||||
|
|
||||||
int code;
|
int code;
|
||||||
while ((code = cpu_cycle(&cpu)) != -1) {
|
uint32_t cycles = 0;
|
||||||
|
while ((code = cpu_cycle(&cpu)) == 0) {
|
||||||
|
cycles++;
|
||||||
//sleep(1);
|
//sleep(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("\n\n----- End of program -----\n");
|
||||||
|
|
||||||
printf("\n Emulator exited with code \033[1m%d\033[0m\n", code);
|
printf("\n Emulator exit code: \033[1m%d\033[0m\n", code);
|
||||||
printf(" Program exited with code \033[1m%d\033[0m\n\n", cpu.registers[10]);
|
printf(" Program exit code: \033[1m%d\033[0m (x10)\n", cpu.registers[10]);
|
||||||
|
printf(" Cycles: \033[1m%u\033[0m\n\n", cycles);
|
||||||
|
|
||||||
int cols[8] = {0};
|
int cols[8] = {0};
|
||||||
int total = 0;
|
int total = 0;
|
||||||
|
|
Loading…
Reference in a new issue