Initial commit
Added base code, can run ELF files and simulate RV32I instructions
This commit is contained in:
		
							
								
								
									
										37
									
								
								src/bootloader/bootloader.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/bootloader/bootloader.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
#include "bootloader.h"
 | 
			
		||||
#include "elf/elf.h"
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
uint32_t bootload(char* file_path)
 | 
			
		||||
{
 | 
			
		||||
    // Open the file
 | 
			
		||||
    FILE* f = fopen(file_path, "r");
 | 
			
		||||
    if(!f)
 | 
			
		||||
    {
 | 
			
		||||
        fprintf(stderr, "Could not open file '%s': %s\n", file_path, strerror(errno));
 | 
			
		||||
        exit(EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Obtain file size
 | 
			
		||||
    fseek(f, 0, SEEK_END);
 | 
			
		||||
	size_t file_size = ftell(f);
 | 
			
		||||
	fseek(f, 0, SEEK_SET);
 | 
			
		||||
 | 
			
		||||
    // Load the file in memory
 | 
			
		||||
    void* file = malloc(file_size);
 | 
			
		||||
    if(fread(file, file_size, 1, f) != 1)
 | 
			
		||||
	{
 | 
			
		||||
		fprintf(stderr, "Could not read file '%s': %s\n", file_path, strerror(errno));
 | 
			
		||||
        fclose(f);
 | 
			
		||||
		exit(EXIT_FAILURE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    // Close the file
 | 
			
		||||
    fclose(f);
 | 
			
		||||
 | 
			
		||||
    // TODO: Check file type (for now we only bootload ELF)
 | 
			
		||||
    return elf_32_load(file);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								src/bootloader/bootloader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/bootloader/bootloader.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
#ifndef BOOTLOADER_H
 | 
			
		||||
#define BOOTLOADER_H
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
uint32_t bootload(char* file_path);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										101
									
								
								src/bootloader/elf/elf.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								src/bootloader/elf/elf.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,101 @@
 | 
			
		||||
#include "elf.h"
 | 
			
		||||
#include "memory/memory.h"
 | 
			
		||||
#include "vriscv.h"
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
uint32_t elf_32_load(void* file)
 | 
			
		||||
{
 | 
			
		||||
    // Parse/verify the header
 | 
			
		||||
    elf_header_32_t* header = file;
 | 
			
		||||
 | 
			
		||||
    // Verify magic number
 | 
			
		||||
    uint8_t* m = header->ELF;
 | 
			
		||||
    if((m[0] != 0x7F) || (m[1] != 'E') || (m[2] != 'L') || (m[3] != 'F'))
 | 
			
		||||
    {
 | 
			
		||||
        fprintf(stderr, "Not a valid ELF file (wrong magic)\n");
 | 
			
		||||
        exit(EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Verify architecture
 | 
			
		||||
    if(header->instruction_set != INSTRUCTION_SET_RISCV)
 | 
			
		||||
    {
 | 
			
		||||
        switch(header->instruction_set)
 | 
			
		||||
        {
 | 
			
		||||
            case INSTRUCTION_SET_X86:
 | 
			
		||||
                fprintf(stderr, "Provided ELF file targets x86 ; perhaps you forgot to cross-compile ? Please provide a RISC-V ELF\n");
 | 
			
		||||
                break;
 | 
			
		||||
            case INSTRUCTION_SET_X86_64:
 | 
			
		||||
                fprintf(stderr, "Provided ELF file targets x86_64; perhaps you forgot to cross-compile ? Please provide a RISC-V ELF\n");
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                fprintf(stderr, "Provided ELF file is for an unknown instruction set architecture (0x%x), please provide a RISC-V ELF\n", header->instruction_set);
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        exit(EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Verify bit count
 | 
			
		||||
    if(header->bits != BITS_32)
 | 
			
		||||
    {
 | 
			
		||||
        switch(header->bits)
 | 
			
		||||
        {
 | 
			
		||||
            case BITS_64:
 | 
			
		||||
                fprintf(stderr, "Provided ELF file targets RISC-V 64 bits ; please provide a 32-bits ELF\n");
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                fprintf(stderr, "Provided ELF file targets an unknown bit count (not 32 bits) ; please provide a 32-bits ELF\n");
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        exit(EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Verify endianness
 | 
			
		||||
    if(header->endianness != ELF_LITTLE_ENDIAN)
 | 
			
		||||
    {
 | 
			
		||||
        fprintf(stderr, "Provided ELF file is encoded in big endian ; please provide a little endian ELF\n");
 | 
			
		||||
        exit(EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // File should be correct ; now load it, using program header table
 | 
			
		||||
    elf_program_header_32_t* program_header = (elf_program_header_32_t*) (((uint8_t*) file) + header->program_header_table_32);
 | 
			
		||||
    size_t program_header_count = header->program_entry_amount;
 | 
			
		||||
    for(size_t i = 0; i < program_header_count; i++)
 | 
			
		||||
    {
 | 
			
		||||
        elf_program_header_32_t current = program_header[i];
 | 
			
		||||
        
 | 
			
		||||
        // Check segment type
 | 
			
		||||
        if(current.segment_type != SEGMENT_TYPE_LOAD)
 | 
			
		||||
        {
 | 
			
		||||
            fprintf(stderr, "WARNING: Unknown segment type %u in ELF file ; skipping\n", current.segment_type);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check memory size
 | 
			
		||||
        size_t memsz = current.segment_memory_size;
 | 
			
		||||
        if(!memsz)
 | 
			
		||||
        {
 | 
			
		||||
            fprintf(stderr, "WARNING: LOAD segment with null size in ELF file ; skipping\n");
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Map the segment : first the part in the file, then the zeros
 | 
			
		||||
        if(memory_size < (current.virtual_address + memsz))
 | 
			
		||||
        {
 | 
			
		||||
            fprintf(stderr, "FATAL: Not enough memory while loading ELF file\n");
 | 
			
		||||
            exit(EXIT_FAILURE);
 | 
			
		||||
        }
 | 
			
		||||
        if(current.segment_file_size != 0)
 | 
			
		||||
        {
 | 
			
		||||
            memcpy(&memory[current.virtual_address], ((uint8_t*) file) + current.segment_offset, current.segment_file_size);
 | 
			
		||||
        }
 | 
			
		||||
        if(memsz > current.segment_file_size)
 | 
			
		||||
        {
 | 
			
		||||
            memset(&memory[current.virtual_address] + current.segment_file_size, 0, memsz - current.segment_file_size);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return header->entry_32;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										83
									
								
								src/bootloader/elf/elf.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/bootloader/elf/elf.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
			
		||||
#ifndef ELF_H
 | 
			
		||||
#define ELF_H
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * ELF handling
 | 
			
		||||
 * Valentin HAUDIQUET
 | 
			
		||||
 * Sources are :
 | 
			
		||||
 * - https://refspecs.linuxfoundation.org/elf/elf.pdf (ELF Reference)
 | 
			
		||||
 * - https://wiki.osdev.org/ELF (OSDev)
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
typedef struct ELF_HEADER_32
 | 
			
		||||
{
 | 
			
		||||
	uint8_t ELF[4];
 | 
			
		||||
	uint8_t bits;       // 1 = 32bits, 2 = 64bits
 | 
			
		||||
	uint8_t endianness; // 1 = little, 2 = big
 | 
			
		||||
	uint8_t header_version;
 | 
			
		||||
	uint8_t abi;
 | 
			
		||||
	uint8_t padding0[8];
 | 
			
		||||
	uint16_t exec_type; // 1 = relocatable, 2 = executable, 3 = shared, 4 = core
 | 
			
		||||
	uint16_t instruction_set;
 | 
			
		||||
	uint32_t elf_version;
 | 
			
		||||
	uint32_t entry_32;
 | 
			
		||||
	uint32_t program_header_table_32;
 | 
			
		||||
	uint32_t section_table_32;
 | 
			
		||||
	uint32_t flags;
 | 
			
		||||
	uint16_t header_size;
 | 
			
		||||
	uint16_t program_entry_size;
 | 
			
		||||
	uint16_t program_entry_amount;
 | 
			
		||||
	uint16_t section_entry_size;
 | 
			
		||||
	uint16_t section_entry_amount;
 | 
			
		||||
	uint16_t section_str_index; // Index of string table associated with section names
 | 
			
		||||
} __attribute__((packed)) elf_header_32_t;
 | 
			
		||||
 | 
			
		||||
#define ELF_LITTLE_ENDIAN 1
 | 
			
		||||
#define ELF_BIG_ENDIAN 2
 | 
			
		||||
 | 
			
		||||
#define BITS_32 1
 | 
			
		||||
#define BITS_64 2
 | 
			
		||||
 | 
			
		||||
#define INSTRUCTION_SET_X86 0x3
 | 
			
		||||
#define INSTRUCTION_SET_X86_64 0x3E
 | 
			
		||||
#define INSTRUCTION_SET_RISCV 0xF3
 | 
			
		||||
 | 
			
		||||
typedef struct ELF_SECTION_HEADER_32
 | 
			
		||||
{
 | 
			
		||||
	uint16_t section_name;
 | 
			
		||||
	uint16_t section_type;
 | 
			
		||||
	uint16_t section_flags;
 | 
			
		||||
	uint32_t section_addr_32;
 | 
			
		||||
	uint32_t section_offset_32;
 | 
			
		||||
	uint16_t section_size;
 | 
			
		||||
	uint16_t section_link;
 | 
			
		||||
	uint16_t section_info;
 | 
			
		||||
	uint16_t section_addralign;
 | 
			
		||||
	uint16_t section_entrysize;
 | 
			
		||||
} __attribute__((packed)) elf_section_header_32_t;
 | 
			
		||||
 | 
			
		||||
typedef struct ELF_PROGRAM_HEADER_32
 | 
			
		||||
{
 | 
			
		||||
    uint32_t segment_type;
 | 
			
		||||
    uint32_t segment_offset;
 | 
			
		||||
    uint32_t virtual_address;
 | 
			
		||||
    uint32_t undefined;
 | 
			
		||||
    uint32_t segment_file_size;
 | 
			
		||||
    uint32_t segment_memory_size;
 | 
			
		||||
    uint32_t flags;
 | 
			
		||||
    uint32_t align;
 | 
			
		||||
} __attribute__((packed)) elf_program_header_32_t;
 | 
			
		||||
 | 
			
		||||
#define SEGMENT_TYPE_NULL 0
 | 
			
		||||
#define SEGMENT_TYPE_LOAD 1
 | 
			
		||||
#define SEGMENT_TYPE_DYNAMIC 2
 | 
			
		||||
#define SEGMENT_TYPE_INTERP 3
 | 
			
		||||
#define SEGMENT_TYPE_NOTE 4
 | 
			
		||||
 | 
			
		||||
uint32_t elf_32_load(void* file);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										63
									
								
								src/cpu/instruction.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/cpu/instruction.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
#ifndef INSTRUCTION_H
 | 
			
		||||
#define INSTRUCTION_H
 | 
			
		||||
 | 
			
		||||
/* RISC-V RV32 I Base Instruction Set */
 | 
			
		||||
#define OPCODE_LUI 0x37
 | 
			
		||||
#define OPCODE_AUIPC 0x17
 | 
			
		||||
#define OPCODE_JAL 0x6F
 | 
			
		||||
#define OPCODE_JALR 0x67
 | 
			
		||||
#define OPCODE_BRANCH 0x63
 | 
			
		||||
#define OPCODE_LOAD 0x3
 | 
			
		||||
#define OPCODE_STORE 0x23
 | 
			
		||||
#define OPCODE_ARITHLOG_IMM 0x13
 | 
			
		||||
#define OPCODE_ARITHLOG 0x33
 | 
			
		||||
#define OPCODE_NOP 0xF
 | 
			
		||||
#define OPCODE_SYSTEM 0x73
 | 
			
		||||
 | 
			
		||||
/* OPCODE_BRANCH sub functions (func3) */
 | 
			
		||||
#define FUNC3_BEQ 0x0
 | 
			
		||||
#define FUNC3_BNE 0x1
 | 
			
		||||
#define FUNC3_BLT 0x4
 | 
			
		||||
#define FUNC3_BGE 0x5
 | 
			
		||||
#define FUNC3_BLTU 0x6
 | 
			
		||||
#define FUNC3_BGEU 0x7
 | 
			
		||||
 | 
			
		||||
/* OPCODE_LOAD sub functions (func3) */
 | 
			
		||||
#define FUNC3_LB 0x0
 | 
			
		||||
#define FUNC3_LH 0x1
 | 
			
		||||
#define FUNC3_LW 0x2
 | 
			
		||||
#define FUNC3_LBU 0x4
 | 
			
		||||
#define FUNC3_LHU 0x5
 | 
			
		||||
 | 
			
		||||
/* OPCODE_STORE sub functions (func3) */
 | 
			
		||||
#define FUNC3_SB 0x0
 | 
			
		||||
#define FUNC3_SH 0x1
 | 
			
		||||
#define FUNC3_SW 0x2
 | 
			
		||||
 | 
			
		||||
/* OPCODE_ARITHLOG_IMM sub functions (func3 + func7) */
 | 
			
		||||
#define FUNC3_ADDI 0x0
 | 
			
		||||
#define FUNC3_SLTI 0x2
 | 
			
		||||
#define FUNC3_SLTIU 0x3
 | 
			
		||||
#define FUNC3_XORI 0x4
 | 
			
		||||
#define FUNC3_ORI 0x6
 | 
			
		||||
#define FUNC3_ANDI 0x7
 | 
			
		||||
#define FUNC3_SLLI 0x1
 | 
			
		||||
#define FUNC3_SRLI_SRAI 0x5
 | 
			
		||||
#define FUNC7_SRLI 0x0
 | 
			
		||||
#define FUNC7_SRAI 0x20
 | 
			
		||||
 | 
			
		||||
/* OPCODE_ARITHLOG sub functions (func3 + func7) */
 | 
			
		||||
#define FUNC3_ADD_SUB 0x0
 | 
			
		||||
#define FUNC7_ADD 0x0
 | 
			
		||||
#define FUNC7_SUB 0x20
 | 
			
		||||
#define FUNC3_SLL 0x1
 | 
			
		||||
#define FUNC3_SLT 0x2
 | 
			
		||||
#define FUNC3_SLTU 0x3
 | 
			
		||||
#define FUNC3_XOR 0x4
 | 
			
		||||
#define FUNC3_SRL_SRA 0x5
 | 
			
		||||
#define FUNC7_SRL 0x0
 | 
			
		||||
#define FUNC7_SRA 0x20
 | 
			
		||||
#define FUNC3_OR 0x7
 | 
			
		||||
#define FUNC3_AND 0x8
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										452
									
								
								src/cpu/rv32cpu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										452
									
								
								src/cpu/rv32cpu.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,452 @@
 | 
			
		||||
#include "rv32cpu.h"
 | 
			
		||||
#include "instruction.h"
 | 
			
		||||
 | 
			
		||||
#include "memory/memory.h"
 | 
			
		||||
#include "memory/mmu/mmu.h"
 | 
			
		||||
#include "vriscv.h"
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
rv32_cpu_t* cpu0;
 | 
			
		||||
 | 
			
		||||
typedef union RAW_INSTRUCTION
 | 
			
		||||
{
 | 
			
		||||
	uint32_t data;
 | 
			
		||||
	struct
 | 
			
		||||
	{
 | 
			
		||||
		uint8_t opcode : 7;
 | 
			
		||||
		uint16_t rd : 5;
 | 
			
		||||
		uint16_t func3 : 3;
 | 
			
		||||
		uint16_t rs1 : 5;
 | 
			
		||||
		uint16_t rs2 : 5;
 | 
			
		||||
		uint16_t func7 : 7;
 | 
			
		||||
	} __attribute__((packed));
 | 
			
		||||
} __attribute__((packed)) raw_instruction_t;
 | 
			
		||||
 | 
			
		||||
typedef struct INSTRUCTION
 | 
			
		||||
{
 | 
			
		||||
	uint8_t opcode;
 | 
			
		||||
	uint32_t immediate;
 | 
			
		||||
	uint8_t func3;
 | 
			
		||||
	uint8_t func7;
 | 
			
		||||
	uint8_t rd;
 | 
			
		||||
	uint8_t rs1;
 | 
			
		||||
	uint8_t rs2;
 | 
			
		||||
} instruction_t;
 | 
			
		||||
 | 
			
		||||
void cpu_init()
 | 
			
		||||
{
 | 
			
		||||
	cpu0 = malloc(sizeof(rv32_cpu_t));
 | 
			
		||||
	cpu0->regs.zero = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cpu_decode(raw_instruction_t raw_instruction, instruction_t* output)
 | 
			
		||||
{
 | 
			
		||||
	output->opcode = raw_instruction.opcode;
 | 
			
		||||
	output->immediate = 0;
 | 
			
		||||
	output->func3 = raw_instruction.func3;
 | 
			
		||||
	output->func7 = raw_instruction.func7;
 | 
			
		||||
	output->rd = raw_instruction.rd;
 | 
			
		||||
	output->rs1 = raw_instruction.rs1;
 | 
			
		||||
	output->rs2 = raw_instruction.rs2;
 | 
			
		||||
 | 
			
		||||
	// Decode immediate, and make sure opcode is correct
 | 
			
		||||
	switch(raw_instruction.opcode)
 | 
			
		||||
	{
 | 
			
		||||
		// U-type instructions
 | 
			
		||||
		case OPCODE_LUI:
 | 
			
		||||
		case OPCODE_AUIPC:
 | 
			
		||||
			output->immediate = raw_instruction.data & 0xFFFFF000;
 | 
			
		||||
			break;
 | 
			
		||||
		// J-type instructions
 | 
			
		||||
		case OPCODE_JAL:
 | 
			
		||||
			// Last bit (31) of data is immediate bit 20
 | 
			
		||||
			output->immediate = (raw_instruction.data & 0x80000000) >> 11;
 | 
			
		||||
			// Then following 10 bits (30-21) are immediate bits 10-1
 | 
			
		||||
			output->immediate |= (raw_instruction.data & 0x7FE00000) >> 20;
 | 
			
		||||
			// Following bit (20) is immediate bit 11
 | 
			
		||||
			output->immediate |= (raw_instruction.data & 0x200000) >> 10;
 | 
			
		||||
			// Last bits (19-12) are immediate bits 19-12
 | 
			
		||||
			output->immediate |= (raw_instruction.data & 0xFF000);
 | 
			
		||||
			break;
 | 
			
		||||
		// I-type instructions
 | 
			
		||||
		case OPCODE_JALR:
 | 
			
		||||
		case OPCODE_LOAD:
 | 
			
		||||
		case OPCODE_ARITHLOG_IMM:
 | 
			
		||||
		case OPCODE_SYSTEM:
 | 
			
		||||
			// Bits 31-20 are immediate bits 11-0
 | 
			
		||||
			output->immediate = (raw_instruction.data & 0xFFF00000) >> 20;
 | 
			
		||||
			break;
 | 
			
		||||
		// B-type instructions
 | 
			
		||||
		case OPCODE_BRANCH:
 | 
			
		||||
			// Last bit (31) of data is immediate bit 12
 | 
			
		||||
			output->immediate = (raw_instruction.data & 0x80000000) >> 19;
 | 
			
		||||
			// Then following 6 bits (30-25) are immediate bits 10-5
 | 
			
		||||
			output->immediate |= (raw_instruction.data & 0x7E000000) >> 20;
 | 
			
		||||
			// On rd field, last 4 bits (4:1) are immediate bits 4:1
 | 
			
		||||
			output->immediate |= (raw_instruction.rd & 0x1E);
 | 
			
		||||
			// On rd field, first bit (0) is immediate bit 11
 | 
			
		||||
			output->immediate |= (raw_instruction.rd & 0x01) << 11;
 | 
			
		||||
			break;
 | 
			
		||||
		// R-type instructions
 | 
			
		||||
		case OPCODE_ARITHLOG:
 | 
			
		||||
			break;
 | 
			
		||||
		// S-type instructions
 | 
			
		||||
		case OPCODE_STORE:
 | 
			
		||||
			// Bits 31-25 (func7) are immediate bits 11:5
 | 
			
		||||
			output->immediate = raw_instruction.func7 << 5;
 | 
			
		||||
			// Bits of rd are immediate bits 4:0
 | 
			
		||||
			output->immediate |= raw_instruction.rd;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			fprintf(stderr, "Error: Unknown instruction opcode 0x%x, could not decode\n", raw_instruction.opcode);
 | 
			
		||||
			exit(EXIT_FAILURE);
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cpu_execute(rv32_cpu_t* cpu, instruction_t* instruction)
 | 
			
		||||
{
 | 
			
		||||
	switch(instruction->opcode)
 | 
			
		||||
	{
 | 
			
		||||
		case OPCODE_LUI:
 | 
			
		||||
		{
 | 
			
		||||
			// Load Upper Immediate (load immediate(31:12 bits) in rd)
 | 
			
		||||
			if(instruction->rd)
 | 
			
		||||
				cpu->regs.x[instruction->rd] = instruction->immediate;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		case OPCODE_AUIPC:
 | 
			
		||||
		{
 | 
			
		||||
			// Add Upper Immediate to PC
 | 
			
		||||
			if(instruction->rd)
 | 
			
		||||
				cpu->regs.x[instruction->rd] = instruction->immediate + cpu->pc;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		case OPCODE_JAL:
 | 
			
		||||
		{
 | 
			
		||||
			// Jump And Link
 | 
			
		||||
			if(instruction->rd)
 | 
			
		||||
				cpu->regs.x[instruction->rd] = cpu->pc + 4;
 | 
			
		||||
			// Sign extend immediate from 21 bits to 32 bits
 | 
			
		||||
			uint32_t immediate = (instruction->immediate & 0x1FFFFF) | (instruction->immediate & 0x100000 ? 0xFFE00000 : 0);
 | 
			
		||||
			cpu->pc += immediate - 4;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		case OPCODE_JALR:
 | 
			
		||||
		{
 | 
			
		||||
			// Jump And Link Register
 | 
			
		||||
			if(instruction->rd)
 | 
			
		||||
				cpu->regs.x[instruction->rd] = cpu->pc + 4;
 | 
			
		||||
			// Sign extend immediate from 12 bits to 32 bits
 | 
			
		||||
			uint32_t immediate = (instruction->immediate & 0xFFF) | (instruction->immediate & 0x800 ? 0xFFFFF000 : 0);
 | 
			
		||||
			cpu->pc = ((cpu->regs.x[instruction->rs1] + immediate) & 0xFFFFFFFE) - 4;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		case OPCODE_BRANCH:
 | 
			
		||||
		{
 | 
			
		||||
			// Branches ; to know which one, we must analyse func3
 | 
			
		||||
			// Sign extend immediate from 13 bits to 32 bits
 | 
			
		||||
			uint32_t immediate = (instruction->immediate & 0xFFF) | (instruction->immediate & 0x1000 ? 0xFFFFE000 : 0);
 | 
			
		||||
			immediate -= 4;
 | 
			
		||||
 | 
			
		||||
			switch(instruction->func3)
 | 
			
		||||
			{
 | 
			
		||||
				case FUNC3_BEQ:
 | 
			
		||||
					// Branch EQual
 | 
			
		||||
					if(cpu->regs.x[instruction->rs1] == cpu->regs.x[instruction->rs2])
 | 
			
		||||
						cpu->pc = immediate;
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_BNE:
 | 
			
		||||
					// Branch Not Equal
 | 
			
		||||
					if(cpu->regs.x[instruction->rs1] != cpu->regs.x[instruction->rs2])
 | 
			
		||||
						cpu->pc = immediate;
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_BLT:
 | 
			
		||||
					// Branch Less Than
 | 
			
		||||
					if(((int32_t) cpu->regs.x[instruction->rs1]) < ((int32_t) cpu->regs.x[instruction->rs2]))
 | 
			
		||||
						cpu->pc = immediate;
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_BLTU:
 | 
			
		||||
					// Branch Less Than Unsigned
 | 
			
		||||
					if(cpu->regs.x[instruction->rs1] < cpu->regs.x[instruction->rs2])
 | 
			
		||||
						cpu->pc = immediate;
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_BGE:
 | 
			
		||||
					// Branch Greater Equal
 | 
			
		||||
					if(((int32_t) cpu->regs.x[instruction->rs1]) >= ((int32_t) cpu->regs.x[instruction->rs2]))
 | 
			
		||||
						cpu->pc = immediate;
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_BGEU:
 | 
			
		||||
					// Branch Greater Equal Unsigned
 | 
			
		||||
					if(cpu->regs.x[instruction->rs1] >= cpu->regs.x[instruction->rs2])
 | 
			
		||||
						cpu->pc = immediate;
 | 
			
		||||
					break;
 | 
			
		||||
				default:
 | 
			
		||||
					fprintf(stderr, "FATAL: Unknown func3 0x%x for branch instruction, could not execute\n", instruction->func3);
 | 
			
		||||
					exit(EXIT_FAILURE);
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		case OPCODE_LOAD:
 | 
			
		||||
		{
 | 
			
		||||
			// Loads ; to know which one, we must analyse func3
 | 
			
		||||
			// Sign extend immediate from 12 bits to 32 bits
 | 
			
		||||
			uint32_t immediate = (instruction->immediate & 0xFFF) | (instruction->immediate & 0x800 ? 0xFFFFF000 : 0);
 | 
			
		||||
			uint32_t address = cpu->regs.x[instruction->rs1] + immediate;
 | 
			
		||||
 | 
			
		||||
			switch(instruction->func3)
 | 
			
		||||
			{
 | 
			
		||||
				case FUNC3_LB:
 | 
			
		||||
					// Load Byte (8-bits)
 | 
			
		||||
					cpu->regs.x[instruction->rd] = memory[mmu_translate(address)];
 | 
			
		||||
					// Sign extend from 8 bits to 32 bits
 | 
			
		||||
					cpu->regs.x[instruction->rd] |= (cpu->regs.x[instruction->rd] & 0x80 ? 0xFFFFFF00 : 0);
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_LH:
 | 
			
		||||
					// Load Halfword (16-bits)
 | 
			
		||||
					cpu->regs.x[instruction->rd] = *((uint16_t*) &memory[mmu_translate(address)]);
 | 
			
		||||
					// Sign extend from 16 bits to 32 bits
 | 
			
		||||
					cpu->regs.x[instruction->rd] |= (cpu->regs.x[instruction->rd] & 0x8000 ? 0xFFFF0000 : 0);
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_LW:
 | 
			
		||||
					// Load Word (32-bits)
 | 
			
		||||
					cpu->regs.x[instruction->rd] = *((uint32_t*) &memory[mmu_translate(address)]);
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_LBU:
 | 
			
		||||
					// Load Byte Unsigned (8-bits)
 | 
			
		||||
					cpu->regs.x[instruction->rd] = memory[mmu_translate(address)];
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_LHU:
 | 
			
		||||
					// Load Halfword Unsigned (16-bits)
 | 
			
		||||
					cpu->regs.x[instruction->rd] = *((uint16_t*) &memory[mmu_translate(address)]);
 | 
			
		||||
					break;
 | 
			
		||||
				default:
 | 
			
		||||
					fprintf(stderr, "FATAL: Unknown func3 0x%x for load instruction, could not execute\n", instruction->func3);
 | 
			
		||||
					exit(EXIT_FAILURE);
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		case OPCODE_STORE:
 | 
			
		||||
		{
 | 
			
		||||
			// Store ; to know which one, we must analyse func3
 | 
			
		||||
			// Sign extend immediate from 12 bits to 32 bits
 | 
			
		||||
			uint32_t immediate = (instruction->immediate & 0xFFF) | (instruction->immediate & 0x800 ? 0xFFFFF000 : 0);
 | 
			
		||||
			uint32_t address = cpu->regs.x[instruction->rs1] + immediate;
 | 
			
		||||
 | 
			
		||||
			switch(instruction->func3)
 | 
			
		||||
			{
 | 
			
		||||
				case FUNC3_SB:
 | 
			
		||||
					// Store Byte (8-bits)
 | 
			
		||||
					memory[mmu_translate(address)] = cpu->regs.x[instruction->rs2] & 0xFF;
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_SH:
 | 
			
		||||
					// Store Halfword (16-bits)
 | 
			
		||||
					*((uint16_t*) &memory[mmu_translate(address)]) = cpu->regs.x[instruction->rs2] & 0xFFFF;
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_SW:
 | 
			
		||||
					// Store Word (32-bits)
 | 
			
		||||
					*((uint32_t*) &memory[mmu_translate(address)]) = cpu->regs.x[instruction->rs2];
 | 
			
		||||
					break;
 | 
			
		||||
				default:
 | 
			
		||||
					fprintf(stderr, "FATAL: Unknown func3 0x%x for store instruction, could not execute\n", instruction->func3);
 | 
			
		||||
					exit(EXIT_FAILURE);
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		case OPCODE_ARITHLOG_IMM:
 | 
			
		||||
		{
 | 
			
		||||
			// Arithmetic and logic instructions on immediate values
 | 
			
		||||
			// To find out which operation, we must analyse func3
 | 
			
		||||
			// Sign extend immediate from 12 bits to 32 bits
 | 
			
		||||
			uint32_t immediate = (instruction->immediate & 0xFFF) | (instruction->immediate & 0x800 ? 0xFFFFF000 : 0);
 | 
			
		||||
 | 
			
		||||
			switch(instruction->func3)
 | 
			
		||||
			{
 | 
			
		||||
				case FUNC3_ADDI:
 | 
			
		||||
					// ADD Immediate
 | 
			
		||||
					cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] + immediate;
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_SLTI:
 | 
			
		||||
					// Set Less Than Immediate
 | 
			
		||||
					if(((int32_t) cpu->regs.x[instruction->rs1]) < ((int32_t) immediate))
 | 
			
		||||
						cpu->regs.x[instruction->rd] = 1;
 | 
			
		||||
					else
 | 
			
		||||
						cpu->regs.x[instruction->rd] = 0;
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_SLTIU:
 | 
			
		||||
					// Set Less Than Immediate Unsigned
 | 
			
		||||
					if(cpu->regs.x[instruction->rs1] < immediate)
 | 
			
		||||
						cpu->regs.x[instruction->rd] = 1;
 | 
			
		||||
					else
 | 
			
		||||
						cpu->regs.x[instruction->rd] = 0;
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_XORI:
 | 
			
		||||
					// XOR Immediate
 | 
			
		||||
					cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] ^ immediate;
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_ORI:
 | 
			
		||||
					// OR Immediate
 | 
			
		||||
					cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] | immediate;
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_ANDI:
 | 
			
		||||
					// AND Immediate
 | 
			
		||||
					cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] & immediate;
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_SLLI:
 | 
			
		||||
					// Sign-extend immediate in rs2 from 5 bits to 32 bits
 | 
			
		||||
					immediate = (cpu->regs.x[instruction->rs2] & 0x1F) | (cpu->regs.x[instruction->rs2] & 0x10 ? 0xFFFFFFE0 : 0);
 | 
			
		||||
					cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] << immediate;
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_SRLI_SRAI:
 | 
			
		||||
					// Sign-extend immediate in rs2 from 5 bits to 32 bits
 | 
			
		||||
					immediate = (cpu->regs.x[instruction->rs2] & 0x1F) | (cpu->regs.x[instruction->rs2] & 0x10 ? 0xFFFFFFE0 : 0);
 | 
			
		||||
					// Analyse func7 to know which is it
 | 
			
		||||
					switch(instruction->func7)
 | 
			
		||||
					{
 | 
			
		||||
						case FUNC7_SRLI:
 | 
			
		||||
							cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] >> immediate;
 | 
			
		||||
							break;
 | 
			
		||||
						case FUNC7_SRAI:
 | 
			
		||||
							// Arithmetic slide
 | 
			
		||||
							uint32_t sign_bit = cpu->regs.x[instruction->rs1] & 0x80000000;
 | 
			
		||||
							cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] >> immediate;
 | 
			
		||||
							if(sign_bit)
 | 
			
		||||
								cpu->regs.x[instruction->rd] |= ~(0xFFFFFFFF >> immediate);
 | 
			
		||||
							break;
 | 
			
		||||
						default:
 | 
			
		||||
							fprintf(stderr, "FATAL: Unknown func7 0x%x for arithlog immediate SRLI/SRAI instruction, could not execute\n", instruction->func7);
 | 
			
		||||
							exit(EXIT_FAILURE);
 | 
			
		||||
							break;
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
				default:
 | 
			
		||||
					fprintf(stderr, "FATAL: Unknown func3 0x%x for arithlog immediate instruction, could not execute\n", instruction->func3);
 | 
			
		||||
					exit(EXIT_FAILURE);
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		case OPCODE_ARITHLOG:
 | 
			
		||||
		{
 | 
			
		||||
			// Arithmetic and logic instructions
 | 
			
		||||
			// To find out which operation, we must analyse func3 and func7
 | 
			
		||||
			switch(instruction->func3)
 | 
			
		||||
			{
 | 
			
		||||
				case FUNC3_ADD_SUB:
 | 
			
		||||
					switch(instruction->func7)
 | 
			
		||||
					{
 | 
			
		||||
						case FUNC7_ADD:
 | 
			
		||||
							cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] + cpu->regs.x[instruction->rs2];
 | 
			
		||||
							break;
 | 
			
		||||
						case FUNC7_SUB:
 | 
			
		||||
							cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] - cpu->regs.x[instruction->rs2];
 | 
			
		||||
							break;
 | 
			
		||||
						default:
 | 
			
		||||
							fprintf(stderr, "FATAL: Unknown func7 0x%x for arithlog ADD/SUB instruction, could not execute\n", instruction->func7);
 | 
			
		||||
							exit(EXIT_FAILURE);
 | 
			
		||||
							break;
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_SLL:
 | 
			
		||||
					// Slide Left Logical
 | 
			
		||||
					uint32_t sll_value = cpu->regs.x[instruction->rs2] & 0x1F;
 | 
			
		||||
					cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] << sll_value;
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_SLT:
 | 
			
		||||
					// Set Less Than
 | 
			
		||||
					if(((int32_t) cpu->regs.x[instruction->rs1]) < ((int32_t) cpu->regs.x[instruction->rs2]))
 | 
			
		||||
						cpu->regs.x[instruction->rd] = 1;
 | 
			
		||||
					else
 | 
			
		||||
						cpu->regs.x[instruction->rd] = 0;
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_SLTIU:
 | 
			
		||||
					// Set Less Than Unsigned
 | 
			
		||||
					if(cpu->regs.x[instruction->rs1] < cpu->regs.x[instruction->rs2])
 | 
			
		||||
						cpu->regs.x[instruction->rd] = 1;
 | 
			
		||||
					else
 | 
			
		||||
						cpu->regs.x[instruction->rd] = 0;
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_XOR:
 | 
			
		||||
					cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] ^ cpu->regs.x[instruction->rs2];
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_OR:
 | 
			
		||||
					cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] | cpu->regs.x[instruction->rs2];
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_AND:
 | 
			
		||||
					cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] & cpu->regs.x[instruction->rs2];
 | 
			
		||||
					break;
 | 
			
		||||
				case FUNC3_SRL_SRA:
 | 
			
		||||
					switch(instruction->func7)
 | 
			
		||||
					{
 | 
			
		||||
						case FUNC7_SRL:
 | 
			
		||||
							// Slide Right Logical
 | 
			
		||||
							uint32_t srl_value = cpu->regs.x[instruction->rs2] & 0x1F;
 | 
			
		||||
							cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] >> srl_value;
 | 
			
		||||
							break;
 | 
			
		||||
						case FUNC7_SRA:
 | 
			
		||||
							// Slide Right Arithmetical
 | 
			
		||||
							uint32_t sra_value = cpu->regs.x[instruction->rs2] & 0x1F;
 | 
			
		||||
							uint32_t sign_bit = cpu->regs.x[instruction->rs1] & 0x80000000;
 | 
			
		||||
							cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] >> sra_value;
 | 
			
		||||
							if(sign_bit)
 | 
			
		||||
								cpu->regs.x[instruction->rd] |= ~(0xFFFFFFFF >> sra_value);
 | 
			
		||||
							break;
 | 
			
		||||
						default:
 | 
			
		||||
							fprintf(stderr, "FATAL: Unknown func7 0x%x for arithlog SRL/SRA instruction, could not execute\n", instruction->func7);
 | 
			
		||||
							exit(EXIT_FAILURE);
 | 
			
		||||
							break;
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
				default:
 | 
			
		||||
					fprintf(stderr, "FATAL: Unknown func3 0x%x for arithlog instruction, could not execute\n", instruction->func3);
 | 
			
		||||
					exit(EXIT_FAILURE);
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		case OPCODE_NOP:
 | 
			
		||||
		{
 | 
			
		||||
			// TODO : Implement PAUSE, FENCE, FENCE.TSO
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		case OPCODE_SYSTEM:
 | 
			
		||||
		{
 | 
			
		||||
			// TODO : Implement ECALL, EBREAK
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		default:
 | 
			
		||||
			fprintf(stderr, "FATAL: Unknown instruction opcode 0x%x while executing; how could this decode ?\n", instruction->opcode);
 | 
			
		||||
			exit(EXIT_FAILURE);
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cpu_loop(rv32_cpu_t* cpu)
 | 
			
		||||
{
 | 
			
		||||
	while(1)
 | 
			
		||||
	{
 | 
			
		||||
		// Fetch
 | 
			
		||||
		raw_instruction_t raw_instruction;
 | 
			
		||||
		if(cpu->pc > memory_size - 4)
 | 
			
		||||
		{
 | 
			
		||||
			fprintf(stderr, "Error: instruction fetch: pc is out of addressable memory\n");
 | 
			
		||||
			exit(EXIT_FAILURE);
 | 
			
		||||
		}
 | 
			
		||||
		raw_instruction.data = *((uint32_t*) (&memory[cpu->pc]));
 | 
			
		||||
 | 
			
		||||
		// Decode
 | 
			
		||||
		instruction_t instruction;
 | 
			
		||||
		cpu_decode(raw_instruction, &instruction);
 | 
			
		||||
 | 
			
		||||
		// Execute
 | 
			
		||||
		cpu_execute(cpu, &instruction);
 | 
			
		||||
 | 
			
		||||
		cpu->pc += 4;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										194
									
								
								src/cpu/rv32cpu.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								src/cpu/rv32cpu.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,194 @@
 | 
			
		||||
#ifndef RV32CPU_H
 | 
			
		||||
#define RV32CPU_H
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
* This is a structure encoding for the registers of
 | 
			
		||||
* the rv32 cpu.
 | 
			
		||||
* It allows access of register x0 using :
 | 
			
		||||
* structname.x0, structname.zero, structname.x[0]
 | 
			
		||||
* This way, access can be really flexible
 | 
			
		||||
*/
 | 
			
		||||
typedef struct RV32_CPU_REGS
 | 
			
		||||
{
 | 
			
		||||
	union
 | 
			
		||||
	{
 | 
			
		||||
		struct
 | 
			
		||||
		{
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x0;
 | 
			
		||||
				uint32_t zero;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x1;
 | 
			
		||||
				uint32_t ra;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x2;
 | 
			
		||||
				uint32_t sp;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x3;
 | 
			
		||||
				uint32_t gp;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x4;
 | 
			
		||||
				uint32_t tp;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x5;
 | 
			
		||||
				uint32_t t0;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x6;
 | 
			
		||||
				uint32_t t1;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x7;
 | 
			
		||||
				uint32_t t2;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x8;
 | 
			
		||||
				uint32_t s0;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x9;
 | 
			
		||||
				uint32_t s1;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x10;
 | 
			
		||||
				uint32_t a0;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x11;
 | 
			
		||||
				uint32_t a1;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x12;
 | 
			
		||||
				uint32_t a2;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x13;
 | 
			
		||||
				uint32_t a3;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x14;
 | 
			
		||||
				uint32_t a4;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x15;
 | 
			
		||||
				uint32_t a5;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x16;
 | 
			
		||||
				uint32_t a6;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x17;
 | 
			
		||||
				uint32_t a7;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x18;
 | 
			
		||||
				uint32_t s2;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x19;
 | 
			
		||||
				uint32_t s3;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x20;
 | 
			
		||||
				uint32_t s4;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x21;
 | 
			
		||||
				uint32_t s5;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x22;
 | 
			
		||||
				uint32_t s6;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x23;
 | 
			
		||||
				uint32_t s7;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x24;
 | 
			
		||||
				uint32_t s8;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x25;
 | 
			
		||||
				uint32_t s9;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x26;
 | 
			
		||||
				uint32_t s10;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x27;
 | 
			
		||||
				uint32_t s11;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x28;
 | 
			
		||||
				uint32_t t3;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x29;
 | 
			
		||||
				uint32_t t4;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x30;
 | 
			
		||||
				uint32_t t5;
 | 
			
		||||
			};
 | 
			
		||||
			union
 | 
			
		||||
			{
 | 
			
		||||
				uint32_t x31;
 | 
			
		||||
				uint32_t t6;
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
		uint32_t x[32];
 | 
			
		||||
	};
 | 
			
		||||
} rv32_cpu_regs_t;
 | 
			
		||||
 | 
			
		||||
typedef struct RV32_CPU
 | 
			
		||||
{
 | 
			
		||||
    rv32_cpu_regs_t regs;
 | 
			
		||||
    uint32_t pc;
 | 
			
		||||
} rv32_cpu_t;
 | 
			
		||||
 | 
			
		||||
extern rv32_cpu_t* cpu0;
 | 
			
		||||
void cpu_init();
 | 
			
		||||
void cpu_loop(rv32_cpu_t* cpu);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										27
									
								
								src/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/main.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
#include "vriscv.h"
 | 
			
		||||
#include "memory/memory.h"
 | 
			
		||||
#include "bootloader/bootloader.h"
 | 
			
		||||
#include "cpu/rv32cpu.h"
 | 
			
		||||
 | 
			
		||||
char* CURRENT_NAME;
 | 
			
		||||
 | 
			
		||||
int main(int argc, char** argv)
 | 
			
		||||
{
 | 
			
		||||
    CURRENT_NAME = argc ? argv[0] : NAME;
 | 
			
		||||
    parse_options(argc, argv);
 | 
			
		||||
 | 
			
		||||
    // Initialize the memory
 | 
			
		||||
    mem_init();
 | 
			
		||||
 | 
			
		||||
    // Bootload the file passed as argument
 | 
			
		||||
    uint32_t entry_point = bootload(file_path);
 | 
			
		||||
 | 
			
		||||
    // Initialize the CPU
 | 
			
		||||
    cpu_init();
 | 
			
		||||
    cpu0->pc = entry_point;
 | 
			
		||||
 | 
			
		||||
    // CPU simulation
 | 
			
		||||
    cpu_loop(cpu0);
 | 
			
		||||
    
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								src/memory/memory.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/memory/memory.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
#include "memory.h"
 | 
			
		||||
#include "vriscv.h"
 | 
			
		||||
 | 
			
		||||
uint8_t* memory;
 | 
			
		||||
 | 
			
		||||
void mem_init()
 | 
			
		||||
{
 | 
			
		||||
    memory = malloc(memory_size);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								src/memory/memory.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/memory/memory.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
#ifndef MEMORY_H
 | 
			
		||||
#define MEMORY_H
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
extern uint8_t* memory;
 | 
			
		||||
 | 
			
		||||
void mem_init();
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										6
									
								
								src/memory/mmu/mmu.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/memory/mmu/mmu.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
#ifndef MMU_H
 | 
			
		||||
#define MMU_H
 | 
			
		||||
 | 
			
		||||
#define mmu_translate(vaddr) (vaddr)
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										178
									
								
								src/option.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								src/option.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,178 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Command-line option parsing
 | 
			
		||||
 */
 | 
			
		||||
#include "vriscv.h"
 | 
			
		||||
 | 
			
		||||
#define OPTION_SEPARATOR "-"
 | 
			
		||||
 | 
			
		||||
uint64_t memory_size = 512 * 1024 * 1024;
 | 
			
		||||
char* file_path;
 | 
			
		||||
 | 
			
		||||
static void print_usage();
 | 
			
		||||
static void print_help();
 | 
			
		||||
static void print_version();
 | 
			
		||||
 | 
			
		||||
static int parse_long_option(char* str, char* argq);
 | 
			
		||||
 | 
			
		||||
void parse_options(int argc, char** argv)
 | 
			
		||||
{
 | 
			
		||||
	for(int i = 1; i < argc; i++)
 | 
			
		||||
	{
 | 
			
		||||
		// Start option parsing for argument 'i'
 | 
			
		||||
		if(argv[i][0] != *OPTION_SEPARATOR) continue;
 | 
			
		||||
 | 
			
		||||
		// Check for long options
 | 
			
		||||
		if(argv[i][1] == *OPTION_SEPARATOR)
 | 
			
		||||
		{
 | 
			
		||||
			i += parse_long_option(argv[i] + 2, argv[i + 1]);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Parse short options
 | 
			
		||||
		int k = 1;
 | 
			
		||||
		while(argv[i][k])
 | 
			
		||||
		{
 | 
			
		||||
			switch(argv[i][k])
 | 
			
		||||
			{
 | 
			
		||||
				case 'h':
 | 
			
		||||
				case '?':
 | 
			
		||||
				{
 | 
			
		||||
					print_help();
 | 
			
		||||
					exit(0);
 | 
			
		||||
				}
 | 
			
		||||
				case 'v':
 | 
			
		||||
				{
 | 
			
		||||
					print_version();
 | 
			
		||||
					exit(0);
 | 
			
		||||
				}
 | 
			
		||||
				case 'm':
 | 
			
		||||
				{
 | 
			
		||||
					// First try to convert next chars into int (ex. -m512)
 | 
			
		||||
					char* end;
 | 
			
		||||
					memory_size = strtol(&argv[i][k + 1], &end, 10);
 | 
			
		||||
					if(*end != '\0' || end == &argv[i][k + 1])
 | 
			
		||||
					{
 | 
			
		||||
						if(argv[i][k + 1])
 | 
			
		||||
						{
 | 
			
		||||
							fprintf(stderr, "Error: Option -m needs an argument, but you used -m in a group of options without an integer next to it\n");
 | 
			
		||||
							exit(EXIT_FAILURE);
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						// Try to parse next arg as integer
 | 
			
		||||
						if(argc <= i + 1)
 | 
			
		||||
						{
 | 
			
		||||
							fprintf(stderr, "Error: Option " OPTION_SEPARATOR "m needs an argument\n");
 | 
			
		||||
							exit(EXIT_FAILURE);
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						memory_size = strtol(argv[i + 1], &end, 10);
 | 
			
		||||
						if(*end != '\0')
 | 
			
		||||
						{
 | 
			
		||||
							fprintf(stderr, "Error: Invalid argument '%s' for option " OPTION_SEPARATOR "m\n", argv[i + 1]);
 | 
			
		||||
							exit(EXIT_FAILURE);
 | 
			
		||||
						}
 | 
			
		||||
						else
 | 
			
		||||
						{
 | 
			
		||||
							i++;
 | 
			
		||||
							k = strlen(argv[i]) - 1;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					else k += (end - &argv[i][k + 1]);
 | 
			
		||||
 | 
			
		||||
					if(memory_size <= 0)
 | 
			
		||||
					{
 | 
			
		||||
						fprintf(stderr, "Error: Memory size needs to be > 0\n");
 | 
			
		||||
						exit(EXIT_FAILURE);
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if(memory_size > 4096)
 | 
			
		||||
					{
 | 
			
		||||
						fprintf(stderr, "Error: Cannot address more than 4 GiB of memory on 32-bits !\n");
 | 
			
		||||
						exit(EXIT_FAILURE);
 | 
			
		||||
					}
 | 
			
		||||
					memory_size *= 1024 * 1024;
 | 
			
		||||
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
				default:
 | 
			
		||||
				{
 | 
			
		||||
					fprintf(stderr, "Error: Unknown short option -%c\n", argv[i][k]);
 | 
			
		||||
					exit(EXIT_FAILURE);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			k++;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(argc <= 1)
 | 
			
		||||
	{
 | 
			
		||||
		print_usage();
 | 
			
		||||
		exit(EXIT_FAILURE);
 | 
			
		||||
	}
 | 
			
		||||
	else file_path = argv[argc - 1];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int parse_long_option(char* str, char* argq)
 | 
			
		||||
{
 | 
			
		||||
	if(strcmp(str, "help") == 0)
 | 
			
		||||
	{
 | 
			
		||||
		print_help();
 | 
			
		||||
		exit(0);
 | 
			
		||||
	}
 | 
			
		||||
	else if(strcmp(str, "version") == 0)
 | 
			
		||||
	{
 | 
			
		||||
		print_version();
 | 
			
		||||
		exit(0);
 | 
			
		||||
	}
 | 
			
		||||
	else if(strcmp(str, "memory") == 0)
 | 
			
		||||
	{
 | 
			
		||||
		if(argq == NULL)
 | 
			
		||||
		{
 | 
			
		||||
			fprintf(stderr, "Error: No argument given for option " OPTION_SEPARATOR "-memory\n");
 | 
			
		||||
			exit(EXIT_FAILURE);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Convert argument to integer
 | 
			
		||||
		char* end;
 | 
			
		||||
		memory_size = strtol(argq, &end, 10);
 | 
			
		||||
		if(*end != '\0')
 | 
			
		||||
		{
 | 
			
		||||
			fprintf(stderr, "Error: Invalid argument '%s' for option " OPTION_SEPARATOR "-memory\n", argq);
 | 
			
		||||
			exit(EXIT_FAILURE);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(memory_size > 4096)
 | 
			
		||||
		{
 | 
			
		||||
			fprintf(stderr, "Error: Cannot address more than 4 GiB of memory on 32-bits !\n");
 | 
			
		||||
			exit(EXIT_FAILURE);
 | 
			
		||||
		}
 | 
			
		||||
		memory_size *= 1024 * 1024;
 | 
			
		||||
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		fprintf(stderr, "Error: Unknown long option " OPTION_SEPARATOR OPTION_SEPARATOR "%s\n", str);
 | 
			
		||||
		exit(EXIT_FAILURE);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void print_usage()
 | 
			
		||||
{
 | 
			
		||||
	printf("Usage: %s [options] <elf-file>\n", CURRENT_NAME);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void print_help()
 | 
			
		||||
{
 | 
			
		||||
	print_usage();
 | 
			
		||||
 | 
			
		||||
	printf("Options:\n");
 | 
			
		||||
	printf("  " OPTION_SEPARATOR "h, " OPTION_SEPARATOR "?, --help\t\tPrint this help message\n");
 | 
			
		||||
	printf("  " OPTION_SEPARATOR "v, --version\t\t\tPrint version information\n");
 | 
			
		||||
	printf("  " OPTION_SEPARATOR "m, --memory\t\t\tSet the simulated memory size, in MiB\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void print_version()
 | 
			
		||||
{
 | 
			
		||||
	printf("%s (%s) version %s\n", CURRENT_NAME, NAME, VERSION);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								src/vriscv.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/vriscv.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
#ifndef VRISCV_H
 | 
			
		||||
#define VRISCV_H
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#define NAME "vriscv"
 | 
			
		||||
#define VERSION "0.1"
 | 
			
		||||
extern char* CURRENT_NAME;
 | 
			
		||||
 | 
			
		||||
/* Program options */
 | 
			
		||||
extern size_t memory_size;
 | 
			
		||||
extern char* file_path;
 | 
			
		||||
void parse_options(int argc, char** argv);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
		Reference in New Issue
	
	Block a user