cnlohr / mini-rv32ima

A tiny C header-only risc-v emulator.
MIT License
1.69k stars 137 forks source link

fix control memory address calculations #22

Closed xvuko closed 1 year ago

xvuko commented 1 year ago

Note that this was working correctly only with default RAM offset. In uint32 field (x - 2^31 - 2^31) is equal x.

cnlohr commented 1 year ago

Interesting. Have you verified this with other RAM offsets?

xvuko commented 1 year ago

Interesting. Have you verified this with other RAM offsets?

Yes. This is minimal example:

#include <stdint.h>
#include <stddef.h>
#include <stdio.h>

#define MINIRV32_DECORATE
#define MINIRV32_IMPLEMENTATION

static uint32_t ram_offset = 0x10000000;
#define MINIRV32_RAM_IMAGE_OFFSET (ram_offset)
#define MINI_RV32_RAM_SIZE (16u)
#include <mini-rv32ima.h>

#define R_A0 10
#define R_A1 11

void do_write(uint32_t addr, uint32_t value) {
    uint32_t image[MINI_RV32_RAM_SIZE / 4] = {
        // sw  a1, 0(a0)
        0x00b52023,
    };

    struct MiniRV32IMAState state = {
        .pc = MINIRV32_RAM_IMAGE_OFFSET,
        .regs = {
            [R_A0] = addr,
            [R_A1] = value,
        }
    };

    printf("pc: 0x%08x wr_value: 0x%08x wr_addr: 0x%08x\n", state.pc, value, addr);
    int ret = MiniRV32IMAStep(&state, (uint8_t *)image, 0, 0, 1);
    printf("mcause: 0x%08x mval: 0x%08x ret: 0x%08x\n", state.mcause, state.mtval, ret);
}

void check_offset(uint32_t ram_off) {
    ram_offset = ram_off;

    printf("write to valid address 0x11100000 (SYSCON)\n");
    do_write(0x11100000, 0xdeadbeef);

    printf("\n");
    printf("write to invalid address: 0x13000000\n");
    do_write(0x13000000, 0xdeadbeef);
}

int main(int argc, char **argv) {
    check_offset(0x80000000);
    printf("\n");
    check_offset(0x40000000);
}

Without my changes this returns:

write to valid address 0x11100000 (SYSCON)
pc: 0x80000000 wr_value: 0xdeadbeef wr_addr: 0x11100000
mcause: 0x00000000 mval: 0x00000000 ret: 0xdeadbeef

write to invalid address: 0x13000000
pc: 0x80000000 wr_value: 0xdeadbeef wr_addr: 0x13000000
mcause: 0x00000007 mval: 0x93000000 ret: 0x00000000

write to valid address 0x11100000 (SYSCON)
pc: 0x40000000 wr_value: 0xdeadbeef wr_addr: 0x11100000
mcause: 0x00000007 mval: 0xd1100000 ret: 0x00000000

write to invalid address: 0x13000000
pc: 0x40000000 wr_value: 0xdeadbeef wr_addr: 0x13000000
mcause: 0x00000007 mval: 0xd3000000 ret: 0x00000000

With ram offset changed to 0x40000000 SYSCON write fails; In both ram offsets mtval is not set to address that caused access fault: 0x13000000

After applying changes:

write to valid address 0x11100000 (SYSCON)
pc: 0x80000000 wr_value: 0xdeadbeef wr_addr: 0x11100000
mcause: 0x00000000 mval: 0x00000000 ret: 0xdeadbeef

write to invalid address: 0x13000000
pc: 0x80000000 wr_value: 0xdeadbeef wr_addr: 0x13000000
mcause: 0x00000007 mval: 0x13000000 ret: 0x00000000

write to valid address 0x11100000 (SYSCON)
pc: 0x40000000 wr_value: 0xdeadbeef wr_addr: 0x11100000
mcause: 0x00000000 mval: 0x00000000 ret: 0xdeadbeef

write to invalid address: 0x13000000
pc: 0x40000000 wr_value: 0xdeadbeef wr_addr: 0x13000000
mcause: 0x00000007 mval: 0x13000000 ret: 0x00000000

SYSCON write succeds in both ram offsets mtval is set to 0x13000000

cnlohr commented 1 year ago

Excellent.