Skip to content

Instantly share code, notes, and snippets.

@bplaat
Created July 16, 2021 08:16
Show Gist options
  • Save bplaat/2e357c434c940aa412d65590904f6ea0 to your computer and use it in GitHub Desktop.
Save bplaat/2e357c434c940aa412d65590904f6ea0 to your computer and use it in GitHub Desktop.
Kora Reincranation: simple RISC-V 32IM core simulator in C
rm -rf .vscode
if gcc -s -Os kora.c -o kora -lelf; then
./kora $(find riscv-tests/isa -name "rv32ui-p-*")
fi
// Kora Reincranation: simple RISC-V 32IM core simulator in C
// That runs risc-v tests kinda???
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <libelf.h>
#define bit(number, bit) ((number >> bit) & 1)
int string_ends_with(const char *str, const char *suffix) {
if (!str || !suffix) return 0;
size_t lenstr = strlen(str);
size_t lensuffix = strlen(suffix);
if (lensuffix > lenstr) return 0;
return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0;
}
typedef struct Kora {
uint32_t ticks;
int32_t r[32];
uint32_t pc;
uint8_t mem[0xffff];
bool running;
bool debug;
bool passed;
} Kora;
const char *kora_rn[] = {
"zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5",
"a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"
};
void kora_init(Kora *kora, bool debug) {
kora->ticks = 0;
kora->r[0] = 0;
kora->pc = 0x80000000;
kora->running = true;
kora->debug = debug;
kora->passed = false;
}
int8_t kora_read_byte(Kora *kora, uint32_t address) {
address -= 0x80000000;
if (address > sizeof(kora->mem)) {
printf("Memory read out of bounds: 0x%08x\n", address);
exit(1);
}
return kora->mem[address];
}
int16_t kora_read_half(Kora *kora, uint32_t address) {
address -= 0x80000000;
if (address > sizeof(kora->mem) - 1) {
printf("Memory read out of bounds: 0x%08x\n", address);
exit(1);
}
return *(int16_t *)&kora->mem[address];
}
int32_t kora_read_word(Kora *kora, uint32_t address) {
address -= 0x80000000;
if (address > sizeof(kora->mem) - 3) {
printf("Memory read out of bounds: 0x%08x\n", address);
exit(1);
}
return *(int32_t *)&kora->mem[address];
}
void kora_write_byte(Kora *kora, uint32_t address, uint8_t data) {
address -= 0x80000000;
if (address > sizeof(kora->mem)) {
printf("Memory write out of bounds: 0x%08x = 0x%02x\n", address, data);
exit(1);
}
kora->mem[address] = data;
}
void kora_write_half(Kora *kora, uint32_t address, uint16_t data) {
address -= 0x80000000;
if (address > sizeof(kora->mem) - 1) {
printf("Memory write out of bounds: 0x%08x = 0x%04x\n", address, data);
exit(1);
}
*(uint16_t *)&kora->mem[address] = data;
}
void kora_write_word(Kora *kora, uint32_t address, uint32_t data) {
address -= 0x80000000;
if (address > sizeof(kora->mem) - 3) {
printf("Memory write out of bounds: 0x%08x = 0x%08x\n", address, data);
exit(1);
}
*(uint32_t *)&kora->mem[address] = data;
}
void kora_step(Kora *p) {
uint32_t i = kora_read_word(p, p->pc);
if (p->debug) {
printf("%08d | pc:%08x ", p->ticks++, p->pc);
for (size_t i = 0; i < 8; i++) {
printf("%s:%08x ", kora_rn[i], p->r[i]);
}
printf("| %02x %02x %02x %02x ", i >> 24, (i >> 16) &0xff, (i >> 8) &0xff, i & 0xff);
}
p->pc += 4;
uint8_t opcode = i & 0b1111111;
// R-type
uint8_t rd = (i >> 7) & 0b11111;
uint8_t funct3 = (i >> 12) & 0b111;
uint8_t rs1 = (i >> 15) & 0b11111;
uint8_t rs2 = (i >> 20) & 0b11111;
uint8_t funct7 = i >> 25;
// I-type
int32_t i_imm = (i >> 20) & 0b11111111111;
if (bit(i, 31)) i_imm = -i_imm;
uint8_t i_shamt = (i >> 20) & 0b11111;
// S-type
int32_t s_imm = ((i >> 7) & 0b11111) | (((i >> 25) & 0b111111) << 4);
if (bit(i, 31)) s_imm = -s_imm;
// B-type
int32_t b_imm = ((i >> 8) & 0b1111) | (((i >> 25) & 0b111111) << 4) | (bit(i, 7) << 10);
b_imm <<= 1;
if (bit(i, 31)) b_imm = -b_imm;
// U-type
int32_t u_imm = (i >> 12) & 0b1111111111111111111;
if (bit(i, 31)) u_imm = -u_imm;
// J-type
int32_t j_imm = ((i >> 21) & 0b1111111111) | (bit(i, 19) << 10) | (((i >> 12) & 0b11111111) << 11);
j_imm <<= 1;
if (bit(i, 31)) j_imm = -j_imm;
// System
uint32_t funct12 = (i >> 20) & 0b11111111111;
// OP
if (opcode == 0b0110011) {
// ADD
if (funct3 == 0b000 && funct7 == 0b0000000) {
if (p->debug) printf("add %s, %s, %s\n", kora_rn[rd], kora_rn[rs1], kora_rn[rs2]);
if (rd != 0) p->r[rd] = p->r[rs1] + p->r[rs2];
return;
}
// MUL
if (funct3 == 0b000 && funct7 == 0b0000001) {
if (p->debug) printf("mul %s, %s, %s\n", kora_rn[rd], kora_rn[rs1], kora_rn[rs2]);
if (rd != 0) p->r[rd] = ((int64_t)p->r[rs1] * (int64_t)p->r[rs2]) & 0xffffffff;
return;
}
// MULH
if (funct3 == 0b001 && funct7 == 0b0000001) {
if (p->debug) printf("mulh %s, %s, %s\n", kora_rn[rd], kora_rn[rs1], kora_rn[rs2]);
if (rd != 0) p->r[rd] = ((int64_t)p->r[rs1] * (int64_t)p->r[rs2]) >> 32;
return;
}
// MULSU
if (funct3 == 0b010 && funct7 == 0b0000001) {
if (p->debug) printf("mulsu %s, %s, %s\n", kora_rn[rd], kora_rn[rs1], kora_rn[rs2]);
if (rd != 0) p->r[rd] = ((int64_t)p->r[rs1] * (uint64_t)p->r[rs2]) >> 32;
return;
}
// MULU
if (funct3 == 0b011 && funct7 == 0b0000001) {
if (p->debug) printf("mulu %s, %s, %s\n", kora_rn[rd], kora_rn[rs1], kora_rn[rs2]);
if (rd != 0) p->r[rd] = ((uint64_t)p->r[rs1] * (uint64_t)p->r[rs2]) >> 32;
return;
}
// DIV
if (funct3 == 0b100 && funct7 == 0b0000001) {
if (p->debug) printf("div %s, %s, %s\n", kora_rn[rd], kora_rn[rs1], kora_rn[rs2]);
if (rd != 0) p->r[rd] = p->r[rs1] / p->r[rs2];
return;
}
// DIVU
if (funct3 == 0b101 && funct7 == 0b0000001) {
if (p->debug) printf("divu %s, %s, %s\n", kora_rn[rd], kora_rn[rs1], kora_rn[rs2]);
if (rd != 0) p->r[rd] = (uint32_t)p->r[rs1] / (uint32_t)p->r[rs2];
return;
}
// REM
if (funct3 == 0b110 && funct7 == 0b0000001) {
if (p->debug) printf("rem %s, %s, %s\n", kora_rn[rd], kora_rn[rs1], kora_rn[rs2]);
if (rd != 0) p->r[rd] = p->r[rs1] % p->r[rs2];
return;
}
// REMU
if (funct3 == 0b111 && funct7 == 0b0000001) {
if (p->debug) printf("remu %s, %s, %s\n", kora_rn[rd], kora_rn[rs1], kora_rn[rs2]);
if (rd != 0) p->r[rd] = (uint32_t)p->r[rs1] % (uint32_t)p->r[rs2];
return;
}
// SUB
if (funct3 == 0b000 && funct7 == 0b0100000) {
if (p->debug) printf("sub %s, %s, %s\n", kora_rn[rd], kora_rn[rs1], kora_rn[rs2]);
if (rd != 0) p->r[rd] = p->r[rs1] - p->r[rs2];
return;
}
// SLL
if (funct3 == 0b001) {
if (p->debug) printf("sll %s, %s, %s\n", kora_rn[rd], kora_rn[rs1], kora_rn[rs2]);
if (rd != 0) p->r[rd] = p->r[rs1] << (p->r[rs2] & 0x11111);
return;
}
// SLT
if (funct3 == 0b010) {
if (p->debug) printf("slt %s, %s, %s\n", kora_rn[rd], kora_rn[rs1], kora_rn[rs2]);
if (rd != 0) p->r[rd] = p->r[rs1] < p->r[rs2];
return;
}
// SLTU
if (funct3 == 0b011) {
if (p->debug) printf("sltu %s, %s, %s\n", kora_rn[rd], kora_rn[rs1], kora_rn[rs2]);
if (rd != 0) p->r[rd] = (uint32_t)p->r[rs1] < (uint32_t)p->r[rs2];
return;
}
// XOR
if (funct3 == 0b100) {
if (p->debug) printf("xor %s, %s, %s\n", kora_rn[rd], kora_rn[rs1], kora_rn[rs2]);
if (rd != 0) p->r[rd] = p->r[rs1] ^ p->r[rs2];
return;
}
// SRL
if (funct3 == 0b101 && funct7 == 0b0000000) {
if (p->debug) printf("srl %s, %s, %s\n", kora_rn[rd], kora_rn[rs1], kora_rn[rs2]);
if (rd != 0) p->r[rd] = (uint32_t)p->r[rs1] >> ((uint32_t)p->r[rs2] & 0x11111);
return;
}
// SRA
if (funct3 == 0b101 && funct7 == 0b0100000) {
if (p->debug) printf("sra %s, %s, %s\n", kora_rn[rd], kora_rn[rs1], kora_rn[rs2]);
if (rd != 0) p->r[rd] = p->r[rs1] >> (p->r[rs2] & 0x11111);
return;
}
// OR
if (funct3 == 0b110) {
if (p->debug) printf("or %s, %s, %s\n", kora_rn[rd], kora_rn[rs1], kora_rn[rs2]);
if (rd != 0) p->r[rd] = p->r[rs1] | p->r[rs2];
return;
}
// AND
if (funct3 == 0b111) {
if (p->debug) printf("and %s, %s, %s\n", kora_rn[rd], kora_rn[rs1], kora_rn[rs2]);
if (rd != 0) p->r[rd] = p->r[rs1] & p->r[rs2];
return;
}
}
// IMM
if (opcode == 0b0010011) {
// ADDI
if (funct3 == 0b000) {
if (p->debug) printf("addi %s, %s, %d\n", kora_rn[rd], kora_rn[rs1], i_imm);
if (rd != 0) p->r[rd] = p->r[rs1] + i_imm;
return;
}
// SLLI
if (funct3 == 0b001) {
if (p->debug) printf("ssli %s, %s, %d\n", kora_rn[rd], kora_rn[rs1], i_shamt);
if (rd != 0) p->r[rd] = p->r[rs1] << i_shamt;
return;
}
// SLTI
if (funct3 == 0b010) {
if (p->debug) printf("slti %s, %s, %d\n", kora_rn[rd], kora_rn[rs1], i_imm);
if (rd != 0) p->r[rd] = p->r[rs1] < i_imm;
return;
}
// SLTIU
if (funct3 == 0b011) {
if (p->debug) printf("sltiu %s, %s, %d\n", kora_rn[rd], kora_rn[rs1], i_imm);
if (rd != 0) p->r[rd] = (uint32_t)p->r[rs1] < (uint32_t)i_imm;
return;
}
// XORI
if (funct3 == 0b100) {
if (p->debug) printf("xori %s, %s, %d\n", kora_rn[rd], kora_rn[rs1], i_imm);
if (rd != 0) p->r[rd] = p->r[rs1] ^ i_imm;
return;
}
// SRLI
if (funct3 == 0b101 && funct7 == 0b0000000) {
if (p->debug) printf("srli %s, %s, %d\n", kora_rn[rd], kora_rn[rs1], i_shamt);
if (rd != 0) p->r[rd] = (uint32_t)p->r[rs1] >> i_shamt;
return;
}
// SRAI
if (funct3 == 0b101 && funct7 == 0b0100000) {
if (p->debug) printf("srai %s, %s, %d\n", kora_rn[rd], kora_rn[rs1], i_shamt);
if (rd != 0) p->r[rd] = p->r[rs1] >> i_shamt;
return;
}
// ORI
if (funct3 == 0b110) {
if (p->debug) printf("ori %s, %s, %d\n", kora_rn[rd], kora_rn[rs1], i_imm);
if (rd != 0) p->r[rd] = p->r[rs1] | i_imm;
return;
}
// ANDI
if (funct3 == 0b111) {
if (p->debug) printf("andi %s, %s, %d\n", kora_rn[rd], kora_rn[rs1], i_imm);
if (rd != 0) p->r[rd] = p->r[rs1] & i_imm;
return;
}
}
// LUI
if (opcode == 0b0110111) {
if (p->debug) printf("lui %s, %d\n", kora_rn[rd], u_imm);
if (rd != 0) p->r[rd] = u_imm << 12;
return;
}
// AUIPC
if (opcode == 0b0010111) {
if (p->debug) printf("auipc %s, %d\n", kora_rn[rd], u_imm);
if (rd != 0) p->r[rd] = (p->pc - 4) + (u_imm << 12);
return;
}
// JAL
if (opcode == 0b1101111) {
if (p->debug) printf("jal %s, %d\n", kora_rn[rd], j_imm);
if (rd != 0) p->r[rd] = p->pc;
p->pc += j_imm - 4;
return;
}
// JALR
if (opcode == 0b1100111) {
if (p->debug) printf("jalr %s, %s, %d\n", kora_rn[rd], kora_rn[rs1], i_imm);
if (rd != 0) p->r[rd] = p->pc;
p->pc = ((p->r[rs1] + i_imm) & (~ 0b1)) - 4;
return;
}
// Branch
if (opcode == 0b1100011) {
// BEQ
if (funct3 == 0b000) {
if (p->debug) printf("beq %s, %s, %d\n", kora_rn[rd], kora_rn[rs1], b_imm);
if (p->r[rd] == p->r[rs1]) p->pc += b_imm - 4;
return;
}
// BNE
if (funct3 == 0b001) {
if (p->debug) printf("bne %s, %s, %d\n", kora_rn[rd], kora_rn[rs1], b_imm);
if (p->r[rd] != p->r[rs1]) p->pc += b_imm - 4;
return;
}
// BLT
if (funct3 == 0b100) {
if (p->debug) printf("blt %s, %s, %d\n", kora_rn[rd], kora_rn[rs1], b_imm);
if (p->r[rd] < p->r[rs1]) p->pc += b_imm - 4;
return;
}
// BGE
if (funct3 == 0b101) {
if (p->debug) printf("bge %s, %s, %d\n", kora_rn[rd], kora_rn[rs1], b_imm);
if (p->r[rd] >= p->r[rs1]) p->pc += b_imm - 4;
return;
}
// BLTU
if (funct3 == 0b110) {
if (p->debug) printf("bltu %s, %s, %d\n", kora_rn[rd], kora_rn[rs1], b_imm);
if ((uint32_t)p->r[rd] < (uint32_t)p->r[rs1]) p->pc += b_imm - 4;
return;
}
// BGEU
if (funct3 == 0b111) {
if (p->debug) printf("bgeu %s, %s, %d\n", kora_rn[rd], kora_rn[rs1], b_imm);
if ((uint32_t)p->r[rd] >= (uint32_t)p->r[rs1]) p->pc += b_imm - 4;
return;
}
}
// Load
if (opcode == 0b0000011) {
// LB
if (funct3 == 0b000) {
if (p->debug) printf("lb %s, %s, %d\n", kora_rn[rd], kora_rn[rs1], i_imm);
if (rd != 0) p->r[rd] = (int8_t)kora_read_byte(p, p->r[rs1] + i_imm);
return;
}
// LH
if (funct3 == 0b001) {
if (p->debug) printf("lh %s, %s, %d\n", kora_rn[rd], kora_rn[rs1], i_imm);
if (rd != 0) p->r[rd] = (int16_t)kora_read_half(p, p->r[rs1] + i_imm);
return;
}
// LW
if (funct3 == 0b010) {
if (p->debug) printf("lw %s, %s, %d\n", kora_rn[rd], kora_rn[rs1], i_imm);
if (rd != 0) p->r[rd] = kora_read_word(p, p->r[rs1] + i_imm);
return;
}
// LBU
if (funct3 == 0b100) {
if (p->debug) printf("lbu %s, %s, %d\n", kora_rn[rd], kora_rn[rs1], i_imm);
if (rd != 0) p->r[rd] = kora_read_byte(p, p->r[rs1] + i_imm);
return;
}
// LHU
if (funct3 == 0b101) {
if (p->debug) printf("lhu %s, %s, %d\n", kora_rn[rd], kora_rn[rs1], i_imm);
if (rd != 0) p->r[rd] = kora_read_half(p, p->r[rs1] + i_imm);
return;
}
}
// Store
if (opcode == 0b0100011) {
// SB
if (funct3 == 0b000) {
if (p->debug) printf("sb %s, %s, %d\n", kora_rn[rs2], kora_rn[rs1], s_imm);
kora_write_byte(p, p->r[rs1] + s_imm, p->r[rs2]);
return;
}
// SH
if (funct3 == 0b001) {
if (p->debug) printf("sh %s, %s, %d\n", kora_rn[rs2], kora_rn[rs1], s_imm);
kora_write_half(p, p->r[rs1] + s_imm, p->r[rs2]);
return;
}
// SW
if (funct3 == 0b010) {
if (p->debug) printf("sw %s, %s, %d\n", kora_rn[rs2], kora_rn[rs1], s_imm);
kora_write_word(p, p->r[rs1] + s_imm, p->r[rs2]);
return;
}
}
// Fench / Misc Mem
if (opcode == 0b0001111) {
if (p->debug) printf("fench skip\n");
return;
}
// System
if (opcode == 0b1110011) {
// ECALL
if (funct12 == 0b000000000000) {
if (p->debug) printf("ecall\n");
p->running = false;
p->passed = p->r[10] == 0;
return;
}
if (p->debug) printf("system skip\n");
return;
}
printf("Unkown instruction: %d\n", opcode);
exit(1);
}
int main(int argc, char **argv) {
printf("Kora RISC-V Processor Tester!\n");
if (elf_version(EV_CURRENT) == EV_NONE)
printf("ELF library initialization failed: %s", elf_errmsg(-1));
for (int i = 1; i < argc; i++) {
if (string_ends_with(argv[i], ".dump")) {
continue;
}
printf("Running test: %s\n", argv[i]);
Kora kora;
kora_init(&kora, false);
FILE *file = fopen(argv[i], "rb");
fseek(file, 0, SEEK_END);
size_t file_size = ftell(file);
fseek(file, 0, SEEK_SET);
uint8_t *file_buffer = malloc(file_size);
fread(file_buffer, 1, file_size, file);
fclose(file);
Elf *elf_file = elf_memory(file_buffer, file_size);
Elf_Scn *section = NULL;
Elf32_Shdr *section_data = NULL;
while ((section = elf_nextscn(elf_file, section)) != NULL) {
section_data = elf32_getshdr(section);
// printf("- Section at %x of %d bytes\n", section_data->sh_addr, section_data->sh_size);
if (section_data->sh_addr != 0) {
Elf_Data *data = NULL;
data = elf_getdata(section, data);
uint8_t *section_buffer = data->d_buf;
for (size_t i = 0; i < section_data->sh_size; i++) {
kora_write_byte(&kora, section_data->sh_addr + i, section_buffer[i]);
}
}
}
elf_end(elf_file);
free(file_buffer);
while (kora.running) {
kora_step(&kora);
}
if (kora.passed) {
printf("Test passed!\n");
} else {
printf("Test failed!\n");
exit(1);
}
}
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment