mirror of
https://github.com/PiMaker/rvc.git
synced 2024-11-21 11:30:07 +00:00
add MMU support (SV32) and fix a bunch of bugs
This commit is contained in:
parent
7f47c7655d
commit
cb6d05178b
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -10,3 +10,6 @@
|
||||
[submodule "opensbi"]
|
||||
path = opensbi
|
||||
url = https://github.com/riscv/opensbi
|
||||
[submodule "linux"]
|
||||
path = linux
|
||||
url = git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
|
||||
|
1
linux
Submodule
1
linux
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 25423f4bd9a9ac3e6b0ce7ecfe56c36f4e514893
|
35
src/cpu.h
35
src/cpu.h
@ -3,12 +3,13 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "types.h"
|
||||
#include "mem.h"
|
||||
#include "emu.h"
|
||||
#include "csr.h"
|
||||
#include "mmu.h"
|
||||
|
||||
cpu_t cpu_init(uint8_t *mem, uint8_t *dtb) {
|
||||
cpu_t cpu_init(uint8_t *mem, uint8_t *dtb, uint8_t *mtd, uint mtd_size) {
|
||||
cpu_t ret;
|
||||
ret.clock = 0;
|
||||
for (uint i = 0; i < 32; i++) {
|
||||
@ -22,6 +23,8 @@ cpu_t cpu_init(uint8_t *mem, uint8_t *dtb) {
|
||||
init_csrs(&ret);
|
||||
|
||||
ret.dtb = dtb;
|
||||
ret.mtd = mtd;
|
||||
ret.mtd_size = mtd_size;
|
||||
|
||||
ret.clint.msip = false;
|
||||
ret.clint.mtimecmp_lo = 0;
|
||||
@ -34,9 +37,14 @@ cpu_t cpu_init(uint8_t *mem, uint8_t *dtb) {
|
||||
ret.uart.thre_ip = false;
|
||||
ret.uart.interrupting = false;
|
||||
|
||||
ret.mmu.mode = 0;
|
||||
ret.mmu.ppn = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *cmd_buf = NULL;
|
||||
|
||||
void cpu_dump(cpu_t *cpu) {
|
||||
printf("CPU state @%d:\n", cpu->clock);
|
||||
for (int i = 0; i < 32; i += 4) {
|
||||
@ -47,7 +55,28 @@ void cpu_dump(cpu_t *cpu) {
|
||||
i+3, cpu->xreg[i+3]);
|
||||
}
|
||||
printf(" .pc = %08x\n", cpu->pc);
|
||||
printf(" next ins: %08x\n", *(uint*)(cpu->mem + (cpu->pc & 0x7FFFFFFF)));
|
||||
/* ins_ret ret; */
|
||||
/* printf(" next ins: %08x\n", *(uint*)(cpu->mem + mmu_translate(&ret, cpu->pc & 0x7FFFFFFF, MMU_ACCESS_FETCH))); */
|
||||
|
||||
/* if (cmd_buf == NULL) { */
|
||||
/* cmd_buf = malloc(4096); */
|
||||
/* } */
|
||||
/* sprintf(cmd_buf, "addr2line -e testinit/init %x", cpu->pc); */
|
||||
/* system(cmd_buf); */
|
||||
|
||||
uint arb[8];
|
||||
for (int iarb = 0; iarb < 8; iarb++) {
|
||||
uint arb_t = cpu->xreg[iarb*4+0] ^
|
||||
cpu->xreg[iarb*4+1] ^
|
||||
cpu->xreg[iarb*4+2] ^
|
||||
cpu->xreg[iarb*4+3];
|
||||
printf("arb%d=0x%08x ", iarb, arb_t);
|
||||
if (iarb == 3) printf("\n");
|
||||
arb[iarb] = arb_t;
|
||||
}
|
||||
printf("\nARB=0x%08x\n", arb[0] ^ arb[1] ^ arb[2] ^ arb[3] ^ arb[4] ^ arb[5] ^ arb[6] ^ arb[7] ^ cpu->pc);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void cpu_tick(cpu_t *cpu) {
|
||||
|
45
src/csr.h
45
src/csr.h
@ -5,6 +5,7 @@
|
||||
#include <stdio.h>
|
||||
#include "types.h"
|
||||
#include "trap.h"
|
||||
#include "mmu.h"
|
||||
|
||||
const uint CSR_USTATUS = 0x000;
|
||||
const uint CSR_UIE = 0x004;
|
||||
@ -44,6 +45,16 @@ const uint CSR_TIME = 0xc01;
|
||||
const uint _CSR_INSERT = 0xc02;
|
||||
const uint CSR_MHARTID = 0xf14;
|
||||
|
||||
const uint CSR_MEMOP_OP = 0x0b0;
|
||||
const uint CSR_MEMOP_SRC = 0x0b1;
|
||||
const uint CSR_MEMOP_DST = 0x0b2;
|
||||
const uint CSR_MEMOP_N = 0x0b3;
|
||||
|
||||
#define MMU_ACCESS_FETCH 0
|
||||
#define MMU_ACCESS_READ 1
|
||||
#define MMU_ACCESS_WRITE 2
|
||||
extern uint mmu_translate(ins_ret *ins, uint addr, uint mode);
|
||||
|
||||
bool has_csr_access_privilege(cpu_t *cpu, uint addr) {
|
||||
uint privilege = (addr >> 8) & 0x3;
|
||||
return privilege <= cpu->csr.privilege;
|
||||
@ -59,6 +70,7 @@ uint read_csr_raw(cpu_t *cpu, uint address) {
|
||||
case CSR_MCYCLE: return cpu->clock;
|
||||
case CSR_CYCLE: return cpu->clock;
|
||||
case CSR_MHARTID: return 0;
|
||||
case CSR_SATP: return (cpu->mmu.mode << 31) | cpu->mmu.ppn;
|
||||
default: return cpu->csr.data[address & 0xffff];
|
||||
}
|
||||
}
|
||||
@ -66,31 +78,33 @@ uint read_csr_raw(cpu_t *cpu, uint address) {
|
||||
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] &= ~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] &= ~0x222;
|
||||
cpu->csr.data[CSR_MIE] |= value & 0x222;
|
||||
break;
|
||||
case CSR_SIP:
|
||||
cpu->csr.data[CSR_MIP] &= !0x222;
|
||||
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); */
|
||||
/* }, */
|
||||
case CSR_TIME:
|
||||
// ignore writes
|
||||
break;
|
||||
case CSR_MEMOP_OP:
|
||||
/* printf("MEMOP:%08x<-%08x(%d)\n", cpu->csr.data[CSR_MEMOP_DST], cpu->csr.data[CSR_MEMOP_SRC], cpu->csr.data[CSR_MEMOP_N]); */
|
||||
{
|
||||
ins_ret ins = ins_ret_noop(cpu);
|
||||
uint src = mmu_translate(&ins, cpu->csr.data[CSR_MEMOP_SRC], MMU_ACCESS_READ);
|
||||
uint dst = mmu_translate(&ins, cpu->csr.data[CSR_MEMOP_DST], MMU_ACCESS_WRITE);
|
||||
uint n = cpu->csr.data[CSR_MEMOP_N];
|
||||
memcpy(&cpu->mem[dst & (~0x80000000)], &cpu->mem[src & (~0x80000000)], n);
|
||||
}
|
||||
break;
|
||||
default: cpu->csr.data[address] = value; break;
|
||||
};
|
||||
}
|
||||
@ -99,7 +113,7 @@ void write_csr_raw(cpu_t *cpu, uint address, uint value) {
|
||||
uint get_csr(cpu_t *cpu, uint address, ins_ret *ret) {
|
||||
if (has_csr_access_privilege(cpu, address)) {
|
||||
uint r = read_csr_raw(cpu, address);
|
||||
if (VERBOSE >= 2)
|
||||
if (VERBOSE >= 3)
|
||||
printf("CSR read @%03x = %08x\n", address, r);
|
||||
return r;
|
||||
} else {
|
||||
@ -111,8 +125,9 @@ uint get_csr(cpu_t *cpu, uint address, ins_ret *ret) {
|
||||
}
|
||||
|
||||
void set_csr(cpu_t *cpu, uint address, uint value, ins_ret *ret) {
|
||||
if (VERBOSE >= 2)
|
||||
if (VERBOSE >= 3)
|
||||
printf("CSR write @%03x = %08x\n", address, value);
|
||||
|
||||
if (has_csr_access_privilege(cpu, address)) {
|
||||
bool read_only = ((address >> 10) & 0x3) == 0x3;
|
||||
if (read_only) {
|
||||
@ -121,9 +136,9 @@ void set_csr(cpu_t *cpu, uint address, uint value, ins_ret *ret) {
|
||||
ret->trap.value = cpu->pc;
|
||||
} else {
|
||||
if (address == CSR_SATP) {
|
||||
// TODO: update MMU addressing mode
|
||||
if (VERBOSE >= 1)
|
||||
printf("WARN: Ignoring write to CSR_SATP (%08x)\n", value);
|
||||
printf("Write to CSR_SATP (%08x)\n", value);
|
||||
mmu_update(value);
|
||||
return;
|
||||
}
|
||||
write_csr_raw(cpu, address, value);
|
||||
|
138
src/emu.h
138
src/emu.h
@ -6,6 +6,7 @@
|
||||
#include "types.h"
|
||||
#include "ins.h"
|
||||
#include "mem.h"
|
||||
#include "mmu.h"
|
||||
#include "trap.h"
|
||||
#include "csr.h"
|
||||
|
||||
@ -32,52 +33,70 @@ 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]);
|
||||
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_WRITE);
|
||||
if (ret->trap.en) return;
|
||||
uint tmp = mem_get_word(cpu, addr);
|
||||
mem_set_word(cpu, addr, cpu->xreg[ins.rs2]);
|
||||
WR_RD(tmp)
|
||||
})
|
||||
DEF(amoadd_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);
|
||||
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_WRITE);
|
||||
if (ret->trap.en) return;
|
||||
uint tmp = mem_get_word(cpu, addr);
|
||||
mem_set_word(cpu, addr, 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);
|
||||
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_WRITE);
|
||||
if (ret->trap.en) return;
|
||||
uint tmp = mem_get_word(cpu, addr);
|
||||
mem_set_word(cpu, addr, cpu->xreg[ins.rs2] ^ tmp);
|
||||
WR_RD(tmp)
|
||||
})
|
||||
DEF(amoand_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);
|
||||
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_WRITE);
|
||||
if (ret->trap.en) return;
|
||||
uint tmp = mem_get_word(cpu, addr);
|
||||
mem_set_word(cpu, addr, cpu->xreg[ins.rs2] & tmp);
|
||||
WR_RD(tmp)
|
||||
})
|
||||
DEF(amoor_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);
|
||||
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_WRITE);
|
||||
if (ret->trap.en) return;
|
||||
uint tmp = mem_get_word(cpu, addr);
|
||||
mem_set_word(cpu, addr, cpu->xreg[ins.rs2] | tmp);
|
||||
WR_RD(tmp)
|
||||
})
|
||||
DEF(amomin_w, FormatR, { // rv32a
|
||||
uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1]);
|
||||
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_WRITE);
|
||||
if (ret->trap.en) return;
|
||||
uint tmp = mem_get_word(cpu, addr);
|
||||
uint sec = cpu->xreg[ins.rs2];
|
||||
mem_set_word(cpu, cpu->xreg[ins.rs1], AS_SIGNED(sec) < AS_SIGNED(tmp) ? sec : tmp);
|
||||
mem_set_word(cpu, addr, 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 addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_WRITE);
|
||||
if (ret->trap.en) return;
|
||||
uint tmp = mem_get_word(cpu, addr);
|
||||
uint sec = cpu->xreg[ins.rs2];
|
||||
mem_set_word(cpu, cpu->xreg[ins.rs1], AS_SIGNED(sec) > AS_SIGNED(tmp) ? sec : tmp);
|
||||
mem_set_word(cpu, addr, 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 addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_WRITE);
|
||||
if (ret->trap.en) return;
|
||||
uint tmp = mem_get_word(cpu, addr);
|
||||
uint sec = cpu->xreg[ins.rs2];
|
||||
mem_set_word(cpu, cpu->xreg[ins.rs1], sec < tmp ? sec : tmp);
|
||||
mem_set_word(cpu, addr, sec < tmp ? sec : tmp);
|
||||
WR_RD(tmp)
|
||||
})
|
||||
DEF(amomaxu_w, FormatR, { // rv32a
|
||||
uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1]);
|
||||
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_WRITE);
|
||||
if (ret->trap.en) return;
|
||||
uint tmp = mem_get_word(cpu, addr);
|
||||
uint sec = cpu->xreg[ins.rs2];
|
||||
mem_set_word(cpu, cpu->xreg[ins.rs1], sec > tmp ? sec : tmp);
|
||||
mem_set_word(cpu, addr, sec > tmp ? sec : tmp);
|
||||
WR_RD(tmp)
|
||||
})
|
||||
DEF(and, FormatR, { // rv32i
|
||||
@ -179,20 +198,23 @@ DEF(divu, FormatR, { // rv32m
|
||||
WR_RD(result)
|
||||
})
|
||||
DEF(ebreak, FormatEmpty, { // system
|
||||
// unnecessary?
|
||||
printf("EBREAK!\n");
|
||||
VERBOSE=4;
|
||||
SINGLE_STEP=1;
|
||||
})
|
||||
DEF(ecall, FormatEmpty, { // system
|
||||
if (cpu->xreg[17] == 93) {
|
||||
// EXIT CALL
|
||||
uint status = cpu->xreg[10] >> 1;
|
||||
printf("ecall EXIT = %d (0x%x)\n", status, status);
|
||||
exit(status);
|
||||
}
|
||||
/* if (cpu->xreg[17] == 93) { */
|
||||
/* // EXIT CALL */
|
||||
/* uint status = cpu->xreg[10] >> 1; */
|
||||
/* printf("ecall EXIT = %d (0x%x)\n", status, status); */
|
||||
/* exit(status); */
|
||||
/* } */
|
||||
|
||||
ret->trap.en = true;
|
||||
ret->trap.value = cpu->pc;
|
||||
if (cpu->csr.privilege == PRIV_USER) {
|
||||
ret->trap.type = trap_EnvironmentCallFromUMode;
|
||||
/* printf("SYSCALL! nr=%d @0x%08x\n", cpu->xreg[17], cpu->pc); */
|
||||
} else if (cpu->csr.privilege == PRIV_SUPERVISOR) {
|
||||
ret->trap.type = trap_EnvironmentCallFromSMode;
|
||||
} else { // PRIV_MACHINE
|
||||
@ -214,23 +236,32 @@ DEF(jalr, FormatI, { // rv32i
|
||||
WR_PC(cpu->xreg[ins.rs1] + ins.imm);
|
||||
})
|
||||
DEF(lb, FormatI, { // rv32i
|
||||
uint tmp = sign_extend(mem_get_byte(cpu, cpu->xreg[ins.rs1] + ins.imm), 8);
|
||||
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1] + ins.imm, MMU_ACCESS_READ);
|
||||
if (ret->trap.en) return;
|
||||
uint tmp = sign_extend(mem_get_byte(cpu, addr), 8);
|
||||
WR_RD(tmp)
|
||||
})
|
||||
DEF(lbu, FormatI, { // rv32i
|
||||
uint tmp = mem_get_byte(cpu, cpu->xreg[ins.rs1] + ins.imm);
|
||||
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1] + ins.imm, MMU_ACCESS_READ);
|
||||
if (ret->trap.en) return;
|
||||
uint tmp = mem_get_byte(cpu, addr);
|
||||
WR_RD(tmp)
|
||||
})
|
||||
DEF(lh, FormatI, { // rv32i
|
||||
uint tmp = sign_extend(mem_get_half_word(cpu, cpu->xreg[ins.rs1] + ins.imm), 16);
|
||||
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1] + ins.imm, MMU_ACCESS_READ);
|
||||
if (ret->trap.en) return;
|
||||
uint tmp = sign_extend(mem_get_half_word(cpu, addr), 16);
|
||||
WR_RD(tmp)
|
||||
})
|
||||
DEF(lhu, FormatI, { // rv32i
|
||||
uint tmp = mem_get_half_word(cpu, cpu->xreg[ins.rs1] + ins.imm);
|
||||
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1] + ins.imm, MMU_ACCESS_READ);
|
||||
if (ret->trap.en) return;
|
||||
uint tmp = mem_get_half_word(cpu, addr);
|
||||
WR_RD(tmp)
|
||||
})
|
||||
DEF(lr_w, FormatR, { // rv32a
|
||||
uint addr = cpu->xreg[ins.rs1];
|
||||
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_READ);
|
||||
if (ret->trap.en) return;
|
||||
uint tmp = mem_get_word(cpu, addr);
|
||||
cpu->reservation_en = true;
|
||||
cpu->reservation_addr = addr;
|
||||
@ -241,7 +272,9 @@ DEF(lui, FormatU, { // rv32i
|
||||
})
|
||||
DEF(lw, FormatI, { // rv32i
|
||||
// would need sign extend for xlen > 32
|
||||
uint tmp = mem_get_word(cpu, cpu->xreg[ins.rs1] + ins.imm);
|
||||
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1] + ins.imm, MMU_ACCESS_READ);
|
||||
if (ret->trap.en) return;
|
||||
uint tmp = mem_get_word(cpu, addr);
|
||||
WR_RD(tmp)
|
||||
})
|
||||
DEF(mret, FormatEmpty, { // system
|
||||
@ -305,11 +338,13 @@ DEF(remu, FormatR, { // rv32m
|
||||
WR_RD(result)
|
||||
})
|
||||
DEF(sb, FormatS, { // rv32i
|
||||
mem_set_byte(cpu, cpu->xreg[ins.rs1] + ins.imm, cpu->xreg[ins.rs2]);
|
||||
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1] + ins.imm, MMU_ACCESS_WRITE);
|
||||
if (ret->trap.en) return;
|
||||
mem_set_byte(cpu, addr, cpu->xreg[ins.rs2]);
|
||||
})
|
||||
DEF(sc_w, FormatR, { // rv32a
|
||||
// I'm pretty sure this is not it chief, but it does the trick for now
|
||||
uint addr = cpu->xreg[ins.rs1];
|
||||
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1], MMU_ACCESS_WRITE);
|
||||
if (cpu->reservation_en && cpu->reservation_addr == addr) {
|
||||
mem_set_word(cpu, addr, cpu->xreg[ins.rs2]);
|
||||
cpu->reservation_en = false;
|
||||
@ -322,7 +357,9 @@ DEF(sfence_vma, FormatEmpty, { // system
|
||||
// skip
|
||||
})
|
||||
DEF(sh, FormatS, { // rv32i
|
||||
mem_set_half_word(cpu, cpu->xreg[ins.rs1] + ins.imm, cpu->xreg[ins.rs2]);
|
||||
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1] + ins.imm, MMU_ACCESS_WRITE);
|
||||
if (ret->trap.en) return;
|
||||
mem_set_half_word(cpu, addr, cpu->xreg[ins.rs2]);
|
||||
})
|
||||
DEF(sll, FormatR, { // rv32i
|
||||
WR_RD(cpu->xreg[ins.rs1] << cpu->xreg[ins.rs2])
|
||||
@ -394,7 +431,9 @@ DEF(sub, FormatR, { // rv32i
|
||||
WR_RD(AS_SIGNED(cpu->xreg[ins.rs1]) - AS_SIGNED(cpu->xreg[ins.rs2]));
|
||||
})
|
||||
DEF(sw, FormatS, { // rv32i
|
||||
mem_set_word(cpu, cpu->xreg[ins.rs1] + ins.imm, cpu->xreg[ins.rs2]);
|
||||
uint addr = mmu_translate(ret, cpu->xreg[ins.rs1] + ins.imm, MMU_ACCESS_WRITE);
|
||||
if (ret->trap.en) return;
|
||||
mem_set_word(cpu, addr, cpu->xreg[ins.rs2]);
|
||||
})
|
||||
DEF(uret, FormatEmpty, { // system
|
||||
// unnecessary?
|
||||
@ -536,6 +575,11 @@ ins_ret ins_select(cpu_t *cpu, uint ins_word) {
|
||||
|
||||
if (VERBOSE >= 1)
|
||||
printf("Invalid instruction: %08x\n", ins_word);
|
||||
|
||||
printf("INVALID INS\n");
|
||||
SINGLE_STEP = 1;
|
||||
VERBOSE = 4;
|
||||
|
||||
ret.trap.en = true;
|
||||
ret.trap.type = trap_IllegalInstruction;
|
||||
ret.trap.value = ins_word;
|
||||
@ -545,20 +589,22 @@ ins_ret ins_select(cpu_t *cpu, uint ins_word) {
|
||||
|
||||
void emulate(cpu_t *cpu) {
|
||||
uint ins_word = 0;
|
||||
ins_ret ret;
|
||||
ins_ret ret = ins_ret_noop(cpu);
|
||||
if ((cpu->pc & 0x3) == 0) {
|
||||
ins_word = mem_get_word(cpu, cpu->pc);
|
||||
ret = ins_select(cpu, ins_word);
|
||||
uint ins_addr = mmu_translate(&ret, cpu->pc, MMU_ACCESS_FETCH);
|
||||
if (!ret.trap.en) {
|
||||
ins_word = mem_get_word(cpu, ins_addr);
|
||||
ret = ins_select(cpu, ins_word);
|
||||
|
||||
if (ret.csr_write && !ret.trap.en) {
|
||||
set_csr(cpu, ret.csr_write, ret.csr_val, &ret);
|
||||
}
|
||||
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;
|
||||
if (!ret.trap.en && ret.write_reg < 32 && ret.write_reg > 0) {
|
||||
cpu->xreg[ret.write_reg] = ret.write_val;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret = ins_ret_noop(cpu);
|
||||
ret.trap.en = true;
|
||||
ret.trap.type = trap_InstructionAddressMisaligned;
|
||||
ret.trap.value = cpu->pc;
|
||||
@ -572,8 +618,10 @@ void emulate(cpu_t *cpu) {
|
||||
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))) {
|
||||
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;
|
||||
if (VERBOSE >= 1)
|
||||
printf("timer irq: %d >= %d\n", cpu->clint.mtime_lo, cpu->clint.mtimecmp_lo);
|
||||
}
|
||||
|
||||
uart_tick(cpu);
|
||||
|
129
src/main.c
129
src/main.c
@ -7,13 +7,42 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include "types.h"
|
||||
#include "cpu.h"
|
||||
#include "uart.h"
|
||||
#include <termios.h>
|
||||
|
||||
#include "../elfy/elfy.h"
|
||||
|
||||
const int MEM_SIZE = 1024 * 1024 * 128;
|
||||
struct termios t, t_orig;
|
||||
void buf_off(void)
|
||||
{
|
||||
tcgetattr(STDIN_FILENO, &t);
|
||||
t_orig = t;
|
||||
t.c_lflag &= ~(ICANON | ECHO);
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &t);
|
||||
}
|
||||
|
||||
void buf_on(void)
|
||||
{
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &t_orig);
|
||||
}
|
||||
|
||||
static uint tracebuf[64];
|
||||
static uint tracebuf_idx = 0;
|
||||
|
||||
void print_tracebuf() {
|
||||
if (cmd_buf == NULL) {
|
||||
cmd_buf = malloc(4096);
|
||||
}
|
||||
for (uint i = tracebuf_idx + 1; i != tracebuf_idx; i = (i + 1) % (sizeof(tracebuf) / sizeof(uint))) {
|
||||
printf("BACKTRACE: 0x %08x\n", tracebuf[i]);
|
||||
fflush(stdout);
|
||||
/* sprintf(cmd_buf, "addr2line -e linux/vmlinux %x", tracebuf[i]); */
|
||||
/* system(cmd_buf); */
|
||||
}
|
||||
}
|
||||
|
||||
size_t get_filesize(const char* filename) {
|
||||
struct stat st;
|
||||
@ -29,17 +58,27 @@ uint8_t* get_mmap_ptr(const char* filename) {
|
||||
}
|
||||
|
||||
void usage() {
|
||||
printf("Usage: rvc (-e <ELF binary>|-b <raw binary>) [-d <device tree binary>] [-v (0|1|2|3)] [-s]\n");
|
||||
printf("Usage: rvc (-e <ELF binary>|-b <raw binary>) [-d <device tree binary>] [-i <initramfs>] [-v (0|1|2|3|4)] [-s] [-t]\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static cpu_t cpu;
|
||||
|
||||
void term(int signum)
|
||||
{
|
||||
printf("\n\nCaught signal!\n");
|
||||
buf_on();
|
||||
cpu_dump(&cpu);
|
||||
printf("\n");
|
||||
|
||||
/* ins_ret ret; */
|
||||
/* for (uint i = 0x10000; i < 0x11000; i += 4) { */
|
||||
/* uint pa = mmu_translate(&ret, i, MMU_ACCESS_READ); */
|
||||
/* uint val = mem_get_word(&cpu, pa); */
|
||||
/* printf("%05x: %08x\n", i, val); */
|
||||
/* } */
|
||||
|
||||
print_tracebuf();
|
||||
|
||||
/* printf("\n"); */
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@ -47,10 +86,13 @@ int main(int argc, char *argv[]) {
|
||||
char *elf = NULL;
|
||||
char *bin = NULL;
|
||||
char *dtb = NULL;
|
||||
char *initrd = NULL;
|
||||
uint mtd_size = 0;
|
||||
|
||||
int c;
|
||||
bool trace = false;
|
||||
|
||||
while ((c = getopt (argc, argv, "e:b:d:v:s")) != -1) {
|
||||
while ((c = getopt (argc, argv, "e:b:d:v:i:st")) != -1) {
|
||||
switch (c)
|
||||
{
|
||||
case 'e':
|
||||
@ -68,6 +110,12 @@ int main(int argc, char *argv[]) {
|
||||
case 's':
|
||||
SINGLE_STEP = 1;
|
||||
break;
|
||||
case 't':
|
||||
trace = true;
|
||||
break;
|
||||
case 'i':
|
||||
initrd = optarg;
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
usage();
|
||||
@ -88,6 +136,14 @@ int main(int argc, char *argv[]) {
|
||||
memcpy(mem, bin_ptr, get_filesize(bin));
|
||||
}
|
||||
|
||||
uint8_t *mtd = NULL;
|
||||
if (initrd) {
|
||||
mtd = malloc(MEM_SIZE);
|
||||
uint8_t *mtd_ptr = get_mmap_ptr(initrd);
|
||||
mtd_size = get_filesize(initrd);
|
||||
memcpy(mtd, mtd_ptr, mtd_size);
|
||||
}
|
||||
|
||||
struct sigaction action;
|
||||
memset(&action, 0, sizeof(action));
|
||||
action.sa_handler = term;
|
||||
@ -96,7 +152,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
fcntl(0, F_SETFL, O_NONBLOCK);
|
||||
|
||||
cpu = cpu_init(mem, dtb ? get_mmap_ptr(dtb) : NULL);
|
||||
cpu = cpu_init(mem, dtb ? get_mmap_ptr(dtb) : NULL, mtd, mtd_size);
|
||||
|
||||
if (VERBOSE >= 1)
|
||||
printf("CPU initialized!\n");
|
||||
@ -104,16 +160,65 @@ int main(int argc, char *argv[]) {
|
||||
if (VERBOSE >= 3)
|
||||
cpu_dump(&cpu);
|
||||
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
buf_off();
|
||||
|
||||
// LIMITER
|
||||
const unsigned long restr = 200000*1000;
|
||||
unsigned long cur = 0;
|
||||
unsigned long t = time(NULL);
|
||||
|
||||
uint init_verbose = VERBOSE;
|
||||
|
||||
while (1) {
|
||||
cur++;
|
||||
unsigned long t2 = time(NULL);
|
||||
if (t != t2) {
|
||||
cur = 0;
|
||||
t = t2;
|
||||
}
|
||||
if (cur >= restr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cpu_tick(&cpu);
|
||||
|
||||
if (VERBOSE >= 3)
|
||||
if (trace)
|
||||
printf("TRACE: PC=0x%08x\n", cpu.pc);
|
||||
|
||||
tracebuf[tracebuf_idx++] = cpu.pc;
|
||||
if (tracebuf_idx >= (sizeof(tracebuf) / sizeof(uint))) {
|
||||
tracebuf_idx = 0;
|
||||
}
|
||||
|
||||
/* if (cpu.clock > 5000000 && cpu.clock % 100000 == 0) { */
|
||||
/* uint arb[8]; */
|
||||
/* for (int iarb = 0; iarb < 8; iarb++) { */
|
||||
/* uint arb_t = cpu.xreg[iarb*4+0] ^ */
|
||||
/* cpu.xreg[iarb*4+1] ^ */
|
||||
/* cpu.xreg[iarb*4+2] ^ */
|
||||
/* cpu.xreg[iarb*4+3]; */
|
||||
/* arb[iarb] = arb_t; */
|
||||
/* } */
|
||||
/* uint arb_f = arb[0] ^ arb[1] ^ arb[2] ^ arb[3] ^ arb[4] ^ arb[5] ^ arb[6] ^ arb[7] ^ cpu.pc; */
|
||||
/* printf("else if (cpu.clock == %d && arb != 0x%08x) ARB_FAIL\n", cpu.clock, arb_f); */
|
||||
/* } */
|
||||
|
||||
if (cpu.pc == 0x80000118) {
|
||||
/* if (cpu.clock == 6350505) { // divergence: 6350908 */
|
||||
/* if (cpu.clock == 6400000) { */
|
||||
printf("BREAKPOINT HIT\n");
|
||||
SINGLE_STEP = 1;
|
||||
VERBOSE = 4;
|
||||
}
|
||||
|
||||
if (VERBOSE >= 4)
|
||||
cpu_dump(&cpu);
|
||||
|
||||
/* char input = 0; */
|
||||
/* if (!SINGLE_STEP && !uart_input_value && read(0, &input, 1)) { */
|
||||
/* uart_input_value = input; */
|
||||
/* } */
|
||||
char input = 0;
|
||||
if (!SINGLE_STEP && !uart_input_value && read(0, &input, 1)) {
|
||||
uart_input_value = input;
|
||||
}
|
||||
|
||||
if (SINGLE_STEP) {
|
||||
fflush(stdout);
|
||||
@ -122,7 +227,7 @@ int main(int argc, char *argv[]) {
|
||||
gc: ch = getchar();
|
||||
switch (ch) {
|
||||
case '\n': break;
|
||||
case 'c': SINGLE_STEP = false; break;
|
||||
case 'c': SINGLE_STEP = false; VERBOSE = init_verbose; break;
|
||||
default: goto gc;
|
||||
}
|
||||
}
|
||||
|
76
src/mem.h
76
src/mem.h
@ -6,10 +6,10 @@
|
||||
#include "types.h"
|
||||
#include "uart.h"
|
||||
|
||||
const int MEM_SIZE = 1024 * 1024 * 64 - 2048 * 128;
|
||||
|
||||
// little endian, zero extended
|
||||
uint mem_get_byte(cpu_t *cpu, uint addr) {
|
||||
if (VERBOSE >= 3)
|
||||
printf("mem_get_byte(%08x)\n", addr);
|
||||
uint do_mem_get_byte(cpu_t *cpu, uint addr) {
|
||||
|
||||
if (cpu->dtb != NULL && addr >= 0x1020 && addr <= 0x1fff) {
|
||||
if (VERBOSE >= 2)
|
||||
@ -17,6 +17,10 @@ uint mem_get_byte(cpu_t *cpu, uint addr) {
|
||||
return cpu->dtb[addr - 0x1020];
|
||||
}
|
||||
|
||||
/* if (cpu->mtd != NULL && addr >= 0x40000000 && addr < (0x40000000 + MEM_SIZE)) { */
|
||||
/* return cpu->mtd[addr - 0x40000000]; */
|
||||
/* } */
|
||||
|
||||
switch (addr) {
|
||||
// CLINT
|
||||
case 0x02000000: return cpu->clint.msip ? 1 : 0;
|
||||
@ -59,20 +63,54 @@ uint mem_get_byte(cpu_t *cpu, uint addr) {
|
||||
case 0x10000004: return UART_GET2(MCR);
|
||||
case 0x10000005: return UART_GET2(LSR);
|
||||
case 0x10000007: return UART_GET2(SCR);
|
||||
|
||||
/* case 0x80001000: return 0; */
|
||||
/* case 0x80001001: return 0; */
|
||||
/* case 0x80001002: return 0; */
|
||||
/* case 0x80001003: return 0; */
|
||||
/* case 0x80001004: return 0; */
|
||||
/* case 0x80001005: return 0; */
|
||||
/* case 0x80001006: return 0; */
|
||||
/* case 0x80001007: return 0; */
|
||||
/* case 0x80001040: return 0; */
|
||||
/* case 0x80001041: return 0; */
|
||||
/* case 0x80001042: return 0; */
|
||||
/* case 0x80001043: return 0; */
|
||||
/* case 0x80001044: return 0; */
|
||||
/* case 0x80001045: return 0; */
|
||||
/* case 0x80001046: return 0; */
|
||||
/* case 0x80001047: return 0; */
|
||||
}
|
||||
|
||||
if ((addr & 0x80000000) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return cpu->mem[addr & 0x7FFFFFFF];
|
||||
addr = addr & 0x7FFFFFFF;
|
||||
if (addr >= MEM_SIZE) {
|
||||
if (VERBOSE >= 1) {
|
||||
printf("WARN: out-of-bounds memory read @0x%08x\n", addr | 0x80000000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return cpu->mem[addr];
|
||||
}
|
||||
|
||||
uint mem_get_byte(cpu_t *cpu, uint addr) {
|
||||
uint ret = do_mem_get_byte(cpu, addr);
|
||||
if (VERBOSE >= 3)
|
||||
printf("mem_get_byte(%08x) = 0x%08x\n", addr, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint mem_get_half_word(cpu_t *cpu, uint addr) {
|
||||
/* assert((addr & ~(0x1)) == addr); */
|
||||
return mem_get_byte(cpu, addr) | ((uint16_t)mem_get_byte(cpu, addr + 1) << 8);
|
||||
}
|
||||
|
||||
uint mem_get_word(cpu_t *cpu, uint addr) {
|
||||
/* assert((addr & ~(0x3)) == addr); */
|
||||
return mem_get_byte(cpu, addr) |
|
||||
((uint16_t)mem_get_byte(cpu, addr + 1) << 8) |
|
||||
((uint16_t)mem_get_byte(cpu, addr + 2) << 16) |
|
||||
@ -131,21 +169,49 @@ void mem_set_byte(cpu_t *cpu, uint addr, uint val) {
|
||||
case 0x10000003: UART_SET2(LCR, val); return;
|
||||
case 0x10000004: UART_SET2(MCR, val); return;
|
||||
case 0x10000007: UART_SET2(SCR, val); return;
|
||||
|
||||
/* case 0x80001000: printf("%c", (unsigned char)val); return; */
|
||||
/* case 0x80001001: return; */
|
||||
/* case 0x80001002: return; */
|
||||
/* case 0x80001003: return; */
|
||||
/* case 0x80001004: return; */
|
||||
/* case 0x80001005: return; */
|
||||
/* case 0x80001006: return; */
|
||||
/* case 0x80001007: return; */
|
||||
/* case 0x80001040: return; */
|
||||
/* case 0x80001041: return; */
|
||||
/* case 0x80001042: return; */
|
||||
/* case 0x80001043: return; */
|
||||
/* case 0x80001044: return; */
|
||||
/* case 0x80001045: return; */
|
||||
/* case 0x80001046: return; */
|
||||
/* case 0x80001047: return; */
|
||||
}
|
||||
|
||||
if ((addr & 0x80000000) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
cpu->mem[addr & 0x7FFFFFFF] = val;
|
||||
const int MEM_SIZE = 1024 * 1024 * 128;
|
||||
|
||||
addr = addr & 0x7FFFFFFF;
|
||||
if (addr >= MEM_SIZE) {
|
||||
if (VERBOSE >= 1) {
|
||||
printf("WARN: out-of-bounds memory write @0x%08x\n", addr | 0x80000000);
|
||||
}
|
||||
return;
|
||||
}
|
||||
cpu->mem[addr] = val;
|
||||
}
|
||||
|
||||
void mem_set_half_word(cpu_t *cpu, uint addr, uint val) {
|
||||
/* assert((addr & ~(0x1)) == addr); */
|
||||
mem_set_byte(cpu, addr, val & 0xFF);
|
||||
mem_set_byte(cpu, addr + 1, (val >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
void mem_set_word(cpu_t *cpu, uint addr, uint val) {
|
||||
/* assert((addr & ~(0x3)) == addr); */
|
||||
mem_set_byte(cpu, addr, val & 0xFF);
|
||||
mem_set_byte(cpu, addr + 1, (val >> 8) & 0xFF);
|
||||
mem_set_byte(cpu, addr + 2, (val >> 16) & 0xFF);
|
||||
|
195
src/mmu.h
Normal file
195
src/mmu.h
Normal file
@ -0,0 +1,195 @@
|
||||
#ifndef MMU_H
|
||||
#define MMU_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "types.h"
|
||||
|
||||
void mmu_update(uint satp) {
|
||||
cpu.mmu.mode = satp >> 31;
|
||||
cpu.mmu.ppn = satp & 0x7fffffff;
|
||||
}
|
||||
|
||||
#include "mem.h"
|
||||
#include "trap.h"
|
||||
|
||||
#define MMU_MODE_OFF 0
|
||||
#define MMU_MODE_SV32 1
|
||||
|
||||
#define MMU_ACCESS_FETCH 0
|
||||
#define MMU_ACCESS_READ 1
|
||||
#define MMU_ACCESS_WRITE 2
|
||||
|
||||
#define PAGESIZE 4096
|
||||
#define PTESIZE 4
|
||||
|
||||
#define ADDR_PART_OFFSET(x) ((x >> 0) & 0xfff)
|
||||
#define ADDR_PART_PN0(x) ((x >> 12) & 0x3ff)
|
||||
#define ADDR_PART_PN1(x) ((x >> 22) & 0xfff)
|
||||
|
||||
typedef struct {
|
||||
bool v, r, w, x, u, g, a, d;
|
||||
uint rsw;
|
||||
uint ppn0;
|
||||
uint ppn1;
|
||||
} mmu_page;
|
||||
|
||||
uint get_trap_type(uint mode) {
|
||||
return mode == MMU_ACCESS_FETCH ? trap_InstructionPageFault :
|
||||
(mode == MMU_ACCESS_READ ? trap_LoadPageFault : trap_StorePageFault);
|
||||
}
|
||||
|
||||
uint get_effective_privilege(uint *sum, uint *mxr) {
|
||||
uint mstatus = read_csr_raw(&cpu, CSR_MSTATUS);
|
||||
*sum = (mstatus >> 18) & 0x1;
|
||||
*mxr = (mstatus >> 19) & 0x1;
|
||||
if ((mstatus >> 17) & 0x1) {
|
||||
// TODO: Check if this shouldn't be 9
|
||||
return (mstatus >> 11) & 0x3;
|
||||
}
|
||||
return cpu.csr.privilege;
|
||||
}
|
||||
|
||||
mmu_page load_page(uint addr) {
|
||||
uint data = mem_get_word(&cpu, addr);
|
||||
mmu_page ret;
|
||||
#define BOOL(name, bit) ret.name = (data >> bit) & 0x1;
|
||||
BOOL(v, 0)
|
||||
BOOL(r, 1)
|
||||
BOOL(w, 2)
|
||||
BOOL(x, 3)
|
||||
BOOL(u, 4)
|
||||
BOOL(g, 5)
|
||||
BOOL(a, 6)
|
||||
BOOL(d, 7)
|
||||
#undef BOOL
|
||||
ret.rsw = (data >> 8) & 0x3;
|
||||
ret.ppn0 = (data >> 10) & 0x3ff;
|
||||
ret.ppn1 = (data >> 20) & 0xfff;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *print_page_buf = NULL;
|
||||
char *print_page(mmu_page page) {
|
||||
if (print_page_buf == NULL) {
|
||||
print_page_buf = (char*)malloc(4096);
|
||||
}
|
||||
|
||||
memset(print_page_buf, 0, 4096);
|
||||
|
||||
sprintf(print_page_buf, "v=%d r=%d w=%d x=%d u=%d g=%d a=%d d=%d rsw=0x%02x ppn[0]=0x%03x ppn[1]=0x%03x", page.v, page.r, page.w, page.x, page.u, page.g, page.a, page.d, page.rsw, page.ppn0, page.ppn1);
|
||||
|
||||
return print_page_buf;
|
||||
}
|
||||
|
||||
uint mmu_translate(ins_ret *ins, uint addr, uint mode) {
|
||||
if (cpu.mmu.mode == MMU_MODE_OFF) {
|
||||
return addr;
|
||||
}
|
||||
|
||||
#define FAULT \
|
||||
ins->trap.en = true; \
|
||||
ins->trap.type = get_trap_type(mode); \
|
||||
ins->trap.value = addr; \
|
||||
return 0;
|
||||
|
||||
uint sum, mxr;
|
||||
uint priv = get_effective_privilege(&sum, &mxr);
|
||||
|
||||
// machine mode fetch will always use physical addresses, otherwise 'mxr'
|
||||
// defines if paging will be used
|
||||
if (priv == PRIV_MACHINE || (cpu.csr.privilege == PRIV_MACHINE && mode == MMU_ACCESS_FETCH)) {
|
||||
return addr;
|
||||
}
|
||||
|
||||
mmu_page page;
|
||||
bool super;
|
||||
|
||||
/* mmu_page page = load_page(cpu.mmu.ppn * PAGESIZE + ADDR_PART_PN1(addr) * PTESIZE); */
|
||||
/* bool super = true; */
|
||||
|
||||
/* if (!page.v || (!page.r && page.w)) { */
|
||||
/* if (VERBOSE >= 2) */
|
||||
/* printf("page fault (invalid root): addr=0x%08x (%s)\n", addr, print_page(page)); */
|
||||
/* FAULT */
|
||||
/* } */
|
||||
|
||||
/* if (!page.r && !page.x) { */
|
||||
/* // non-leaf PTE, we need to dig deeper */
|
||||
/* page = load_page((page.ppn0 | (page.ppn1 << 10)) * PAGESIZE + ADDR_PART_PN0(addr) * PTESIZE); */
|
||||
|
||||
/* if (!page.v || (!page.r && page.w) || (!page.r && !page.x)) { */
|
||||
/* if (VERBOSE >= 2) */
|
||||
/* printf("page fault (invalid PTE): addr=0x%08x (%s)\n", addr, print_page(page)); */
|
||||
/* FAULT */
|
||||
/* } */
|
||||
|
||||
/* super = false; */
|
||||
/* } */
|
||||
for (uint im = 0; im < 2; im++) {
|
||||
uint page_addr = im == 0 ?
|
||||
(cpu.mmu.ppn * PAGESIZE + ADDR_PART_PN1(addr) * PTESIZE) :
|
||||
((page.ppn0 | (page.ppn1 << 10)) * PAGESIZE + ADDR_PART_PN0(addr) * PTESIZE);
|
||||
page = load_page(page_addr);
|
||||
super = im == 0;
|
||||
|
||||
if (!page.v || (!page.r && page.w)) {
|
||||
/* printf("page fault (invalid): addr=0x%08x (%s, %d)\n", addr, print_page(page), im); */
|
||||
FAULT
|
||||
}
|
||||
|
||||
if (page.r || page.x) {
|
||||
break;
|
||||
} else if (im == 1) {
|
||||
// non-leaf page at bottom level
|
||||
/* printf("page fault (non-leaf): addr=0x%08x (%s, %d)\n", addr, print_page(page), im); */
|
||||
FAULT
|
||||
}
|
||||
}
|
||||
|
||||
// PTE has been found, permission check
|
||||
bool perm =
|
||||
priv == PRIV_MACHINE || // machine can read everything
|
||||
(priv == PRIV_USER && page.u) || // this is a user page
|
||||
(priv == PRIV_SUPERVISOR && (!page.u || sum)); // supervisor page or SUM
|
||||
bool access =
|
||||
(mode == MMU_ACCESS_FETCH && page.x) ||
|
||||
(mode == MMU_ACCESS_READ && (page.r || (page.x && mxr))) ||
|
||||
(mode == MMU_ACCESS_WRITE && page.w);
|
||||
bool allowed = perm && access;
|
||||
|
||||
if (!allowed) {
|
||||
if (VERBOSE >= 1)
|
||||
printf("page fault (perm/access): addr=0x%08x perm=%d access=%d (%s)\n", addr, perm, access, print_page(page));
|
||||
FAULT
|
||||
}
|
||||
|
||||
if (super && page.ppn0 != 0) {
|
||||
if (VERBOSE >= 1)
|
||||
printf("page fault (misaligned super page): addr=0x%08x (%s)\n", addr, print_page(page));
|
||||
FAULT
|
||||
}
|
||||
|
||||
if (!page.a || (mode == MMU_ACCESS_WRITE && !page.d)) {
|
||||
if (VERBOSE >= 2)
|
||||
printf("page fault (a/d not set): addr=0x%08x (%s)\n", addr, print_page(page));
|
||||
FAULT
|
||||
}
|
||||
|
||||
// translation success
|
||||
uint pa = ADDR_PART_OFFSET(addr);
|
||||
pa |= super ? ADDR_PART_PN0(addr) << 12 : page.ppn0 << 12;
|
||||
pa |= page.ppn1 << 22;
|
||||
|
||||
/* const int MEM_SIZE = 1024 * 1024 * 128; */
|
||||
/* if (!(pa & 0x80000000) || (pa & 0x7fffffff) >= MEM_SIZE) { */
|
||||
/* FAULT */
|
||||
/* } */
|
||||
|
||||
if (VERBOSE >= 3)
|
||||
printf("MMU translated (mode=%d): 0x%08x -> 0x%08x (super=%d) (%s)\n", mode, addr, pa, super, print_page(page));
|
||||
|
||||
return pa;
|
||||
}
|
||||
|
||||
#endif
|
14
src/trap.h
14
src/trap.h
@ -120,7 +120,7 @@ bool handle_trap(cpu_t *cpu, ins_ret *ret, bool is_interrupt) {
|
||||
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_epc_addr, is_interrupt ? ret->pc_val : 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);
|
||||
@ -133,16 +133,16 @@ bool handle_trap(cpu_t *cpu, ins_ret *ret, bool is_interrupt) {
|
||||
// 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);
|
||||
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);
|
||||
uint sie = (sstatus >> 1) & 1;
|
||||
uint new_status = (sstatus & ~0x122) | (sie << 5) | ((current_privilege & 1) << 8);
|
||||
write_csr_raw(cpu, CSR_SSTATUS, new_status);
|
||||
}
|
||||
|
||||
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);
|
||||
if (VERBOSE >= (is_interrupt ? 1 : 2))
|
||||
printf("trap: type=%08x value=%08x (IRQ: %d) moved PC from @%08x (#%d) to @%08x (#%d)\n", t.type, t.value, is_interrupt, cpu->pc, current_privilege, ret->pc_val, new_privilege);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -172,6 +172,8 @@ void handle_irq_and_trap(cpu_t *cpu, ins_ret *ret) {
|
||||
if (handled && irq) {
|
||||
// reset MIP value since IRQ was handled
|
||||
write_csr_raw(cpu, CSR_MIP, cur_mip & ~mip_reset);
|
||||
if (VERBOSE >= 3)
|
||||
printf("IRQ handled: old_mip=%03x new_mip=%03x\n", cur_mip, read_csr_raw(cpu, CSR_MIP));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
10
src/types.h
10
src/types.h
@ -31,15 +31,23 @@ typedef struct {
|
||||
uint mtime_hi;
|
||||
} clint_state;
|
||||
|
||||
typedef struct {
|
||||
uint mode;
|
||||
uint ppn;
|
||||
} mmu_state;
|
||||
|
||||
typedef struct {
|
||||
uint clock;
|
||||
uint xreg[32];
|
||||
uint pc;
|
||||
uint8_t *mem;
|
||||
uint8_t *dtb;
|
||||
uint8_t *mtd;
|
||||
uint mtd_size;
|
||||
csr_state csr;
|
||||
clint_state clint;
|
||||
uart_state uart;
|
||||
mmu_state mmu;
|
||||
|
||||
bool reservation_en;
|
||||
uint reservation_addr;
|
||||
@ -73,4 +81,6 @@ uint sign_extend(uint x, uint b) {
|
||||
return (x ^ m) - m;
|
||||
}
|
||||
|
||||
static cpu_t cpu;
|
||||
|
||||
#endif
|
||||
|
10
src/uart.h
10
src/uart.h
@ -40,10 +40,12 @@ void uart_update_iir(cpu_t *cpu) {
|
||||
void uart_tick(cpu_t *cpu) {
|
||||
bool rx_ip = false;
|
||||
|
||||
if ((cpu->clock % 0x38400) == 0 && UART_GET1(RBR) == 0) {
|
||||
if ((cpu->clock % 0x1000) == 0 && UART_GET1(RBR) == 0) {
|
||||
uint value = uart_input_value; // TODO: Add actual input logic
|
||||
uart_input_value = 0;
|
||||
if (value == '\n') value = '\r'; // This is necessary I think?
|
||||
if (value != 0) {
|
||||
/* fprintf(stderr, "I'%d'", value); */
|
||||
UART_SET1(RBR, value);
|
||||
UART_SET2(LSR, (UART_GET2(LSR) | LSR_DATA_AVAILABLE));
|
||||
uart_update_iir(cpu);
|
||||
@ -54,8 +56,8 @@ void uart_tick(cpu_t *cpu) {
|
||||
}
|
||||
|
||||
uint thr = UART_GET1(THR);
|
||||
if ((cpu->clock & 0x16) == 0 && thr != 0) {
|
||||
printf("%c", (char)thr);
|
||||
if ((cpu->clock % 0x16) == 0 && thr != 0) {
|
||||
printf(" %d", (unsigned char)thr);
|
||||
fflush(stdout);
|
||||
UART_SET1(THR, 0);
|
||||
UART_SET2(LSR, (UART_GET2(LSR) | LSR_THR_EMPTY));
|
||||
@ -65,7 +67,7 @@ void uart_tick(cpu_t *cpu) {
|
||||
}
|
||||
}
|
||||
|
||||
if (cpu->uart.thre_ip || rx_ip) {
|
||||
if (!cpu->uart.interrupting && (cpu->uart.thre_ip || rx_ip)) {
|
||||
cpu->uart.interrupting = true;
|
||||
cpu->uart.thre_ip = false;
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user