mirror of
https://github.com/PiMaker/rvc.git
synced 2024-11-23 12:40:09 +00:00
implement UART, CLINT, device tree, opensbi build
...plus some comfort improvements, more testing. This now successfully boots OpenSBI when built like specified in the Makefile!
This commit is contained in:
parent
9d170cc8d3
commit
ed82c5487f
2
.gitignore
vendored
2
.gitignore
vendored
@ -5,3 +5,5 @@ rvc
|
||||
*.o
|
||||
elfy/target
|
||||
elfy/Cargo.lock
|
||||
fw_payload.*
|
||||
*.dtb
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -7,3 +7,6 @@
|
||||
[submodule "riscv-rust"]
|
||||
path = riscv-rust
|
||||
url = https://github.com/takahirox/riscv-rust
|
||||
[submodule "opensbi"]
|
||||
path = opensbi
|
||||
url = https://github.com/riscv/opensbi
|
||||
|
20
Makefile
20
Makefile
@ -11,7 +11,7 @@ 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
|
||||
$(CC) $(LDFLAGS) -I./elfy/elfy.h $(OBJS) -o $@ $(LOADLIBES) $(LDLIBS) -L./elfy/target/release/ -Wl,--no-as-needed -ldl -lpthread -lelfy
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@ -19,3 +19,21 @@ clean:
|
||||
|
||||
-include $(DEPS)
|
||||
|
||||
# build device tree
|
||||
dts.dtb: dts.dts
|
||||
dtc -o $@ $<
|
||||
|
||||
OPENSBI_BUILD="opensbi/build/platform/generic/firmware"
|
||||
|
||||
fw_payload.bin: fw_payload.elf
|
||||
cp $(OPENSBI_BUILD)/fw_payload.bin .
|
||||
fw_payload.elf: opensbi
|
||||
env CROSS_COMPILE=riscv64-elf- \
|
||||
PLATFORM=generic \
|
||||
PLATFORM_RISCV_XLEN=32 \
|
||||
PLATFORM_RISCV_ISA=rv32ima \
|
||||
PLATFORM_RISCV_ABI=ilp32 \
|
||||
FW_PIC=n \
|
||||
ELFFLAGS=-L/usr/lib/gcc/riscv64-elf/10.2.0/rv32im/ilp32 \
|
||||
$(MAKE) -C opensbi all
|
||||
cp $(OPENSBI_BUILD)/fw_payload.elf .
|
||||
|
69
dts.dts
Normal file
69
dts.dts
Normal file
@ -0,0 +1,69 @@
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <0x2>;
|
||||
#size-cells = <0x2>;
|
||||
compatible = "riscv-virtio";
|
||||
model = "riscv-virtio,qemu";
|
||||
|
||||
chosen {
|
||||
bootargs = "ttyS0";
|
||||
stdout-path = "/uart@10000000";
|
||||
};
|
||||
|
||||
uart@10000000 {
|
||||
interrupts = <0x1>;
|
||||
interrupt-parent = <0x2>;
|
||||
clock-frequency = <0x384000>;
|
||||
reg = <0x0 0x10000000 0x0 0x100>;
|
||||
compatible = "ns16550a";
|
||||
};
|
||||
|
||||
cpus {
|
||||
#address-cells = <0x1>;
|
||||
#size-cells = <0x0>;
|
||||
timebase-frequency = <0x989680>;
|
||||
|
||||
cpu-map {
|
||||
cluster0 {
|
||||
core0 {
|
||||
cpu = <0x1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
cpu@0 {
|
||||
phandle = <0x1>;
|
||||
device_type = "cpu";
|
||||
reg = <0x0>;
|
||||
status = "okay";
|
||||
compatible = "riscv";
|
||||
riscv,isa = "rv64imasu";
|
||||
|
||||
interrupt-controller {
|
||||
phandle = <0x2>;
|
||||
#interrupt-cells = <0x1>;
|
||||
interrupt-controller;
|
||||
compatible = "riscv,cpu-intc";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
memory@80000000 {
|
||||
device_type = "memory";
|
||||
reg = <0x0 0x80000000 0x0 0x8000000>;
|
||||
};
|
||||
|
||||
soc {
|
||||
#address-cells = <0x2>;
|
||||
#size-cells = <0x2>;
|
||||
compatible = "simple-bus";
|
||||
ranges;
|
||||
|
||||
clint@2000000 {
|
||||
interrupts-extended = <0x2 0x3 0x2 0x7>;
|
||||
reg = <0x0 0x2000000 0x0 0x10000>;
|
||||
compatible = "riscv,clint0";
|
||||
};
|
||||
};
|
||||
};
|
81
dts.dts.final
Normal file
81
dts.dts.final
Normal file
@ -0,0 +1,81 @@
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <0x2>;
|
||||
#size-cells = <0x2>;
|
||||
compatible = "riscv-virtio";
|
||||
model = "riscv-virtio,qemu";
|
||||
|
||||
chosen {
|
||||
bootargs = "ttyS0";
|
||||
stdout-path = "/uart@10000000";
|
||||
};
|
||||
|
||||
uart@10000000 {
|
||||
interrupts = <0x1>;
|
||||
interrupt-parent = <0x3>;
|
||||
clock-frequency = <0x384000>;
|
||||
reg = <0x0 0x10000000 0x0 0x100>;
|
||||
compatible = "ns16550a";
|
||||
};
|
||||
|
||||
cpus {
|
||||
#address-cells = <0x1>;
|
||||
#size-cells = <0x0>;
|
||||
timebase-frequency = <0x989680>;
|
||||
|
||||
cpu-map {
|
||||
cluster0 {
|
||||
core0 {
|
||||
cpu = <0x1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
cpu@0 {
|
||||
phandle = <0x1>;
|
||||
device_type = "cpu";
|
||||
reg = <0x0>;
|
||||
status = "okay";
|
||||
compatible = "riscv";
|
||||
riscv,isa = "rv64imasu";
|
||||
mmu-type = "riscv,sv32";
|
||||
|
||||
interrupt-controller {
|
||||
phandle = <0x2>;
|
||||
#interrupt-cells = <0x1>;
|
||||
interrupt-controller;
|
||||
compatible = "riscv,cpu-intc";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
memory@80000000 {
|
||||
device_type = "memory";
|
||||
reg = <0x0 0x80000000 0x0 0x8000000>;
|
||||
};
|
||||
|
||||
soc {
|
||||
#address-cells = <0x2>;
|
||||
#size-cells = <0x2>;
|
||||
compatible = "simple-bus";
|
||||
ranges;
|
||||
|
||||
interrupt-controller@c000000 {
|
||||
phandle = <0x3>;
|
||||
riscv,ndev = <0x35>;
|
||||
reg = <0x0 0xc000000 0x0 0x4000000>;
|
||||
interrupts-extended = <0x2 0xb 0x2 0x9>;
|
||||
interrupt-controller;
|
||||
compatible = "riscv,plic0";
|
||||
#interrupt-cells = <0x1>;
|
||||
#address-cells = <0x0>;
|
||||
};
|
||||
|
||||
clint@2000000 {
|
||||
interrupts-extended = <0x2 0x3 0x2 0x7>;
|
||||
reg = <0x0 0x2000000 0x0 0x10000>;
|
||||
compatible = "riscv,clint0";
|
||||
};
|
||||
};
|
||||
};
|
@ -3,4 +3,8 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void load_elf(const char *path, uint64_t path_len, uint8_t *data, uint64_t data_len);
|
||||
int32_t load_elf(const char *path,
|
||||
uint64_t path_len,
|
||||
uint8_t *data,
|
||||
uint64_t data_len,
|
||||
bool verbose);
|
||||
|
@ -4,12 +4,24 @@ 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) {
|
||||
pub extern "C" fn load_elf(path: *const c_char, path_len: u64, data: *mut u8, data_len: u64, verbose: bool) -> i32 {
|
||||
let res = std::panic::catch_unwind(|| {
|
||||
do_load_elf(path, path_len, data, data_len, verbose);
|
||||
});
|
||||
match res {
|
||||
Ok(()) => 0,
|
||||
_ => 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn do_load_elf(path: *const c_char, path_len: u64, data: *mut u8, data_len: u64, verbose: bool) {
|
||||
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);
|
||||
|
||||
if verbose {
|
||||
println!("Loading ELF binary '{}'", path.display());
|
||||
}
|
||||
|
||||
let elf = elf::File::open_path(path).unwrap();
|
||||
|
||||
@ -22,7 +34,25 @@ pub extern "C" fn load_elf(path: *const c_char, path_len: u64, data: *mut u8, da
|
||||
let addr = section.shdr.addr;
|
||||
let addr_real = addr & 0x7FFFFFFF;
|
||||
let size = section.data.len() as u64;
|
||||
|
||||
if name == ".comment" {
|
||||
if verbose {
|
||||
let comment = String::from_utf8_lossy(§ion.data);
|
||||
println!("ELF comment: {}", comment);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if addr == 0 {
|
||||
if verbose {
|
||||
println!("Skipping 'zero address' ELF section '{}' @{:#x} (@{:#x}) size={}", name, addr, addr_real, size);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if verbose {
|
||||
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");
|
||||
|
1
opensbi
Submodule
1
opensbi
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit f30b18944e900174bfa9a372ed9d344256891e3a
|
@ -1 +1 @@
|
||||
Subproject commit 09cfdaacd9322cf0ac94818d8c852e1f4dc5bc4f
|
||||
Subproject commit 1b2c3ea84a7f8d8a833fca4d2b9aebb7d1ba4269
|
23
src/cpu.h
23
src/cpu.h
@ -8,26 +8,31 @@
|
||||
#include "emu.h"
|
||||
#include "csr.h"
|
||||
|
||||
cpu_t cpu_init(uint8_t *mem) {
|
||||
cpu_t cpu_init(uint8_t *mem, uint8_t *dtb) {
|
||||
cpu_t ret;
|
||||
ret.clock = 0;
|
||||
for (uint i = 0; i < 32; i++) {
|
||||
ret.xreg[i] = 0;
|
||||
}
|
||||
ret.xreg[0xb] = 0x1020; // linux?
|
||||
ret.xreg[0xb] = 0x1020; // linux? device tree?
|
||||
ret.pc = 0x80000000;
|
||||
ret.mem = mem;
|
||||
ret.reservation_en = false;
|
||||
|
||||
init_csrs(&ret);
|
||||
|
||||
ret.debug_single_step =
|
||||
#ifdef SINGLE_STEP
|
||||
true
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
;
|
||||
ret.dtb = dtb;
|
||||
|
||||
ret.clint.msip = false;
|
||||
ret.clint.mtimecmp_lo = 0;
|
||||
ret.clint.mtimecmp_hi = 0;
|
||||
ret.clint.mtime_lo = 0;
|
||||
ret.clint.mtime_hi = 0;
|
||||
|
||||
ret.uart.rbr_thr_ier_iir = 0;
|
||||
ret.uart.lcr_mcr_lsr_scr = 0x00200000; // LSR_THR_EMPTY is set
|
||||
ret.uart.thre_ip = false;
|
||||
ret.uart.interrupting = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
18
src/csr.h
18
src/csr.h
@ -55,9 +55,9 @@ uint read_csr_raw(cpu_t *cpu, uint address) {
|
||||
case CSR_SSTATUS: return cpu->csr.data[CSR_MSTATUS] & 0x000de162;
|
||||
case CSR_SIE: return cpu->csr.data[CSR_MIE] & 0x222;
|
||||
case CSR_SIP: return cpu->csr.data[CSR_MIP] & 0x222;
|
||||
case CSR_TIME: return cpu->clint.mtime_lo;
|
||||
case CSR_CYCLE: return cpu->clock;
|
||||
case CSR_MHARTID: return 0; // this has to be 0, always
|
||||
/* case CSR_TIME => self.mmu.get_clint().read_mtime(), */
|
||||
case CSR_MHARTID: return 0;
|
||||
default: return cpu->csr.data[address & 0xffff];
|
||||
}
|
||||
}
|
||||
@ -87,6 +87,9 @@ void write_csr_raw(cpu_t *cpu, uint address, uint value) {
|
||||
/* CSR_TIME => { */
|
||||
/* self.mmu.get_mut_clint().write_mtime(value); */
|
||||
/* }, */
|
||||
case CSR_TIME:
|
||||
// ignore writes
|
||||
break;
|
||||
default: cpu->csr.data[address] = value; break;
|
||||
};
|
||||
}
|
||||
@ -95,9 +98,8 @@ void write_csr_raw(cpu_t *cpu, uint address, uint value) {
|
||||
uint get_csr(cpu_t *cpu, uint address, ins_ret *ret) {
|
||||
if (has_csr_access_privilege(cpu, address)) {
|
||||
uint r = read_csr_raw(cpu, address);
|
||||
#ifdef VERBOSE
|
||||
if (VERBOSE >= 2)
|
||||
printf("CSR read @%03x = %08x\n", address, r);
|
||||
#endif
|
||||
return r;
|
||||
} else {
|
||||
ret->trap.en = true;
|
||||
@ -108,9 +110,8 @@ uint get_csr(cpu_t *cpu, uint address, ins_ret *ret) {
|
||||
}
|
||||
|
||||
void set_csr(cpu_t *cpu, uint address, uint value, ins_ret *ret) {
|
||||
#ifdef VERBOSE
|
||||
if (VERBOSE >= 2)
|
||||
printf("CSR write @%03x = %08x\n", address, value);
|
||||
#endif
|
||||
if (has_csr_access_privilege(cpu, address)) {
|
||||
bool read_only = ((address >> 10) & 0x3) == 0x3;
|
||||
if (read_only) {
|
||||
@ -118,10 +119,13 @@ void set_csr(cpu_t *cpu, uint address, uint value, ins_ret *ret) {
|
||||
ret->trap.type = trap_IllegalInstruction;
|
||||
ret->trap.value = cpu->pc;
|
||||
} else {
|
||||
write_csr_raw(cpu, address, value);
|
||||
if (address == CSR_SATP) {
|
||||
// TODO: update MMU addressing mode
|
||||
if (VERBOSE >= 1)
|
||||
printf("WARN: Ignoring write to CSR_SATP\n");
|
||||
return;
|
||||
}
|
||||
write_csr_raw(cpu, address, value);
|
||||
}
|
||||
} else {
|
||||
ret->trap.en = true;
|
||||
|
29
src/emu.h
29
src/emu.h
@ -413,11 +413,7 @@ DEF(xori, FormatI, { // rv32i
|
||||
* END INSTRUCTIONS
|
||||
*/
|
||||
|
||||
#ifdef VERBOSE
|
||||
#define pr_ins(name) printf("INS %s (%08x)\n", #name, ins_word);
|
||||
#else
|
||||
#define pr_ins(name) {}
|
||||
#endif
|
||||
#define pr_ins(name) if (VERBOSE >= 3) printf("INS %s (%08x)\n", #name, ins_word);
|
||||
|
||||
#define RUN(name, data, insf) case data : { \
|
||||
pr_ins(name) \
|
||||
@ -538,10 +534,11 @@ ins_ret ins_select(cpu_t *cpu, uint ins_word) {
|
||||
RUN(wfi, 0x10500073, ins_FormatEmpty)
|
||||
}
|
||||
|
||||
if (VERBOSE >= 1)
|
||||
printf("Invalid instruction: %08x\n", ins_word);
|
||||
ret.trap.en = true;
|
||||
ret.trap.type = trap_IllegalInstruction;
|
||||
ret.trap.value = cpu->pc;
|
||||
ret.trap.value = ins_word;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -567,10 +564,26 @@ void emulate(cpu_t *cpu) {
|
||||
ret.trap.value = cpu->pc;
|
||||
}
|
||||
|
||||
if (ret.trap.en) {
|
||||
handle_trap(cpu, &ret, false);
|
||||
// handle CLINT IRQs
|
||||
if (cpu->clint.msip) {
|
||||
cpu->csr.data[CSR_MIP] |= MIP_MSIP;
|
||||
}
|
||||
|
||||
cpu->clint.mtime_lo++;
|
||||
cpu->clint.mtime_hi += cpu->clint.mtime_lo == 0 ? 1 : 0;
|
||||
|
||||
if (cpu->clint.mtimecmp_lo != 0 && cpu->clint.mtimecmp_hi != 0 && (cpu->clint.mtime_hi > cpu->clint.mtimecmp_hi || (cpu->clint.mtime_hi == cpu->clint.mtimecmp_hi && cpu->clint.mtime_lo >= cpu->clint.mtimecmp_lo))) {
|
||||
cpu->csr.data[CSR_MIP] |= MIP_MTIP;
|
||||
}
|
||||
|
||||
uart_tick(cpu);
|
||||
if (cpu->uart.interrupting) {
|
||||
uint cur_mip = read_csr_raw(cpu, CSR_MIP);
|
||||
write_csr_raw(cpu, CSR_MIP, cur_mip | MIP_SEIP);
|
||||
}
|
||||
|
||||
handle_irq_and_trap(cpu, &ret);
|
||||
|
||||
// ret.pc_val should be set to pc+4 by default
|
||||
cpu->pc = ret.pc_val;
|
||||
}
|
||||
|
96
src/main.c
96
src/main.c
@ -1,6 +1,3 @@
|
||||
/* #define VERBOSE */
|
||||
/* #define SINGLE_STEP */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
@ -14,44 +11,97 @@
|
||||
|
||||
#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");
|
||||
size_t get_filesize(const char* filename) {
|
||||
struct stat st;
|
||||
stat(filename, &st);
|
||||
return st.st_size;
|
||||
}
|
||||
|
||||
uint8_t* get_mmap_ptr(const char* filename) {
|
||||
size_t filesize = get_filesize(filename);
|
||||
int fd = open(filename, O_RDONLY, 0);
|
||||
uint8_t* mmapped_data = mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
return mmapped_data;
|
||||
}
|
||||
|
||||
void usage() {
|
||||
printf("Usage: rvc (-e <ELF binary>|-b <raw binary>) [-d <device tree binary>] [-v (0|1|2|3)] [-s]\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
char *elf = NULL;
|
||||
char *bin = NULL;
|
||||
char *dtb = NULL;
|
||||
|
||||
int c;
|
||||
|
||||
while ((c = getopt (argc, argv, "e:b:d:v:s")) != -1) {
|
||||
switch (c)
|
||||
{
|
||||
case 'e':
|
||||
elf = optarg;
|
||||
break;
|
||||
case 'b':
|
||||
bin = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
dtb = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
VERBOSE = atoi(optarg);
|
||||
break;
|
||||
case 's':
|
||||
SINGLE_STEP = 1;
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if ((!elf && !bin) || (elf && bin)) {
|
||||
usage();
|
||||
}
|
||||
|
||||
uint8_t *mem = malloc(MEM_SIZE);
|
||||
|
||||
if (argc == 2) {
|
||||
load_elf(argv[1], strlen(argv[1]) + 1, mem, MEM_SIZE);
|
||||
if (elf) {
|
||||
if (load_elf(elf, strlen(elf) + 1, mem, MEM_SIZE, VERBOSE >= 1)) {
|
||||
exit(EXIT_FAILURE+1);
|
||||
}
|
||||
} else if (bin) {
|
||||
uint8_t *bin_ptr = get_mmap_ptr(bin);
|
||||
memcpy(mem, bin_ptr, get_filesize(bin));
|
||||
}
|
||||
|
||||
cpu_t cpu = cpu_init(mem);
|
||||
uint8_t *dtb_ptr = get_mmap_ptr("./dts.dtb");
|
||||
|
||||
cpu_t cpu = cpu_init(mem, dtb_ptr);
|
||||
|
||||
if (VERBOSE >= 1)
|
||||
printf("CPU initialized!\n");
|
||||
|
||||
#ifdef VERBOSE
|
||||
if (VERBOSE >= 3)
|
||||
cpu_dump(&cpu);
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
cpu_tick(&cpu);
|
||||
#ifdef VERBOSE
|
||||
cpu_dump(&cpu);
|
||||
#endif
|
||||
|
||||
if (cpu.debug_single_step) {
|
||||
if (VERBOSE >= 3)
|
||||
cpu_dump(&cpu);
|
||||
|
||||
if (SINGLE_STEP) {
|
||||
fflush(stdout);
|
||||
fflush(stdin);
|
||||
while(getchar()!='\n');
|
||||
char ch;
|
||||
gc: ch = getchar();
|
||||
switch (ch) {
|
||||
case '\n': break;
|
||||
case 'c': SINGLE_STEP = 0; break;
|
||||
default: goto gc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
119
src/mem.h
119
src/mem.h
@ -1,13 +1,70 @@
|
||||
#ifndef MEM_H
|
||||
#define MEM_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include "types.h"
|
||||
#include "uart.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);
|
||||
if (VERBOSE >= 3)
|
||||
printf("mem_get_byte(%08x)\n", addr);
|
||||
|
||||
if (cpu->dtb != NULL && addr >= 0x1020 && addr <= 0x1fff) {
|
||||
if (VERBOSE >= 2)
|
||||
printf("DTB read @%04x/%04x\n", addr, addr - 0x1020);
|
||||
return cpu->dtb[addr - 0x1020];
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
// CLINT
|
||||
case 0x02000000: return cpu->clint.msip ? 1 : 0;
|
||||
case 0x02000001: return 0;
|
||||
case 0x02000002: return 0;
|
||||
case 0x02000003: return 0;
|
||||
|
||||
case 0x02004000: return (cpu->clint.mtimecmp_lo >> 0) & 0xFF;
|
||||
case 0x02004001: return (cpu->clint.mtimecmp_lo >> 8) & 0xFF;
|
||||
case 0x02004002: return (cpu->clint.mtimecmp_lo >> 16) & 0xFF;
|
||||
case 0x02004003: return (cpu->clint.mtimecmp_lo >> 24) & 0xFF;
|
||||
case 0x02004004: return (cpu->clint.mtimecmp_hi >> 0) & 0xFF;
|
||||
case 0x02004005: return (cpu->clint.mtimecmp_hi >> 8) & 0xFF;
|
||||
case 0x02004006: return (cpu->clint.mtimecmp_hi >> 16) & 0xFF;
|
||||
case 0x02004007: return (cpu->clint.mtimecmp_hi >> 24) & 0xFF;
|
||||
|
||||
case 0x0200bff8: return (cpu->clint.mtime_lo >> 0) & 0xFF;
|
||||
case 0x0200bff9: return (cpu->clint.mtime_lo >> 8) & 0xFF;
|
||||
case 0x0200bffa: return (cpu->clint.mtime_lo >> 16) & 0xFF;
|
||||
case 0x0200bffb: return (cpu->clint.mtime_lo >> 24) & 0xFF;
|
||||
case 0x0200bffc: return (cpu->clint.mtime_hi >> 0) & 0xFF;
|
||||
case 0x0200bffd: return (cpu->clint.mtime_hi >> 8) & 0xFF;
|
||||
case 0x0200bffe: return (cpu->clint.mtime_hi >> 16) & 0xFF;
|
||||
case 0x0200bfff: return (cpu->clint.mtime_hi >> 24) & 0xFF;
|
||||
|
||||
// UART (first has rbr_thr_ier_iir, second has lcr_mcr_lsr_scr)
|
||||
case 0x10000000:
|
||||
if ((UART_GET2(LCR) >> 7) == 0) {
|
||||
uint rbr = UART_GET1(RBR);
|
||||
UART_SET1(RBR, 0);
|
||||
UART_SET2(LSR, (UART_GET2(LSR) & ~LSR_DATA_AVAILABLE));
|
||||
uart_update_iir(cpu);
|
||||
return rbr;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
case 0x10000001: return UART_GET2(LCR) >> 7 == 0 ? UART_GET1(IER) : 0;
|
||||
case 0x10000002: return UART_GET1(IIR);
|
||||
case 0x10000003: return UART_GET2(LCR);
|
||||
case 0x10000004: return UART_GET2(MCR);
|
||||
case 0x10000005: return UART_GET2(LSR);
|
||||
case 0x10000007: return UART_GET2(SCR);
|
||||
}
|
||||
|
||||
if ((addr & 0x80000000) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return cpu->mem[addr & 0x7FFFFFFF];
|
||||
}
|
||||
|
||||
@ -23,7 +80,63 @@ uint mem_get_word(cpu_t *cpu, uint addr) {
|
||||
}
|
||||
|
||||
void mem_set_byte(cpu_t *cpu, uint addr, uint val) {
|
||||
assert(addr & 0x80000000);
|
||||
if (VERBOSE >= 3)
|
||||
printf("mem_set_byte(%08x, %08x)\n", addr, val);
|
||||
|
||||
switch (addr) {
|
||||
// CLINT
|
||||
case 0x02000000: cpu->clint.msip = (val & 1) != 0; return;
|
||||
case 0x02000001: return;
|
||||
case 0x02000002: return;
|
||||
case 0x02000003: return;
|
||||
|
||||
case 0x02004000: cpu->clint.mtimecmp_lo = (cpu->clint.mtimecmp_lo & ~(0xff << 0)) | (val << 0); return;
|
||||
case 0x02004001: cpu->clint.mtimecmp_lo = (cpu->clint.mtimecmp_lo & ~(0xff << 8)) | (val << 8); return;
|
||||
case 0x02004002: cpu->clint.mtimecmp_lo = (cpu->clint.mtimecmp_lo & ~(0xff << 16)) | (val << 16); return;
|
||||
case 0x02004003: cpu->clint.mtimecmp_lo = (cpu->clint.mtimecmp_lo & ~(0xff << 24)) | (val << 24); return;
|
||||
case 0x02004004: cpu->clint.mtimecmp_hi = (cpu->clint.mtimecmp_hi & ~(0xff << 0)) | (val << 0); return;
|
||||
case 0x02004005: cpu->clint.mtimecmp_hi = (cpu->clint.mtimecmp_hi & ~(0xff << 8)) | (val << 8); return;
|
||||
case 0x02004006: cpu->clint.mtimecmp_hi = (cpu->clint.mtimecmp_hi & ~(0xff << 16)) | (val << 16); return;
|
||||
case 0x02004007: cpu->clint.mtimecmp_hi = (cpu->clint.mtimecmp_hi & ~(0xff << 24)) | (val << 24); return;
|
||||
|
||||
case 0x0200bff8: cpu->clint.mtime_lo = (cpu->clint.mtime_lo & ~(0xff << 0)) | (val << 0); return;
|
||||
case 0x0200bff9: cpu->clint.mtime_lo = (cpu->clint.mtime_lo & ~(0xff << 8)) | (val << 8); return;
|
||||
case 0x0200bffa: cpu->clint.mtime_lo = (cpu->clint.mtime_lo & ~(0xff << 16)) | (val << 16); return;
|
||||
case 0x0200bffb: cpu->clint.mtime_lo = (cpu->clint.mtime_lo & ~(0xff << 24)) | (val << 24); return;
|
||||
case 0x0200bffc: cpu->clint.mtime_hi = (cpu->clint.mtime_hi & ~(0xff << 0)) | (val << 0); return;
|
||||
case 0x0200bffd: cpu->clint.mtime_hi = (cpu->clint.mtime_hi & ~(0xff << 8)) | (val << 8); return;
|
||||
case 0x0200bffe: cpu->clint.mtime_hi = (cpu->clint.mtime_hi & ~(0xff << 16)) | (val << 16); return;
|
||||
case 0x0200bfff: cpu->clint.mtime_hi = (cpu->clint.mtime_hi & ~(0xff << 24)) | (val << 24); return;
|
||||
|
||||
// UART (first has rbr_thr_ier_iir, second has lcr_mcr_lsr_scr)
|
||||
case 0x10000000:
|
||||
if ((UART_GET2(LCR) >> 7) == 0) {
|
||||
UART_SET1(THR, val);
|
||||
UART_SET2(LSR, (UART_GET2(LSR) & ~LSR_THR_EMPTY));
|
||||
uart_update_iir(cpu);
|
||||
}
|
||||
return;
|
||||
case 0x10000001:
|
||||
if (UART_GET2(LCR) >> 7 == 0) {
|
||||
if ((UART_GET1(IER) & IER_THREINT_BIT) == 0 &&
|
||||
(val & IER_THREINT_BIT) != 0 &&
|
||||
UART_GET1(THR) == 0)
|
||||
{
|
||||
cpu->uart.thre_ip = true;
|
||||
}
|
||||
UART_SET1(IER, val);
|
||||
uart_update_iir(cpu);
|
||||
}
|
||||
return;
|
||||
case 0x10000003: UART_SET2(LCR, val); return;
|
||||
case 0x10000004: UART_SET2(MCR, val); return;
|
||||
case 0x10000007: UART_SET2(SCR, val); return;
|
||||
}
|
||||
|
||||
if ((addr & 0x80000000) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
cpu->mem[addr & 0x7FFFFFFF] = val;
|
||||
}
|
||||
|
||||
|
41
src/trap.h
41
src/trap.h
@ -33,6 +33,14 @@ const uint trap_UserExternalInterrupt = interrupt_offset + 8;
|
||||
const uint trap_SupervisorExternalInterrupt = interrupt_offset + 9;
|
||||
const uint trap_MachineExternalInterrupt = interrupt_offset + 11;
|
||||
|
||||
const uint MIP_MEIP = 0x800;
|
||||
const uint MIP_MTIP = 0x080;
|
||||
const uint MIP_MSIP = 0x008;
|
||||
const uint MIP_SEIP = 0x200;
|
||||
const uint MIP_STIP = 0x020;
|
||||
const uint MIP_SSIP = 0x002;
|
||||
const uint MIP_ALL = MIP_MEIP | MIP_MTIP | MIP_MSIP | MIP_SEIP | MIP_STIP | MIP_SSIP;
|
||||
|
||||
// include after trap_ definitions
|
||||
#include "csr.h"
|
||||
|
||||
@ -133,12 +141,39 @@ bool handle_trap(cpu_t *cpu, ins_ret *ret, bool is_interrupt) {
|
||||
write_csr_raw(cpu, CSR_SSTATUS, new_status);
|
||||
}
|
||||
|
||||
#ifdef VERBOSE
|
||||
if (VERBOSE >= 1)
|
||||
printf("trap: type=%08x value=%08x (IRQ: %d) moved PC from @%08x to @%08x\n", t.type, t.value, is_interrupt, cpu->pc, ret->pc_val);
|
||||
#endif
|
||||
/* cpu->debug_single_step = true; */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void handle_irq_and_trap(cpu_t *cpu, ins_ret *ret) {
|
||||
bool trap = ret->trap.en;
|
||||
uint mip_reset = MIP_ALL;
|
||||
uint cur_mip = read_csr_raw(cpu, CSR_MIP);
|
||||
|
||||
if (!trap) {
|
||||
uint mirq = cur_mip & read_csr_raw(cpu, CSR_MIE);
|
||||
#define HANDLE(mip, ttype) case mip: mip_reset = mip; ret->trap.en = true; ret->trap.type = ttype; break;
|
||||
switch (mirq & MIP_ALL) {
|
||||
HANDLE(MIP_MEIP, trap_MachineExternalInterrupt)
|
||||
HANDLE(MIP_MSIP, trap_MachineSoftwareInterrupt)
|
||||
HANDLE(MIP_MTIP, trap_MachineTimerInterrupt)
|
||||
HANDLE(MIP_SEIP, trap_SupervisorExternalInterrupt)
|
||||
HANDLE(MIP_SSIP, trap_SupervisorSoftwareInterrupt)
|
||||
HANDLE(MIP_STIP, trap_SupervisorTimerInterrupt)
|
||||
}
|
||||
#undef HANDLE
|
||||
}
|
||||
|
||||
bool irq = mip_reset != MIP_ALL;
|
||||
if (trap || irq) {
|
||||
bool handled = handle_trap(cpu, ret, irq);
|
||||
if (handled && irq) {
|
||||
// reset MIP value since IRQ was handled
|
||||
write_csr_raw(cpu, CSR_MIP, cur_mip & ~mip_reset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
24
src/types.h
24
src/types.h
@ -5,6 +5,9 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
static int VERBOSE = 0;
|
||||
static bool SINGLE_STEP = false;
|
||||
|
||||
typedef uint32_t uint;
|
||||
typedef uint32_t uint;
|
||||
|
||||
@ -13,21 +16,38 @@ typedef struct {
|
||||
uint privilege;
|
||||
} csr_state;
|
||||
|
||||
typedef struct {
|
||||
uint rbr_thr_ier_iir;
|
||||
uint lcr_mcr_lsr_scr;
|
||||
bool thre_ip;
|
||||
bool interrupting;
|
||||
} uart_state;
|
||||
|
||||
typedef struct {
|
||||
bool msip;
|
||||
uint mtimecmp_lo;
|
||||
uint mtimecmp_hi;
|
||||
uint mtime_lo;
|
||||
uint mtime_hi;
|
||||
} clint_state;
|
||||
|
||||
typedef struct {
|
||||
uint clock;
|
||||
uint xreg[32];
|
||||
uint pc;
|
||||
uint8_t *mem;
|
||||
uint8_t *dtb;
|
||||
csr_state csr;
|
||||
clint_state clint;
|
||||
uart_state uart;
|
||||
|
||||
bool reservation_en;
|
||||
uint reservation_addr;
|
||||
|
||||
bool debug_single_step;
|
||||
} cpu_t;
|
||||
|
||||
typedef struct {
|
||||
bool en;
|
||||
bool irq;
|
||||
uint type;
|
||||
uint value;
|
||||
} trap;
|
||||
|
72
src/uart.h
Normal file
72
src/uart.h
Normal file
@ -0,0 +1,72 @@
|
||||
#ifndef UART_H
|
||||
#define UART_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "types.h"
|
||||
|
||||
const uint SHIFT_RBR = 0;
|
||||
const uint SHIFT_THR = 8;
|
||||
const uint SHIFT_IER = 16;
|
||||
const uint SHIFT_IIR = 24;
|
||||
const uint SHIFT_LCR = 0;
|
||||
const uint SHIFT_MCR = 8;
|
||||
const uint SHIFT_LSR = 16;
|
||||
const uint SHIFT_SCR = 24;
|
||||
|
||||
#define UART_GET1(x) ((cpu->uart.rbr_thr_ier_iir >> SHIFT_##x) & 0xff)
|
||||
#define UART_GET2(x) ((cpu->uart.lcr_mcr_lsr_scr >> SHIFT_##x) & 0xff)
|
||||
|
||||
#define UART_SET1(x, val) cpu->uart.rbr_thr_ier_iir = (cpu->uart.rbr_thr_ier_iir & ~(0xff << SHIFT_##x)) | (val << SHIFT_##x)
|
||||
#define UART_SET2(x, val) cpu->uart.lcr_mcr_lsr_scr = (cpu->uart.lcr_mcr_lsr_scr & ~(0xff << SHIFT_##x)) | (val << SHIFT_##x)
|
||||
|
||||
const uint IER_RXINT_BIT = 0x1;
|
||||
const uint IER_THREINT_BIT = 0x2;
|
||||
|
||||
const uint IIR_THR_EMPTY = 0x2;
|
||||
const uint IIR_RD_AVAILABLE = 0x4;
|
||||
const uint IIR_NO_INTERRUPT = 0x7;
|
||||
|
||||
const uint LSR_DATA_AVAILABLE = 0x1;
|
||||
const uint LSR_THR_EMPTY = 0x20;
|
||||
|
||||
void uart_update_iir(cpu_t *cpu) {
|
||||
bool rx_ip = (UART_GET1(IER) & IER_RXINT_BIT) != 0 && UART_GET1(RBR) != 0;
|
||||
bool thre_ip = (UART_GET1(IER) & IER_THREINT_BIT) != 0 && UART_GET1(THR) == 0;
|
||||
UART_SET1(IIR, (rx_ip ? IIR_RD_AVAILABLE : (thre_ip ? IIR_THR_EMPTY : IIR_NO_INTERRUPT)));
|
||||
}
|
||||
|
||||
void uart_tick(cpu_t *cpu) {
|
||||
bool rx_ip = false;
|
||||
|
||||
if ((cpu->clock % 0x38400) == 0 && UART_GET1(RBR) == 0) {
|
||||
uint value = 0; // TODO: Add actual input logic
|
||||
if (value != 0) {
|
||||
UART_SET1(RBR, value);
|
||||
UART_SET2(LSR, (UART_GET2(LSR) | LSR_DATA_AVAILABLE));
|
||||
uart_update_iir(cpu);
|
||||
if ((UART_GET1(IER) & IER_RXINT_BIT) != 0) {
|
||||
rx_ip = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint thr = UART_GET1(THR);
|
||||
if ((cpu->clock & 0x16) == 0 && thr != 0) {
|
||||
printf("%c", (char)thr);
|
||||
UART_SET1(THR, 0);
|
||||
UART_SET2(LSR, (UART_GET2(LSR) | LSR_THR_EMPTY));
|
||||
uart_update_iir(cpu);
|
||||
if ((UART_GET1(IER) & IER_THREINT_BIT) != 0) {
|
||||
cpu->uart.thre_ip = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (cpu->uart.thre_ip || rx_ip) {
|
||||
cpu->uart.interrupting = true;
|
||||
cpu->uart.thre_ip = false;
|
||||
} else {
|
||||
cpu->uart.interrupting = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
16
test.sh
16
test.sh
@ -19,8 +19,8 @@ function run_test {
|
||||
|
||||
popd
|
||||
|
||||
echo "Running: ./rvc \"./riscv-tests/isa/$1\""
|
||||
timeout 5s ./rvc "./riscv-tests/isa/$1"
|
||||
echo "Running: ./rvc -e \"./riscv-tests/isa/$1\""
|
||||
timeout 5s ./rvc -e "./riscv-tests/isa/$1"
|
||||
|
||||
if [ $? -gt 0 ]; then
|
||||
echo "Test failed!"
|
||||
@ -88,7 +88,17 @@ rv32ua-p-amoswap_w
|
||||
rv32ua-p-amoxor_w
|
||||
rv32mi-p-mcsr
|
||||
rv32mi-p-csr
|
||||
rv32si-p-csr"
|
||||
rv32si-p-csr
|
||||
rv32si-p-scall"
|
||||
|
||||
# excluded tests:
|
||||
# rv32mi-p-scall, successful, but defined as "success if never returning"
|
||||
# rv32mi-p-shamt, newer spec says ignoring is OK, so we do that instead
|
||||
# rv32mi-p-sbreak, breakpoint, I believe from debug spec, no need
|
||||
# rv32mi-p-illegal, requires virtual machine paging? (TVM)
|
||||
# rv32mi-p-ma_addr, misaligned address access is ignored (this one passes though?)
|
||||
# rv32mi-p-ma_fetch, ignored for same reason, fails though
|
||||
|
||||
for t in $TESTS; do
|
||||
if [[ "$t" =~ ^# ]]; then continue; fi
|
||||
echo
|
||||
|
Loading…
Reference in New Issue
Block a user