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
|
*.o
|
||||||
elfy/target
|
elfy/target
|
||||||
elfy/Cargo.lock
|
elfy/Cargo.lock
|
||||||
|
fw_payload.*
|
||||||
|
*.dtb
|
||||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -7,3 +7,6 @@
|
|||||||
[submodule "riscv-rust"]
|
[submodule "riscv-rust"]
|
||||||
path = riscv-rust
|
path = riscv-rust
|
||||||
url = https://github.com/takahirox/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))
|
INC_FLAGS := $(addprefix -I,$(INC_DIRS))
|
||||||
|
|
||||||
$(TARGET): $(OBJS)
|
$(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
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
@ -19,3 +19,21 @@ clean:
|
|||||||
|
|
||||||
-include $(DEPS)
|
-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 <stdint.h>
|
||||||
#include <stdlib.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;
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
#[no_mangle]
|
#[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 = 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 = OsStr::from_bytes(path.to_bytes());
|
||||||
let path = PathBuf::from(path);
|
let path = PathBuf::from(path);
|
||||||
|
|
||||||
|
if verbose {
|
||||||
println!("Loading ELF binary '{}'", path.display());
|
println!("Loading ELF binary '{}'", path.display());
|
||||||
|
}
|
||||||
|
|
||||||
let elf = elf::File::open_path(path).unwrap();
|
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 = section.shdr.addr;
|
||||||
let addr_real = addr & 0x7FFFFFFF;
|
let addr_real = addr & 0x7FFFFFFF;
|
||||||
let size = section.data.len() as u64;
|
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);
|
println!("Loading ELF section '{}' @{:#x} (@{:#x}) size={}", name, addr, addr_real, size);
|
||||||
|
}
|
||||||
|
|
||||||
if addr_real + size > data_len {
|
if addr_real + size > data_len {
|
||||||
panic!("ELF section too big or offset to great");
|
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 "emu.h"
|
||||||
#include "csr.h"
|
#include "csr.h"
|
||||||
|
|
||||||
cpu_t cpu_init(uint8_t *mem) {
|
cpu_t cpu_init(uint8_t *mem, uint8_t *dtb) {
|
||||||
cpu_t ret;
|
cpu_t ret;
|
||||||
ret.clock = 0;
|
ret.clock = 0;
|
||||||
for (uint i = 0; i < 32; i++) {
|
for (uint i = 0; i < 32; i++) {
|
||||||
ret.xreg[i] = 0;
|
ret.xreg[i] = 0;
|
||||||
}
|
}
|
||||||
ret.xreg[0xb] = 0x1020; // linux?
|
ret.xreg[0xb] = 0x1020; // linux? device tree?
|
||||||
ret.pc = 0x80000000;
|
ret.pc = 0x80000000;
|
||||||
ret.mem = mem;
|
ret.mem = mem;
|
||||||
ret.reservation_en = false;
|
ret.reservation_en = false;
|
||||||
|
|
||||||
init_csrs(&ret);
|
init_csrs(&ret);
|
||||||
|
|
||||||
ret.debug_single_step =
|
ret.dtb = dtb;
|
||||||
#ifdef SINGLE_STEP
|
|
||||||
true
|
ret.clint.msip = false;
|
||||||
#else
|
ret.clint.mtimecmp_lo = 0;
|
||||||
false
|
ret.clint.mtimecmp_hi = 0;
|
||||||
#endif
|
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;
|
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_SSTATUS: return cpu->csr.data[CSR_MSTATUS] & 0x000de162;
|
||||||
case CSR_SIE: return cpu->csr.data[CSR_MIE] & 0x222;
|
case CSR_SIE: return cpu->csr.data[CSR_MIE] & 0x222;
|
||||||
case CSR_SIP: return cpu->csr.data[CSR_MIP] & 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_CYCLE: return cpu->clock;
|
||||||
case CSR_MHARTID: return 0; // this has to be 0, always
|
case CSR_MHARTID: return 0;
|
||||||
/* case CSR_TIME => self.mmu.get_clint().read_mtime(), */
|
|
||||||
default: return cpu->csr.data[address & 0xffff];
|
default: return cpu->csr.data[address & 0xffff];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,6 +87,9 @@ void write_csr_raw(cpu_t *cpu, uint address, uint value) {
|
|||||||
/* CSR_TIME => { */
|
/* CSR_TIME => { */
|
||||||
/* self.mmu.get_mut_clint().write_mtime(value); */
|
/* self.mmu.get_mut_clint().write_mtime(value); */
|
||||||
/* }, */
|
/* }, */
|
||||||
|
case CSR_TIME:
|
||||||
|
// ignore writes
|
||||||
|
break;
|
||||||
default: cpu->csr.data[address] = value; 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) {
|
uint get_csr(cpu_t *cpu, uint address, ins_ret *ret) {
|
||||||
if (has_csr_access_privilege(cpu, address)) {
|
if (has_csr_access_privilege(cpu, address)) {
|
||||||
uint r = read_csr_raw(cpu, address);
|
uint r = read_csr_raw(cpu, address);
|
||||||
#ifdef VERBOSE
|
if (VERBOSE >= 2)
|
||||||
printf("CSR read @%03x = %08x\n", address, r);
|
printf("CSR read @%03x = %08x\n", address, r);
|
||||||
#endif
|
|
||||||
return r;
|
return r;
|
||||||
} else {
|
} else {
|
||||||
ret->trap.en = true;
|
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) {
|
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);
|
printf("CSR write @%03x = %08x\n", address, value);
|
||||||
#endif
|
|
||||||
if (has_csr_access_privilege(cpu, address)) {
|
if (has_csr_access_privilege(cpu, address)) {
|
||||||
bool read_only = ((address >> 10) & 0x3) == 0x3;
|
bool read_only = ((address >> 10) & 0x3) == 0x3;
|
||||||
if (read_only) {
|
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.type = trap_IllegalInstruction;
|
||||||
ret->trap.value = cpu->pc;
|
ret->trap.value = cpu->pc;
|
||||||
} else {
|
} else {
|
||||||
write_csr_raw(cpu, address, value);
|
|
||||||
if (address == CSR_SATP) {
|
if (address == CSR_SATP) {
|
||||||
// TODO: update MMU addressing mode
|
// TODO: update MMU addressing mode
|
||||||
|
if (VERBOSE >= 1)
|
||||||
|
printf("WARN: Ignoring write to CSR_SATP\n");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
write_csr_raw(cpu, address, value);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ret->trap.en = true;
|
ret->trap.en = true;
|
||||||
|
29
src/emu.h
29
src/emu.h
@ -413,11 +413,7 @@ DEF(xori, FormatI, { // rv32i
|
|||||||
* END INSTRUCTIONS
|
* END INSTRUCTIONS
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef VERBOSE
|
#define pr_ins(name) if (VERBOSE >= 3) printf("INS %s (%08x)\n", #name, ins_word);
|
||||||
#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 : { \
|
#define RUN(name, data, insf) case data : { \
|
||||||
pr_ins(name) \
|
pr_ins(name) \
|
||||||
@ -538,10 +534,11 @@ ins_ret ins_select(cpu_t *cpu, uint ins_word) {
|
|||||||
RUN(wfi, 0x10500073, ins_FormatEmpty)
|
RUN(wfi, 0x10500073, ins_FormatEmpty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (VERBOSE >= 1)
|
||||||
printf("Invalid instruction: %08x\n", ins_word);
|
printf("Invalid instruction: %08x\n", ins_word);
|
||||||
ret.trap.en = true;
|
ret.trap.en = true;
|
||||||
ret.trap.type = trap_IllegalInstruction;
|
ret.trap.type = trap_IllegalInstruction;
|
||||||
ret.trap.value = cpu->pc;
|
ret.trap.value = ins_word;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -567,10 +564,26 @@ void emulate(cpu_t *cpu) {
|
|||||||
ret.trap.value = cpu->pc;
|
ret.trap.value = cpu->pc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret.trap.en) {
|
// handle CLINT IRQs
|
||||||
handle_trap(cpu, &ret, false);
|
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
|
// ret.pc_val should be set to pc+4 by default
|
||||||
cpu->pc = ret.pc_val;
|
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 <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
@ -14,44 +11,97 @@
|
|||||||
|
|
||||||
#include "../elfy/elfy.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;
|
const int MEM_SIZE = 1024 * 1024 * 128;
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
size_t get_filesize(const char* filename) {
|
||||||
if (argc > 2 || (argc == 2 && !strcmp(argv[1], "--help"))) {
|
struct stat st;
|
||||||
printf("Usage: rvc [<ELF binary>]\n");
|
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);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *mem = malloc(MEM_SIZE);
|
int main(int argc, char *argv[]) {
|
||||||
|
char *elf = NULL;
|
||||||
|
char *bin = NULL;
|
||||||
|
char *dtb = NULL;
|
||||||
|
|
||||||
if (argc == 2) {
|
int c;
|
||||||
load_elf(argv[1], strlen(argv[1]) + 1, mem, MEM_SIZE);
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu_t cpu = cpu_init(mem);
|
if ((!elf && !bin) || (elf && bin)) {
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *mem = malloc(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));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *dtb_ptr = get_mmap_ptr("./dts.dtb");
|
||||||
|
|
||||||
|
cpu_t cpu = cpu_init(mem, dtb_ptr);
|
||||||
|
|
||||||
|
if (VERBOSE >= 1)
|
||||||
printf("CPU initialized!\n");
|
printf("CPU initialized!\n");
|
||||||
|
|
||||||
#ifdef VERBOSE
|
if (VERBOSE >= 3)
|
||||||
cpu_dump(&cpu);
|
cpu_dump(&cpu);
|
||||||
#endif
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
cpu_tick(&cpu);
|
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(stdout);
|
||||||
fflush(stdin);
|
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
|
#ifndef MEM_H
|
||||||
#define MEM_H
|
#define MEM_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#include "uart.h"
|
||||||
|
|
||||||
// little endian, zero extended
|
// little endian, zero extended
|
||||||
uint mem_get_byte(cpu_t *cpu, uint addr) {
|
uint mem_get_byte(cpu_t *cpu, uint addr) {
|
||||||
/* printf("TRACE: mem_get_byte(%d)\n", addr); */
|
if (VERBOSE >= 3)
|
||||||
assert(addr & 0x80000000);
|
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];
|
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) {
|
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;
|
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_SupervisorExternalInterrupt = interrupt_offset + 9;
|
||||||
const uint trap_MachineExternalInterrupt = interrupt_offset + 11;
|
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 after trap_ definitions
|
||||||
#include "csr.h"
|
#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);
|
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);
|
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;
|
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
|
#endif
|
||||||
|
24
src/types.h
24
src/types.h
@ -5,6 +5,9 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
static int VERBOSE = 0;
|
||||||
|
static bool SINGLE_STEP = false;
|
||||||
|
|
||||||
typedef uint32_t uint;
|
typedef uint32_t uint;
|
||||||
typedef uint32_t uint;
|
typedef uint32_t uint;
|
||||||
|
|
||||||
@ -13,21 +16,38 @@ typedef struct {
|
|||||||
uint privilege;
|
uint privilege;
|
||||||
} csr_state;
|
} 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 {
|
typedef struct {
|
||||||
uint clock;
|
uint clock;
|
||||||
uint xreg[32];
|
uint xreg[32];
|
||||||
uint pc;
|
uint pc;
|
||||||
uint8_t *mem;
|
uint8_t *mem;
|
||||||
|
uint8_t *dtb;
|
||||||
csr_state csr;
|
csr_state csr;
|
||||||
|
clint_state clint;
|
||||||
|
uart_state uart;
|
||||||
|
|
||||||
bool reservation_en;
|
bool reservation_en;
|
||||||
uint reservation_addr;
|
uint reservation_addr;
|
||||||
|
|
||||||
bool debug_single_step;
|
|
||||||
} cpu_t;
|
} cpu_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool en;
|
bool en;
|
||||||
|
bool irq;
|
||||||
uint type;
|
uint type;
|
||||||
uint value;
|
uint value;
|
||||||
} trap;
|
} 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
|
popd
|
||||||
|
|
||||||
echo "Running: ./rvc \"./riscv-tests/isa/$1\""
|
echo "Running: ./rvc -e \"./riscv-tests/isa/$1\""
|
||||||
timeout 5s ./rvc "./riscv-tests/isa/$1"
|
timeout 5s ./rvc -e "./riscv-tests/isa/$1"
|
||||||
|
|
||||||
if [ $? -gt 0 ]; then
|
if [ $? -gt 0 ]; then
|
||||||
echo "Test failed!"
|
echo "Test failed!"
|
||||||
@ -88,7 +88,17 @@ rv32ua-p-amoswap_w
|
|||||||
rv32ua-p-amoxor_w
|
rv32ua-p-amoxor_w
|
||||||
rv32mi-p-mcsr
|
rv32mi-p-mcsr
|
||||||
rv32mi-p-csr
|
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
|
for t in $TESTS; do
|
||||||
if [[ "$t" =~ ^# ]]; then continue; fi
|
if [[ "$t" =~ ^# ]]; then continue; fi
|
||||||
echo
|
echo
|
||||||
|
Loading…
Reference in New Issue
Block a user