parent
0e9a116bad
commit
d9bd57f984
9 changed files with 103 additions and 38 deletions
|
@ -11,6 +11,7 @@ Emulator exit codes:
|
|||
- `0` - never happens
|
||||
- `1` - invalid opcode
|
||||
- `2` - illegal instruction argument (like funct3)
|
||||
- `3` - memory error (for example writing to ROM)
|
||||
|
||||
Compile:
|
||||
1. Dependencies: `libelf`
|
||||
|
|
|
@ -2,11 +2,19 @@
|
|||
#define ADDRESS_SPACE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct AddressSpace_s {
|
||||
// A pointer to a ROM array. The array can vary in length.
|
||||
uint8_t *rom;
|
||||
uint32_t romSize;
|
||||
// The size of ROM.
|
||||
uint32_t romSize; // TODO look into making it const
|
||||
// Is ROM read only
|
||||
bool romLocked;
|
||||
|
||||
// A pointer to a RAM array. The array can vary in length.
|
||||
uint8_t *ram;
|
||||
// The size of RAM.
|
||||
uint32_t ramSize;
|
||||
};
|
||||
|
||||
|
|
BIN
programs/segfault.elf
Executable file
BIN
programs/segfault.elf
Executable file
Binary file not shown.
15
programs/src/segfault.c
Normal file
15
programs/src/segfault.c
Normal file
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* This normally causes a segfault, but won't on the emulator because it requires a strict check.
|
||||
* The emulator emulates bare metal, where there's no such checks.
|
||||
*/
|
||||
|
||||
int main() {
|
||||
int *i = (int*)1;
|
||||
*i = 10;
|
||||
|
||||
for (int j = 0; j < 100; j++) {
|
||||
*(i + j) = j;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
|
@ -3,20 +3,7 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct AddressSpace_s {
|
||||
// A pointer to a ROM array. The array can vary in length.
|
||||
uint8_t *rom;
|
||||
// The size of ROM.
|
||||
uint32_t romSize; // TODO look into making it const
|
||||
|
||||
// A pointer to a RAM array. The array can vary in length.
|
||||
uint8_t *ram;
|
||||
// The size of RAM.
|
||||
uint32_t ramSize;
|
||||
};
|
||||
|
||||
typedef struct AddressSpace_s AddressSpace;
|
||||
|
||||
#include "address_space.h"
|
||||
|
||||
AddressSpace *create_address_space(const uint32_t romSize, const uint32_t ramSize) {
|
||||
uint8_t *rom = calloc(romSize, 1);
|
||||
|
@ -33,6 +20,7 @@ AddressSpace *create_address_space(const uint32_t romSize, const uint32_t ramSiz
|
|||
|
||||
addressSpace->rom = rom;
|
||||
addressSpace->romSize = romSize;
|
||||
addressSpace->romLocked = false;
|
||||
addressSpace->ram = ram;
|
||||
addressSpace->ramSize = ramSize;
|
||||
|
||||
|
@ -71,6 +59,11 @@ int write_address_space(const AddressSpace *addressSpace, const uint32_t address
|
|||
uint32_t ramSize = addressSpace->ramSize;
|
||||
|
||||
if (address < romSize) {
|
||||
if (addressSpace->romLocked) {
|
||||
fprintf(stderr, "Cannot write to ROM\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (address + n >= romSize) {
|
||||
fprintf(stderr, "Writing %d bytes to %d will exceed ROM address space of %d\n", n, address, romSize);
|
||||
return 1;
|
||||
|
|
|
@ -69,8 +69,7 @@ void print_registers(CPU *cpu) {
|
|||
for (int rown=0; rown<rows; rown++) {
|
||||
printf("│ ");
|
||||
for (int coln=0; coln<columns; coln++) {
|
||||
//printf("%d", colSize[coln]);
|
||||
char *entry = malloc(colSize[coln]);
|
||||
char entry[50] = {0};
|
||||
int len = snprintf(entry, colSize[coln]+1, "x%d: \033[1m0x%X\033[0m (%u)", i, registers[i], registers[i]);
|
||||
memset(entry + len, ' ', colSize[coln] - len);
|
||||
|
||||
|
|
|
@ -40,6 +40,12 @@ static inline int process_elf(Elf *elf, CPU *cpu) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
uint32_t textSize = 0;
|
||||
uint8_t *text;
|
||||
|
||||
uint32_t rodataSize = 0;
|
||||
uint8_t *rodata;
|
||||
|
||||
while ((scn = elf_nextscn(elf, scn)) != NULL) {
|
||||
if (gelf_getshdr(scn, &shdr) != &shdr) {
|
||||
fprintf(stderr, "getshdr() failed: %s\n", elf_errmsg(-1));
|
||||
|
@ -73,17 +79,18 @@ static inline int process_elf(Elf *elf, CPU *cpu) {
|
|||
continue;
|
||||
}
|
||||
|
||||
char *content = (char *)data->d_buf;
|
||||
uint32_t contentSize = data->d_size;
|
||||
|
||||
if (contentSize > addressSpace->romSize) {
|
||||
fprintf(stderr, "Code has %d bytes, and doesn't fit in ROM of capacity %d bytes\n", contentSize, addressSpace->romSize);
|
||||
return 1;
|
||||
text = (uint8_t *)data->d_buf;
|
||||
textSize = data->d_size;
|
||||
} else if (strcmp(sectionName, ".rodata") == 0) {
|
||||
Elf_Data *data = elf_getdata(scn, NULL);
|
||||
|
||||
if (data == NULL || data->d_size == 0) {
|
||||
fprintf(stderr, "elf_getdata() failed: %s\n", elf_errmsg(-1));
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy(addressSpace->rom, content, contentSize);
|
||||
|
||||
printf("Loaded %d bytes or %d instructions from an ELF file\n", contentSize, contentSize / 4);
|
||||
rodata = (uint8_t *)data->d_buf;
|
||||
rodataSize = data->d_size;
|
||||
} else {
|
||||
printf("Unrecognized section: %s\n", sectionName);
|
||||
}
|
||||
|
@ -93,6 +100,30 @@ static inline int process_elf(Elf *elf, CPU *cpu) {
|
|||
printf("I couldn't verify whether the ELF was compiled for the correct instruction set!\n");
|
||||
}
|
||||
|
||||
if (textSize > 0) {
|
||||
if (textSize > addressSpace->romSize) {
|
||||
fprintf(stderr, "Code has %d bytes, and doesn't fit in ROM of capacity %d bytes\n", textSize, addressSpace->romSize);
|
||||
return 1;
|
||||
}
|
||||
|
||||
memcpy(addressSpace->rom, text, textSize);
|
||||
} else {
|
||||
fprintf(stderr, "There's no code\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (rodataSize > 0) {
|
||||
uint32_t totalSize = rodataSize + textSize;
|
||||
if (totalSize > addressSpace->romSize) {
|
||||
fprintf(stderr, "Code %d + rodata %d = %d bytes, which doesn't fit in ROM of capacity %d bytes\n", textSize, rodataSize, totalSize, addressSpace->romSize);
|
||||
return 1;
|
||||
}
|
||||
|
||||
memcpy(addressSpace->rom + textSize, rodata, rodataSize);
|
||||
}
|
||||
|
||||
printf("Loaded from ELF | Code: %d bytes (%d insts) | ROdata: %d bytes \n", textSize, textSize / 4, rodataSize);
|
||||
|
||||
cpu->programCounter = ehdr.e_entry;
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define VERBOSE_LOGGING 0
|
||||
#define VERBOSE_LOGGING 1
|
||||
|
||||
static void printBinary(uint32_t num) {
|
||||
if (num > 1) {
|
||||
|
@ -40,8 +40,8 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside
|
|||
|
||||
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 |= ((instruction >> 25) & 0x3F) << 5; // imm[10:5]
|
||||
imm |= ((instruction >> 8) & 0xF) << 1; // imm[4:1]
|
||||
// imm[0] is implicitly 0
|
||||
|
||||
bool branch = false;
|
||||
|
@ -69,10 +69,10 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside
|
|||
}
|
||||
|
||||
if (branch) {
|
||||
printf(" Yes, branching");
|
||||
printf(" Yes, branching by 0x%X to 0x%X", imm, cpu->programCounter + imm);
|
||||
cpu->programCounter += imm - 4; // program counter is always incremented after executing instruction so -4
|
||||
} else {
|
||||
printf(" No, not branching");
|
||||
printf(" No, not branching by 0x%X to 0x%X", imm, cpu->programCounter + imm);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -82,15 +82,29 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside
|
|||
int32_t imm = (int32_t)instruction >> 20;
|
||||
|
||||
switch (funct3) {
|
||||
case 0b000: // ADDI
|
||||
printf("ADDI - x%u 0x%X (%d) + 0x%X (%d) = 0x%X -> x%u", rs1, registers[rs1], registers[rs1], imm, imm, registers[rs1] + imm, rd);
|
||||
case 0b000: {
|
||||
// ADDI
|
||||
printf("ADDI: x%u 0x%X (%d) + 0x%X (%d) = 0x%X -> x%u", rs1, registers[rs1], registers[rs1], imm, imm, registers[rs1] + imm, rd);
|
||||
registers[rd] = registers[rs1] + imm;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
case 0b001: { // SLLI
|
||||
uint8_t shamt = imm & 0x1F;
|
||||
imm = imm >> 5 & 0x3F;
|
||||
if (imm != 0b0000000) {
|
||||
fprintf(stderr, "Invalid SLLI imm[11:5], must be zero but is 0x%X", (imm >> 5 & 0x3F));
|
||||
return 2;
|
||||
}
|
||||
printf("SLLI: x%u 0x%X (%d) << %d = 0x%X -> x%u", rs1, registers[rs1], registers[rs1], imm, registers[rs1] << shamt, rd);
|
||||
registers[rd] = registers[rs1] << shamt;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// TODO proper error handling
|
||||
fprintf(stderr, "Invalid OP-IMM funct3: 0x%X", funct3);
|
||||
return 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* code */
|
||||
break;
|
||||
|
@ -119,11 +133,11 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside
|
|||
registers[rd] = cpu->programCounter + 4;
|
||||
cpu->programCounter = result - 4; // program counter is always incremented after executing instruction
|
||||
|
||||
printf("JALR - x%u %u + %u = %u", rs1, result - imm, imm, result);
|
||||
printf("JALR - x%u %u + %u = 0x%X", rs1, result - imm, imm, result);
|
||||
|
||||
break;
|
||||
}
|
||||
case 0b0100011: { // STORE instruction (S type)
|
||||
case 0b0100011: { // STORE instruction (S type), write errors are ignored
|
||||
uint8_t funct3 = instruction >> 12 & 0x7;
|
||||
uint8_t rs1 = instruction >> 15 & 0x1F;
|
||||
uint8_t rs2 = instruction >> 20 & 0x1F;
|
||||
|
@ -152,6 +166,7 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside
|
|||
fprintf(stderr, "Invalid STORE funct3: 0x%X", funct3);
|
||||
return 2;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 0b0000011: { // LOAD instruction (I type)
|
||||
|
@ -218,7 +233,7 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO conside
|
|||
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);
|
||||
printf("JAL: Jumped to %u + %d = 0x%X (inst %d)", registers[rd], imm, cpu->programCounter, cpu->programCounter / 4);
|
||||
break;
|
||||
}
|
||||
case 0b0110011: { // OP for Integer Register-Register Operations (R type)
|
||||
|
|
|
@ -14,7 +14,7 @@ int main(int argc, char *argv[]) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
AddressSpace *addressSpace = create_address_space(256, 256);
|
||||
AddressSpace *addressSpace = create_address_space(512, 512);
|
||||
printf("Address space: %dB ROM, %dB RAM\n", addressSpace->romSize, addressSpace->ramSize);
|
||||
|
||||
CPU cpu = create_cpu(addressSpace);
|
||||
|
@ -31,6 +31,8 @@ int main(int argc, char *argv[]) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
addressSpace->romLocked = true;
|
||||
|
||||
|
||||
printf("\n----- Start of program (0x%X) -----", cpu.programCounter);
|
||||
|
||||
|
@ -47,6 +49,7 @@ int main(int argc, char *argv[]) {
|
|||
printf(" Program exit code: \033[1m%d\033[0m (x10)\n", cpu.registers[10]);
|
||||
printf(" Cycles: \033[1m%u\033[0m\n\n", cycles);
|
||||
|
||||
cpu.registers[0] = 0; // if x0 is changed in the last instruction (it usually is) it will be displayed what if was set to
|
||||
print_registers(&cpu);
|
||||
|
||||
return 0;
|
||||
|
|
Loading…
Reference in a new issue