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).
This commit is contained in:
Stefan 2021-05-28 19:02:11 +02:00
parent 64e2d0b45c
commit cf50244181
9 changed files with 489 additions and 71 deletions

3
.gitmodules vendored
View File

@ -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

1
riscv-rust Submodule

@ -0,0 +1 @@
Subproject commit b4895fc56b16815d622b088d188ac65d640d25ab

View File

@ -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

139
src/csr.h
View File

@ -1,17 +1,142 @@
#ifndef CSR_H
#define CSR_H
#include <stdbool.h>
#include <stdio.h>
#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

181
src/emu.h
View File

@ -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

View File

@ -1,4 +1,5 @@
/* #define VERBOSE */
/* #define SINGLE_STEP */
#include <stdio.h>
#include <stdlib.h>
@ -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;

144
src/trap.h Normal file
View File

@ -0,0 +1,144 @@
#ifndef TRAP_H
#define TRAP_H
#include <stdbool.h>
#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

View File

@ -2,35 +2,54 @@
#define TYPES_H
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
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;
}

34
test.sh
View File

@ -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