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:
Luke Wren 2024-04-27 13:38:10 +01:00
parent 117c52e7b1
commit fce1c087d4
4 changed files with 151 additions and 11 deletions

View File

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

View File

@ -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);
};

View File

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

View File

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