Add basic PMP implementation to rvcpp. Seems like the RWX vs XWR order might be transposed in both the hardware and the tests
This commit is contained in:
parent
117c52e7b1
commit
fce1c087d4
|
@ -59,8 +59,10 @@ struct RVCore {
|
|||
};
|
||||
|
||||
// Functions to read/write memory from this hart's point of view
|
||||
std::optional<uint8_t> r8(ux_t addr) {
|
||||
if (addr >= ram_base && addr < ram_top) {
|
||||
std::optional<uint8_t> r8(ux_t addr, uint permissions=0x1u) {
|
||||
if (!(csr.get_pmp_xwr(addr) & permissions)) {
|
||||
return {};
|
||||
} else if (addr >= ram_base && addr < ram_top) {
|
||||
return ram[(addr - ram_base) >> 2] >> 8 * (addr & 0x3) & 0xffu;
|
||||
} else {
|
||||
return mem.r8(addr);
|
||||
|
@ -68,7 +70,9 @@ struct RVCore {
|
|||
}
|
||||
|
||||
bool w8(ux_t addr, uint8_t data) {
|
||||
if (addr >= ram_base && addr < ram_top) {
|
||||
if (!(csr.get_pmp_xwr(addr) & 0x2u)) {
|
||||
return false;
|
||||
} else if (addr >= ram_base && addr < ram_top) {
|
||||
ram[(addr - ram_base) >> 2] &= ~(0xffu << 8 * (addr & 0x3));
|
||||
ram[(addr - ram_base) >> 2] |= (uint32_t)data << 8 * (addr & 0x3);
|
||||
return true;
|
||||
|
@ -77,8 +81,10 @@ struct RVCore {
|
|||
}
|
||||
}
|
||||
|
||||
std::optional<uint16_t> r16(ux_t addr) {
|
||||
if (addr >= ram_base && addr < ram_top) {
|
||||
std::optional<uint16_t> r16(ux_t addr, uint permissions=0x1u) {
|
||||
if (!(csr.get_pmp_xwr(addr) & permissions)) {
|
||||
return {};
|
||||
} else if (addr >= ram_base && addr < ram_top) {
|
||||
return ram[(addr - ram_base) >> 2] >> 8 * (addr & 0x2) & 0xffffu;
|
||||
} else {
|
||||
return mem.r16(addr);
|
||||
|
@ -86,7 +92,9 @@ struct RVCore {
|
|||
}
|
||||
|
||||
bool w16(ux_t addr, uint16_t data) {
|
||||
if (addr >= ram_base && addr < ram_top) {
|
||||
if (!(csr.get_pmp_xwr(addr) & 0x2u)) {
|
||||
return false;
|
||||
} else if (addr >= ram_base && addr < ram_top) {
|
||||
ram[(addr - ram_base) >> 2] &= ~(0xffffu << 8 * (addr & 0x2));
|
||||
ram[(addr - ram_base) >> 2] |= (uint32_t)data << 8 * (addr & 0x2);
|
||||
return true;
|
||||
|
@ -95,8 +103,10 @@ struct RVCore {
|
|||
}
|
||||
}
|
||||
|
||||
std::optional<uint32_t> r32(ux_t addr) {
|
||||
if (addr >= ram_base && addr < ram_top) {
|
||||
std::optional<uint32_t> r32(ux_t addr, uint permissions=0x1u) {
|
||||
if (!(csr.get_pmp_xwr(addr) & permissions)) {
|
||||
return {};
|
||||
} else if (addr >= ram_base && addr < ram_top) {
|
||||
return ram[(addr - ram_base) >> 2];
|
||||
} else {
|
||||
return mem.r32(addr);
|
||||
|
@ -104,7 +114,9 @@ struct RVCore {
|
|||
}
|
||||
|
||||
bool w32(ux_t addr, uint32_t data) {
|
||||
if (addr >= ram_base && addr < ram_top) {
|
||||
if (!(csr.get_pmp_xwr(addr) & 0x2u)) {
|
||||
return false;
|
||||
} else if (addr >= ram_base && addr < ram_top) {
|
||||
ram[(addr - ram_base) >> 2] = data;
|
||||
return true;
|
||||
} else {
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
class RVCSR {
|
||||
|
||||
static const int PMP_REGIONS = 16;
|
||||
|
||||
// Latched IRQ signals into core
|
||||
bool irq_t;
|
||||
bool irq_s;
|
||||
|
@ -25,6 +27,9 @@ class RVCSR {
|
|||
ux_t mepc;
|
||||
ux_t mcause;
|
||||
|
||||
ux_t pmpaddr[PMP_REGIONS];
|
||||
ux_t pmpcfg[PMP_REGIONS / 4];
|
||||
|
||||
std::optional<ux_t> pending_write_addr;
|
||||
ux_t pending_write_data;
|
||||
|
||||
|
@ -33,6 +38,21 @@ class RVCSR {
|
|||
// Internal interface for updating trap state. Returns trap target pc.
|
||||
ux_t trap_enter(uint xcause, ux_t xepc);
|
||||
|
||||
ux_t pmpcfg_a(int i) {
|
||||
uint8_t cfg_bits = pmpcfg[i / 4] >> 8 * (i % 4);
|
||||
return (cfg_bits >> 3) & 0x3u;
|
||||
}
|
||||
|
||||
ux_t pmpcfg_xwr(int i) {
|
||||
uint8_t cfg_bits = pmpcfg[i / 4] >> 8 * (i % 4);
|
||||
return cfg_bits & 0x7u;
|
||||
}
|
||||
|
||||
ux_t pmpcfg_l(int i) {
|
||||
uint8_t cfg_bits = pmpcfg[i / 4] >> 8 * (i % 4);
|
||||
return (cfg_bits >> 7) & 0x1u;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
enum {
|
||||
|
@ -59,6 +79,12 @@ public:
|
|||
mepc = 0;
|
||||
mcause = 0;
|
||||
pending_write_addr = {};
|
||||
for (int i = 0; i < PMP_REGIONS; ++i) {
|
||||
pmpaddr[i] = 0;
|
||||
}
|
||||
for (int i = 0; i < PMP_REGIONS / 4; ++i) {
|
||||
pmpcfg[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void step();
|
||||
|
@ -101,4 +127,5 @@ public:
|
|||
return mcause;
|
||||
}
|
||||
|
||||
uint get_pmp_xwr(ux_t addr);
|
||||
};
|
||||
|
|
|
@ -135,8 +135,8 @@ void RVCore::step(bool trace) {
|
|||
std::optional<ux_t> trace_csr_result;
|
||||
std::optional<uint> trace_priv;
|
||||
|
||||
std::optional<uint16_t> fetch0 = r16(pc);
|
||||
std::optional<uint16_t> fetch1 = r16(pc + 2);
|
||||
std::optional<uint16_t> fetch0 = r16(pc, 0x4u);
|
||||
std::optional<uint16_t> fetch1 = r16(pc + 2, 0x4u);
|
||||
uint32_t instr = *fetch0 | ((uint32_t)*fetch1 << 16);
|
||||
|
||||
uint opc = instr >> 2 & 0x1f;
|
||||
|
|
|
@ -52,6 +52,29 @@ void RVCSR::step() {
|
|||
case CSR_MINSTRET: minstret = pending_write_data; break;
|
||||
case CSR_MINSTRETH: minstreth = pending_write_data; break;
|
||||
case CSR_MCOUNTINHIBIT: mcountinhibit = pending_write_data & 0x7u; break;
|
||||
|
||||
case CSR_PMPCFG0: pmpcfg[0] = pending_write_data & 0x9f9f9f9f; break;
|
||||
case CSR_PMPCFG1: pmpcfg[1] = pending_write_data & 0x9f9f9f9f; break;
|
||||
case CSR_PMPCFG2: pmpcfg[2] = pending_write_data & 0x9f9f9f9f; break;
|
||||
case CSR_PMPCFG3: pmpcfg[3] = pending_write_data & 0x9f9f9f9f; break;
|
||||
|
||||
case CSR_PMPADDR0: pmpaddr[0] = pending_write_data; break;
|
||||
case CSR_PMPADDR1: pmpaddr[1] = pending_write_data; break;
|
||||
case CSR_PMPADDR2: pmpaddr[2] = pending_write_data; break;
|
||||
case CSR_PMPADDR3: pmpaddr[3] = pending_write_data; break;
|
||||
case CSR_PMPADDR4: pmpaddr[4] = pending_write_data; break;
|
||||
case CSR_PMPADDR5: pmpaddr[5] = pending_write_data; break;
|
||||
case CSR_PMPADDR6: pmpaddr[6] = pending_write_data; break;
|
||||
case CSR_PMPADDR7: pmpaddr[7] = pending_write_data; break;
|
||||
case CSR_PMPADDR8: pmpaddr[8] = pending_write_data; break;
|
||||
case CSR_PMPADDR9: pmpaddr[9] = pending_write_data; break;
|
||||
case CSR_PMPADDR10: pmpaddr[10] = pending_write_data; break;
|
||||
case CSR_PMPADDR11: pmpaddr[11] = pending_write_data; break;
|
||||
case CSR_PMPADDR12: pmpaddr[12] = pending_write_data; break;
|
||||
case CSR_PMPADDR13: pmpaddr[13] = pending_write_data; break;
|
||||
case CSR_PMPADDR14: pmpaddr[14] = pending_write_data; break;
|
||||
case CSR_PMPADDR15: pmpaddr[15] = pending_write_data; break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
pending_write_addr = {};
|
||||
|
@ -87,6 +110,28 @@ std::optional<ux_t> RVCSR::read(uint16_t addr, bool side_effect) {
|
|||
case CSR_MINSTRET: return minstret;
|
||||
case CSR_MINSTRETH: return minstreth;
|
||||
|
||||
case CSR_PMPCFG0: return pmpcfg[0];
|
||||
case CSR_PMPCFG1: return pmpcfg[1];
|
||||
case CSR_PMPCFG2: return pmpcfg[2];
|
||||
case CSR_PMPCFG3: return pmpcfg[3];
|
||||
|
||||
case CSR_PMPADDR0: return pmpaddr[0];
|
||||
case CSR_PMPADDR1: return pmpaddr[1];
|
||||
case CSR_PMPADDR2: return pmpaddr[2];
|
||||
case CSR_PMPADDR3: return pmpaddr[3];
|
||||
case CSR_PMPADDR4: return pmpaddr[4];
|
||||
case CSR_PMPADDR5: return pmpaddr[5];
|
||||
case CSR_PMPADDR6: return pmpaddr[6];
|
||||
case CSR_PMPADDR7: return pmpaddr[7];
|
||||
case CSR_PMPADDR8: return pmpaddr[8];
|
||||
case CSR_PMPADDR9: return pmpaddr[9];
|
||||
case CSR_PMPADDR10: return pmpaddr[10];
|
||||
case CSR_PMPADDR11: return pmpaddr[11];
|
||||
case CSR_PMPADDR12: return pmpaddr[12];
|
||||
case CSR_PMPADDR13: return pmpaddr[13];
|
||||
case CSR_PMPADDR14: return pmpaddr[14];
|
||||
case CSR_PMPADDR15: return pmpaddr[15];
|
||||
|
||||
default: return {};
|
||||
}
|
||||
}
|
||||
|
@ -129,6 +174,29 @@ bool RVCSR::write(uint16_t addr, ux_t data, uint op) {
|
|||
case CSR_MINSTRET: break;
|
||||
case CSR_MINSTRETH: break;
|
||||
case CSR_MCOUNTINHIBIT: break;
|
||||
|
||||
case CSR_PMPCFG0: break;
|
||||
case CSR_PMPCFG1: break;
|
||||
case CSR_PMPCFG2: break;
|
||||
case CSR_PMPCFG3: break;
|
||||
|
||||
case CSR_PMPADDR0: break;
|
||||
case CSR_PMPADDR1: break;
|
||||
case CSR_PMPADDR2: break;
|
||||
case CSR_PMPADDR3: break;
|
||||
case CSR_PMPADDR4: break;
|
||||
case CSR_PMPADDR5: break;
|
||||
case CSR_PMPADDR6: break;
|
||||
case CSR_PMPADDR7: break;
|
||||
case CSR_PMPADDR8: break;
|
||||
case CSR_PMPADDR9: break;
|
||||
case CSR_PMPADDR10: break;
|
||||
case CSR_PMPADDR11: break;
|
||||
case CSR_PMPADDR12: break;
|
||||
case CSR_PMPADDR13: break;
|
||||
case CSR_PMPADDR14: break;
|
||||
case CSR_PMPADDR15: break;
|
||||
|
||||
default: return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -182,3 +250,36 @@ ux_t RVCSR::trap_mret() {
|
|||
|
||||
return mepc;
|
||||
}
|
||||
|
||||
uint RVCSR::get_pmp_xwr(ux_t addr) {
|
||||
bool match = false;
|
||||
uint matching_xwr = 0;
|
||||
uint matching_l = 0;
|
||||
bool trace_pmp = get_true_priv() == PRV_U && true;
|
||||
for (int i = 0; i < PMP_REGIONS; ++i) {
|
||||
if (pmpcfg_a(i) == 0u) {
|
||||
continue;
|
||||
}
|
||||
uint32_t mask = 0xffffffffu;
|
||||
if (pmpcfg_a(i) == 2) {
|
||||
mask = 0xfffffffeu << __builtin_ctz(~pmpaddr[i]);
|
||||
}
|
||||
match = ((addr >> 2) & mask) == (pmpaddr[i] & mask);
|
||||
if (match) {
|
||||
matching_xwr = pmpcfg_xwr(i);
|
||||
matching_l = pmpcfg_l(i);
|
||||
// Lowest-numbered match determines success/failure:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
// TODO MPRV
|
||||
if (get_true_priv() == PRV_M && !matching_l) {
|
||||
return 0x7u;
|
||||
} else {
|
||||
return matching_xwr;
|
||||
}
|
||||
} else {
|
||||
return get_true_priv() == PRV_M ? 0x7u : 0x0u;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue