initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
build/
|
||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "tinyriscv"]
|
||||||
|
path = tinyriscv
|
||||||
|
url = git@github.com:inixyz/tinyriscv.git
|
||||||
46
Makefile
Normal file
46
Makefile
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
RISCV_CC ?= riscv64-unknown-elf-gcc
|
||||||
|
RISCV_ARCH_FLAGS ?= -march=rv32im -mabi=ilp32
|
||||||
|
HOST_CC ?= gcc
|
||||||
|
HOST_CFLAGS ?= -Wall -Wextra -O3 -g
|
||||||
|
HOST_LDFLAGS ?= -Wl,-s
|
||||||
|
|
||||||
|
EMU_SRC_FILES := $(wildcard src/emulated/*.c)
|
||||||
|
EMU_OBJ_FILES := $(EMU_SRC_FILES:src/emulated/%.c=build/%.riscv.o)
|
||||||
|
EMU_OBJ := build/emulated.riscv.elf
|
||||||
|
|
||||||
|
HOST_SRC := src/main.c src/elf.c
|
||||||
|
HOST_EXE := build/main.exe
|
||||||
|
|
||||||
|
# Disassembly
|
||||||
|
RISCV_OBJDUMP ?= $(patsubst %-gcc,%-objdump,$(RISCV_CC))
|
||||||
|
_DISAS_TARGETS := $(filter disas-%,$(MAKECMDGOALS))
|
||||||
|
_DISAS_FILES := $(if $(_DISAS_TARGETS),$(filter %.elf %.o,$(MAKECMDGOALS)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: all emulated-riscv host clean disas-risc
|
||||||
|
|
||||||
|
all: $(HOST_EXE) $(EMU_OBJ)
|
||||||
|
|
||||||
|
emulated-riscv: $(EMU_OBJ)
|
||||||
|
|
||||||
|
host: $(HOST_EXE)
|
||||||
|
|
||||||
|
$(HOST_EXE): $(HOST_SRC) tinyriscv/src/tinyriscv.h src/config.h src/elf.h | build
|
||||||
|
$(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -I src -I tinyriscv/src $(HOST_SRC) -o $@
|
||||||
|
|
||||||
|
build/%.riscv.o: src/emulated/%.c | build
|
||||||
|
$(RISCV_CC) $(RISCV_ARCH_FLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
$(EMU_OBJ): $(EMU_OBJ_FILES) | build
|
||||||
|
$(RISCV_CC) $(RISCV_ARCH_FLAGS) -nostdlib -Wl,-e,entry -Wl,-Ttext=0 $(EMU_OBJ_FILES) -o $@
|
||||||
|
|
||||||
|
build:
|
||||||
|
mkdir -p $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf build
|
||||||
|
|
||||||
|
disas-risc: $(_DISAS_FILES)
|
||||||
|
$(RISCV_OBJDUMP) -d $^
|
||||||
|
|
||||||
5
src/config.h
Normal file
5
src/config.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define EMU_MEM_SIZE 4096u
|
||||||
|
#define EMU_PROGRAM_PATH "build/emulated.riscv.elf"
|
||||||
|
#define EMU_ENTRY_SYMBOL "entry"
|
||||||
346
src/elf.c
Normal file
346
src/elf.c
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
#include "elf.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
static int read_file_bytes(const char* path, uint8_t** out_buf, size_t* out_size) {
|
||||||
|
FILE* f = fopen(path, "rb");
|
||||||
|
if (!f) {
|
||||||
|
fprintf(stderr, "Failed to open object file: %s (%s)\n", path, strerror(errno));
|
||||||
|
return LOAD_ERR_OPEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fseek(f, 0, SEEK_END) != 0) {
|
||||||
|
fclose(f);
|
||||||
|
fprintf(stderr, "Failed to seek object file: %s\n", path);
|
||||||
|
return LOAD_ERR_SMALL_FILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
long file_size = ftell(f);
|
||||||
|
if (file_size < 0) {
|
||||||
|
fclose(f);
|
||||||
|
fprintf(stderr, "Failed to read object file size: %s\n", path);
|
||||||
|
return LOAD_ERR_SMALL_FILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fseek(f, 0, SEEK_SET) != 0) {
|
||||||
|
fclose(f);
|
||||||
|
fprintf(stderr, "Failed to rewind object file: %s\n", path);
|
||||||
|
return LOAD_ERR_SMALL_FILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* file_buf = (uint8_t*)malloc((size_t)file_size);
|
||||||
|
if (!file_buf) {
|
||||||
|
fclose(f);
|
||||||
|
fprintf(stderr, "Failed to allocate %ld bytes for object file\n", file_size);
|
||||||
|
return LOAD_ERR_OOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t read_count = fread(file_buf, 1, (size_t)file_size, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
if (read_count != (size_t)file_size) {
|
||||||
|
free(file_buf);
|
||||||
|
fprintf(stderr, "Object file too small or unreadable: %s\n", path);
|
||||||
|
return LOAD_ERR_SMALL_FILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_buf = file_buf;
|
||||||
|
*out_size = read_count;
|
||||||
|
return LOAD_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint16_t read_u16_le(const uint8_t* data) {
|
||||||
|
return (uint16_t)data[0] | ((uint16_t)data[1] << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t read_u32_le(const uint8_t* data) {
|
||||||
|
return (uint32_t)data[0]
|
||||||
|
| ((uint32_t)data[1] << 8)
|
||||||
|
| ((uint32_t)data[2] << 16)
|
||||||
|
| ((uint32_t)data[3] << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t align_up(uint32_t value, uint32_t align) {
|
||||||
|
if (align == 0u) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return (value + align - 1u) & ~(align - 1u);
|
||||||
|
}
|
||||||
|
|
||||||
|
int load_elf_object_sections(const char* path, uint8_t* memory, uint32_t mem_size) {
|
||||||
|
uint8_t* file_buf = NULL;
|
||||||
|
size_t bytes = 0u;
|
||||||
|
int file_status = read_file_bytes(path, &file_buf, &bytes);
|
||||||
|
if (file_status != LOAD_OK) {
|
||||||
|
return file_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes < 52u) {
|
||||||
|
fprintf(stderr, "Object file too small or unreadable: %s\n", path);
|
||||||
|
free(file_buf);
|
||||||
|
return LOAD_ERR_SMALL_FILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_buf[0] != ELF_MAGIC_0 || file_buf[1] != ELF_MAGIC_1
|
||||||
|
|| file_buf[2] != ELF_MAGIC_2 || file_buf[3] != ELF_MAGIC_3) {
|
||||||
|
fprintf(stderr, "Not an ELF file: %s\n", path);
|
||||||
|
free(file_buf);
|
||||||
|
return LOAD_ERR_NOT_ELF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_buf[4] != ELFCLASS32 || file_buf[5] != ELFDATA2LSB) {
|
||||||
|
fprintf(stderr, "Unsupported ELF format in %s (need 32-bit little-endian)\n", path);
|
||||||
|
free(file_buf);
|
||||||
|
return LOAD_ERR_UNSUPPORTED_FORMAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t e_shoff = read_u32_le(file_buf + 32);
|
||||||
|
uint16_t e_shentsize = read_u16_le(file_buf + 46);
|
||||||
|
uint16_t e_shnum = read_u16_le(file_buf + 48);
|
||||||
|
|
||||||
|
if (e_shoff >= bytes || e_shentsize == 0u) {
|
||||||
|
fprintf(stderr, "Invalid section header table in %s\n", path);
|
||||||
|
free(file_buf);
|
||||||
|
return LOAD_ERR_INVALID_SHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((uint32_t)e_shoff + (uint32_t)e_shentsize * (uint32_t)e_shnum > bytes) {
|
||||||
|
fprintf(stderr, "Section headers out of file bounds in %s\n", path);
|
||||||
|
free(file_buf);
|
||||||
|
return LOAD_ERR_SHT_OUT_OF_BOUNDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t cursor = 0u;
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < e_shnum; ++i) {
|
||||||
|
const uint8_t* sh = file_buf + e_shoff + (uint32_t)i * e_shentsize;
|
||||||
|
|
||||||
|
uint32_t sh_type = read_u32_le(sh + 4);
|
||||||
|
uint32_t sh_flags = read_u32_le(sh + 8);
|
||||||
|
uint32_t sh_addr = read_u32_le(sh + 12);
|
||||||
|
uint32_t sh_offset = read_u32_le(sh + 16);
|
||||||
|
uint32_t sh_size = read_u32_le(sh + 20);
|
||||||
|
uint32_t sh_addralign = read_u32_le(sh + 32);
|
||||||
|
|
||||||
|
if ((sh_flags & SHF_ALLOC) == 0u || sh_size == 0u) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t dst = (sh_addr != 0u) ? sh_addr : align_up(cursor, sh_addralign);
|
||||||
|
|
||||||
|
if (dst >= mem_size || sh_size > mem_size - dst) {
|
||||||
|
fprintf(stderr, "Section %u does not fit into emulator memory\n", (unsigned)i);
|
||||||
|
free(file_buf);
|
||||||
|
return LOAD_ERR_SECTION_TOO_LARGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sh_type == SHT_NOBITS) {
|
||||||
|
memset(memory + dst, 0, sh_size);
|
||||||
|
} else {
|
||||||
|
if (sh_offset >= bytes || sh_size > (uint32_t)bytes - sh_offset) {
|
||||||
|
fprintf(stderr, "Section %u exceeds object file bounds\n", (unsigned)i);
|
||||||
|
free(file_buf);
|
||||||
|
return LOAD_ERR_SECTION_FILE_BOUNDS;
|
||||||
|
}
|
||||||
|
memcpy(memory + dst, file_buf + sh_offset, sh_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sh_addr == 0u) {
|
||||||
|
cursor = dst + sh_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(file_buf);
|
||||||
|
return LOAD_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int resolve_elf_symbol_memory_offset(const char* path, const char* symbol_name,
|
||||||
|
uint32_t mem_size, uint32_t* out_offset) {
|
||||||
|
uint8_t* file_buf = NULL;
|
||||||
|
size_t bytes = 0u;
|
||||||
|
int file_status = read_file_bytes(path, &file_buf, &bytes);
|
||||||
|
if (file_status != LOAD_OK) {
|
||||||
|
return file_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes < 52u) {
|
||||||
|
free(file_buf);
|
||||||
|
return LOAD_ERR_SMALL_FILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_buf[0] != ELF_MAGIC_0 || file_buf[1] != ELF_MAGIC_1
|
||||||
|
|| file_buf[2] != ELF_MAGIC_2 || file_buf[3] != ELF_MAGIC_3) {
|
||||||
|
free(file_buf);
|
||||||
|
return LOAD_ERR_NOT_ELF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_buf[4] != ELFCLASS32 || file_buf[5] != ELFDATA2LSB) {
|
||||||
|
free(file_buf);
|
||||||
|
return LOAD_ERR_UNSUPPORTED_FORMAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t e_shoff = read_u32_le(file_buf + 32);
|
||||||
|
uint16_t e_shentsize = read_u16_le(file_buf + 46);
|
||||||
|
uint16_t e_shnum = read_u16_le(file_buf + 48);
|
||||||
|
|
||||||
|
if (e_shoff >= bytes || e_shentsize == 0u) {
|
||||||
|
free(file_buf);
|
||||||
|
return LOAD_ERR_INVALID_SHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((uint32_t)e_shoff + (uint32_t)e_shentsize * (uint32_t)e_shnum > bytes) {
|
||||||
|
free(file_buf);
|
||||||
|
return LOAD_ERR_SHT_OUT_OF_BOUNDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t* section_runtime = (uint32_t*)calloc(e_shnum, sizeof(uint32_t));
|
||||||
|
uint32_t* section_addr = (uint32_t*)calloc(e_shnum, sizeof(uint32_t));
|
||||||
|
uint8_t* section_loaded = (uint8_t*)calloc(e_shnum, sizeof(uint8_t));
|
||||||
|
if (!section_runtime || !section_addr || !section_loaded) {
|
||||||
|
free(section_runtime);
|
||||||
|
free(section_addr);
|
||||||
|
free(section_loaded);
|
||||||
|
free(file_buf);
|
||||||
|
return LOAD_ERR_OOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t cursor = 0u;
|
||||||
|
for (uint16_t i = 0; i < e_shnum; ++i) {
|
||||||
|
const uint8_t* sh = file_buf + e_shoff + (uint32_t)i * e_shentsize;
|
||||||
|
uint32_t sh_flags = read_u32_le(sh + 8);
|
||||||
|
uint32_t sh_addr = read_u32_le(sh + 12);
|
||||||
|
uint32_t sh_size = read_u32_le(sh + 20);
|
||||||
|
uint32_t sh_addralign = read_u32_le(sh + 32);
|
||||||
|
|
||||||
|
if ((sh_flags & SHF_ALLOC) == 0u || sh_size == 0u) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t dst = (sh_addr != 0u) ? sh_addr : align_up(cursor, sh_addralign);
|
||||||
|
if (dst >= mem_size || sh_size > mem_size - dst) {
|
||||||
|
free(section_runtime);
|
||||||
|
free(section_addr);
|
||||||
|
free(section_loaded);
|
||||||
|
free(file_buf);
|
||||||
|
return LOAD_ERR_SECTION_TOO_LARGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
section_runtime[i] = dst;
|
||||||
|
section_addr[i] = sh_addr;
|
||||||
|
section_loaded[i] = 1u;
|
||||||
|
|
||||||
|
if (sh_addr == 0u) {
|
||||||
|
cursor = dst + sh_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int found_symtab = 0;
|
||||||
|
int status = LOAD_ERR_SYMBOL_NOT_FOUND;
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < e_shnum; ++i) {
|
||||||
|
const uint8_t* sh = file_buf + e_shoff + (uint32_t)i * e_shentsize;
|
||||||
|
uint32_t sh_type = read_u32_le(sh + 4);
|
||||||
|
uint32_t sh_offset = read_u32_le(sh + 16);
|
||||||
|
uint32_t sh_size = read_u32_le(sh + 20);
|
||||||
|
uint32_t sh_link = read_u32_le(sh + 24);
|
||||||
|
uint32_t sh_entsize = read_u32_le(sh + 36);
|
||||||
|
|
||||||
|
if (sh_type != SHT_SYMTAB) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
found_symtab = 1;
|
||||||
|
|
||||||
|
if (sh_link >= e_shnum || sh_offset >= bytes || sh_size > (uint32_t)bytes - sh_offset) {
|
||||||
|
status = LOAD_ERR_STRTAB_INVALID;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* strtab_sh = file_buf + e_shoff + sh_link * (uint32_t)e_shentsize;
|
||||||
|
uint32_t strtab_offset = read_u32_le(strtab_sh + 16);
|
||||||
|
uint32_t strtab_size = read_u32_le(strtab_sh + 20);
|
||||||
|
|
||||||
|
if (strtab_offset >= bytes || strtab_size > (uint32_t)bytes - strtab_offset) {
|
||||||
|
status = LOAD_ERR_STRTAB_INVALID;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sh_entsize == 0u) {
|
||||||
|
sh_entsize = 16u;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sh_entsize < 16u) {
|
||||||
|
status = LOAD_ERR_STRTAB_INVALID;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t count = sh_size / sh_entsize;
|
||||||
|
const uint8_t* symtab = file_buf + sh_offset;
|
||||||
|
const char* strtab = (const char*)(file_buf + strtab_offset);
|
||||||
|
|
||||||
|
for (uint32_t s = 0u; s < count; ++s) {
|
||||||
|
const uint8_t* sym = symtab + s * sh_entsize;
|
||||||
|
uint32_t st_name = read_u32_le(sym + 0);
|
||||||
|
uint32_t st_value = read_u32_le(sym + 4);
|
||||||
|
uint16_t st_shndx = read_u16_le(sym + 14);
|
||||||
|
|
||||||
|
if (st_name >= strtab_size) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* name = strtab + st_name;
|
||||||
|
if (strcmp(name, symbol_name) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (st_shndx == 0u) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (st_shndx >= e_shnum || !section_loaded[st_shndx]) {
|
||||||
|
status = LOAD_ERR_SYMBOL_INVALID_SECTION;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t section_base_runtime = section_runtime[st_shndx];
|
||||||
|
uint32_t section_base_elf = section_addr[st_shndx];
|
||||||
|
if (st_value < section_base_elf) {
|
||||||
|
status = LOAD_ERR_SYMBOL_INVALID_SECTION;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t symbol_offset = section_base_runtime + (st_value - section_base_elf);
|
||||||
|
if (symbol_offset >= mem_size) {
|
||||||
|
status = LOAD_ERR_SYMBOL_OUT_OF_RANGE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_offset = symbol_offset;
|
||||||
|
status = LOAD_OK;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == LOAD_OK || status == LOAD_ERR_SYMBOL_INVALID_SECTION
|
||||||
|
|| status == LOAD_ERR_SYMBOL_OUT_OF_RANGE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found_symtab) {
|
||||||
|
status = LOAD_ERR_SYMTAB_MISSING;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(section_runtime);
|
||||||
|
free(section_addr);
|
||||||
|
free(section_loaded);
|
||||||
|
free(file_buf);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
41
src/elf.h
Normal file
41
src/elf.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define ELF_MAGIC_0 0x7f
|
||||||
|
#define ELF_MAGIC_1 'E'
|
||||||
|
#define ELF_MAGIC_2 'L'
|
||||||
|
#define ELF_MAGIC_3 'F'
|
||||||
|
|
||||||
|
#define ELFCLASS32 1
|
||||||
|
#define ELFDATA2LSB 1
|
||||||
|
|
||||||
|
#define SHT_NOBITS 8u
|
||||||
|
#define SHT_SYMTAB 2u
|
||||||
|
#define SHF_ALLOC 0x2u
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
LOAD_OK = 1,
|
||||||
|
LOAD_ERR_OPEN = -1,
|
||||||
|
LOAD_ERR_SMALL_FILE = -2,
|
||||||
|
LOAD_ERR_NOT_ELF = -3,
|
||||||
|
LOAD_ERR_UNSUPPORTED_FORMAT = -4,
|
||||||
|
LOAD_ERR_INVALID_SHT = -5,
|
||||||
|
LOAD_ERR_SHT_OUT_OF_BOUNDS = -6,
|
||||||
|
LOAD_ERR_SECTION_TOO_LARGE = -7,
|
||||||
|
LOAD_ERR_SECTION_FILE_BOUNDS = -8,
|
||||||
|
LOAD_ERR_OOM = -9,
|
||||||
|
LOAD_ERR_SYMTAB_MISSING = -10,
|
||||||
|
LOAD_ERR_STRTAB_INVALID = -11,
|
||||||
|
LOAD_ERR_SYMBOL_NOT_FOUND = -12,
|
||||||
|
LOAD_ERR_SYMBOL_INVALID_SECTION = -13,
|
||||||
|
LOAD_ERR_SYMBOL_OUT_OF_RANGE = -14
|
||||||
|
};
|
||||||
|
|
||||||
|
uint16_t read_u16_le(const uint8_t* data);
|
||||||
|
uint32_t read_u32_le(const uint8_t* data);
|
||||||
|
uint32_t align_up(uint32_t value, uint32_t align);
|
||||||
|
int load_elf_object_sections(const char* path, uint8_t* memory, uint32_t mem_size);
|
||||||
|
int resolve_elf_symbol_memory_offset(const char* path, const char* symbol_name,
|
||||||
|
uint32_t mem_size, uint32_t* out_offset);
|
||||||
|
|
||||||
15
src/emulated/custom_instructions.h
Normal file
15
src/emulated/custom_instructions.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#define trunc_xor(a, imm12) \
|
||||||
|
__extension__ ({ \
|
||||||
|
int32_t _result; \
|
||||||
|
asm volatile ( \
|
||||||
|
".insn i 0x0B, 0x00, %0, %1, %2" \
|
||||||
|
: "=r" (_result) \
|
||||||
|
: "r" ((int32_t)(a)), \
|
||||||
|
"I" (imm12) \
|
||||||
|
); \
|
||||||
|
_result; \
|
||||||
|
})
|
||||||
|
|
||||||
13
src/emulated/function.c
Normal file
13
src/emulated/function.c
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
#include "function.h"
|
||||||
|
|
||||||
|
#include "custom_instructions.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int32_t operation(int32_t a) {
|
||||||
|
return trunc_xor(a, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
6
src/emulated/function.h
Normal file
6
src/emulated/function.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
int32_t operation(int32_t a);
|
||||||
|
|
||||||
10
src/emulated/program.c
Normal file
10
src/emulated/program.c
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "function.h"
|
||||||
|
#include "custom_instructions.h"
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t entry(uint8_t a) {
|
||||||
|
return operation(a);
|
||||||
|
}
|
||||||
|
|
||||||
75
src/main.c
Normal file
75
src/main.c
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#define TINYRISCV_M
|
||||||
|
|
||||||
|
#include "tinyriscv.h"
|
||||||
|
#include "elf.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define TEST_A 5u
|
||||||
|
#define TEST_B 7u
|
||||||
|
#define MAX_STEPS 1024u
|
||||||
|
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
uint8_t memory[EMU_MEM_SIZE]; //4kb of internal memory
|
||||||
|
tinyriscv_core cpu; //create the emulation core
|
||||||
|
memset(&cpu, 0, sizeof(cpu));
|
||||||
|
cpu.mem = memory; //use the uint8_t array as the memory
|
||||||
|
cpu.mem_size = EMU_MEM_SIZE;
|
||||||
|
memset(memory, 0, sizeof(memory));
|
||||||
|
|
||||||
|
int load_status = load_elf_object_sections(EMU_PROGRAM_PATH, memory, cpu.mem_size);
|
||||||
|
if (load_status != LOAD_OK) {
|
||||||
|
fprintf(stderr, "ELF load failed with code %d for '%s'\n", load_status, EMU_PROGRAM_PATH);
|
||||||
|
fflush(stderr);
|
||||||
|
printf("ELF load failed with code %d\n", load_status);
|
||||||
|
fflush(stdout);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t entry_offset = 0u;
|
||||||
|
int symbol_status = resolve_elf_symbol_memory_offset(
|
||||||
|
EMU_PROGRAM_PATH, EMU_ENTRY_SYMBOL, cpu.mem_size, &entry_offset);
|
||||||
|
if (symbol_status != LOAD_OK) {
|
||||||
|
fprintf(stderr, "Entry symbol '%s' resolve failed with code %d for '%s'\n",
|
||||||
|
EMU_ENTRY_SYMBOL, symbol_status, EMU_PROGRAM_PATH);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
tinyriscv_init(&cpu); //initialise core for execution
|
||||||
|
cpu.pc = tinyriscv_MEM_OFFSET + entry_offset;
|
||||||
|
|
||||||
|
// Call operation(a, b) using RV32 calling convention:
|
||||||
|
// a0=x10, return value in a0, return address in ra=x1.
|
||||||
|
cpu.x[10] = TEST_A;
|
||||||
|
cpu.x[1] = tinyriscv_MEM_OFFSET + cpu.mem_size;
|
||||||
|
|
||||||
|
uint32_t steps = 0u;
|
||||||
|
while (tinyriscv_valid_step(&cpu) && steps < MAX_STEPS) {
|
||||||
|
tinyriscv_step(&cpu);
|
||||||
|
++steps;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (steps == MAX_STEPS) {
|
||||||
|
fprintf(stderr, "Execution did not halt within %u steps\n", MAX_STEPS);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t actual = (uint32_t)cpu.x[10];
|
||||||
|
uint32_t expected = ((TEST_A << 3) ^ 0xFF) >> 3;
|
||||||
|
printf("operation(%u) -> %u (expected %u)\n",
|
||||||
|
(unsigned)TEST_A,
|
||||||
|
(unsigned)actual, (unsigned)expected);
|
||||||
|
|
||||||
|
if (actual != expected) {
|
||||||
|
fprintf(stderr, "Verification failed\n");
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
1
tinyriscv
Submodule
1
tinyriscv
Submodule
Submodule tinyriscv added at 2ca19e3f78
Reference in New Issue
Block a user