mirror of
https://github.com/PiMaker/rvc.git
synced 2024-11-21 19:40:08 +00:00
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:
parent
64e2d0b45c
commit
cf50244181
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -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
1
riscv-rust
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit b4895fc56b16815d622b088d188ac65d640d25ab
|
20
src/cpu.h
20
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
|
||||
|
139
src/csr.h
139
src/csr.h
@ -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
|
||||
|
177
src/emu.h
177
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
|
||||
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
|
||||
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;
|
||||
} else {
|
||||
cpu->pc += 4;
|
||||
if (ret.csr_write && !ret.trap.en) {
|
||||
set_csr(cpu, ret.csr_write, ret.csr_val, &ret);
|
||||
}
|
||||
|
||||
if (ret.write_reg < 32 && ret.write_reg > 0) {
|
||||
if (!ret.trap.en && ret.write_reg < 32 && ret.write_reg > 0) {
|
||||
cpu->xreg[ret.write_reg] = ret.write_val;
|
||||
}
|
||||
|
||||
if (ret.csr_write) {
|
||||
set_csr(ret.csr_write, ret.csr_val);
|
||||
} else {
|
||||
ret = ins_ret_noop(cpu);
|
||||
ret.trap.en = true;
|
||||
ret.trap.type = trap_InstructionAddressMisaligned;
|
||||
ret.trap.value = cpu->pc;
|
||||
}
|
||||
|
||||
if (ret.trap.en) {
|
||||
handle_trap(cpu, &ret, false);
|
||||
}
|
||||
|
||||
// ret.pc_val should be set to pc+4 by default
|
||||
cpu->pc = ret.pc_val;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -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
144
src/trap.h
Normal 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
|
31
src/types.h
31
src/types.h
@ -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;
|
||||
}
|
||||
|
||||
|
32
test.sh
32
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"
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user