commit
981c35584c
@ -0,0 +1,55 @@ |
|||||||
|
--- |
||||||
|
Language: Cpp |
||||||
|
AllowShortFunctionsOnASingleLine: Empty |
||||||
|
AllowShortIfStatementsOnASingleLine: AllIfsAndElse |
||||||
|
AllowShortLoopsOnASingleLine: true |
||||||
|
AlwaysBreakAfterReturnType: None |
||||||
|
BreakBeforeBraces: Custom |
||||||
|
BraceWrapping: |
||||||
|
AfterCaseLabel: true |
||||||
|
AfterControlStatement: Always |
||||||
|
AfterEnum: true |
||||||
|
AfterFunction: true |
||||||
|
AfterStruct: true |
||||||
|
AfterUnion: true |
||||||
|
BeforeElse: true |
||||||
|
BeforeWhile: false |
||||||
|
IndentBraces: false |
||||||
|
SplitEmptyFunction: false |
||||||
|
# BreakBeforeBraces: Allman |
||||||
|
BreakStringLiterals: false |
||||||
|
ColumnLimit: 0 |
||||||
|
ContinuationIndentWidth: 2 |
||||||
|
IndentCaseBlocks: false |
||||||
|
IndentCaseLabels: true |
||||||
|
PointerAlignment: Left |
||||||
|
|
||||||
|
# Indent |
||||||
|
IndentWidth: 4 |
||||||
|
TabWidth: 4 |
||||||
|
UseTab: ForIndentation |
||||||
|
|
||||||
|
# Spaces |
||||||
|
SpaceAfterCStyleCast: true |
||||||
|
SpaceAfterLogicalNot: false |
||||||
|
SpaceAroundPointerQualifiers: Default |
||||||
|
SpaceBeforeAssignmentOperators: true |
||||||
|
SpaceBeforeCaseColon: false |
||||||
|
SpaceBeforeParens: Custom |
||||||
|
SpaceBeforeParensOptions: |
||||||
|
AfterControlStatements: false |
||||||
|
AfterFunctionDeclarationName: false |
||||||
|
AfterFunctionDefinitionName: false |
||||||
|
BeforeNonEmptyParentheses: false |
||||||
|
SpaceBeforeRangeBasedForLoopColon: true |
||||||
|
SpaceBeforeSquareBrackets: false |
||||||
|
SpaceInEmptyBlock: false |
||||||
|
SpaceInEmptyParentheses: false |
||||||
|
SpacesInCStyleCastParentheses: false |
||||||
|
SpacesInConditionalStatement: false |
||||||
|
SpacesInContainerLiterals: false |
||||||
|
SpacesInParentheses: false |
||||||
|
SpacesInSquareBrackets: false |
||||||
|
|
||||||
|
# Preprocessor |
||||||
|
SortIncludes: Never |
@ -0,0 +1 @@ |
|||||||
|
build/ |
@ -0,0 +1,27 @@ |
|||||||
|
NAME=vriscv
|
||||||
|
CC=gcc
|
||||||
|
CFLAGS=-O3 -Wall -I src
|
||||||
|
LDFLAGS=
|
||||||
|
BUILD_DIR=build
|
||||||
|
|
||||||
|
C_FILES := $(shell find src/ -name '*.c')
|
||||||
|
|
||||||
|
all: $(BUILD_DIR)/$(NAME) |
||||||
|
|
||||||
|
# Top-level targets
|
||||||
|
$(BUILD_DIR)/$(NAME): $(C_FILES) | $(BUILD_DIR) |
||||||
|
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
|
# Build directory
|
||||||
|
$(BUILD_DIR): |
||||||
|
mkdir -p $(BUILD_DIR)
|
||||||
|
|
||||||
|
# Phony targets
|
||||||
|
.PHONY: clean |
||||||
|
clean: |
||||||
|
rm -rf $(BUILD_DIR)
|
||||||
|
|
||||||
|
.PHONY: run |
||||||
|
.SILENT: run |
||||||
|
run: all |
||||||
|
./$(BUILD_DIR)/$(NAME)
|
@ -0,0 +1,10 @@ |
|||||||
|
# vriscv - a risc-v simulator |
||||||
|
|
||||||
|
## Resources used |
||||||
|
|
||||||
|
Juraj's Blog, mostly: |
||||||
|
- https://jborza.com/post/2021-04-04-riscv-supervisor-mode/ |
||||||
|
- https://jborza.com/emulation/2021/04/22/ecalls-and-syscalls.html |
||||||
|
|
||||||
|
Buildroot fork for nommu linux: |
||||||
|
- https://github.com/regymm/buildroot |
@ -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); |
||||||
|
} |
@ -0,0 +1,8 @@ |
|||||||
|
#ifndef BOOTLOADER_H |
||||||
|
#define BOOTLOADER_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
uint32_t bootload(char* file_path); |
||||||
|
|
||||||
|
#endif |
@ -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; |
||||||
|
} |
@ -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 |
@ -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 |
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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 |
@ -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; |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
#include "memory.h" |
||||||
|
#include "vriscv.h" |
||||||
|
|
||||||
|
uint8_t* memory; |
||||||
|
|
||||||
|
void mem_init() |
||||||
|
{ |
||||||
|
memory = malloc(memory_size); |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
#ifndef MEMORY_H |
||||||
|
#define MEMORY_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
extern uint8_t* memory; |
||||||
|
|
||||||
|
void mem_init(); |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,6 @@ |
|||||||
|
#ifndef MMU_H |
||||||
|
#define MMU_H |
||||||
|
|
||||||
|
#define mmu_translate(vaddr) (vaddr) |
||||||
|
|
||||||
|
#endif |
@ -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); |
||||||
|
} |
@ -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 |
Loading…
Reference in new issue