initial commit

passes all RV32IM tests (run './test.sh all')

instructions.txt is extracted from takahirox/riscv-rust
This commit is contained in:
Stefan 2021-05-28 15:08:21 +02:00
commit 64e2d0b45c
19 changed files with 2815 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
compile_commands.json
.ccls-cache
elfy/.ccls-cache
rvc
*.o
elfy/target
elfy/Cargo.lock

6
.gitmodules vendored Normal file
View File

@ -0,0 +1,6 @@
[submodule "riscv-opcodes"]
path = riscv-opcodes
url = https://github.com/riscv/riscv-opcodes
[submodule "riscv-tests"]
path = riscv-tests
url = https://github.com/riscv/riscv-tests

21
Makefile Normal file
View File

@ -0,0 +1,21 @@
TARGET ?= rvc
SRC_DIRS ?= ./src
CC=clang
SRCS := $(shell find $(SRC_DIRS) -name '*.cpp' -or -name '*.c' -or -name '*.s')
OBJS := $(addsuffix .o,$(basename $(SRCS)))
DEPS := $(OBJS:.o=.d)
INC_DIRS := $(shell find $(SRC_DIRS) -type d)
INC_FLAGS := $(addprefix -I,$(INC_DIRS))
$(TARGET): $(OBJS)
$(CC) $(LDFLAGS) -I./elfy/elfy.h $(OBJS) -o $@ $(LOADLIBES) $(LDLIBS) -L./elfy/target/debug/ -Wl,--no-as-needed -ldl -lpthread -lelfy
.PHONY: clean
clean:
$(RM) $(TARGET) $(OBJS) $(DEPS)
-include $(DEPS)

15
elfy/Cargo.toml Normal file
View File

@ -0,0 +1,15 @@
[package]
name = "elfy"
version = "0.1.0"
authors = ["Stefan <stefan@pimaker.at>"]
edition = "2018"
[lib]
name = "elfy"
crate-type = ["staticlib"]
[dependencies]
elf = "0.0.10"
[build-dependencies]
cbindgen = "0.19"

15
elfy/build.rs Normal file
View File

@ -0,0 +1,15 @@
use std::env;
use std::path::PathBuf;
fn main() {
let crate_dir = PathBuf::from(
env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR env var is not defined"),
);
cbindgen::Builder::new()
.with_crate(crate_dir)
.with_language(cbindgen::Language::C)
.generate()
.expect("Unable to generate bindings")
.write_to_file("elfy.h");
}

6
elfy/elfy.h Normal file
View File

@ -0,0 +1,6 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
void load_elf(const char *path, uint64_t path_len, uint8_t *data, uint64_t data_len);

37
elfy/src/lib.rs Normal file
View File

@ -0,0 +1,37 @@
use std::ffi::{CStr, OsStr};
use std::path::PathBuf;
use std::os::unix::ffi::OsStrExt;
use std::os::raw::c_char;
#[no_mangle]
pub extern "C" fn load_elf(path: *const c_char, path_len: u64, data: *mut u8, data_len: u64) {
let path = unsafe { CStr::from_bytes_with_nul_unchecked(std::slice::from_raw_parts(path as *const u8, path_len as usize)) };
let path = OsStr::from_bytes(path.to_bytes());
let path = PathBuf::from(path);
println!("Loading ELF binary '{}'", path.display());
let elf = elf::File::open_path(path).unwrap();
for section in elf.sections {
if section.shdr.shtype.0 != 1 {
continue;
}
let name = section.shdr.name;
let addr = section.shdr.addr;
let addr_real = addr & 0x7FFFFFFF;
let size = section.data.len() as u64;
println!("Loading ELF section '{}' @{:#x} (@{:#x}) size={}", name, addr, addr_real, size);
if addr_real + size > data_len {
panic!("ELF section too big or offset to great");
}
let mut i = 0;
for byte in section.data {
unsafe { std::ptr::write(data.add(addr_real as usize + i), byte) };
i += 1;
}
}
}

1682
instructions.txt Normal file

File diff suppressed because it is too large Load Diff

138
parse_ins.pl Executable file
View File

@ -0,0 +1,138 @@
#!/usr/bin/env perl
use warnings;
use strict;
my $file = 'instructions.txt';
open my $info, $file or die "Could not open $file: $!\n";
my $ins = {};
my $cur_ins;
while(my $line = <$info>) {
$line =~ s/(^\s+|\s+$)//;
if ($line =~ m/^Instruction/) {
if ($cur_ins && $cur_ins->{name}) {
$ins->{delete $cur_ins->{name}} = $cur_ins;
}
$cur_ins = {};
} elsif ($line =~ m/^mask: ([0-9abcdefx]+),/) {
$cur_ins->{mask} = $1;
} elsif ($line =~ m/^data: ([0-9abcdefx]+),/) {
$cur_ins->{data} = $1;
} elsif ($line =~ m/^name:\s*"(.+)",/) {
my $n = lc $1;
$n =~ s/\./_/g;
$cur_ins->{name} = $n;
} elsif ($line =~ m/^disassemble: dump_(.+),?$/) {
my $f = $1;
$f =~ s/_mem$//;
$cur_ins->{format} = $f;
}
}
$ins->{delete $cur_ins->{name}} = $cur_ins;
# add layers to the onion
my $ins_masks = {};
foreach my $name (keys %$ins) {
my $mask = $ins->{$name}->{mask};
$ins_masks->{$mask}->{$name} = $ins->{$name};
}
# parse opcode directory
sub parse_opcode_file {
my ($file) = @_;
}
my $opcode_dir = {};
for my $fname (<riscv-opcodes/opcodes-*>) {
$fname =~ m/\/opcodes-(.*)$/;
my $dir = $1;
open my $dirf, $fname or die "Could not open $fname: $!\n";
while(my $line = <$dirf>) {
next if $line =~ m/^\s*$/;
next if $line =~ m/^@/;
next if $line =~ m/^#/;
$line =~ m/^(.+?)\s/;
my $op = $1;
$op =~ s/\./_/g;
if (exists($opcode_dir->{$op})) {
die "instruction registered multiple times: $op\n";
}
$opcode_dir->{$op} = $dir;
}
close $dirf;
}
my $fmt = {
format_r => "FormatR",
format_r2 => "FormatR",
format_i => "FormatI",
format_s => "FormatS",
format_u => "FormatU",
format_j => "FormatJ",
format_b => "FormatB",
format_csr => "FormatCSR",
empty => "FormatEmpty",
unknown => "FormatEmpty /* UNKNOWN */",
};
my $l1 = 0;
my $l2 = 0;
my $supported = {
rv32i => 1,
rv32m => 1,
rv32a => 1,
system => 1,
};
my @accepted = ();
foreach my $name (sort keys %$ins) {
# DEF(addi, FormatI, {
$l2++;
my $format = $fmt->{$ins->{$name}->{format} // "unknown"};
die "unknown format: '$ins->{$name}->{format}'\n" if !$format;
my $dir = $opcode_dir->{$name} or die "unknown instruction origin: $name\n";
next if !exists($supported->{$dir});
$l1++;
print "DEF($name, $format, { // $dir\n NOT_IMPL\n})\n";
}
print "\n";
foreach my $mask (sort keys %$ins_masks) {
my $out = "";
$out .= "ins_masked = ins_word & $mask;\n";
$out .= "switch (ins_masked) {\n";
my $found = 0;
foreach my $name (sort keys %{$ins_masks->{$mask}}) {
# RUN(addi, 0x00000013)
my $dir = $opcode_dir->{$name} or die "unknown instruction origin: $name\n";
next if !exists($supported->{$dir});
my $format = $fmt->{$ins->{$name}->{format} // "unknown"};
$out .= " RUN($name, $ins->{$name}->{data}, ins_$format)\n";
$found++;
}
next if !$found;
$out .= "}\n";
print $out;
}
print "\n";
print "Found $l2 instructions, accepted $l1\n";
close $info;

1
riscv-opcodes Submodule

@ -0,0 +1 @@
Subproject commit 7d1a0e3153c37cd180be9e95f331f32c225d9257

1
riscv-tests Submodule

@ -0,0 +1 @@
Subproject commit 09cfdaacd9322cf0ac94818d8c852e1f4dc5bc4f

41
src/cpu.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef CPU_H
#define CPU_H
#include <stdio.h>
#include <stdint.h>
#include "types.h"
#include "mem.h"
#include "emu.h"
cpu_t cpu_init(uint8_t *mem) {
cpu_t ret;
ret.clock = 0;
for (unsigned char i = 0; i < 32; i++) {
ret.xreg[i] = 0;
}
ret.pc = 0x80000000;
ret.mem = mem;
return ret;
}
void cpu_dump(cpu_t *cpu) {
printf("CPU state @%d:\n", cpu->clock);
for (int i = 0; i < 32; i += 4) {
printf(" .x%02d = %08x .x%02d = %08x .%02d = %08x .%02d = %08x\n",
i, cpu->xreg[i],
i+1, cpu->xreg[i+1],
i+2, cpu->xreg[i+2],
i+3, cpu->xreg[i+3]);
}
printf(" .pc = %08x\n", cpu->pc);
printf(" next ins: %08x\n", *(uint*)(cpu->mem + (cpu->pc & 0x7FFFFFFF)));
}
void cpu_tick(cpu_t *cpu) {
cpu->clock++;
uint ins_raw = mem_get_word(cpu, cpu->pc);
emulate(cpu, ins_raw);
}
#endif

17
src/csr.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef CSR_H
#define CSR_H
#include "types.h"
uint get_csr(uint addr) {
// TODO
printf("CSR read: %x\n", addr);
return 0;
}
void set_csr(uint addr, uint val) {
// TODO
printf("CSR write: %x <- %x\n", addr, val);
}
#endif

477
src/emu.h Normal file
View File

@ -0,0 +1,477 @@
#ifndef EMU_H
#define EMU_H
#include <stdio.h>
#include <stdlib.h>
#include "types.h"
#include "ins.h"
#include "mem.h"
#include "csr.h"
#define AS_SIGNED(val) (*(int32_t*)&val)
#define AS_UNSIGNED(val) (*(uint*)&val)
const uint ZERO = 0;
const uint ONE = 1;
#define DEF(name, fmt_t, code) \
void emu_##name(cpu_t *cpu, uint ins_word, ins_ret *ret, fmt_t ins) { code }
#define NOT_IMPL { printf("Unimplemented instruction: %08x\n", ins_word); exit(3); }
#define WR_RD(code) { ret->write_reg = ins.rd; ret->write_val = AS_UNSIGNED(code); }
#define WR_PC(code) { ret->pc_write = 1; ret->pc_val = code; }
#define WR_CSR(code) { ret->csr_write = ins.csr; ret->csr_val = code; }
/*
* BEGIN INSTRUCTIONS
*/
DEF(add, FormatR, { // rv32i
WR_RD(AS_SIGNED(cpu->xreg[ins.rs1]) + AS_SIGNED(cpu->xreg[ins.rs2]));
})
DEF(addi, FormatI, { // rv32i
WR_RD(AS_SIGNED(cpu->xreg[ins.rs1]) + AS_SIGNED(ins.imm));
})
DEF(amoadd_w, FormatR, { // rv32a
NOT_IMPL
})
DEF(amoand_w, FormatR, { // rv32a
NOT_IMPL
})
DEF(amomaxu_w, FormatR, { // rv32a
NOT_IMPL
})
DEF(amoor_w, FormatR, { // rv32a
NOT_IMPL
})
DEF(amoswap_w, FormatR, { // rv32a
NOT_IMPL
})
DEF(and, FormatR, { // rv32i
WR_RD(cpu->xreg[ins.rs1] & cpu->xreg[ins.rs2])
})
DEF(andi, FormatI, { // rv32i
WR_RD(cpu->xreg[ins.rs1] & ins.imm)
})
DEF(auipc, FormatU, { // rv32i
WR_RD(cpu->pc + ins.imm)
})
DEF(beq, FormatB, { // rv32i
if (cpu->xreg[ins.rs1] == cpu->xreg[ins.rs2]) {
WR_PC(cpu->pc + ins.imm);
}
})
DEF(bge, FormatB, { // rv32i
if (AS_SIGNED(cpu->xreg[ins.rs1]) >= AS_SIGNED(cpu->xreg[ins.rs2])) {
WR_PC(cpu->pc + ins.imm);
}
})
DEF(bgeu, FormatB, { // rv32i
if (AS_UNSIGNED(cpu->xreg[ins.rs1]) >= AS_UNSIGNED(cpu->xreg[ins.rs2])) {
WR_PC(cpu->pc + ins.imm);
}
})
DEF(blt, FormatB, { // rv32i
if (AS_SIGNED(cpu->xreg[ins.rs1]) < AS_SIGNED(cpu->xreg[ins.rs2])) {
WR_PC(cpu->pc + ins.imm);
}
})
DEF(bltu, FormatB, { // rv32i
if (AS_UNSIGNED(cpu->xreg[ins.rs1]) < AS_UNSIGNED(cpu->xreg[ins.rs2])) {
WR_PC(cpu->pc + ins.imm);
}
})
DEF(bne, FormatB, { // rv32i
if (cpu->xreg[ins.rs1] != cpu->xreg[ins.rs2]) {
WR_PC(cpu->pc + ins.imm);
}
})
DEF(csrrc, FormatCSR, { // system
WR_CSR(ins.value & (~cpu->xreg[ins.rs]));
WR_RD(ins.value)
})
DEF(csrrci, FormatCSR, { // system
WR_CSR(ins.value & (~ins.rs));
WR_RD(ins.value)
})
DEF(csrrs, FormatCSR, { // system
WR_CSR(ins.value | cpu->xreg[ins.rs]);
WR_RD(ins.value)
})
DEF(csrrsi, FormatCSR, { // system
WR_CSR(ins.value | ins.rs);
WR_RD(ins.value)
})
DEF(csrrw, FormatCSR, { // system
WR_CSR(cpu->xreg[ins.rs]);
WR_RD(ins.value)
})
DEF(csrrwi, FormatCSR, { // system
WR_CSR(ins.rs);
WR_RD(ins.value)
})
DEF(div, FormatR, { // rv32m
uint dividend = cpu->xreg[ins.rs1];
uint divisor = cpu->xreg[ins.rs2];
uint result;
if (divisor == 0) {
result = 0xFFFFFFFF;
} else if (dividend == 0x80000000 && divisor == 0xFFFFFFFF) {
result = dividend;
} else {
int32_t tmp = AS_SIGNED(dividend) / AS_SIGNED(divisor);
result = AS_UNSIGNED(tmp);
}
WR_RD(result)
})
DEF(divu, FormatR, { // rv32m
uint dividend = cpu->xreg[ins.rs1];
uint divisor = cpu->xreg[ins.rs2];
uint result;
if (divisor == 0) {
result = 0xFFFFFFFF;
} else {
result = dividend / divisor;
}
WR_RD(result)
})
DEF(ebreak, FormatEmpty, { // system
NOT_IMPL
})
DEF(ecall, FormatEmpty, { // system
if (cpu->xreg[17] == 93) {
// EXIT CALL
uint status = cpu->xreg[10] >> 1;
printf("ecall EXIT = %d (0x%x)\n", status, status);
exit(status);
}
NOT_IMPL
})
DEF(fence, FormatEmpty, { // rv32i
// skip
})
DEF(fence_i, FormatEmpty, { // rv32i
// skip
})
DEF(jal, FormatJ, { // rv32i
WR_RD(cpu->pc + 4);
WR_PC(cpu->pc + ins.imm);
})
DEF(jalr, FormatI, { // rv32i
WR_RD(cpu->pc + 4);
WR_PC(cpu->xreg[ins.rs1] + ins.imm);
})
DEF(lb, FormatI, { // rv32i
uint tmp = sign_extend(mem_get_byte(cpu, cpu->xreg[ins.rs1] + ins.imm), 8);
WR_RD(tmp)
})
DEF(lbu, FormatI, { // rv32i
uint tmp = mem_get_byte(cpu, cpu->xreg[ins.rs1] + ins.imm);
WR_RD(tmp)
})
DEF(lh, FormatI, { // rv32i
uint tmp = sign_extend(mem_get_half_word(cpu, cpu->xreg[ins.rs1] + ins.imm), 16);
WR_RD(tmp)
})
DEF(lhu, FormatI, { // rv32i
uint tmp = mem_get_half_word(cpu, cpu->xreg[ins.rs1] + ins.imm);
WR_RD(tmp)
})
DEF(lr_w, FormatR, { // rv32a
NOT_IMPL
})
DEF(lui, FormatU, { // rv32i
WR_RD(ins.imm)
})
DEF(lw, FormatI, { // rv32i
// would need sign extend for xlen > 32
uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1] + ins.imm);
WR_RD(tmp)
})
DEF(mret, FormatEmpty, { // system
// skip ? FIXME
})
DEF(mul, FormatR, { // rv32m
uint tmp = AS_SIGNED(cpu->xreg[ins.rs1]) * AS_SIGNED(cpu->xreg[ins.rs2]);
WR_RD(tmp)
})
DEF(mulh, FormatR, { // rv32m
uint tmp = ((int64_t)AS_SIGNED(cpu->xreg[ins.rs1]) * (int64_t)AS_SIGNED(cpu->xreg[ins.rs2])) >> 32;
WR_RD(tmp)
})
DEF(mulhsu, FormatR, { // rv32m
uint tmp = ((int64_t)AS_SIGNED(cpu->xreg[ins.rs1]) * (uint64_t)AS_UNSIGNED(cpu->xreg[ins.rs2])) >> 32;
WR_RD(tmp)
})
DEF(mulhu, FormatR, { // rv32m
uint tmp = ((uint64_t)AS_UNSIGNED(cpu->xreg[ins.rs1]) * (uint64_t)AS_UNSIGNED(cpu->xreg[ins.rs2])) >> 32;
WR_RD(tmp)
})
DEF(or, FormatR, { // rv32i
WR_RD(cpu->xreg[ins.rs1] | cpu->xreg[ins.rs2])
})
DEF(ori, FormatI, { // rv32i
WR_RD(cpu->xreg[ins.rs1] | ins.imm)
})
DEF(rem, FormatR, { // rv32m
uint dividend = cpu->xreg[ins.rs1];
uint divisor = cpu->xreg[ins.rs2];
uint result;
if (divisor == 0) {
result = dividend;
} else if (dividend == 0x80000000 && divisor == 0xFFFFFFFF) {
result = 0;
} else {
int32_t tmp = AS_SIGNED(dividend) % AS_SIGNED(divisor);
result = AS_UNSIGNED(tmp);
}
WR_RD(result)
})
DEF(remu, FormatR, { // rv32m
uint dividend = cpu->xreg[ins.rs1];
uint divisor = cpu->xreg[ins.rs2];
uint result;
if (divisor == 0) {
result = dividend;
} else {
result = dividend % divisor;
}
WR_RD(result)
})
DEF(sb, FormatS, { // rv32i
mem_set_byte(cpu, cpu->xreg[ins.rs1] + ins.imm, cpu->xreg[ins.rs2]);
})
DEF(sc_w, FormatR, { // rv32a
NOT_IMPL
})
DEF(sfence_vma, FormatEmpty, { // system
// skip
})
DEF(sh, FormatS, { // rv32i
mem_set_half_word(cpu, cpu->xreg[ins.rs1] + ins.imm, cpu->xreg[ins.rs2]);
})
DEF(sll, FormatR, { // rv32i
WR_RD(cpu->xreg[ins.rs1] << cpu->xreg[ins.rs2])
})
DEF(slli, FormatR, { // rv32i
uint shamt = (ins_word >> 20) & 0x1F;
WR_RD(cpu->xreg[ins.rs1] << shamt)
})
DEF(slt, FormatR, { // rv32i
if (AS_SIGNED(cpu->xreg[ins.rs1]) < AS_SIGNED(cpu->xreg[ins.rs2])) {
WR_RD(ONE)
} else {
WR_RD(ZERO)
}
})
DEF(slti, FormatI, { // rv32i
if (AS_SIGNED(cpu->xreg[ins.rs1]) < AS_SIGNED(ins.imm)) {
WR_RD(ONE)
} else {
WR_RD(ZERO)
}
})
DEF(sltiu, FormatI, { // rv32i
if (AS_UNSIGNED(cpu->xreg[ins.rs1]) < AS_UNSIGNED(ins.imm)) {
WR_RD(ONE)
} else {
WR_RD(ZERO)
}
})
DEF(sltu, FormatR, { // rv32i
if (AS_UNSIGNED(cpu->xreg[ins.rs1]) < AS_UNSIGNED(cpu->xreg[ins.rs2])) {
WR_RD(ONE)
} else {
WR_RD(ZERO)
}
})
DEF(sra, FormatR, { // rv32i
uint msr = cpu->xreg[ins.rs1] & 0x80000000;
WR_RD(msr ? ~(~cpu->xreg[ins.rs1] >> cpu->xreg[ins.rs2]) :
cpu->xreg[ins.rs1] >> cpu->xreg[ins.rs2])
})
DEF(srai, FormatR, { // rv32i
uint msr = cpu->xreg[ins.rs1] & 0x80000000;
uint shamt = (ins_word >> 20) & 0x1F;
WR_RD(msr ? ~(~cpu->xreg[ins.rs1] >> shamt) :
cpu->xreg[ins.rs1] >> shamt)
})
DEF(sret, FormatEmpty, { // system
NOT_IMPL
})
DEF(srl, FormatR, { // rv32i
WR_RD(cpu->xreg[ins.rs1] >> cpu->xreg[ins.rs2])
})
DEF(srli, FormatR, { // rv32i
uint shamt = (ins_word >> 20) & 0x1F;
WR_RD(cpu->xreg[ins.rs1] >> shamt)
})
DEF(sub, FormatR, { // rv32i
WR_RD(AS_SIGNED(cpu->xreg[ins.rs1]) - AS_SIGNED(cpu->xreg[ins.rs2]));
})
DEF(sw, FormatS, { // rv32i
mem_set_word(cpu, cpu->xreg[ins.rs1] + ins.imm, cpu->xreg[ins.rs2]);
})
DEF(uret, FormatEmpty, { // system
NOT_IMPL
})
DEF(wfi, FormatEmpty, { // system
NOT_IMPL
})
DEF(xor, FormatR, { // rv32i
WR_RD(cpu->xreg[ins.rs1] ^ cpu->xreg[ins.rs2])
})
DEF(xori, FormatI, { // rv32i
WR_RD(cpu->xreg[ins.rs1] ^ ins.imm)
})
/*
* END INSTRUCTIONS
*/
#ifdef VERBOSE
#define pr_ins(name) printf("INS %s (%08x)\n", #name, ins_word);
#else
#define pr_ins(name) {}
#endif
#define RUN(name, data, insf) case data : { \
pr_ins(name) \
emu_##name(cpu, ins_word, &ret, insf); \
return ret; \
}
ins_ret ins_select(cpu_t *cpu, uint ins_word) {
uint ins_masked;
ins_ret ret = ins_ret_noop();
FormatR ins_FormatR = parse_FormatR(ins_word);
FormatI ins_FormatI = parse_FormatI(ins_word);
FormatS ins_FormatS = parse_FormatS(ins_word);
FormatU ins_FormatU = parse_FormatU(ins_word);
FormatJ ins_FormatJ = parse_FormatJ(ins_word);
FormatB ins_FormatB = parse_FormatB(ins_word);
FormatCSR ins_FormatCSR = parse_FormatCSR(ins_word);
FormatEmpty ins_FormatEmpty = parse_FormatEmpty(ins_word);
if ((ins_word & 0x00000073) == 0x00000073) {
// could be CSR instruction
ins_FormatCSR.value = get_csr(ins_FormatCSR.csr);
}
ins_masked = ins_word & 0x0000007f;
switch (ins_masked) {
RUN(auipc, 0x00000017, ins_FormatU)
RUN(jal, 0x0000006f, ins_FormatJ)
RUN(lui, 0x00000037, ins_FormatU)
}
ins_masked = ins_word & 0x0000707f;
switch (ins_masked) {
RUN(addi, 0x00000013, ins_FormatI)
RUN(andi, 0x00007013, ins_FormatI)
RUN(beq, 0x00000063, ins_FormatB)
RUN(bge, 0x00005063, ins_FormatB)
RUN(bgeu, 0x00007063, ins_FormatB)
RUN(blt, 0x00004063, ins_FormatB)
RUN(bltu, 0x00006063, ins_FormatB)
RUN(bne, 0x00001063, ins_FormatB)
RUN(csrrc, 0x00003073, ins_FormatCSR)
RUN(csrrci, 0x00007073, ins_FormatCSR)
RUN(csrrs, 0x00002073, ins_FormatCSR)
RUN(csrrsi, 0x00006073, ins_FormatCSR)
RUN(csrrw, 0x00001073, ins_FormatCSR)
RUN(csrrwi, 0x00005073, ins_FormatCSR)
RUN(fence, 0x0000000f, ins_FormatEmpty)
RUN(fence_i, 0x0000100f, ins_FormatEmpty)
RUN(jalr, 0x00000067, ins_FormatI)
RUN(lb, 0x00000003, ins_FormatI)
RUN(lbu, 0x00004003, ins_FormatI)
RUN(lh, 0x00001003, ins_FormatI)
RUN(lhu, 0x00005003, ins_FormatI)
RUN(lw, 0x00002003, ins_FormatI)
RUN(ori, 0x00006013, ins_FormatI)
RUN(sb, 0x00000023, ins_FormatS)
RUN(sh, 0x00001023, ins_FormatS)
RUN(slti, 0x00002013, ins_FormatI)
RUN(sltiu, 0x00003013, ins_FormatI)
RUN(sw, 0x00002023, ins_FormatS)
RUN(xori, 0x00004013, ins_FormatI)
}
ins_masked = ins_word & 0xf800707f;
switch (ins_masked) {
RUN(amoadd_w, 0x0000202f, ins_FormatR)
RUN(amoand_w, 0x6000202f, ins_FormatR)
RUN(amomaxu_w, 0xe000202f, ins_FormatR)
RUN(amoor_w, 0x4000202f, ins_FormatR)
RUN(amoswap_w, 0x0800202f, ins_FormatR)
RUN(sc_w, 0x1800202f, ins_FormatR)
}
ins_masked = ins_word & 0xf9f0707f;
switch (ins_masked) {
RUN(lr_w, 0x1000202f, ins_FormatR)
}
ins_masked = ins_word & 0xfc00707f;
switch (ins_masked) {
RUN(slli, 0x00001013, ins_FormatR)
RUN(srai, 0x40005013, ins_FormatR)
RUN(srli, 0x00005013, ins_FormatR)
}
ins_masked = ins_word & 0xfe00707f;
switch (ins_masked) {
RUN(add, 0x00000033, ins_FormatR)
RUN(and, 0x00007033, ins_FormatR)
RUN(div, 0x02004033, ins_FormatR)
RUN(divu, 0x02005033, ins_FormatR)
RUN(mul, 0x02000033, ins_FormatR)
RUN(mulh, 0x02001033, ins_FormatR)
RUN(mulhsu, 0x02002033, ins_FormatR)
RUN(mulhu, 0x02003033, ins_FormatR)
RUN(or, 0x00006033, ins_FormatR)
RUN(rem, 0x02006033, ins_FormatR)
RUN(remu, 0x02007033, ins_FormatR)
RUN(sll, 0x00001033, ins_FormatR)
RUN(slt, 0x00002033, ins_FormatR)
RUN(sltu, 0x00003033, ins_FormatR)
RUN(sra, 0x40005033, ins_FormatR)
RUN(srl, 0x00005033, ins_FormatR)
RUN(sub, 0x40000033, ins_FormatR)
RUN(xor, 0x00004033, ins_FormatR)
}
ins_masked = ins_word & 0xfe007fff;
switch (ins_masked) {
RUN(sfence_vma, 0x12000073, ins_FormatEmpty)
}
ins_masked = ins_word & 0xffffffff;
switch (ins_masked) {
RUN(ebreak, 0x00100073, ins_FormatEmpty)
RUN(ecall, 0x00000073, ins_FormatEmpty)
RUN(mret, 0x30200073, ins_FormatEmpty)
RUN(sret, 0x10200073, ins_FormatEmpty)
RUN(uret, 0x00200073, ins_FormatEmpty)
RUN(wfi, 0x10500073, ins_FormatEmpty)
}
printf("Invalid instruction: %08x\n", ins_word);
exit(2);
}
void emulate(cpu_t *cpu, uint ins_word) {
ins_ret ret = ins_select(cpu, ins_word);
if (ret.pc_write > 0) {
cpu->pc = ret.pc_val;
} else {
cpu->pc += 4;
}
if (ret.write_reg < 32 && ret.write_reg > 0) {
cpu->xreg[ret.write_reg] = ret.write_val;
}
if (ret.csr_write) {
set_csr(ret.csr_write, ret.csr_val);
}
}
#endif

120
src/ins.h Normal file
View File

@ -0,0 +1,120 @@
#ifndef INS_H
#define INS_H
#include "types.h"
typedef struct {
uint rs1;
uint rs2;
uint imm;
} FormatB;
FormatB parse_FormatB(uint word) {
FormatB ret;
ret.rs1 = (word >> 15) & 0x1f;
ret.rs2 = (word >> 20) & 0x1f;
ret.imm = (word & 0x80000000 ? 0xfffff000 : 0) |
((word << 4) & 0x00000800) |
((word >> 20) & 0x000007e0) |
((word >> 7) & 0x0000001e);
return ret;
}
typedef struct {
uint csr;
uint rs;
uint rd;
uint value;
} FormatCSR;
FormatCSR parse_FormatCSR(uint word) {
FormatCSR ret;
ret.csr = (word >> 20) & 0xfff;
ret.rs = (word >> 15) & 0x1f;
ret.rd = (word >> 7) & 0x1f;
return ret;
}
typedef struct {
uint rd;
uint rs1;
uint imm;
} FormatI;
FormatI parse_FormatI(uint word) {
FormatI ret;
ret.rd = (word >> 7) & 0x1f;
ret.rs1 = (word >> 15) & 0x1f;
ret.imm = (word & 0x80000000 ? 0xfffff800 : 0) |
((word >> 20) & 0x000007ff);
return ret;
}
typedef struct {
uint rd;
uint imm;
} FormatJ;
FormatJ parse_FormatJ(uint word) {
FormatJ ret;
ret.rd = (word >> 7) & 0x1f;
ret.imm = (word & 0x80000000 ? 0xfff00000 : 0) |
(word & 0x000ff000) |
((word & 0x00100000) >> 9) |
((word & 0x7fe00000) >> 20);
return ret;
}
typedef struct {
uint rd;
uint rs1;
uint rs2;
uint rs3;
} FormatR;
FormatR parse_FormatR(uint word) {
FormatR ret;
ret.rd = (word >> 7) & 0x1f;
ret.rs1 = (word >> 15) & 0x1f;
ret.rs2 = (word >> 20) & 0x1f;
ret.rs3 = (word >> 27) & 0x1f;
return ret;
}
typedef struct {
uint rs1;
uint rs2;
uint imm;
} FormatS;
FormatS parse_FormatS(uint word) {
FormatS ret;
ret.rs1 = (word >> 15) & 0x1f;
ret.rs2 = (word >> 20) & 0x1f;
ret.imm = (word & 0x80000000 ? 0xfffff000 : 0) |
((word >> 20) & 0xfe0) |
((word >> 7) & 0x1f);
return ret;
}
typedef struct {
uint rd;
uint imm;
} FormatU;
FormatU parse_FormatU(uint word) {
FormatU ret;
ret.rd = (word >> 7) & 0x1f;
ret.imm = word & 0xfffff000;
return ret;
}
typedef struct {
} FormatEmpty;
FormatEmpty parse_FormatEmpty(uint word) {
FormatEmpty ret;
return ret;
}
#endif

52
src/main.c Normal file
View File

@ -0,0 +1,52 @@
/* #define VERBOSE */
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include "types.h"
#include "cpu.h"
#include "../elfy/elfy.h"
// lots of inspiration from:
// https://github.com/takahirox/riscv-rust/blob/master/src/cpu.rs
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
const int MEM_SIZE = 1024 * 1024 * 128;
int main(int argc, char *argv[]) {
if (argc > 2 || (argc == 2 && !strcmp(argv[1], "--help"))) {
printf("Usage: rvc [<ELF binary>]\n");
exit(EXIT_FAILURE);
}
uint8_t *mem = malloc(MEM_SIZE);
if (argc == 2) {
load_elf(argv[1], strlen(argv[1]) + 1, mem, MEM_SIZE);
}
cpu_t cpu = cpu_init(mem);
printf("CPU initialized!\n");
#ifdef VERBOSE
cpu_dump(&cpu);
#endif
while (1) {
cpu_tick(&cpu);
#ifdef VERBOSE
cpu_dump(&cpu);
#endif
}
return 0;
}

42
src/mem.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef MEM_H
#define MEM_H
#include <assert.h>
#include "types.h"
// little endian, zero extended
uint mem_get_byte(cpu_t *cpu, uint addr) {
/* printf("TRACE: mem_get_byte(%d)\n", addr); */
assert(addr & 0x80000000);
return cpu->mem[addr & 0x7FFFFFFF];
}
uint mem_get_half_word(cpu_t *cpu, uint addr) {
return mem_get_byte(cpu, addr) | ((uint16_t)mem_get_byte(cpu, addr + 1) << 8);
}
uint mem_get_word(cpu_t *cpu, uint addr) {
return mem_get_byte(cpu, addr) |
((uint16_t)mem_get_byte(cpu, addr + 1) << 8) |
((uint16_t)mem_get_byte(cpu, addr + 2) << 16) |
((uint16_t)mem_get_byte(cpu, addr + 3) << 24);
}
void mem_set_byte(cpu_t *cpu, uint addr, uint val) {
assert(addr & 0x80000000);
cpu->mem[addr & 0x7FFFFFFF] = val;
}
void mem_set_half_word(cpu_t *cpu, uint addr, uint val) {
mem_set_byte(cpu, addr, val & 0xFF);
mem_set_byte(cpu, addr + 1, (val >> 8) & 0xFF);
}
void mem_set_word(cpu_t *cpu, uint addr, uint val) {
mem_set_byte(cpu, addr, val & 0xFF);
mem_set_byte(cpu, addr + 1, (val >> 8) & 0xFF);
mem_set_byte(cpu, addr + 2, (val >> 16) & 0xFF);
mem_set_byte(cpu, addr + 3, val >> 24);
}
#endif

37
src/types.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef TYPES_H
#define TYPES_H
#include <stdint.h>
#include <string.h>
typedef uint32_t uint;
typedef uint32_t uint;
typedef struct {
uint32_t clock;
uint32_t xreg[32];
uint32_t pc;
uint8_t *mem;
} cpu_t;
typedef struct {
uint write_reg;
uint write_val;
uint pc_write;
uint pc_val;
uint csr_write;
uint csr_val;
} ins_ret;
ins_ret ins_ret_noop() {
ins_ret ret;
memset(&ret, 0, sizeof(ins_ret));
return ret;
}
uint sign_extend(uint x, uint b) {
uint m = 1U << (b - 1);
return (x ^ m) - m;
}
#endif

100
test.sh Executable file
View File

@ -0,0 +1,100 @@
#!/bin/bash
echo "==== Building rvc ===="
make clean
intercept-build make
function run_test {
pushd ./riscv-tests/isa
# rm "$1"
# rm "$1.text"
make "$1"
# riscv64-unknown-elf-objcopy -j .text.init -O binary "$1" "$1.text"
popd
timeout 5s ./rvc "./riscv-tests/isa/$1"
if [ $? -gt 0 ]; then
echo "Test failed!"
exit $?
fi
}
if [ "$1" == "all" ]; then
TESTS="rv32ui-p-add
rv32ui-p-addi
rv32ui-p-and
rv32ui-p-andi
rv32ui-p-auipc
rv32ui-p-beq
rv32ui-p-bge
rv32ui-p-bgeu
rv32ui-p-blt
rv32ui-p-bltu
rv32ui-p-bne
rv32ui-p-fence_i
rv32ui-p-jal
rv32ui-p-jalr
rv32ui-p-lb
rv32ui-p-lbu
rv32ui-p-lh
rv32ui-p-lhu
rv32ui-p-lui
rv32ui-p-lw
rv32ui-p-or
rv32ui-p-ori
rv32ui-p-sb
rv32ui-p-sh
rv32ui-p-simple
rv32ui-p-sll
rv32ui-p-slli
rv32ui-p-slt
rv32ui-p-slti
rv32ui-p-sltiu
rv32ui-p-sltu
rv32ui-p-sra
rv32ui-p-srai
rv32ui-p-srl
rv32ui-p-srli
rv32ui-p-sub
rv32ui-p-sw
rv32ui-p-xor
rv32ui-p-xori
rv32um-p-div
rv32um-p-divu
rv32um-p-mul
rv32um-p-mulh
rv32um-p-mulhsu
rv32um-p-mulhu
rv32um-p-rem
rv32um-p-remu
#rv32ua-p-amoadd_w
#rv32ua-p-amoand_w
#rv32ua-p-amomaxu_w
#rv32ua-p-amomax_w
#rv32ua-p-amominu_w
#rv32ua-p-amomin_w
#rv32ua-p-amoor_w
#rv32ua-p-amoswap_w
#rv32ua-p-amoxor_w
#rv32mi-p-mcsr
#rv32mi-p-csr
#rv32si-p-csr"
for t in $TESTS; do
if [[ "$t" =~ ^# ]]; then continue; fi
echo
echo "==== TEST: $t ===="
echo
run_test "$t"
done
echo
echo "All tests done!"
elif [ -n "$1" ]; then
run_test "$1"
else
echo "./test.sh (all|rv32ui-p-foo...)"
fi