diff --git a/Makefile b/Makefile index a21e7816..24980d4c 100644 --- a/Makefile +++ b/Makefile @@ -53,7 +53,7 @@ OBJS := \ plic.o \ uart.o \ main.o \ - clint.o \ + aclint.o \ $(OBJS_EXTRA) deps := $(OBJS:%.o=.%.o.d) diff --git a/aclint.c b/aclint.c new file mode 100644 index 00000000..2be7d46e --- /dev/null +++ b/aclint.c @@ -0,0 +1,218 @@ +#include +#include "device.h" +#include "riscv.h" +#include "riscv_private.h" + +/* ACLINT MTIMER */ +void aclint_mtimer_update_interrupts(hart_t *hart, mtimer_state_t *mtimer) +{ + if (semu_timer_get(&mtimer->mtime) >= mtimer->mtimecmp[hart->mhartid]) + hart->sip |= RV_INT_STI_BIT; /* Set Supervisor Timer Interrupt */ + else + hart->sip &= ~RV_INT_STI_BIT; /* Clear Supervisor Timer Interrupt */ +} + +static bool aclint_mtimer_reg_read(mtimer_state_t *mtimer, + uint32_t addr, + uint32_t *value) +{ + /* 'addr & 0x4' is used to determine the upper or lower 32 bits + * of the mtimecmp register. If 'addr & 0x4' is 0, then the lower 32 + * bits are accessed. + * + * 'addr >> 3' is used to get the index of the mtimecmp array. In + * "ACLINT MTIMER Compare Register Map", each mtimecmp register is 8 + * bytes long. So, we need to divide the address by 8 to get the index. + */ + + /* mtimecmp (0x4300000 ~ 0x4307FF8) */ + if (addr < 0x7FF8) { + *value = + (uint32_t) (mtimer->mtimecmp[addr >> 3] >> (addr & 0x4 ? 32 : 0)); + return true; + } + + /* mtime (0x4307FF8 ~ 0x4308000) */ + if (addr < 0x8000) { + *value = (uint32_t) (semu_timer_get(&mtimer->mtime) >> + (addr & 0x4 ? 32 : 0)); + return true; + } + return false; +} + +static bool aclint_mtimer_reg_write(mtimer_state_t *mtimer, + uint32_t addr, + uint32_t value) +{ + /* The 'cmp_val & 0xFFFFFFFF' is used to select the upper 32 bits + * of mtimer->mtimecmp[addr >> 3], then shift the value to the left by + * 32 bits to set the upper 32 bits. + * + * Similarly, 'cmp_val & 0xFFFFFFFF00000000ULL' is used to select the lower + * 32 bits of mtimer->mtimecmp[addr >> 3]. + */ + + /* mtimecmp (0x4300000 ~ 0x4307FF8) */ + if (addr < 0x7FF8) { + uint64_t cmp_val = mtimer->mtimecmp[addr >> 3]; + + if (addr & 0x4) + cmp_val = (cmp_val & 0xFFFFFFFF) | ((uint64_t) value << 32); + else + cmp_val = (cmp_val & 0xFFFFFFFF00000000ULL) | value; + + mtimer->mtimecmp[addr >> 3] = cmp_val; + return true; + } + + /* mtime (0x4307FF8 ~ 0x4308000) */ + if (addr < 0x8000) { + uint64_t mtime_val = mtimer->mtime.begin; + if (addr & 0x4) + mtime_val = (mtime_val & 0xFFFFFFFF) | ((uint64_t) value << 32); + else + mtime_val = (mtime_val & 0xFFFFFFFF00000000ULL) | value; + + semu_timer_rebase(&mtimer->mtime, mtime_val); + return true; + } + + return false; +} + +void aclint_mtimer_read(hart_t *hart, + mtimer_state_t *mtimer, + uint32_t addr, + uint8_t width, + uint32_t *value) +{ + if (!aclint_mtimer_reg_read(mtimer, addr, value)) + vm_set_exception(hart, RV_EXC_LOAD_FAULT, hart->exc_val); + + *value >>= RV_MEM_SW - width; +} + +void aclint_mtimer_write(hart_t *hart, + mtimer_state_t *mtimer, + uint32_t addr, + uint8_t width, + uint32_t value) +{ + if (!aclint_mtimer_reg_write(mtimer, addr, value << (RV_MEM_SW - width))) + vm_set_exception(hart, RV_EXC_STORE_FAULT, hart->exc_val); +} + +/* ACLINT MSWI */ +void aclint_mswi_update_interrupts(hart_t *hart, mswi_state_t *mswi) +{ + if (mswi->msip[hart->mhartid]) + hart->sip |= RV_INT_SSI_BIT; /* Set Machine Software Interrupt */ + else + hart->sip &= ~RV_INT_SSI_BIT; /* Clear Machine Software Interrupt */ +} + +static bool aclint_mswi_reg_read(mswi_state_t *mswi, + uint32_t addr, + uint32_t *value) +{ + /* 'msip' is an array where each entry corresponds to a Hart, + * each entry is 4 bytes (32 bits). So, we need to divide the address + * by 4 to get the index. + */ + + /* Address range for msip: 0x4400000 ~ 0x4404000 */ + if (addr < 0x4000) { + *value = mswi->msip[addr >> 2]; + return true; + } + return false; +} + +static bool aclint_mswi_reg_write(mswi_state_t *mswi, + uint32_t addr, + uint32_t value) +{ + if (addr < 0x4000) { + mswi->msip[addr >> 2] = value & 0x1; /* Only the LSB is valid */ + return true; + } + return false; +} + +void aclint_mswi_read(hart_t *hart, + mswi_state_t *mswi, + uint32_t addr, + uint8_t width, + uint32_t *value) +{ + if (!aclint_mswi_reg_read(mswi, addr, value)) + vm_set_exception(hart, RV_EXC_LOAD_FAULT, hart->exc_val); + + *value >>= RV_MEM_SW - width; +} + +void aclint_mswi_write(hart_t *hart, + mswi_state_t *mswi, + uint32_t addr, + uint8_t width, + uint32_t value) +{ + if (!aclint_mswi_reg_write(mswi, addr, value << (RV_MEM_SW - width))) + vm_set_exception(hart, RV_EXC_STORE_FAULT, hart->exc_val); +} + +/* ACLINT SSWI */ +void aclint_sswi_update_interrupts(hart_t *hart, sswi_state_t *sswi) +{ + if (sswi->ssip[hart->mhartid]) + hart->sip |= RV_INT_SSI_BIT; /* Set Supervisor Software Interrupt */ + else + hart->sip &= ~RV_INT_SSI_BIT; /* Clear Supervisor Software Interrupt */ +} + +static bool aclint_sswi_reg_read(__attribute__((unused)) sswi_state_t *sswi, + uint32_t addr, + uint32_t *value) +{ + /* Address range for ssip: 0x4500000 ~ 0x4504000 */ + if (addr < 0x4000) { + *value = 0; /* Upper 31 bits are zero, and LSB reads as 0 */ + return true; + } + return false; +} + +static bool aclint_sswi_reg_write(sswi_state_t *sswi, + uint32_t addr, + uint32_t value) +{ + if (addr < 0x4000) { + sswi->ssip[addr >> 2] = value & 0x1; /* Only the LSB is valid */ + + return true; + } + return false; +} + +void aclint_sswi_read(hart_t *hart, + sswi_state_t *sswi, + uint32_t addr, + uint8_t width, + uint32_t *value) +{ + if (!aclint_sswi_reg_read(sswi, addr, value)) + vm_set_exception(hart, RV_EXC_LOAD_FAULT, hart->exc_val); + + *value >>= RV_MEM_SW - width; +} + +void aclint_sswi_write(hart_t *hart, + sswi_state_t *sswi, + uint32_t addr, + uint8_t width, + uint32_t value) +{ + if (!aclint_sswi_reg_write(sswi, addr, value << (RV_MEM_SW - width))) + vm_set_exception(hart, RV_EXC_STORE_FAULT, hart->exc_val); +} diff --git a/clint.c b/clint.c deleted file mode 100644 index fd3c8e9c..00000000 --- a/clint.c +++ /dev/null @@ -1,96 +0,0 @@ -#include -#include "device.h" -#include "riscv.h" -#include "riscv_private.h" - -void clint_update_interrupts(hart_t *hart, clint_state_t *clint) -{ - uint64_t time_delta = - clint->mtimecmp[hart->mhartid] - semu_timer_get(&hart->time); - if ((int64_t) time_delta <= 0) - hart->sip |= RV_INT_STI_BIT; - else - hart->sip &= ~RV_INT_STI_BIT; - - if (clint->msip[hart->mhartid]) { - hart->sip |= RV_INT_SSI_BIT; - } else - hart->sip &= ~RV_INT_SSI_BIT; -} - -static bool clint_reg_read(clint_state_t *clint, uint32_t addr, uint32_t *value) -{ - if (addr < 0x4000) { - *value = clint->msip[addr >> 2]; - return true; - } - - if (addr < 0xBFF8) { - addr -= 0x4000; - *value = - (uint32_t) (clint->mtimecmp[addr >> 3] >> (32 & -!!(addr & 0b100))); - return true; - } - - if (addr < 0xBFFF) { - *value = (uint32_t) (semu_timer_get(&clint->mtime) >> - (32 & -!!(addr & 0b100))); - return true; - } - return false; -} - -static bool clint_reg_write(clint_state_t *clint, uint32_t addr, uint32_t value) -{ - if (addr < 0x4000) { - clint->msip[addr >> 2] = value; - return true; - } - - if (addr < 0xBFF8) { - addr -= 0x4000; - int32_t upper = clint->mtimecmp[addr >> 3] >> 32; - int32_t lowwer = clint->mtimecmp[addr >> 3]; - if (addr & 0b100) - upper = value; - else - lowwer = value; - - clint->mtimecmp[addr >> 3] = (uint64_t) upper << 32 | lowwer; - return true; - } - - if (addr < 0xBFFF) { - int32_t upper = clint->mtime.begin >> 32; - int32_t lower = clint->mtime.begin; - if (addr & 0b100) - upper = value; - else - lower = value; - - semu_timer_rebase(&clint->mtime, (uint64_t) upper << 32 | lower); - return true; - } - return false; -} - -void clint_read(hart_t *vm, - clint_state_t *clint, - uint32_t addr, - uint8_t width, - uint32_t *value) -{ - if (!clint_reg_read(clint, addr, value)) - vm_set_exception(vm, RV_EXC_LOAD_FAULT, vm->exc_val); - *value = (*value) >> (RV_MEM_SW - width); -} - -void clint_write(hart_t *vm, - clint_state_t *clint, - uint32_t addr, - uint8_t width, - uint32_t value) -{ - if (!clint_reg_write(clint, addr, value >> (RV_MEM_SW - width))) - vm_set_exception(vm, RV_EXC_STORE_FAULT, vm->exc_val); -} diff --git a/device.h b/device.h index 489cae6c..4610f45b 100644 --- a/device.h +++ b/device.h @@ -172,27 +172,95 @@ void virtio_blk_write(hart_t *vm, uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file); #endif /* SEMU_HAS(VIRTIOBLK) */ -/* clint */ +/* ACLINT MTIMER */ typedef struct { - uint32_t msip[4096]; - uint64_t mtimecmp[4095]; + /* A MTIMER device has two separate base addresses: one for the MTIME + * register and another for the MTIMECMP registers. + * + * The MTIME register is a 64-bit read-write register that contains the + * number of cycles counted based on a fixed reference frequency. + * + * The MTIMECMP registers are 'per-HART' 64-bit read-write registers. It + * contains the MTIME register value at which machine-level timer interrupt + * is to be triggered for the corresponding HART. + * + * Up to 4095 MTIMECMP registers can exist, corresponding to 4095 HARTs in + * the system. + * + * For more details, please refer to the register map at: + * https://github.com/riscv/riscv-aclint/blob/main/riscv-aclint.adoc#21-register-map + */ + uint64_t *mtimecmp; semu_timer_t mtime; -} clint_state_t; +} mtimer_state_t; -void clint_update_interrupts(hart_t *vm, clint_state_t *clint); -void clint_read(hart_t *vm, - clint_state_t *clint, - uint32_t addr, - uint8_t width, - uint32_t *value); -void clint_write(hart_t *vm, - clint_state_t *clint, - uint32_t addr, - uint8_t width, - uint32_t value); +void aclint_mtimer_update_interrupts(hart_t *hart, mtimer_state_t *mtimer); +void aclint_mtimer_read(hart_t *hart, + mtimer_state_t *mtimer, + uint32_t addr, + uint8_t width, + uint32_t *value); +void aclint_mtimer_write(hart_t *hart, + mtimer_state_t *mtimer, + uint32_t addr, + uint8_t width, + uint32_t value); -/* memory mapping */ +/* ACLINT MSWI */ +typedef struct { + /* The MSWI device provides machine-level IPI functionality for a set of + * HARTs on a RISC-V platform. It has an IPI register (MSIP) for each HART + * connected to the MSWI device. + * + * Up to 4095 MSIP registers can be used, corresponding to 4095 HARTs in the + * system. The 4096th MSIP register is reserved for future use. + * + * For more details, please refer to the register map at: + * https://github.com/riscv/riscv-aclint/blob/main/riscv-aclint.adoc#31-register-map + */ + uint32_t *msip; +} mswi_state_t; +void aclint_mswi_update_interrupts(hart_t *hart, mswi_state_t *mswi); +void aclint_mswi_read(hart_t *hart, + mswi_state_t *mswi, + uint32_t addr, + uint8_t width, + uint32_t *value); +void aclint_mswi_write(hart_t *hart, + mswi_state_t *mswi, + uint32_t addr, + uint8_t width, + uint32_t value); + +/* ACLINT SSWI */ +typedef struct { + /* The SSWI device provides supervisor-level IPI functionality for a set of + * HARTs on a RISC-V platform. It provides a register to set an IPI + * (SETSSIP) for each HART connected to the SSWI device. + * + * Up to 4095 SETSSIP registers can be used, corresponding to 4095 HARTs in + * the system. The 4096th SETSSIP register is reserved for future use. + * + * For more details, please refer to the register map at: + * https://github.com/riscv/riscv-aclint/blob/main/riscv-aclint.adoc#41-register-map + */ + uint32_t *ssip; +} sswi_state_t; + +void aclint_sswi_update_interrupts(hart_t *hart, sswi_state_t *sswi); +void aclint_sswi_read(hart_t *hart, + sswi_state_t *sswi, + uint32_t addr, + uint8_t width, + uint32_t *value); +void aclint_sswi_write(hart_t *hart, + sswi_state_t *sswi, + uint32_t addr, + uint8_t width, + uint32_t value); + +/* memory mapping */ typedef struct { bool stopped; uint32_t *ram; @@ -205,5 +273,8 @@ typedef struct { #if SEMU_HAS(VIRTIOBLK) virtio_blk_state_t vblk; #endif - clint_state_t clint; + /* ACLINT */ + mtimer_state_t mtimer; + mswi_state_t mswi; + sswi_state_t sswi; } emu_state_t; diff --git a/main.c b/main.c index 39935f89..c6e81cb9 100644 --- a/main.c +++ b/main.c @@ -77,8 +77,15 @@ static void emu_update_timer_interrupt(hart_t *hart) emu_state_t *data = PRIV(hart); /* Sync global timer with local timer */ - hart->time = data->clint.mtime; - clint_update_interrupts(hart, &data->clint); + hart->time = data->mtimer.mtime; + aclint_mtimer_update_interrupts(hart, &data->mtimer); +} + +static void emu_update_swi_interrupt(hart_t *hart) +{ + emu_state_t *data = PRIV(hart); + aclint_mswi_update_interrupts(hart, &data->mswi); + aclint_sswi_update_interrupts(hart, &data->sswi); } static void mem_load(hart_t *hart, @@ -117,9 +124,18 @@ static void mem_load(hart_t *hart, emu_update_vblk_interrupts(hart->vm); return; #endif - case 0x43: /* clint */ - clint_read(hart, &data->clint, addr & 0xFFFFF, width, value); - clint_update_interrupts(hart, &data->clint); + case 0x43: /* mtimer */ + aclint_mtimer_read(hart, &data->mtimer, addr & 0xFFFFF, width, + value); + aclint_mtimer_update_interrupts(hart, &data->mtimer); + return; + case 0x44: /* mswi */ + aclint_mswi_read(hart, &data->mswi, addr & 0xFFFFF, width, value); + aclint_mswi_update_interrupts(hart, &data->mswi); + return; + case 0x45: /* sswi */ + aclint_sswi_read(hart, &data->sswi, addr & 0xFFFFF, width, value); + aclint_sswi_update_interrupts(hart, &data->sswi); return; } } @@ -162,9 +178,18 @@ static void mem_store(hart_t *hart, emu_update_vblk_interrupts(hart->vm); return; #endif - case 0x43: /* clint */ - clint_write(hart, &data->clint, addr & 0xFFFFF, width, value); - clint_update_interrupts(hart, &data->clint); + case 0x43: /* mtimer */ + aclint_mtimer_write(hart, &data->mtimer, addr & 0xFFFFF, width, + value); + aclint_mtimer_update_interrupts(hart, &data->mtimer); + return; + case 0x44: /* mswi */ + aclint_mswi_write(hart, &data->mswi, addr & 0xFFFFF, width, value); + aclint_mswi_update_interrupts(hart, &data->mswi); + return; + case 0x45: /* sswi */ + aclint_sswi_write(hart, &data->sswi, addr & 0xFFFFF, width, value); + aclint_sswi_update_interrupts(hart, &data->sswi); return; } } @@ -185,7 +210,7 @@ static inline sbi_ret_t handle_sbi_ecall_TIMER(hart_t *hart, int32_t fid) emu_state_t *data = PRIV(hart); switch (fid) { case SBI_TIMER__SET_TIMER: - data->clint.mtimecmp[hart->mhartid] = + data->mtimer.mtimecmp[hart->mhartid] = (((uint64_t) hart->x_regs[RV_R_A1]) << 32) | (uint64_t) (hart->x_regs[RV_R_A0]); hart->sip &= ~RV_INT_STI_BIT; @@ -261,13 +286,11 @@ static inline sbi_ret_t handle_sbi_ecall_IPI(hart_t *hart, int32_t fid) hart_mask = (uint64_t) hart->x_regs[RV_R_A0]; hart_mask_base = (uint64_t) hart->x_regs[RV_R_A1]; if (hart_mask_base == 0xFFFFFFFFFFFFFFFF) { - for (uint32_t i = 0; i < hart->vm->n_hart; i++) { - data->clint.msip[i] = 1; - } + for (uint32_t i = 0; i < hart->vm->n_hart; i++) + data->sswi.ssip[i] = 1; } else { - for (int i = hart_mask_base; hart_mask; hart_mask >>= 1, i++) { - data->clint.msip[i] = hart_mask & 1; - } + for (int i = hart_mask_base; hart_mask; hart_mask >>= 1, i++) + data->sswi.ssip[i] = hart_mask & 1; } return (sbi_ret_t){SBI_SUCCESS, 0}; @@ -279,7 +302,12 @@ static inline sbi_ret_t handle_sbi_ecall_IPI(hart_t *hart, int32_t fid) static inline sbi_ret_t handle_sbi_ecall_RFENCE(hart_t *hart, int32_t fid) { - /* TODO: RFENCE SBI extension */ + /* TODO: Since the current implementation sequentially emulates + * multi-core execution, the implementation of RFENCE extension is not + * complete, for example, FENCE.I is currently ignored. To support + * multi-threaded system emulation, RFENCE extension has to be implemented + * completely. + */ uint64_t hart_mask, hart_mask_base; switch (fid) { case 0: @@ -526,7 +554,6 @@ static int semu_start(int argc, char **argv) /* Initialize the emulator */ emu_state_t emu; memset(&emu, 0, sizeof(emu)); - semu_timer_init(&emu.clint.mtime, CLOCK_FREQ); /* Set up RAM */ emu.ram = mmap(NULL, RAM_SIZE, PROT_READ | PROT_WRITE, @@ -591,10 +618,18 @@ static int semu_start(int argc, char **argv) emu.vblk.ram = emu.ram; emu.disk = virtio_blk_init(&(emu.vblk), disk_file); #endif + /* Set up ACLINT */ + semu_timer_init(&emu.mtimer.mtime, CLOCK_FREQ); + emu.mtimer.mtimecmp = calloc(vm.n_hart, sizeof(uint64_t)); + emu.mswi.msip = calloc(vm.n_hart, sizeof(uint32_t)); + emu.sswi.ssip = calloc(vm.n_hart, sizeof(uint32_t)); /* Emulate */ uint32_t peripheral_update_ctr = 0; while (!emu.stopped) { + /* TODO: Add support for multi-threaded system emulation after the + * RFENCE extension is completely implemented. + */ for (uint32_t i = 0; i < vm.n_hart; i++) { if (peripheral_update_ctr-- == 0) { peripheral_update_ctr = 64; @@ -616,6 +651,7 @@ static int semu_start(int argc, char **argv) } emu_update_timer_interrupt(vm.hart[i]); + emu_update_swi_interrupt(vm.hart[i]); vm_step(vm.hart[i]); if (likely(!vm.hart[i]->error)) diff --git a/riscv.c b/riscv.c index 069aeeb7..c3fd394a 100644 --- a/riscv.c +++ b/riscv.c @@ -805,7 +805,7 @@ void vm_step(hart_t *vm) uint8_t idx = ilog2(applicable); if (idx == 1) { emu_state_t *data = PRIV(vm); - data->clint.msip[vm->mhartid] = 0; + data->sswi.ssip[vm->mhartid] = 0; } vm->exc_cause = (1U << 31) | idx; vm->stval = 0; diff --git a/scripts/gen-hart-dts.py b/scripts/gen-hart-dts.py index 22df971d..03cebbc1 100644 --- a/scripts/gen-hart-dts.py +++ b/scripts/gen-hart-dts.py @@ -27,14 +27,26 @@ def plic_irq_format(nums): for i in range(nums): s += f"<&cpu{i}_intc 9>, " return s[:-2] - -def clint_irq_format(nums): + +def sswi_irq_format(nums): + s = "" + for i in range(nums): + s += f"<&cpu{i}_intc 1>, " # 1 is the SSWI interrupt number (Supervisor Software Interrupt) + return s[:-2] + +def mswi_irq_format(nums): + s = "" + for i in range(nums): + s += f"<&cpu{i}_intc 3>, " # 3 is the MSWI interrupt number (Machine Software Interrupt) + return s[:-2] + +def mtimer_irq_format(nums): s = "" for i in range(nums): - s += f"<&cpu{i}_intc 3 &cpu{i}_intc 7>, " + s += f"<&cpu{i}_intc 7>, " # 7 is the MTIMER interrupt number (Machine Timer Interrupt) return s[:-2] -def dtsi_template (cpu_list: str, plic_list, clint_list, clock_freq): +def dtsi_template (cpu_list: str, plic_list, sswi_list, mtimer_list, mswi_list, clock_freq): return f"""/{{ cpus {{ #address-cells = <1>; @@ -54,11 +66,28 @@ def dtsi_template (cpu_list: str, plic_list, clint_list, clock_freq): riscv,ndev = <31>; }}; - clint0: clint@4300000 {{ - compatible = "riscv,clint0"; - interrupts-extended = - {clint_list}; - reg = <0x4300000 0x10000>; + sswi0: sswi@4500000 {{ + #interrupt-cells = <0>; + #address-cells = <0>; + interrupt-controller; + interrupts-extended = {sswi_list}; + reg = <0x4500000 0x4000>; + compatible = "riscv,aclint-sswi"; + }}; + + mswi0: mswi@4400000 {{ + #interrupt-cells = <0>; + #address-cells = <0>; + interrupt-controller; + interrupts-extended = {mswi_list}; + reg = <0x4400000 0x4000>; + compatible = "riscv,aclint-mswi"; + }}; + + mtimer0: mtimer@4300000 {{ + interrupts-extended = {mtimer_list}; + reg = <0x4300000 0x8000>; + compatible = "riscv,aclint-mtimer"; }}; }}; }}; @@ -69,4 +98,4 @@ def dtsi_template (cpu_list: str, plic_list, clint_list, clock_freq): clock_freq = int(sys.argv[3]) with open(dtsi, "w") as dts: - dts.write(dtsi_template(cpu_format(harts), plic_irq_format(harts), clint_irq_format(harts), clock_freq)) + dts.write(dtsi_template(cpu_format(harts), plic_irq_format(harts), sswi_irq_format(harts), mswi_irq_format(harts), mtimer_irq_format(harts), clock_freq))