From e6b0addbbac7b0517a196e8cb04d647b9ce9b9d7 Mon Sep 17 00:00:00 2001 From: Mes Date: Sat, 14 Dec 2024 16:37:23 +0800 Subject: [PATCH] Replace CLINT with preliminary ACLINT support Based on the implementation of CLINT, a preliminary ACLINT is implemented, including the basic logic for operating `mtimer`, `mswi`, and `sswi`. CLINT was replaced with ACLINT, and the old CLINT implementation was removed entirely. Currently, due to the lack of implementation, the introduced ACLINT uses only supervisor-level IPI. Therefore, although the logic for mswi is implemented, it is not being used at the moment. The implementation can be tested by `make check SMP=n`, where n is the number of harts you want to simulate. --- Makefile | 2 +- aclint.c | 220 ++++++++++++++++++++++++++++++++++++++++ clint.c | 96 ------------------ device.h | 73 +++++++++---- main.c | 56 +++++++--- riscv.c | 2 +- scripts/gen-hart-dts.py | 49 +++++++-- 7 files changed, 355 insertions(+), 143 deletions(-) create mode 100644 aclint.c delete mode 100644 clint.c diff --git a/Makefile b/Makefile index a21e781..24980d4 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 0000000..605463f --- /dev/null +++ b/aclint.c @@ -0,0 +1,220 @@ +#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) +{ + /** + * @brief `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) +{ + /** + * @brief 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. + * + */ + + /* 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) +{ + /** + * @brief `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 fd3c8e9..0000000 --- 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 489cae6..23d195a 100644 --- a/device.h +++ b/device.h @@ -172,27 +172,59 @@ 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]; - semu_timer_t mtime; -} clint_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); + uint64_t mtimecmp[4095]; // Machine Timer Compare array + semu_timer_t mtime; // Machine Timer Counter +} mtimer_state_t; + +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); + +/* ACLINT MSWI */ +typedef struct { + uint32_t msip[4096]; // Machine Software Interrupt Pending array +} mswi_state_t; -/* memory mapping */ +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 { + uint32_t ssip[4096]; // Supervisor Software Interrupt Pending array +} 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 +237,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 39935f8..42369c6 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}; @@ -526,7 +549,7 @@ 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); + semu_timer_init(&emu.mtimer.mtime, CLOCK_FREQ); /* Set up RAM */ emu.ram = mmap(NULL, RAM_SIZE, PROT_READ | PROT_WRITE, @@ -616,6 +639,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 069aeeb..c3fd394 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 22df971..03cebbc 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))