mirror of
https://github.com/PiMaker/rvc.git
synced 2024-11-21 19:40:08 +00:00
initial commit
passes all RV32IM tests (run './test.sh all') instructions.txt is extracted from takahirox/riscv-rust
This commit is contained in:
commit
64e2d0b45c
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal 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
6
.gitmodules
vendored
Normal 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
21
Makefile
Normal 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
15
elfy/Cargo.toml
Normal 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
15
elfy/build.rs
Normal 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
6
elfy/elfy.h
Normal 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
37
elfy/src/lib.rs
Normal 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
1682
instructions.txt
Normal file
File diff suppressed because it is too large
Load Diff
138
parse_ins.pl
Executable file
138
parse_ins.pl
Executable 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
1
riscv-opcodes
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 7d1a0e3153c37cd180be9e95f331f32c225d9257
|
1
riscv-tests
Submodule
1
riscv-tests
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 09cfdaacd9322cf0ac94818d8c852e1f4dc5bc4f
|
41
src/cpu.h
Normal file
41
src/cpu.h
Normal 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
17
src/csr.h
Normal 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
477
src/emu.h
Normal 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
120
src/ins.h
Normal 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
52
src/main.c
Normal 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
42
src/mem.h
Normal 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
37
src/types.h
Normal 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
100
test.sh
Executable 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
|
Loading…
Reference in New Issue
Block a user