From cf5024418117186a409030b8f58638484df94295 Mon Sep 17 00:00:00 2001 From: Stefan Date: Fri, 28 May 2021 19:02:11 +0200 Subject: [PATCH] support for all necessary CSRs, privilege modes, traps, atomics ...plus some cleanups and debug improvements (single-step mode) All tests specified in test.sh now pass! This pretty much means full compliance with the RV32I base spec, M and A extensions, as well as correct machine, supervisor and user mode traps/switches. Next up is the SV32 MMU and external devices (UART, CLINT timer). --- .gitmodules | 3 + riscv-rust | 1 + src/cpu.h | 20 ++++-- src/csr.h | 139 ++++++++++++++++++++++++++++++++++++++-- src/emu.h | 181 ++++++++++++++++++++++++++++++++++++++++------------ src/main.c | 7 ++ src/trap.h | 144 +++++++++++++++++++++++++++++++++++++++++ src/types.h | 31 +++++++-- test.sh | 34 ++++++---- 9 files changed, 489 insertions(+), 71 deletions(-) create mode 160000 riscv-rust create mode 100644 src/trap.h diff --git a/.gitmodules b/.gitmodules index 25605790..b643c44e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "riscv-tests"] path = riscv-tests url = https://github.com/riscv/riscv-tests +[submodule "riscv-rust"] + path = riscv-rust + url = https://github.com/takahirox/riscv-rust diff --git a/riscv-rust b/riscv-rust new file mode 160000 index 00000000..b4895fc5 --- /dev/null +++ b/riscv-rust @@ -0,0 +1 @@ +Subproject commit b4895fc56b16815d622b088d188ac65d640d25ab diff --git a/src/cpu.h b/src/cpu.h index 3f425ffb..e0e9be03 100644 --- a/src/cpu.h +++ b/src/cpu.h @@ -6,15 +6,29 @@ #include "types.h" #include "mem.h" #include "emu.h" +#include "csr.h" cpu_t cpu_init(uint8_t *mem) { cpu_t ret; ret.clock = 0; - for (unsigned char i = 0; i < 32; i++) { + for (uint i = 0; i < 32; i++) { ret.xreg[i] = 0; } + ret.xreg[0xb] = 0x1020; // linux? ret.pc = 0x80000000; ret.mem = mem; + ret.reservation_en = false; + + init_csrs(&ret); + + ret.debug_single_step = +#ifdef SINGLE_STEP + true +#else + false +#endif + ; + return ret; } @@ -33,9 +47,7 @@ void cpu_dump(cpu_t *cpu) { void cpu_tick(cpu_t *cpu) { cpu->clock++; - - uint ins_raw = mem_get_word(cpu, cpu->pc); - emulate(cpu, ins_raw); + emulate(cpu); } #endif diff --git a/src/csr.h b/src/csr.h index 7a69cbc7..e72d5cc7 100644 --- a/src/csr.h +++ b/src/csr.h @@ -1,17 +1,142 @@ #ifndef CSR_H #define CSR_H +#include +#include #include "types.h" +#include "trap.h" -uint get_csr(uint addr) { - // TODO - printf("CSR read: %x\n", addr); - return 0; +const uint CSR_USTATUS = 0x000; +const uint CSR_UIE = 0x004; +const uint CSR_UTVEC = 0x005; +const uint _CSR_USCRATCH = 0x040; +const uint CSR_UEPC = 0x041; +const uint CSR_UCAUSE = 0x042; +const uint CSR_UTVAL = 0x043; +const uint _CSR_UIP = 0x044; +const uint CSR_SSTATUS = 0x100; +const uint CSR_SEDELEG = 0x102; +const uint CSR_SIDELEG = 0x103; +const uint CSR_SIE = 0x104; +const uint CSR_STVEC = 0x105; +const uint _CSR_SSCRATCH = 0x140; +const uint CSR_SEPC = 0x141; +const uint CSR_SCAUSE = 0x142; +const uint CSR_STVAL = 0x143; +const uint CSR_SIP = 0x144; +const uint CSR_SATP = 0x180; +const uint CSR_MSTATUS = 0x300; +const uint CSR_MISA = 0x301; +const uint CSR_MEDELEG = 0x302; +const uint CSR_MIDELEG = 0x303; +const uint CSR_MIE = 0x304; +const uint CSR_MTVEC = 0x305; +const uint _CSR_MSCRATCH = 0x340; +const uint CSR_MEPC = 0x341; +const uint CSR_MCAUSE = 0x342; +const uint CSR_MTVAL = 0x343; +const uint CSR_MIP = 0x344; +const uint _CSR_PMPCFG0 = 0x3a0; +const uint _CSR_PMPADDR0 = 0x3b0; +const uint _CSR_MCYCLE = 0xb00; +const uint CSR_CYCLE = 0xc00; +const uint CSR_TIME = 0xc01; +const uint _CSR_INSERT = 0xc02; +const uint CSR_MHARTID = 0xf14; + +bool has_csr_access_privilege(cpu_t *cpu, uint addr) { + uint privilege = (addr >> 8) & 0x3; + return privilege <= cpu->csr.privilege; } -void set_csr(uint addr, uint val) { - // TODO - printf("CSR write: %x <- %x\n", addr, val); +// SSTATUS, SIE, and SIP are subsets of MSTATUS, MIE, and MIP +uint read_csr_raw(cpu_t *cpu, uint address) { + switch (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_CYCLE: return cpu->clock; + case CSR_MHARTID: return 0; // this has to be 0, always + /* case CSR_TIME => self.mmu.get_clint().read_mtime(), */ + default: return cpu->csr.data[address & 0xffff]; + } +} + +void write_csr_raw(cpu_t *cpu, uint address, uint value) { + switch (address) { + case CSR_SSTATUS: + cpu->csr.data[CSR_MSTATUS] &= !0x000de162; + cpu->csr.data[CSR_MSTATUS] |= value & 0x000de162; + /* self.mmu.update_mstatus(self.read_csr_raw(CSR_MSTATUS)); */ + break; + case CSR_SIE: + cpu->csr.data[CSR_MIE] &= !0x222; + cpu->csr.data[CSR_MIE] |= value & 0x222; + break; + case CSR_SIP: + cpu->csr.data[CSR_MIP] &= !0x222; + cpu->csr.data[CSR_MIP] |= value & 0x222; + break; + case CSR_MIDELEG: + cpu->csr.data[address] = value & 0x666; // from qemu + break; + /* case CSR_MSTATUS: */ + /* cpu->csr.data[address] = value; */ + /* self.mmu.update_mstatus(self.read_csr_raw(CSR_MSTATUS)); */ + /* break; */ + /* CSR_TIME => { */ + /* self.mmu.get_mut_clint().write_mtime(value); */ + /* }, */ + default: cpu->csr.data[address] = value; break; + }; +} + + +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 + printf("CSR read @%03x = %08x\n", address, r); +#endif + return r; + } else { + ret->trap.en = true; + ret->trap.type = trap_IllegalInstruction; + ret->trap.value = cpu->pc; + return 0; + } +} + +void set_csr(cpu_t *cpu, uint address, uint value, ins_ret *ret) { +#ifdef VERBOSE + 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) { + ret->trap.en = true; + 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 + } + } + } else { + ret->trap.en = true; + ret->trap.type = trap_IllegalInstruction; + ret->trap.value = cpu->pc; + } +} + +void init_csrs(cpu_t *cpu) { + cpu->csr.privilege = PRIV_MACHINE; + for (uint i = 0; i < 4096; i++) { + cpu->csr.data[i] = 0; + } + // RV32AIMSU + cpu->csr.data[CSR_MISA] = 0b01000000000101000001000100000001; } #endif diff --git a/src/emu.h b/src/emu.h index 48d9324e..7e1e07ed 100644 --- a/src/emu.h +++ b/src/emu.h @@ -6,6 +6,7 @@ #include "types.h" #include "ins.h" #include "mem.h" +#include "trap.h" #include "csr.h" #define AS_SIGNED(val) (*(int32_t*)&val) @@ -16,10 +17,8 @@ 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_PC(code) { ret->pc_val = code; } #define WR_CSR(code) { ret->csr_write = ins.csr; ret->csr_val = code; } /* @@ -32,20 +31,54 @@ DEF(add, FormatR, { // rv32i DEF(addi, FormatI, { // rv32i WR_RD(AS_SIGNED(cpu->xreg[ins.rs1]) + AS_SIGNED(ins.imm)); }) +DEF(amoswap_w, FormatR, { // rv32a + uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1]); + mem_set_word(cpu, cpu->xreg[ins.rs1], cpu->xreg[ins.rs2]); + WR_RD(tmp) +}) DEF(amoadd_w, FormatR, { // rv32a - NOT_IMPL + uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1]); + mem_set_word(cpu, cpu->xreg[ins.rs1], cpu->xreg[ins.rs2] + tmp); + WR_RD(tmp) +}) +DEF(amoxor_w, FormatR, { // rv32a + uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1]); + mem_set_word(cpu, cpu->xreg[ins.rs1], cpu->xreg[ins.rs2] ^ tmp); + WR_RD(tmp) }) DEF(amoand_w, FormatR, { // rv32a - NOT_IMPL -}) -DEF(amomaxu_w, FormatR, { // rv32a - NOT_IMPL + uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1]); + mem_set_word(cpu, cpu->xreg[ins.rs1], cpu->xreg[ins.rs2] & tmp); + WR_RD(tmp) }) DEF(amoor_w, FormatR, { // rv32a - NOT_IMPL + uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1]); + mem_set_word(cpu, cpu->xreg[ins.rs1], cpu->xreg[ins.rs2] | tmp); + WR_RD(tmp) }) -DEF(amoswap_w, FormatR, { // rv32a - NOT_IMPL +DEF(amomin_w, FormatR, { // rv32a + uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1]); + uint sec = cpu->xreg[ins.rs2]; + mem_set_word(cpu, cpu->xreg[ins.rs1], AS_SIGNED(sec) < AS_SIGNED(tmp) ? sec : tmp); + WR_RD(tmp) +}) +DEF(amomax_w, FormatR, { // rv32a + uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1]); + uint sec = cpu->xreg[ins.rs2]; + mem_set_word(cpu, cpu->xreg[ins.rs1], AS_SIGNED(sec) > AS_SIGNED(tmp) ? sec : tmp); + WR_RD(tmp) +}) +DEF(amominu_w, FormatR, { // rv32a + uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1]); + uint sec = cpu->xreg[ins.rs2]; + mem_set_word(cpu, cpu->xreg[ins.rs1], sec < tmp ? sec : tmp); + WR_RD(tmp) +}) +DEF(amomaxu_w, FormatR, { // rv32a + uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1]); + uint sec = cpu->xreg[ins.rs2]; + mem_set_word(cpu, cpu->xreg[ins.rs1], sec > tmp ? sec : tmp); + WR_RD(tmp) }) DEF(and, FormatR, { // rv32i WR_RD(cpu->xreg[ins.rs1] & cpu->xreg[ins.rs2]) @@ -87,19 +120,29 @@ DEF(bne, FormatB, { // rv32i } }) DEF(csrrc, FormatCSR, { // system - WR_CSR(ins.value & (~cpu->xreg[ins.rs])); + uint rs = cpu->xreg[ins.rs]; + if (rs != 0) { + WR_CSR(ins.value & ~rs); + } WR_RD(ins.value) }) DEF(csrrci, FormatCSR, { // system - WR_CSR(ins.value & (~ins.rs)); + if (ins.rs != 0) { + WR_CSR(ins.value & (~ins.rs)); + } WR_RD(ins.value) }) DEF(csrrs, FormatCSR, { // system - WR_CSR(ins.value | cpu->xreg[ins.rs]); + uint rs = cpu->xreg[ins.rs]; + if (rs != 0) { + WR_CSR(ins.value | rs); + } WR_RD(ins.value) }) DEF(csrrsi, FormatCSR, { // system - WR_CSR(ins.value | ins.rs); + if (ins.rs != 0) { + WR_CSR(ins.value | ins.rs); + } WR_RD(ins.value) }) DEF(csrrw, FormatCSR, { // system @@ -136,7 +179,7 @@ DEF(divu, FormatR, { // rv32m WR_RD(result) }) DEF(ebreak, FormatEmpty, { // system - NOT_IMPL + // unnecessary? }) DEF(ecall, FormatEmpty, { // system if (cpu->xreg[17] == 93) { @@ -146,7 +189,15 @@ DEF(ecall, FormatEmpty, { // system exit(status); } - NOT_IMPL + ret->trap.en = true; + ret->trap.value = cpu->pc; + if (cpu->csr.privilege == PRIV_USER) { + ret->trap.type = trap_EnvironmentCallFromUMode; + } else if (cpu->csr.privilege == PRIV_SUPERVISOR) { + ret->trap.type = trap_EnvironmentCallFromSMode; + } else { // PRIV_MACHINE + ret->trap.type = trap_EnvironmentCallFromMMode; + } }) DEF(fence, FormatEmpty, { // rv32i // skip @@ -179,7 +230,11 @@ DEF(lhu, FormatI, { // rv32i WR_RD(tmp) }) DEF(lr_w, FormatR, { // rv32a - NOT_IMPL + uint addr = cpu->xreg[ins.rs1]; + uint tmp = mem_get_word(cpu, addr); + cpu->reservation_en = true; + cpu->reservation_addr = addr; + WR_RD(tmp) }) DEF(lui, FormatU, { // rv32i WR_RD(ins.imm) @@ -190,7 +245,17 @@ DEF(lw, FormatI, { // rv32i WR_RD(tmp) }) DEF(mret, FormatEmpty, { // system - // skip ? FIXME + uint newpc = get_csr(cpu, CSR_MEPC, ret); + if (!ret->trap.en) { + uint status = read_csr_raw(cpu, CSR_MSTATUS); + uint mpie = (status >> 7) & 1; + uint mpp = (status >> 11) & 0x3; + uint mprv = mpp == PRIV_MACHINE ? ((status >> 17) & 1) : 0; + uint new_status = (status & ~0x21888) | (mprv << 17) | (mpie << 3) | (1 << 7); + write_csr_raw(cpu, CSR_MSTATUS, new_status); + cpu->csr.privilege = mpp; + WR_PC(newpc) + } }) DEF(mul, FormatR, { // rv32m uint tmp = AS_SIGNED(cpu->xreg[ins.rs1]) * AS_SIGNED(cpu->xreg[ins.rs2]); @@ -243,7 +308,15 @@ 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 + // I'm pretty sure this is not it chief, but it does the trick for now + uint addr = cpu->xreg[ins.rs1]; + if (cpu->reservation_en && cpu->reservation_addr == addr) { + mem_set_word(cpu, addr, cpu->xreg[ins.rs2]); + cpu->reservation_en = false; + WR_RD(ZERO) + } else { + WR_RD(ONE) + } }) DEF(sfence_vma, FormatEmpty, { // system // skip @@ -298,7 +371,17 @@ DEF(srai, FormatR, { // rv32i cpu->xreg[ins.rs1] >> shamt) }) DEF(sret, FormatEmpty, { // system - NOT_IMPL + uint newpc = get_csr(cpu, CSR_SEPC, ret); + if (!ret->trap.en) { + uint status = read_csr_raw(cpu, CSR_SSTATUS); + uint spie = (status >> 5) & 1; + uint spp = (status >> 8) & 1; + uint mprv = spp == PRIV_MACHINE ? ((status >> 17) & 1) : 0; + uint new_status = (status & ~0x20122) | (mprv << 17) | (spie << 1) | (1 << 5); + write_csr_raw(cpu, CSR_SSTATUS, new_status); + cpu->csr.privilege = spp; + WR_PC(newpc) + } }) DEF(srl, FormatR, { // rv32i WR_RD(cpu->xreg[ins.rs1] >> cpu->xreg[ins.rs2]) @@ -314,10 +397,10 @@ DEF(sw, FormatS, { // rv32i mem_set_word(cpu, cpu->xreg[ins.rs1] + ins.imm, cpu->xreg[ins.rs2]); }) DEF(uret, FormatEmpty, { // system - NOT_IMPL + // unnecessary? }) DEF(wfi, FormatEmpty, { // system - NOT_IMPL + // no-op is valid here, so skip }) DEF(xor, FormatR, { // rv32i WR_RD(cpu->xreg[ins.rs1] ^ cpu->xreg[ins.rs2]) @@ -343,7 +426,7 @@ DEF(xori, FormatI, { // rv32i } ins_ret ins_select(cpu_t *cpu, uint ins_word) { uint ins_masked; - ins_ret ret = ins_ret_noop(); + ins_ret ret = ins_ret_noop(cpu); FormatR ins_FormatR = parse_FormatR(ins_word); FormatI ins_FormatI = parse_FormatI(ins_word); @@ -356,7 +439,7 @@ ins_ret ins_select(cpu_t *cpu, uint ins_word) { if ((ins_word & 0x00000073) == 0x00000073) { // could be CSR instruction - ins_FormatCSR.value = get_csr(ins_FormatCSR.csr); + ins_FormatCSR.value = get_csr(cpu, ins_FormatCSR.csr, &ret); } ins_masked = ins_word & 0x0000007f; @@ -399,11 +482,15 @@ ins_ret ins_select(cpu_t *cpu, uint ins_word) { } 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(amoadd_w, 0x0000202f, ins_FormatR) + RUN(amoxor_w, 0x2000202f, ins_FormatR) + RUN(amoand_w, 0x6000202f, ins_FormatR) + RUN(amoor_w, 0x4000202f, ins_FormatR) + RUN(amomin_w, 0x8000202f, ins_FormatR) + RUN(amomax_w, 0xa000202f, ins_FormatR) + RUN(amominu_w, 0xc000202f, ins_FormatR) + RUN(amomaxu_w, 0xe000202f, ins_FormatR) RUN(sc_w, 0x1800202f, ins_FormatR) } ins_masked = ins_word & 0xf9f0707f; @@ -452,26 +539,40 @@ ins_ret ins_select(cpu_t *cpu, uint ins_word) { } printf("Invalid instruction: %08x\n", ins_word); - exit(2); + ret.trap.en = true; + ret.trap.type = trap_IllegalInstruction; + ret.trap.value = cpu->pc; + return ret; } -void emulate(cpu_t *cpu, uint ins_word) { - ins_ret ret = ins_select(cpu, ins_word); +void emulate(cpu_t *cpu) { + uint ins_word = 0; + ins_ret ret; + if ((cpu->pc & 0x3) == 0) { + ins_word = mem_get_word(cpu, cpu->pc); + ret = ins_select(cpu, ins_word); - if (ret.pc_write > 0) { - cpu->pc = ret.pc_val; + if (ret.csr_write && !ret.trap.en) { + set_csr(cpu, ret.csr_write, ret.csr_val, &ret); + } + + if (!ret.trap.en && ret.write_reg < 32 && ret.write_reg > 0) { + cpu->xreg[ret.write_reg] = ret.write_val; + } } else { - cpu->pc += 4; + ret = ins_ret_noop(cpu); + ret.trap.en = true; + ret.trap.type = trap_InstructionAddressMisaligned; + ret.trap.value = cpu->pc; } - if (ret.write_reg < 32 && ret.write_reg > 0) { - cpu->xreg[ret.write_reg] = ret.write_val; + if (ret.trap.en) { + handle_trap(cpu, &ret, false); } - if (ret.csr_write) { - set_csr(ret.csr_write, ret.csr_val); - } + // ret.pc_val should be set to pc+4 by default + cpu->pc = ret.pc_val; } #endif diff --git a/src/main.c b/src/main.c index f8d2bac3..611306cb 100644 --- a/src/main.c +++ b/src/main.c @@ -1,4 +1,5 @@ /* #define VERBOSE */ +/* #define SINGLE_STEP */ #include #include @@ -46,6 +47,12 @@ int main(int argc, char *argv[]) { #ifdef VERBOSE cpu_dump(&cpu); #endif + + if (cpu.debug_single_step) { + fflush(stdout); + fflush(stdin); + while(getchar()!='\n'); + } } return 0; diff --git a/src/trap.h b/src/trap.h new file mode 100644 index 00000000..032e2b9c --- /dev/null +++ b/src/trap.h @@ -0,0 +1,144 @@ +#ifndef TRAP_H +#define TRAP_H + +#include +#include "types.h" + +#define PRIV_USER 0 +#define PRIV_SUPERVISOR 1 +#define PRIV_MACHINE 3 + +static const uint interrupt_offset = 0x80000000; +const uint trap_InstructionAddressMisaligned = 0; +const uint trap_InstructionAccessFault = 1; +const uint trap_IllegalInstruction = 2; +const uint trap_Breakpoint = 3; +const uint trap_LoadAddressMisaligned = 4; +const uint trap_LoadAccessFault = 5; +const uint trap_StoreAddressMisaligned = 6; +const uint trap_StoreAccessFault = 7; +const uint trap_EnvironmentCallFromUMode = 8; +const uint trap_EnvironmentCallFromSMode = 9; +const uint trap_EnvironmentCallFromMMode = 11; +const uint trap_InstructionPageFault = 12; +const uint trap_LoadPageFault = 13; +const uint trap_StorePageFault = 15; +const uint trap_UserSoftwareInterrupt = interrupt_offset + 0; +const uint trap_SupervisorSoftwareInterrupt = interrupt_offset + 1; +const uint trap_MachineSoftwareInterrupt = interrupt_offset + 3; +const uint trap_UserTimerInterrupt = interrupt_offset + 4; +const uint trap_SupervisorTimerInterrupt = interrupt_offset + 5; +const uint trap_MachineTimerInterrupt = interrupt_offset + 7; +const uint trap_UserExternalInterrupt = interrupt_offset + 8; +const uint trap_SupervisorExternalInterrupt = interrupt_offset + 9; +const uint trap_MachineExternalInterrupt = interrupt_offset + 11; + +// include after trap_ definitions +#include "csr.h" + +// returns true if IRQ was handled or !is_interrupt +bool handle_trap(cpu_t *cpu, ins_ret *ret, bool is_interrupt) { + trap t = ret->trap; + uint current_privilege = cpu->csr.privilege; + + uint mdeleg = read_csr_raw(cpu, is_interrupt ? CSR_MIDELEG : CSR_MEDELEG); + uint sdeleg = read_csr_raw(cpu, is_interrupt ? CSR_SIDELEG : CSR_SEDELEG); + uint pos = t.type & 0xFFFF; + + uint new_privilege = ((mdeleg >> pos) & 1) == 0 ? + PRIV_MACHINE : (((sdeleg >> pos) & 1) == 0 ? + PRIV_SUPERVISOR : PRIV_USER); + + uint mstatus = read_csr_raw(cpu, CSR_MSTATUS); + uint sstatus = read_csr_raw(cpu, CSR_SSTATUS); + uint current_status = current_privilege == PRIV_MACHINE ? + mstatus : (current_privilege == PRIV_SUPERVISOR ? + sstatus : read_csr_raw(cpu, CSR_USTATUS)); + + // check if IRQ should be ignored + if (is_interrupt) { + uint ie = new_privilege == PRIV_MACHINE ? + read_csr_raw(cpu, CSR_MIE) : (new_privilege == PRIV_SUPERVISOR ? + read_csr_raw(cpu, CSR_SIE) : read_csr_raw(cpu, CSR_UIE)); + + uint current_mie = (current_status >> 3) & 1; + uint current_sie = (current_status >> 1) & 1; + uint current_uie = current_status & 1; + + uint msie = (ie >> 3) & 1; + uint ssie = (ie >> 1) & 1; + uint usie = ie & 1; + + uint mtie = (ie >> 7) & 1; + uint stie = (ie >> 5) & 1; + uint utie = (ie >> 4) & 1; + + uint meie = (ie >> 11) & 1; + uint seie = (ie >> 9) & 1; + uint ueie = (ie >> 8) & 1; + + if (new_privilege < current_privilege) { + return false; + } else if (new_privilege == current_privilege) { + if (current_privilege == PRIV_MACHINE && current_mie == 0) { + return false; + } else if (current_privilege == PRIV_SUPERVISOR && current_sie == 0) { + return false; + } else if (current_privilege == PRIV_USER && current_uie == 0) { + return false; + } + } + +#define MASK(trap, val) case trap: if (val == 0) { return false; } else { break; } + switch (t.type) { + MASK(trap_UserSoftwareInterrupt, usie) + MASK(trap_SupervisorSoftwareInterrupt, ssie) + MASK(trap_MachineSoftwareInterrupt, msie) + MASK(trap_UserTimerInterrupt, utie) + MASK(trap_SupervisorTimerInterrupt, stie) + MASK(trap_MachineTimerInterrupt, mtie) + MASK(trap_UserExternalInterrupt, ueie) + MASK(trap_SupervisorExternalInterrupt, seie) + MASK(trap_MachineExternalInterrupt, meie) + } +#undef MASK + } + + // should be handled, do that now + cpu->csr.privilege = new_privilege; + + uint csr_epc_addr = new_privilege == PRIV_MACHINE ? CSR_MEPC : (new_privilege == PRIV_SUPERVISOR ? CSR_SEPC : CSR_UEPC); + uint csr_cause_addr = new_privilege == PRIV_MACHINE ? CSR_MCAUSE : (new_privilege == PRIV_SUPERVISOR ? CSR_SCAUSE : CSR_UCAUSE); + uint csr_tval_addr = new_privilege == PRIV_MACHINE ? CSR_MTVAL : (new_privilege == PRIV_SUPERVISOR ? CSR_STVAL : CSR_UTVAL); + uint csr_tvec_addr = new_privilege == PRIV_MACHINE ? CSR_MTVEC : (new_privilege == PRIV_SUPERVISOR ? CSR_STVEC : CSR_UTVEC); + + write_csr_raw(cpu, csr_epc_addr, cpu->pc); + write_csr_raw(cpu, csr_cause_addr, t.type); + write_csr_raw(cpu, csr_tval_addr, t.value); + ret->pc_val = read_csr_raw(cpu, csr_tvec_addr); + + if ((ret->pc_val & 0x3) != 0) { + // vectored handler + ret->pc_val = (ret->pc_val & ~0x3) + 4*pos; + } + + // NOTE: No user mode interrupt/exception handling! + if (new_privilege == PRIV_MACHINE) { + uint mie = (mstatus >> 3) & 1; + uint new_status = (mstatus & !0x1888) | (mie << 7) | (current_privilege << 11); + write_csr_raw(cpu, CSR_MSTATUS, new_status); + } else { // PRIV_SUPERVISOR + uint sie = (sstatus >> 3) & 1; + uint new_status = (sstatus & !0x122) | (sie << 5) | ((current_privilege & 1) << 8); + write_csr_raw(cpu, CSR_SSTATUS, new_status); + } + +#ifdef VERBOSE + 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; +} + +#endif diff --git a/src/types.h b/src/types.h index ed4df316..38dd52d5 100644 --- a/src/types.h +++ b/src/types.h @@ -2,35 +2,54 @@ #define TYPES_H #include +#include #include typedef uint32_t uint; typedef uint32_t uint; typedef struct { - uint32_t clock; - uint32_t xreg[32]; - uint32_t pc; + uint data[4096]; + uint privilege; +} csr_state; + +typedef struct { + uint clock; + uint xreg[32]; + uint pc; uint8_t *mem; + csr_state csr; + + bool reservation_en; + uint reservation_addr; + + bool debug_single_step; } cpu_t; +typedef struct { + bool en; + uint type; + uint value; +} trap; + typedef struct { uint write_reg; uint write_val; - uint pc_write; uint pc_val; uint csr_write; uint csr_val; + trap trap; } ins_ret; -ins_ret ins_ret_noop() { +ins_ret ins_ret_noop(cpu_t *cpu) { ins_ret ret; memset(&ret, 0, sizeof(ins_ret)); + ret.pc_val = cpu->pc + 4; return ret; } uint sign_extend(uint x, uint b) { - uint m = 1U << (b - 1); + uint m = ((uint)1) << (b - 1); return (x ^ m) - m; } diff --git a/test.sh b/test.sh index 9421e5a9..91f9fa35 100755 --- a/test.sh +++ b/test.sh @@ -4,17 +4,22 @@ echo "==== Building rvc ====" make clean intercept-build make +CLEAN="$2" + function run_test { pushd ./riscv-tests/isa # rm "$1" # rm "$1.text" - make "$1" + if [ ! -f "$1" ] || [ -n "$CLEAN" ]; then + make "$1" + fi # riscv64-unknown-elf-objcopy -j .text.init -O binary "$1" "$1.text" popd + echo "Running: ./rvc \"./riscv-tests/isa/$1\"" timeout 5s ./rvc "./riscv-tests/isa/$1" if [ $? -gt 0 ]; then @@ -71,18 +76,19 @@ 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" +rv32ua-p-lrsc +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 @@ -96,5 +102,5 @@ rv32um-p-remu elif [ -n "$1" ]; then run_test "$1" else - echo "./test.sh (all|rv32ui-p-foo...)" + echo "./test.sh (all|rv32ui-p-foo...) [--clean]" fi