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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Minecon724
				Minecon724