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;
 | 
			
		||||
            text = (uint8_t *)data->d_buf;
 | 
			
		||||
            textSize = data->d_size;
 | 
			
		||||
        } else if (strcmp(sectionName, ".rodata") == 0) {
 | 
			
		||||
            Elf_Data *data = elf_getdata(scn, NULL);
 | 
			
		||||
            
 | 
			
		||||
            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;
 | 
			
		||||
            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,16 +82,30 @@ 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…
	
	Add table
		Add a link
		
	
		Reference in a new issue